123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902 |
- using System.Net.Sockets;
- using System.Text.RegularExpressions;
- namespace FinsTcp;
- //This is a copy of a Open library POmronFinsTCP.Net
- //I have modified tcpclient sendtimeout and receivetimeout time to make sure it can know connect status when device connect into a switch
- //Please ignore all the warnings and Messages,I know the code is in a mess, but it is working.
- //I will modified and optimize the code later - Zixuan
- //Fix all warnings and Messages and make code more readable - 2025/06/10 Zixuan
- public class FinsTcpBase : Tcp
- {
- public byte PlcNode { get; private set; }
- public byte PcNode { get; private set; }
- //
- // Summary:
- // Fins读写指令生成
- //
- // Parameters:
- // rw:
- // 读写类型
- //
- // mr:
- // 寄存器类型
- //
- // mt:
- // 地址类型
- //
- // ch:
- // 起始地址
- //
- // offset:
- // 位地址:00-15,字地址则为00
- //
- // cnt:
- // 地址个数,按位读写只能是1
- public bool Link(string rIP, int rPort = 9600, int sendTimeout = 1000, int receiveTimeout = 1000)
- {
- if (this._client is null)
- return false;
- _client.SendTimeout = sendTimeout;
- _client.ReceiveTimeout = receiveTimeout;
- _client.NoDelay = true;
- if (!_client.ConnectAsync(rIP, rPort).Wait(sendTimeout))
- return false;
- _stream = _client.GetStream();
- Thread.Sleep(10);
- if (!SendData(FinsClass.HandShakePack))
- return false;
- byte[] array = new byte[24];
- if (!ReceiveData(array))
- return false;
- if (array[15] != 0)
- return false;
- PcNode = array[19];
- PlcNode = array[23];
- return true;
- }
- public bool Close()
- {
- try
- {
- _stream?.Close();
- _client?.Close();
- return true;
- }
- catch
- {
- return false;
- }
- }
- public bool ReadWords(string mrch, short cnt, out short[]? reData)
- {
- reData = default;
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return ReadWords(plcMemory, short.Parse(txtq), cnt, out reData);
- }
- public bool ReadWords(PlcMemory mr, short ch, short cnt, out short[] reData)
- {
- reData = new short[cnt];
- int num = 30 + cnt * 2;
- byte[] array = new byte[num];
- byte[] sd = FinsClass.FinsCmd(Operation.Read, mr, MemoryType.Word, ch, 0, cnt, PlcNode, PcNode);
- if (!SendData(sd))
- return false;
- if (!ReceiveData(array))
- return false;
- bool flag = array[11] switch
- {
- 3 => ErrorCode.CheckHeadError(array[15]),
- _ => true
- };
- if (!flag)
- return false;
- if (!ErrorCode.CheckEndCode(array[28], array[29]))
- return false;
- for (int i = 0; i < cnt; i++)
- {
- byte[] value = [array[30 + i * 2 + 1], array[30 + i * 2]];
- reData[i] = BitConverter.ToInt16(value, 0);
- }
- return true;
- }
- public bool ReadWord(string mrch, out short reData)
- {
- reData = default;
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return ReadWord(plcMemory, short.Parse(txtq), out reData);
- }
- public bool ReadWord(PlcMemory mr, short ch, out short reData)
- {
- reData = 0;
- if (!ReadWords(mr, ch, 1, out var reData2))
- return false;
- reData = reData2[0];
- return true;
- }
- public bool WriteWords(string mrch, short cnt, short[] inData)
- {
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return WriteWords(plcMemory, short.Parse(txtq), cnt, inData);
- }
- public bool WriteWords(PlcMemory mr, short ch, short cnt, short[] inData)
- {
- byte[] array = new byte[30];
- byte[] array2 = FinsClass.FinsCmd(Operation.Write, mr, MemoryType.Word, ch, 0, cnt, PlcNode, PcNode);
- byte[] array3 = new byte[cnt * 2];
- for (int i = 0; i < cnt; i++)
- {
- byte[] bytes = BitConverter.GetBytes(inData[i]);
- array3[i * 2] = bytes[1];
- array3[i * 2 + 1] = bytes[0];
- }
- byte[] array4 = new byte[cnt * 2 + 34];
- array2.CopyTo(array4, 0);
- array3.CopyTo(array4, 34);
- if (!SendData(array4))
- return false;
- if (!ReceiveData(array))
- return false;
- bool flag = array[11] switch
- {
- 3 => ErrorCode.CheckHeadError(array[15]),
- _ => true
- };
- if (!flag)
- return false;
- return ErrorCode.CheckEndCode(array[28], array[29]);
- }
- public bool WriteWord(string mrch, short inData)
- {
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return WriteWord(plcMemory, short.Parse(txtq), inData);
- }
- public bool WriteWord(PlcMemory mr, short ch, short inData)
- {
- short[] inData2 = [inData];
- if (!WriteWords(mr, ch, 1, inData2))
- return false;
- return true;
- }
- public bool GetBitState(string mrch, out short bs)
- {
- bs = default;
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return GetBitState(plcMemory, txtq, out bs);
- }
- public bool GetBitStates(string mrch, out bool[]? bs, short cnt = 1)
- {
- bs = default;
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return GetBitStates(plcMemory, txtq, out bs, cnt);
- }
- public bool GetBitStates(PlcMemory mr, string ch, out bool[] bs, short cnt = 1)
- {
- bs = new bool[cnt];
- byte[] array = new byte[30 + cnt];
- short ch2 = short.Parse(ch.Split(['.'])[0]);
- short offset = short.Parse(ch.Split(['.'])[1]);
- byte[] command = FinsClass.FinsCmd(Operation.Read, mr, MemoryType.Bit, ch2, offset, cnt, PlcNode, PcNode);
- if (!SendData(command))
- return false;
- if (!ReceiveData(array))
- return false;
- bool flag = array[11] switch
- {
- 3 => ErrorCode.CheckHeadError(array[15]),
- _ => true
- };
- if (!flag)
- return false;
- if (!ErrorCode.CheckEndCode(array[28], array[29]))
- return false;
- for (int i = 0; i < cnt; i++)
- bs[i] = array[30 + i] == 1;
- return true;
- }
- public bool GetBitState(PlcMemory mr, string ch, out short bs)
- {
- bs = 0;
- byte[] array = new byte[31];
- string[] spilts = ch.Split('.');
- short ch2 = short.Parse(spilts[0]);
- short offset = short.Parse(spilts[1]);
- byte[] sd = FinsClass.FinsCmd(Operation.Read, mr, MemoryType.Bit, ch2, offset, 1, PlcNode, PcNode);
- if (!SendData(sd))
- return false;
- if (!ReceiveData(array))
- return false;
- bool flag = array[11] switch
- {
- 3 => ErrorCode.CheckHeadError(array[15]),
- _ => true
- };
- if (!flag)
- return false;
- if (!ErrorCode.CheckEndCode(array[28], array[29]))
- return false;
- bs = array[30];
- return true;
- }
- public bool SetBitState(string mrch, BitState bs)
- {
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return SetBitState(plcMemory, txtq, bs);
- }
- public bool SetBitState(PlcMemory mr, string ch, BitState bs)
- {
- byte[] array = new byte[30];
- string[] spilts = ch.Split('.');
- short ch2 = short.Parse(spilts[0]);
- short offset = short.Parse(spilts[1]);
- byte[] array2 = FinsClass.FinsCmd(Operation.Write, mr, MemoryType.Bit, ch2, offset, 1, PlcNode, PcNode);
- byte[] array3 = new byte[35];
- array2.CopyTo(array3, 0);
- array3[34] = (byte)bs;
- if (!SendData(array3))
- return false;
- if (!ReceiveData(array))
- return false;
- bool flag = array[11] switch
- {
- 3 => ErrorCode.CheckHeadError(array[15]),
- _ => true
- };
- if (!flag)
- return false;
- if (!ErrorCode.CheckEndCode(array[28], array[29]))
- return false;
- return true;
- }
- public bool ReadReal(string mrch, out float reData)
- {
- reData = default;
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return ReadReal(plcMemory, short.Parse(txtq), out reData);
- }
- public bool ReadReal(PlcMemory mr, short ch, out float reData)
- {
- reData = 0f;
- int num = 34;
- byte[] array = new byte[num];
- byte[] sd = FinsClass.FinsCmd(Operation.Read, mr, MemoryType.Word, ch, 0, 2, PlcNode, PcNode);
- if (!SendData(sd))
- return false;
- if (!ReceiveData(array))
- return false;
- bool flag = array[11] switch
- {
- 3 => ErrorCode.CheckHeadError(array[15]),
- _ => true
- };
- if (!flag)
- return false;
- if (!ErrorCode.CheckEndCode(array[28], array[29]))
- return false;
- byte[] value = [array[31], array[30], array[33], array[32]];
- reData = BitConverter.ToSingle(value, 0);
- return true;
- }
- public bool WriteReal(string mrch, float reData)
- {
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return WriteReal(plcMemory, short.Parse(txtq), reData);
- }
- public bool WriteReal(PlcMemory mr, short ch, float reData)
- {
- if (BitConverter.GetBytes(reData) is not byte[] bytes)
- return false;
- short[] array = new short[2];
- array[0] = BitConverter.ToInt16(bytes, 0);
- if (bytes.Length > 2)
- array[1] = BitConverter.ToInt16(bytes, 2);
- return WriteWords(mr, ch, 2, array);
- }
- public bool ReadInt32(string mrch, out int reData)
- {
- reData = default;
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return ReadInt32(plcMemory, short.Parse(txtq), out reData);
- }
- public bool ReadInt32(PlcMemory mr, short ch, out int reData)
- {
- reData = 0;
- int num = 34;
- byte[] array = new byte[num];
- byte[] sd = FinsClass.FinsCmd(Operation.Read, mr, MemoryType.Word, ch, 0, 2, PlcNode, PcNode);
- if (!SendData(sd))
- return false;
- if (!ReceiveData(array))
- return false;
- bool flag = array[11] switch
- {
- 3 => ErrorCode.CheckHeadError(array[15]),
- _ => true
- };
- if (!flag)
- return false;
- if (!ErrorCode.CheckEndCode(array[28], array[29]))
- return false;
- byte[] value = [array[31], array[30], array[33], array[32]];
- reData = BitConverter.ToInt32(value, 0);
- return true;
- }
- public bool WriteInt32(string mrch, int reData)
- {
- if (!ConvertClass.GetPlcMemory(mrch, out string txtq, out PlcMemory plcMemory))
- return false;
- return WriteInt32(plcMemory, short.Parse(txtq), reData);
- }
- public bool WriteInt32(PlcMemory mr, short ch, int reData)
- {
- if (BitConverter.GetBytes(reData) is not byte[] bytes)
- return false;
- short[] array = new short[2];
- array[0] = BitConverter.ToInt16(bytes, 0);
- if (bytes.Length > 2)
- array[1] = BitConverter.ToInt16(bytes, 2);
- return WriteWords(mr, ch, 2, array);
- }
- }
- public class Tcp
- {
- public Tcp()
- {
- this._client = new();
- }
- protected readonly TcpClient _client;
- protected NetworkStream? _stream;
- protected bool SendData(byte[] sd)
- {
- if (_stream == null)
- return false;
- try
- {
- _stream.Write(sd, 0, sd.Length);
- return true;
- }
- catch
- {
- return false;
- }
- }
- protected bool ReceiveData(byte[] rd)
- {
- if (_stream == null)
- return false;
- try
- {
- int num = 0;
- do
- {
- int num2 = _stream.Read(rd, num, rd.Length - num);
- if (num2 == 0)
- return false;
- num += num2;
- }
- while (num < rd.Length);
- return true;
- }
- catch
- {
- return false;
- }
- }
- }
- internal class ConvertClass
- {
- //
- // Summary:
- // 得到枚举值
- //
- // Parameters:
- // txt:
- // 如:D100,W100.1
- //
- // txtq:100.1
- internal static bool GetPlcMemory(string txt, out string txtq, out PlcMemory plcMemory)
- {
- txtq = string.Empty;
- char c = txt.Trim().ToUpper().FirstOrDefault();
- plcMemory = c switch
- {
- 'D' => PlcMemory.DM,
- 'W' => PlcMemory.WR,
- 'H' => PlcMemory.HR,
- 'A' => PlcMemory.AR,
- 'C' => PlcMemory.CNT,
- 'I' => PlcMemory.CIO,
- 'T' => PlcMemory.TIM,
- _ => PlcMemory.Undefined,
- };
- if (plcMemory == PlcMemory.Undefined)
- return false;
- txtq = Regex.Replace(txt, "[^0-9.]", "");
- return true;
- }
- }
- internal class ErrorCode
- {
- //
- // Summary:
- // (若返回的头指令为3)检查命令头中的错误代码
- //
- // Parameters:
- // Code:
- // 错误代码
- //
- // Returns:
- // 指示程序是否可以继续进行
- internal static bool CheckHeadError(byte Code)
- {
- Console.WriteLine($"Error Code {Code}");
- return Code == 0;
- }
- //
- // Summary:
- // 检查命令帧中的EndCode
- //
- // Parameters:
- // Main:
- // 主码
- //
- // Sub:
- // 副码
- //
- // Returns:
- // 指示程序是否可以继续进行
- internal static bool CheckEndCode(byte Main, byte Sub)
- {
- //Totally dont understand what's doing here, Replace with the code below
- return Main == 0 && (Sub == 64 || Sub == 0);
- //switch (Main)
- //{
- // case 0:
- // switch (Sub)
- // {
- // case 0:
- // case 64:
- // return true;
- // case 1:
- // return false;
- // }
- // break;
- // case 1:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 4:
- // case 5:
- // case 6:
- // return false;
- // }
- // break;
- // case 2:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 4:
- // case 5:
- // return false;
- // }
- // break;
- // case 3:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 4:
- // return false;
- // }
- // break;
- // case 4:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // return false;
- // }
- // break;
- // case 5:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 4:
- // return false;
- // }
- // break;
- // case 16:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 4:
- // case 5:
- // return false;
- // }
- // break;
- // case 17:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 4:
- // case 6:
- // case 9:
- // case 10:
- // case 11:
- // case 12:
- // return false;
- // }
- // break;
- // case 32:
- // switch (Sub)
- // {
- // case 2:
- // case 3:
- // case 4:
- // case 5:
- // case 6:
- // case 7:
- // return false;
- // }
- // break;
- // case 33:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 5:
- // case 6:
- // case 7:
- // case 8:
- // return false;
- // }
- // break;
- // case 34:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // case 4:
- // case 5:
- // case 6:
- // case 7:
- // case 8:
- // return false;
- // }
- // break;
- // case 35:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 3:
- // return false;
- // }
- // break;
- // case 36:
- // {
- // byte b5 = Sub;
- // byte b6 = b5;
- // if (b6 != 1)
- // {
- // break;
- // }
- // return false;
- // }
- // case 37:
- // switch (Sub)
- // {
- // case 2:
- // case 3:
- // case 4:
- // case 5:
- // case 6:
- // case 7:
- // case 9:
- // case 10:
- // case 13:
- // case 15:
- // case 16:
- // return false;
- // }
- // break;
- // case 38:
- // switch (Sub)
- // {
- // case 1:
- // case 2:
- // case 4:
- // case 5:
- // case 6:
- // case 7:
- // case 8:
- // case 9:
- // case 10:
- // case 11:
- // return false;
- // }
- // break;
- // case 48:
- // {
- // byte b3 = Sub;
- // byte b4 = b3;
- // if (b4 != 1)
- // {
- // break;
- // }
- // return false;
- // }
- // case 64:
- // {
- // byte b = Sub;
- // byte b2 = b;
- // if (b2 != 1)
- // {
- // break;
- // }
- // return false;
- // }
- //}
- //return false;
- }
- }
- internal class FinsClass
- {
- //
- // Summary:
- // 获取内存区码
- //
- // Parameters:
- // mr:
- // 寄存器类型
- //
- // mt:
- // 地址类型
- internal static byte GetMemoryCode(PlcMemory mr, MemoryType mt)
- {
- if (mt == MemoryType.Bit)
- {
- return mr switch
- {
- PlcMemory.CIO => 48,
- PlcMemory.WR => 49,
- PlcMemory.HR => 50,
- PlcMemory.AR => 51,
- PlcMemory.DM => 2,
- PlcMemory.CNT or PlcMemory.TIM => 9,
- _ => 0,
- };
- }
- return mr switch
- {
- PlcMemory.CIO => 176,
- PlcMemory.WR => 177,
- PlcMemory.HR => 178,
- PlcMemory.AR => 179,
- PlcMemory.DM => 130,
- PlcMemory.CNT or PlcMemory.TIM => 137,
- _ => 0,
- };
- }
- internal static byte[] FinsCmd(Operation rw, PlcMemory mr, MemoryType mt, short ch, short offset, short cnt, byte PlcNode, byte PcNode)
- {
- byte[] array =
- [
- 70, 73, 78, 83, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0
- ];
- if (rw == Operation.Read)
- {
- array[6] = 0;
- array[7] = 26;
- }
- else if (mt == MemoryType.Word)
- {
- array[6] = (byte)((cnt * 2 + 26) / 256);
- array[7] = (byte)((cnt * 2 + 26) % 256);
- }
- else
- {
- array[6] = 0;
- array[7] = 27;
- }
- array[11] = 2;
- array[16] = 128;
- array[18] = 2;
- array[20] = PlcNode;
- array[23] = PcNode;
- array[25] = byte.MaxValue;
- if (rw == Operation.Read)
- {
- array[26] = 1;
- array[27] = 1;
- }
- else
- {
- array[26] = 1;
- array[27] = 2;
- }
- array[28] = FinsClass.GetMemoryCode(mr, mt);
- if (mr == PlcMemory.CNT)
- {
- array[29] = (byte)(ch / 256 + 128);
- array[30] = (byte)(ch % 256);
- }
- else
- {
- array[29] = (byte)(ch / 256);
- array[30] = (byte)(ch % 256);
- array[31] = (byte)offset;
- }
- array[32] = (byte)(cnt / 256);
- array[33] = (byte)(cnt % 256);
- return array;
- }
- public static byte[] Header = [0x46, 0x49, 0x4E, 0x53];
- public static byte[] HandShakePack =
- [
- 0x46, 0x49, 0x4E, 0x53, //Header
- 0x00, 0x00, 0x00, 0x0c, //Length
- 0x00, 0x00, 0x00, 0x00, //Command
- 0x00, 0x00, 0x00, 0x00, //ErrorCode
- 0x00, 0x00, 0x00, 0x00 //Client Node
- ];
- }
- public enum PlcMemory
- {
- CIO,
- WR,
- HR,
- AR,
- DM,
- CNT,
- TIM,
- Undefined,
- }
- public enum Operation
- {
- Read,
- Write
- }
- public enum MemoryType
- {
- Bit,
- Word
- }
- public enum BitState
- {
- ON = 1,
- OFF = 0
- }
|