using ACS.SPiiPlusNET; using Aurora.Api; using Aurora.Api.Devices; using Hermes; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace AuroraACS { public class ACSIO : Device { private readonly Logger Log = new Logger("ACSDIO"); ACSDevice _ACS; ACSIOConfig _Config; // For Simulation Buffer public int[] BuffDIN { get; set; } public int[] BuffDOUT { get; set; } public List BitInputs { get; private set; } public List BitOutputs { get; private set; } public List InputBuffer { get; set; } public List OutputBuffer { get; set; } private int _failCount = 0; #region Buffer Infos. For 3rdParty - BeckHoff private string EC_DIN = "EC_DIN"; private string EC_DOUT = "EC_DOUT"; private string EC_AIN = "EC_AIN"; private string EC_AOUT = "EC_AOUT"; #endregion private object _LockInputObject = new object(); private object _LockOutputObject = new object(); private eIOType _Type; private CancellationTokenSource _cts; public ACSIO(ACSDevice acs) { Type = DeviceType.Etc; _ACS = acs; } private void Init() { } protected override int DoActivate(IDeviceConfig config) { _Config = config as ACSIOConfig; if (!_ACS.IsActivated) { return E1.NETWORK; } IsSimul = _ACS.IsSimul; _Type = _Config.Type; if (_Type == eIOType.BeckHoff) { EC_DIN = _Config.InputIOName; EC_DOUT = _Config.OutputIOName; } BuffDIN = new int[_Config.IO_INPUT_BOARD_NUM]; BuffDOUT = new int[_Config.IO_OUTPUT_BOARD_NUM]; BitInputs = _Config.InputList; BitOutputs = _Config.OutputList; InputBuffer = new List(_Config.IO_INPUT_BOARD_NUM * _Config.IO_COUNT); OutputBuffer = new List(_Config.IO_OUTPUT_BOARD_NUM * _Config.IO_COUNT); StartMonitoring(500); return E1.OK; } public void StartMonitoring(int intervalMs = 100) { _failCount = 0; _cts = new CancellationTokenSource(); _ = Task.Run(() => MonitoringLoop(intervalMs, _cts.Token)); } private async Task MonitoringLoop(int intervalMs, CancellationToken token) { while (!token.IsCancellationRequested) { try { if (_failCount > _Config.RetryCount) { Deactivate(); MessageBox.Show("ACS IO Interface DeActivate"); } ReadInputIOAll(); ReadOutputIOAll(); } catch (ACSException ex) { Console.WriteLine($"ACS IO 상태 갱신 실패: {ex.Message}"); _failCount++; } await Task.Delay(intervalMs, token); } } public void StopMonitoring() { _cts?.Cancel(); } protected override int DoDeactivate() { StopMonitoring(); return E1.OK; } private bool CheckInputRange(int port, int bit = 0) { if (CheckInputRange(port)) return false; if (CheckBit(bit)) return false; return true; } private bool CheckOutputRange(int port, int bit = 0) { if (CheckInputRange(port)) return false; if (CheckBit(bit)) return false; return true; } private bool CheckInputRange(int port) => (port < 0) || (port >= _Config.IO_INPUT_BOARD_NUM); private bool CheckOutputRange(int port) => (port < 0) || (port >= _Config.IO_OUTPUT_BOARD_NUM); private bool CheckBit(int bit) => bit < 0 || bit >= _Config.IO_COUNT; #region public methods public int GetInputValue(int port, int bit) { if (!CheckInputRange(port, bit)) return -1; if (BuffDIN != null) { return GetBitValue(bit, BuffDIN[port]); } return -1; } public int GetOutputValue(int port, int bit) { if (!CheckInputRange(port, bit)) return -1; if (BuffDIN != null) { return GetBitValue(bit, BuffDOUT[port]); } return -1; } public bool SetOutput(int port, int bit, int value) { if (CheckOutputRange(port)) return false; if (_Type == eIOType.Internal) { return _ACS.SetOutput(port, bit, value); } bool res = false; lock (_LockInputObject) { // int값을 읽어서 업데이트 하는 계념이므로 여기저기서 쓰면 안 된다. int val = GetOutputInternal(port, bit, out int totVal); if (value == val) { return true; } totVal ^= GetBitPos(bit); res = SetOutputPort(port, totVal); } return res; } #endregion #region internal methods internal int GetInput(int port, int bit) { if (!CheckInputRange(port, bit)) return -1; if (_Type == eIOType.Internal) { return _ACS.GetInput(port, bit); } int val = GetNodeInternal(EC_DIN, port); return GetBitValue(bit, val); } internal int GetInputPort(int port) { if (CheckInputRange(port)) return -1; if (_Type == eIOType.Internal) { return _ACS.GetInputPort(port); } return GetNodeInternal(EC_DIN, port); } internal int GetOutput(int port, int bit) { if (!CheckOutputRange(port, bit)) return -1; if (_Type == eIOType.Internal) { return _ACS.GetOutput(port, bit); } int val = GetNodeInternal(EC_DOUT, port); return GetBitValue(bit, val); } internal int GetOutputPort(int port) { if (!CheckOutputRange(port)) return -1; if (_Type == eIOType.Internal) { return _ACS.GetOutputPort(port); } return GetNodeInternal(EC_DOUT, port); } internal int[] GetInputAll() { if (IsSimul) return Enumerable.Repeat(200, 1).ToArray(); int[] ret = { -1, }; if (!IsActivated) return ret; if (_Type == eIOType.Internal) { ret = new int[_Config.IO_INPUT_BOARD_NUM]; for (int i = 0; i < _Config.IO_INPUT_BOARD_NUM; i++){ ret[i] = GetInputPort(i); } return ret; } if (!_ACS.ReadVariableIntArr(EC_DIN, out int[] val)) return null; return val; } internal int[] GetOutputAll() { if (IsSimul) return Enumerable.Repeat(200, 1).ToArray(); if (!IsActivated) return null; if (_Type == eIOType.Internal) { int[] ret = new int[_Config.IO_INPUT_BOARD_NUM]; for (int i = 0; i < _Config.IO_INPUT_BOARD_NUM; i++) { ret[i] = GetOutputPort(i); } return ret; } if (!_ACS.ReadVariableIntArr(EC_DOUT, out int[] val)) { return null; } return val; } internal bool SetOutputAll(int[] vals) { if (!IsActivated) return false; if (_Type == eIOType.Internal) { for (int i = 0; i < vals.Length; i++) { if (!_ACS.SetOutputPort(i, vals[i])) { return false; } } return true; } if (!_ACS.WriteVariableIntArr(EC_DOUT, vals)) return false; return true; } #endregion #region BackHoff Only private int GetBitValue(int pos, int val) => (val >> pos) & 1; private int GetBitPos(int pos, int val = 1) => val << pos; private int GetNodeInternal(string inout, int node) { if (_ACS.ReadVariableInt(inout, out int val, node)) return val; return -1; } internal int GetOutputInternal(int node, int bit, out int val) { val = -1; if (!CheckOutputRange(node, bit)) return -1; val = GetNodeInternal(EC_DOUT, node); return GetBitValue(bit, val); } private bool SetOutputPort(int node, int value) { if (!IsActivated) { return false; } if (CheckOutputRange(node)) { return false; } bool res = false; if (IsSimul) { BuffDOUT[node] = value; res = true; } else { res = _ACS.WriteVariableInt(EC_DOUT, value, node); } return res; } #endregion private void ReadInputIOAll() { try { string InputIOName = _Config.InputIOName; var boardInput = (int[])_ACS.ReadVariable(InputIOName, ProgramBuffer.ACSC_NONE, 0, _Config.IO_INPUT_BOARD_NUM); lock (_LockInputObject) { //for (int i = 0; i < InputBuffer.Count; i++) // InputBuffer[i] = (boardInput[i / _Config.IO_INPUT_BOARD_NUM] & 0x00000001 << (i)) > 0; for (int i = 0; i < BitInputs.Count; i++) BitInputs[i].Value = (boardInput[BitInputs[i].Port / _Config.IO_INPUT_BOARD_NUM] & 0x00000001 << (BitInputs[i].Bit)) > 0; } } catch (ACSException ex) { Log.I(ex.Message); throw; } } public bool ReadInputIO(int IONo) { int ReturnValue; int ArrayIndex = IONo / _Config.IO_COUNT; int BitIndex = IONo % _Config.IO_COUNT; string InputIOName = "ECAT_IN"; ReturnValue = (int)_ACS.ReadVariable(InputIOName, ProgramBuffer.ACSC_NONE, ArrayIndex, ArrayIndex); bool bOn = (ReturnValue & 0x00000001 << (BitIndex)) > 0; return bOn; } private void ReadOutputIOAll() { try { string OutputIOName = _Config.OutputIOName; var boardOutput = (int[])_ACS.ReadVariable(OutputIOName, ProgramBuffer.ACSC_NONE, 0, _Config.IO_OUTPUT_BOARD_NUM); lock (_LockOutputObject) { //for (int i = 0; i < OutputBuffer.Count; i++) // OutputBuffer[i] = (boardOutput[i / _Config.IO_OUTPUT_BOARD_NUM] & 0x00000001 << (i)) > 0; for (int i = 0; i < BitOutputs.Count; i++) BitOutputs[i].Value = (boardOutput[BitOutputs[i].Port / _Config.IO_OUTPUT_BOARD_NUM] & 0x00000001 << (BitOutputs[i].Bit)) > 0; } } catch (ACSException ex) { Log.I(ex.Message); throw; } } public bool ReadOutputIO(int IONo) { int ReturnValue; int ArrayIndex = IONo / _Config.IO_COUNT; int BitIndex = IONo % _Config.IO_COUNT; string InputIOName = "ECAT_OUT"; ReturnValue = (int)_ACS.ReadVariable(InputIOName, ProgramBuffer.ACSC_NONE, ArrayIndex, ArrayIndex); bool bOn = (ReturnValue & 0x00000001 << (BitIndex)) > 0; return bOn; } public bool WriteOutputIO(int IONo, bool bOn) { //string cmd = string.Format("ECAT_OUT({0:d}.{1:d}={2:d})", IONo / _Config.IO_COUNT, IONo % _Config.IO_COUNT, bOn ? 1 : 0); //return _ACS.SendCMD(cmd); try { int arrayIndex = IONo / _Config.IO_COUNT; int bitIndex = IONo % _Config.IO_COUNT; string outputIOName = "ECAT_OUT"; int value = (int)_ACS.ReadVariable(outputIOName, ProgramBuffer.ACSC_NONE, arrayIndex, arrayIndex); if (bOn) value |= (1 << bitIndex); // 해당 비트를 1로 설정 else value &= ~(1 << bitIndex); // 해당 비트를 0으로 설정 if (!_ACS.WriteVariable(outputIOName, value, ProgramBuffer.ACSC_NONE, arrayIndex)) return false; return true; } catch (ACSException ex) { Log.W(ex.Message); return false; } } #region AI, EXT public double GetAnalogInputNT(int port) => _ACS.GetAnalogInputNT(port); public double GetAnalogOutputNT(int port) => _ACS.GetAnalogOutputNT(port); public bool SetAnalogOutputNT(int port, double value) => _ACS.SetAnalogOutputNT(port, value); public int GetExtInput(int port, int bit) => _ACS.GetExtInput(port, bit); public int GetExtInputPort(int port) => _ACS.GetExtInputPort(port); public int GetExtOutput(int port, int bit) => _ACS.GetExtOutput(port, bit); public int GetExtOutputPort(int port) => _ACS.GetExtOutputPort(port); public bool SetExtOutput(int port, int bit, int value) => _ACS.SetExtOutput(port, bit, value); public bool SetExtOutputPort(int port, int value) => _ACS.SetExtOutputPort(port, value); #endregion } }