using Aurora; using Aurora.Api; using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Text; using System.Threading.Tasks; using System.Windows; namespace AuroraACS { class IOMonitor { private readonly Logger Log = new Logger("IOMonitor"); private static IOMonitor _instance; private IOMonitor() { _ioInSubject = new Subject(); _ioOutSubject = new Subject(); BitInputs = DIN.GetAllInfo().ToArray(); BitOutputs = DOUT.GetAllInfo().ToArray(); } public static IOMonitor GetInstance() { if (_instance == null) _instance = new IOMonitor(); return _instance; } private AddrInfo[] BitInputs; private AddrInfo[] BitOutputs; private ACSIOEx _dio; private IDisposable _ioMonitor; private IDisposable _ioOutMonitor; private Subject _ioInSubject; // Address, value private Subject _ioOutSubject; // Address, value public IObservable IO_Observable => _ioInSubject.AsObservable(); public IObservable IOOut_Observable => _ioOutSubject.AsObservable(); private int[] _prevInputPorts = new int[IOItem.NUM_PORTS]; private int[] _prevOutputPorts = new int[IOItem.NUM_PORTS]; private IOItem _ioInItem = new IOItem(); private IOOutItem _ioOutItem = new IOOutItem(); public void Init(ACSIOEx dio) { _dio = dio; var active = Application.Current.AuroraEnv().EventObservable.AsObservable(AuroraSysEvent.SYS_DEVICE_ACTIVATED); var deactive = Application.Current.AuroraEnv().EventObservable.AsObservable(AuroraSysEvent.SYS_DEVICE_DEACTIVATED); Observable.Merge(active, deactive) .Where(msg => msg.From == dio.DeviceId) .Subscribe(msg => DevActivated(msg)); _ioMonitor = Observable.Interval(TimeSpan.FromMilliseconds(1)) .Where(x => _dio.IsActivated) .Where(tick => GetIO()) .Subscribe(_ => { _ioInItem.IsInit = false; _ioInSubject.OnNext(_ioInItem); }); _ioOutMonitor = Observable.Interval(TimeSpan.FromMilliseconds(1)) .Where(x => _dio.IsActivated) .Where(tick => GetIOOut()) .Subscribe(_ => { _ioOutItem.IsInit = false; _ioOutSubject.OnNext(_ioOutItem); }); } private void DevActivated(EventMessage msg) { if (msg.From == _dio.DeviceId) { if (msg.Subject == AuroraSysEvent.SYS_DEVICE_ACTIVATED) { Log.D("Init start."); _initializing = true; if (GetIOAll()) { _ioInItem.IsInit = true; _ioInSubject.OnNext(_ioInItem); _ioOutItem.IsInit = true; _ioOutSubject.OnNext(_ioOutItem); //System.Threading.Thread.Sleep(50); } _initializing = false; Log.D("Init end."); } } } bool _initializing = false; private bool GetIO() { if (_initializing == true) return false; //int k = 0; bool changed = false; int[] val = _dio.GetInputAll(); if (val == null) return false; for (int i = 0; i < IOItem.NUM_PORTS; i++) { int nodeAddr = (i << 4); CalcIO(ref _prevInputPorts[i], val[i], nodeAddr, _ioInItem.IsChanged, _ioInItem.ChangedVaslues, ref changed, BitInputs); } return changed; } private bool GetIOOut() { if (_initializing == true) return false; //int k = 0; bool changed = false; int[] outVal = _dio.GetOutputAll(); if (outVal == null) return false; for (int i = 0; i < IOItem.NUM_PORTS; i++) { int nodeAddr = (i << 4); CalcIO(ref _prevOutputPorts[i], outVal[i], nodeAddr, _ioOutItem.IsOutChanged, _ioOutItem.ChangedOutVaslues, ref changed, BitOutputs); } return changed; } private void CalcIO(ref int _prevport, int val, int nodeAddr, bool[] isChanged, byte[] changedVals, ref bool changed, AddrInfo[] addrInfos) { if (_prevport != val) { // Port보고 다르면... 세부적으로 언 놈 다른지 보고 날리기... var diff = _prevport ^ val; for (int j = 0; j < 8; j++) { int addr = nodeAddr + j; isChanged[addr] = false; if ((diff & (1 << j)) != 0) { // 참이면 changed = true; isChanged[addr] = true; changedVals[addr] = (byte)((val >> j) & 1); } } _prevport = val; } else { for (int j = 0; j < 8; j++) { int addr = nodeAddr + j; isChanged[addr] = false; } _prevport = val; } if (changed) { UpdateValues(isChanged, changedVals, addrInfos); } } private unsafe void UpdateValues(bool[] isChanged, byte[] changedVals, AddrInfo[] addrInfos) { fixed (bool* ptrIsChanged = isChanged) fixed (byte* ptrChangedVals = changedVals) { for (int i = 0; i < addrInfos.Length; i++) { if (isChanged[addrInfos[i].Idx]) addrInfos[i].Value = changedVals[addrInfos[i].Idx]; } } } private bool GetIOAll() { try { _ioInItem.Data = _dio.GetInputAll(); _ioOutItem.Data = _dio.GetOutputAll(); if (_ioInItem.Data == null || _ioOutItem.Data == null) return false; for (int i = 0; i < IOItem.NUM_PORTS; i++) { int nodeAddr = (i << 4); for (int j = 0; j < 8; j++) { int addr = nodeAddr + j; _ioInItem.IsChanged[addr] = true; _ioInItem.ChangedVaslues[addr] = (byte)((_ioInItem.Data[i] >> j) & 1); _ioOutItem.IsOutChanged[addr] = true; _ioOutItem.ChangedOutVaslues[addr] = (byte)((_ioOutItem.Data[i] >> j) & 1); } } return true; } catch (Exception exc) { return false; } } } public class IOItem { private object lockObj = new object(); public bool IsInit = false; public const int NUM_PORTS = 64; public byte[] ChangedVaslues = new byte[1040]; public bool[] IsChanged = new bool[1040]; private int[] data = new int[NUM_PORTS]; public int[] Data { get => data; set { lock (lockObj) { data = value; } } } } public class IOOutItem { private object lockObj = new object(); public bool IsInit = false; public const int NUM_PORTS = 64; public byte[] ChangedOutVaslues = new byte[1040]; public bool[] IsOutChanged = new bool[1040]; private int[] data = new int[NUM_PORTS]; public int[] Data { get => data; set { lock (lockObj) { data = value; } } } } }