Mini8Communcator_Modbus_FluentModbus.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. using GeneralData;
  2. using HardwareData;
  3. using Mini8Communicator;
  4. using Mini8CommunicatorData;
  5. using ModBusTcp;
  6. using ProtocalGeneral;
  7. using System.Collections.Concurrent;
  8. using Universal;
  9. namespace Mini8CommunicatorModbus;
  10. public class Mini8Communcator_Modbus_FluentModbus : IMini8Communicator, ITcpConnectNority
  11. {
  12. //Mini8 can not handle commands faster than this interval frequency (~200ms)
  13. private int _interval = 200;
  14. //Modbus Max request length is 125
  15. private ushort _groupeCount = 125;
  16. private ushort _totalCount = 500;
  17. //The first Address of Mini8 communication is 15500
  18. private ushort _startIndex = 15500;
  19. //A mini8 Data Area for Limits
  20. private ushort _limitCount = 64;
  21. private ushort _limitStartIndex = 5193;
  22. private int _port = -1;
  23. private string _ip = string.Empty;
  24. private IDictionary<byte, ChannelAddress>? _addresses;
  25. private readonly ConcurrentDictionary<byte, Mini8Output> _outputCache = [];
  26. private Timer? _updateDataTimer;
  27. private IMini8Provider? _provider;
  28. private DataSpilterByte? _spilter;
  29. private Modbus_Tcp? _modbus;
  30. bool IMini8Communicator.Initialize(IMini8Provider provider, object mini8Config, object address)
  31. {
  32. if (provider is null || this._modbus is not null)
  33. return false;
  34. if (mini8Config is not Mini8Address mini8)
  35. return false;
  36. if (address is not IDictionary<byte, ChannelAddress> addresses)
  37. return false;
  38. if (string.IsNullOrEmpty(mini8.Address) || !mini8.Port.InRange(0, 65535))
  39. return false;
  40. this._interval = mini8.Interval;
  41. this._groupeCount = mini8.GroupeCount;
  42. this._totalCount = mini8.TotalCount;
  43. this._startIndex = mini8.StartIndex;
  44. this._limitCount = mini8.LimitCount;
  45. this._limitStartIndex = mini8.LimitStartIndex;
  46. this._ip = mini8.Address;
  47. this._port = mini8.Port;
  48. this._addresses = addresses;
  49. this._provider = provider;
  50. return true;
  51. }
  52. bool IMini8Communicator.Open()
  53. {
  54. if (this._modbus is not null)
  55. return false;
  56. if (this._provider is null)
  57. return false;
  58. if (this._addresses is null)
  59. return false;
  60. if (string.IsNullOrEmpty(this._ip) || !this._port.InRange(0, 65535))
  61. return false;
  62. Modbus_Tcp modbus = new();
  63. modbus.Initialize($"Mini8Communicator_Modbus {this._ip} : {this._port}", this);
  64. if (!modbus.Open(this._ip, (ushort)this._port))
  65. return false;
  66. this._modbus = modbus;
  67. this._spilter = new DataSpilterByte(_startIndex);
  68. this._addresses.Foreach(t => _outputCache[t.Value.ChannelIndex] = new());
  69. GetMini8Output(false);
  70. return true;
  71. }
  72. bool IMini8Communicator.GetRealtimeDataFromDevice(out Dictionary<byte, Mini8Output>? data)
  73. {
  74. data = null;
  75. if (Checker.IsNull(_provider, _modbus, _addresses, _spilter, _outputCache))
  76. return false;
  77. lock (_outputCache)
  78. data = _outputCache.ToDictionary();
  79. return true;
  80. }
  81. bool IMini8Communicator.GetRealtimeLimit(out Dictionary<byte, Mini8Limit>? data)
  82. {
  83. data = null;
  84. if (Checker.IsNull(_provider, _modbus, _addresses))
  85. return false;
  86. data = [];
  87. foreach (ChannelAddress item in _addresses!.Values)
  88. {
  89. data[item.ChannelIndex] = new();
  90. byte[]? caps = this._modbus!.GetBuffer(item.Caps, 1);
  91. byte[]? floor = this._modbus.GetBuffer(item.Floor, 1);
  92. byte[]? capsWarning = this._modbus.GetBuffer(item.CapsWarning, 1);
  93. byte[]? floorWarning = this._modbus.GetBuffer(item.FloorWarning, 1);
  94. data[item.ChannelIndex].Caps = BufferToUshort(caps);
  95. data[item.ChannelIndex].Floor = BufferToUshort(floor);
  96. data[item.ChannelIndex].CapsWarning = BufferToUshort(capsWarning);
  97. data[item.ChannelIndex].FloorWarning = BufferToUshort(floorWarning);
  98. }
  99. return true;
  100. }
  101. bool IMini8Communicator.SendMini8Data(byte channelIndex, Mini8Input output)
  102. {
  103. if (Checker.IsNull(_provider, _modbus, _addresses))
  104. return false;
  105. if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
  106. return false;
  107. bool b = true;
  108. b &= this._modbus!.SetFloat(value.SetPoint, output.SetPoint);
  109. b &= this._modbus.SetFloat(value.Running_P, output.Running_P);
  110. b &= this._modbus.SetFloat(value.Running_I, output.Running_I);
  111. b &= this._modbus.SetFloat(value.Running_D, output.Running_D);
  112. b &= this._modbus.SetFloat(value.SetpointUpRate, output.SetpointUpRate);
  113. b &= this._modbus.SetFloat(value.SetpointDownRate, output.SetpointDownRate);
  114. try
  115. {
  116. b &= this._modbus.SetUshort(value.Caps, Convert.ToUInt16(output.Caps));
  117. b &= this._modbus.SetUshort(value.Floor, Convert.ToUInt16(output.Floor));
  118. b &= this._modbus.SetUshort(value.CapsWarning, Convert.ToUInt16(output.CapsWarning));
  119. b &= this._modbus.SetUshort(value.FloorWarning, Convert.ToUInt16(output.FloorWarning));
  120. }
  121. catch
  122. {
  123. }
  124. return b;
  125. }
  126. bool IMini8Communicator.EnableChannel(byte channelIndex, Inhibit inhibit)
  127. {
  128. if (Checker.IsNull(_provider, _modbus, _addresses, _spilter))
  129. return false;
  130. if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
  131. return false;
  132. return this._modbus!.SetUshort(value.Inhibit, (ushort)inhibit);
  133. }
  134. bool IMini8Communicator.EnableChannelAutoTune(byte channelIndex, bool isEnable)
  135. {
  136. if (Checker.IsNull(_provider, _modbus, _addresses, _spilter))
  137. return false;
  138. if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
  139. return false;
  140. (ActiveTuneSet set, AutotuneActive active) = isEnable switch
  141. {
  142. true => (ActiveTuneSet.AutoTune, AutotuneActive.Active),
  143. false => (ActiveTuneSet.Running, AutotuneActive.OFF),
  144. };
  145. if (!this._modbus!.SetUshort(value.ActiveTuneSet, (ushort)set))
  146. return false;
  147. Thread.Sleep(_interval);
  148. if (!this._modbus!.SetUshort(value.ActiveAutoTune, (ushort)active))
  149. return false;
  150. return true;
  151. }
  152. bool IMini8Communicator.SelectPID(byte channelIndex, ActiveTuneSet activeTuneSet)
  153. {
  154. if (Checker.IsNull(_provider, _modbus, _addresses))
  155. return false;
  156. if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
  157. return false;
  158. return this._modbus!.SetUshort(value.ActiveTuneSet, (ushort)activeTuneSet);
  159. }
  160. bool IMini8Communicator.Pause()
  161. {
  162. this._updateDataTimer?.Dispose();
  163. this._provider?.OnDisconnected(this._ip, this._port);
  164. return true;
  165. }
  166. bool IMini8Communicator.StartDataCollector()
  167. {
  168. StartDataCollection();
  169. this._provider?.OnConnected(this._ip, this._port);
  170. return true;
  171. }
  172. bool IMini8Communicator.Close()
  173. {
  174. this._updateDataTimer?.Dispose();
  175. this._modbus?.Close();
  176. this._provider = null;
  177. this._modbus = null;
  178. return true;
  179. }
  180. private bool StartDataCollection()
  181. {
  182. if (Checker.IsNull(_provider, _modbus, _addresses, _spilter))
  183. return false;
  184. _updateDataTimer?.Dispose();
  185. _updateDataTimer = new(GetMini8Output, true, 1000, 1000);
  186. return true;
  187. }
  188. private void GetMini8Output(object? requireNotify)
  189. {
  190. if (requireNotify is not bool notify)
  191. return;
  192. if (this._spilter is null || this._addresses is null)
  193. return;
  194. if (!this.TryGetMini8Data(out byte[]? data) || data is null || data.Length < _totalCount)
  195. return;
  196. this._spilter.Source = data;
  197. lock (_outputCache)
  198. foreach (ChannelAddress item in _addresses.Values)
  199. {
  200. Mini8Output output = _outputCache[item.ChannelIndex];
  201. output.CollectTime = DateTime.Now;
  202. GetMini8Input(output, item, this._spilter);
  203. if (!notify)
  204. continue;
  205. output.ChannelIndex = item.ChannelIndex;
  206. this._provider?.ChannelInfoNotify(item.ChannelIndex, output);
  207. }
  208. }
  209. private readonly byte[] _cache = new byte[1000];
  210. private bool TryGetMini8Data(out byte[]? data)
  211. {
  212. data = null;
  213. if (this._modbus is null)
  214. return false;
  215. for (int index = 0; index < _totalCount; index += _groupeCount)
  216. {
  217. byte[]? result = this._modbus?.GetBuffer((ushort)(_startIndex + index), _groupeCount);
  218. if (result is null)
  219. return false;
  220. Array.Copy(result, 0, _cache, index * 2, result.Length);
  221. Thread.Sleep(_interval);
  222. }
  223. data = this._cache;
  224. return true;
  225. }
  226. private unsafe ushort BufferToUshort(byte[]? bytes)
  227. {
  228. if (bytes is null)
  229. return ushort.MaxValue;
  230. Span<byte> sb = bytes.AsSpan<byte>();
  231. sb.Reverse();
  232. fixed (byte* pByte = sb)
  233. {
  234. ushort* pResult = (ushort*)pByte;
  235. return *pResult;
  236. }
  237. }
  238. private static void GetMini8Input(Mini8Output ref_source, ChannelAddress address, DataSpilterByte spilter)
  239. {
  240. ref_source.PV = spilter.GetFloat(address.PV);
  241. ref_source.WorkingOutput = spilter.GetFloat(address.WorkingOutput);
  242. ref_source.AutoTuneStatus = (AutoTuneStatus)spilter.GetUshort(address.AutoTuneStatus);
  243. ref_source.AutoTune_P = spilter.GetFloat(address.AutoTune_P);
  244. ref_source.AutoTune_I = spilter.GetFloat(address.AutoTune_I);
  245. ref_source.AutoTune_D = spilter.GetFloat(address.AutoTune_D);
  246. ref_source.SetPoint = spilter.GetFloat(address.SetPoint);
  247. ref_source.ActiveTuneSet = (ActiveTuneSet)spilter.GetUshort(address.ActiveTuneSet);
  248. ref_source.Running_P = spilter.GetFloat(address.Running_P);
  249. ref_source.Running_I = spilter.GetFloat(address.Running_I);
  250. ref_source.Running_D = spilter.GetFloat(address.Running_D);
  251. ref_source.SetpointUpRate = spilter.GetFloat(address.SetpointUpRate);
  252. ref_source.SetpointDownRate = spilter.GetFloat(address.SetpointDownRate);
  253. ref_source.Inhibit = (Inhibit)spilter.GetUshort(address.Inhibit);
  254. ref_source.AutotuneActive = (AutotuneActive)spilter.GetUshort(address.ActiveAutoTune);
  255. ref_source.SensorBreakAlarm = address.ChannelIndex switch
  256. {
  257. <= 8 => (TcBorken)spilter.GetBit(address.SensorBreakAlarm1, address.ChannelIndex),
  258. _ => (TcBorken)spilter.GetBit(address.SensorBreakAlarm2, address.ChannelIndex - 8),
  259. };
  260. }
  261. void ITcpConnectNority.Connect(string ip, int port)
  262. {
  263. this._provider?.OnConnected(this._ip, this._port);
  264. StartDataCollection();
  265. }
  266. void ITcpConnectNority.DisConnect(string ip, int port)
  267. {
  268. this._updateDataTimer?.Dispose();
  269. this._provider?.OnDisconnected(this._ip, this._port);
  270. }
  271. }
  272. internal class DataSpilterByte(ushort startIndex)
  273. {
  274. public byte[]? Source { get; set; }
  275. public unsafe float GetFloat(ushort address)
  276. {
  277. address -= startIndex;
  278. if (Source is null || Source.Length < address * 2 + 4)
  279. return -1f;
  280. Span<byte> buffer = Source.AsSpan<byte>().Slice(address * 2, 4);
  281. buffer.Reverse();
  282. fixed (byte* pByte = buffer)
  283. {
  284. float* pResult = (float*)pByte;
  285. return *pResult;
  286. }
  287. }
  288. public unsafe Half GetHalf(ushort address)
  289. {
  290. address -= startIndex;
  291. if (Source is null || Source.Length < address * 2 + 2)
  292. return Half.MaxValue;
  293. Span<byte> buffer = Source.AsSpan<byte>().Slice(address * 2, 2);
  294. buffer.Reverse();
  295. fixed (byte* pByte = buffer)
  296. {
  297. Half* pResult = (Half*)pByte;
  298. return *pResult;
  299. }
  300. }
  301. public unsafe ushort GetUshort(ushort address)
  302. {
  303. address -= startIndex;
  304. if (Source is null || Source.Length < address * 2 + 2)
  305. return ushort.MaxValue;
  306. Span<byte> buffer = Source.AsSpan<byte>().Slice(address * 2, 2);
  307. buffer.Reverse();
  308. fixed (byte* pByte = buffer)
  309. {
  310. ushort* pResult = (ushort*)pByte;
  311. return *pResult;
  312. }
  313. }
  314. public unsafe byte GetBit(ushort address, int index)
  315. {
  316. address -= startIndex;
  317. if (Source is null || Source.Length < address + 1)
  318. return byte.MaxValue;
  319. if (index > 8 || index < 0)
  320. return byte.MaxValue;
  321. return (byte)((Source[address * 2 + 1] >> (index - 1)) & 0b01);
  322. }
  323. }