123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- using GeneralData;
- using HardwareData;
- using Mini8Communicator;
- using Mini8CommunicatorData;
- using ModBusTcp;
- using ProtocalGeneral;
- using System.Collections.Concurrent;
- using Universal;
- namespace Mini8CommunicatorModbus;
- public class Mini8Communcator_Modbus_FluentModbus : IMini8Communicator, ITcpConnectNority
- {
- //Mini8 can not handle commands faster than this interval frequency (~200ms)
- private int _interval = 200;
- //Modbus Max request length is 125
- private ushort _groupeCount = 125;
- private ushort _totalCount = 500;
- //The first Address of Mini8 communication is 15500
- private ushort _startIndex = 15500;
- //A mini8 Data Area for Limits
- private ushort _limitCount = 64;
- private ushort _limitStartIndex = 5193;
- private int _port = -1;
- private string _ip = string.Empty;
- private IDictionary<byte, ChannelAddress>? _addresses;
- private readonly ConcurrentDictionary<byte, Mini8Output> _outputCache = [];
- private Timer? _updateDataTimer;
- private IMini8Provider? _provider;
- private DataSpilterByte? _spilter;
- private Modbus_Tcp? _modbus;
- bool IMini8Communicator.Initialize(IMini8Provider provider, object mini8Config, object address)
- {
- if (provider is null || this._modbus is not null)
- return false;
- if (mini8Config is not Mini8Address mini8)
- return false;
- if (address is not IDictionary<byte, ChannelAddress> addresses)
- return false;
- if (string.IsNullOrEmpty(mini8.Address) || !mini8.Port.InRange(0, 65535))
- return false;
- this._interval = mini8.Interval;
- this._groupeCount = mini8.GroupeCount;
- this._totalCount = mini8.TotalCount;
- this._startIndex = mini8.StartIndex;
- this._limitCount = mini8.LimitCount;
- this._limitStartIndex = mini8.LimitStartIndex;
- this._ip = mini8.Address;
- this._port = mini8.Port;
- this._addresses = addresses;
- this._provider = provider;
- return true;
- }
- bool IMini8Communicator.Open()
- {
- if (this._modbus is not null)
- return false;
- if (this._provider is null)
- return false;
- if (this._addresses is null)
- return false;
- if (string.IsNullOrEmpty(this._ip) || !this._port.InRange(0, 65535))
- return false;
- Modbus_Tcp modbus = new();
- modbus.Initialize($"Mini8Communicator_Modbus {this._ip} : {this._port}", this);
- if (!modbus.Open(this._ip, (ushort)this._port))
- return false;
- this._modbus = modbus;
- this._spilter = new DataSpilterByte(_startIndex);
- this._addresses.Foreach(t => _outputCache[t.Value.ChannelIndex] = new());
- GetMini8Output(false);
- return true;
- }
- bool IMini8Communicator.GetRealtimeDataFromDevice(out Dictionary<byte, Mini8Output>? data)
- {
- data = null;
- if (Checker.IsNull(_provider, _modbus, _addresses, _spilter, _outputCache))
- return false;
- lock (_outputCache)
- data = _outputCache.ToDictionary();
- return true;
- }
- bool IMini8Communicator.GetRealtimeLimit(out Dictionary<byte, Mini8Limit>? data)
- {
- data = null;
- if (Checker.IsNull(_provider, _modbus, _addresses))
- return false;
- data = [];
- foreach (ChannelAddress item in _addresses!.Values)
- {
- data[item.ChannelIndex] = new();
- byte[]? caps = this._modbus!.GetBuffer(item.Caps, 1);
- byte[]? floor = this._modbus.GetBuffer(item.Floor, 1);
- byte[]? capsWarning = this._modbus.GetBuffer(item.CapsWarning, 1);
- byte[]? floorWarning = this._modbus.GetBuffer(item.FloorWarning, 1);
- data[item.ChannelIndex].Caps = BufferToUshort(caps);
- data[item.ChannelIndex].Floor = BufferToUshort(floor);
- data[item.ChannelIndex].CapsWarning = BufferToUshort(capsWarning);
- data[item.ChannelIndex].FloorWarning = BufferToUshort(floorWarning);
- }
- return true;
- }
- bool IMini8Communicator.SendMini8Data(byte channelIndex, Mini8Input output)
- {
- if (Checker.IsNull(_provider, _modbus, _addresses))
- return false;
- if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
- return false;
- bool b = true;
- b &= this._modbus!.SetFloat(value.SetPoint, output.SetPoint);
- b &= this._modbus.SetFloat(value.Running_P, output.Running_P);
- b &= this._modbus.SetFloat(value.Running_I, output.Running_I);
- b &= this._modbus.SetFloat(value.Running_D, output.Running_D);
- b &= this._modbus.SetFloat(value.SetpointUpRate, output.SetpointUpRate);
- b &= this._modbus.SetFloat(value.SetpointDownRate, output.SetpointDownRate);
- try
- {
- b &= this._modbus.SetUshort(value.Caps, Convert.ToUInt16(output.Caps));
- b &= this._modbus.SetUshort(value.Floor, Convert.ToUInt16(output.Floor));
- b &= this._modbus.SetUshort(value.CapsWarning, Convert.ToUInt16(output.CapsWarning));
- b &= this._modbus.SetUshort(value.FloorWarning, Convert.ToUInt16(output.FloorWarning));
- }
- catch
- {
- }
- return b;
- }
- bool IMini8Communicator.EnableChannel(byte channelIndex, Inhibit inhibit)
- {
- if (Checker.IsNull(_provider, _modbus, _addresses, _spilter))
- return false;
- if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
- return false;
- return this._modbus!.SetUshort(value.Inhibit, (ushort)inhibit);
- }
- bool IMini8Communicator.EnableChannelAutoTune(byte channelIndex, bool isEnable)
- {
- if (Checker.IsNull(_provider, _modbus, _addresses, _spilter))
- return false;
- if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
- return false;
- (ActiveTuneSet set, AutotuneActive active) = isEnable switch
- {
- true => (ActiveTuneSet.AutoTune, AutotuneActive.Active),
- false => (ActiveTuneSet.Running, AutotuneActive.OFF),
- };
- if (!this._modbus!.SetUshort(value.ActiveTuneSet, (ushort)set))
- return false;
- Thread.Sleep(_interval);
- if (!this._modbus!.SetUshort(value.ActiveAutoTune, (ushort)active))
- return false;
- return true;
- }
- bool IMini8Communicator.SelectPID(byte channelIndex, ActiveTuneSet activeTuneSet)
- {
- if (Checker.IsNull(_provider, _modbus, _addresses))
- return false;
- if (!this._addresses!.TryGetValueNotNull(channelIndex, out ChannelAddress value))
- return false;
- return this._modbus!.SetUshort(value.ActiveTuneSet, (ushort)activeTuneSet);
- }
- bool IMini8Communicator.Pause()
- {
- this._updateDataTimer?.Dispose();
- this._provider?.OnDisconnected(this._ip, this._port);
- return true;
- }
- bool IMini8Communicator.StartDataCollector()
- {
- StartDataCollection();
- this._provider?.OnConnected(this._ip, this._port);
- return true;
- }
- bool IMini8Communicator.Close()
- {
- this._updateDataTimer?.Dispose();
- this._modbus?.Close();
- this._provider = null;
- this._modbus = null;
- return true;
- }
- private bool StartDataCollection()
- {
- if (Checker.IsNull(_provider, _modbus, _addresses, _spilter))
- return false;
- _updateDataTimer?.Dispose();
- _updateDataTimer = new(GetMini8Output, true, 1000, 1000);
- return true;
- }
- private void GetMini8Output(object? requireNotify)
- {
- if (requireNotify is not bool notify)
- return;
- if (this._spilter is null || this._addresses is null)
- return;
- if (!this.TryGetMini8Data(out byte[]? data) || data is null || data.Length < _totalCount)
- return;
- this._spilter.Source = data;
- lock (_outputCache)
- foreach (ChannelAddress item in _addresses.Values)
- {
- Mini8Output output = _outputCache[item.ChannelIndex];
- output.CollectTime = DateTime.Now;
- GetMini8Input(output, item, this._spilter);
- if (!notify)
- continue;
- output.ChannelIndex = item.ChannelIndex;
- this._provider?.ChannelInfoNotify(item.ChannelIndex, output);
- }
- }
- private readonly byte[] _cache = new byte[1000];
- private bool TryGetMini8Data(out byte[]? data)
- {
- data = null;
- if (this._modbus is null)
- return false;
- for (int index = 0; index < _totalCount; index += _groupeCount)
- {
- byte[]? result = this._modbus?.GetBuffer((ushort)(_startIndex + index), _groupeCount);
- if (result is null)
- return false;
- Array.Copy(result, 0, _cache, index * 2, result.Length);
- Thread.Sleep(_interval);
- }
- data = this._cache;
- return true;
- }
- private unsafe ushort BufferToUshort(byte[]? bytes)
- {
- if (bytes is null)
- return ushort.MaxValue;
- Span<byte> sb = bytes.AsSpan<byte>();
- sb.Reverse();
- fixed (byte* pByte = sb)
- {
- ushort* pResult = (ushort*)pByte;
- return *pResult;
- }
- }
- private static void GetMini8Input(Mini8Output ref_source, ChannelAddress address, DataSpilterByte spilter)
- {
- ref_source.PV = spilter.GetFloat(address.PV);
- ref_source.WorkingOutput = spilter.GetFloat(address.WorkingOutput);
- ref_source.AutoTuneStatus = (AutoTuneStatus)spilter.GetUshort(address.AutoTuneStatus);
- ref_source.AutoTune_P = spilter.GetFloat(address.AutoTune_P);
- ref_source.AutoTune_I = spilter.GetFloat(address.AutoTune_I);
- ref_source.AutoTune_D = spilter.GetFloat(address.AutoTune_D);
- ref_source.SetPoint = spilter.GetFloat(address.SetPoint);
- ref_source.ActiveTuneSet = (ActiveTuneSet)spilter.GetUshort(address.ActiveTuneSet);
- ref_source.Running_P = spilter.GetFloat(address.Running_P);
- ref_source.Running_I = spilter.GetFloat(address.Running_I);
- ref_source.Running_D = spilter.GetFloat(address.Running_D);
- ref_source.SetpointUpRate = spilter.GetFloat(address.SetpointUpRate);
- ref_source.SetpointDownRate = spilter.GetFloat(address.SetpointDownRate);
- ref_source.Inhibit = (Inhibit)spilter.GetUshort(address.Inhibit);
- ref_source.AutotuneActive = (AutotuneActive)spilter.GetUshort(address.ActiveAutoTune);
- ref_source.SensorBreakAlarm = address.ChannelIndex switch
- {
- <= 8 => (TcBorken)spilter.GetBit(address.SensorBreakAlarm1, address.ChannelIndex),
- _ => (TcBorken)spilter.GetBit(address.SensorBreakAlarm2, address.ChannelIndex - 8),
- };
- }
- void ITcpConnectNority.Connect(string ip, int port)
- {
- this._provider?.OnConnected(this._ip, this._port);
- StartDataCollection();
- }
- void ITcpConnectNority.DisConnect(string ip, int port)
- {
- this._updateDataTimer?.Dispose();
- this._provider?.OnDisconnected(this._ip, this._port);
- }
- }
- internal class DataSpilterByte(ushort startIndex)
- {
- public byte[]? Source { get; set; }
- public unsafe float GetFloat(ushort address)
- {
- address -= startIndex;
- if (Source is null || Source.Length < address * 2 + 4)
- return -1f;
- Span<byte> buffer = Source.AsSpan<byte>().Slice(address * 2, 4);
- buffer.Reverse();
- fixed (byte* pByte = buffer)
- {
- float* pResult = (float*)pByte;
- return *pResult;
- }
- }
- public unsafe Half GetHalf(ushort address)
- {
- address -= startIndex;
- if (Source is null || Source.Length < address * 2 + 2)
- return Half.MaxValue;
- Span<byte> buffer = Source.AsSpan<byte>().Slice(address * 2, 2);
- buffer.Reverse();
- fixed (byte* pByte = buffer)
- {
- Half* pResult = (Half*)pByte;
- return *pResult;
- }
- }
- public unsafe ushort GetUshort(ushort address)
- {
- address -= startIndex;
- if (Source is null || Source.Length < address * 2 + 2)
- return ushort.MaxValue;
- Span<byte> buffer = Source.AsSpan<byte>().Slice(address * 2, 2);
- buffer.Reverse();
- fixed (byte* pByte = buffer)
- {
- ushort* pResult = (ushort*)pByte;
- return *pResult;
- }
- }
- public unsafe byte GetBit(ushort address, int index)
- {
- address -= startIndex;
- if (Source is null || Source.Length < address + 1)
- return byte.MaxValue;
- if (index > 8 || index < 0)
- return byte.MaxValue;
- return (byte)((Source[address * 2 + 1] >> (index - 1)) & 0b01);
- }
- }
|