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; 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 int[] SimulDIN { get; set; } public int[] SimulDOUT { get; set; } #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 _LockObject = new object(); private eIOType _Type; public int RetryCount = 0; 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; } //[Coverity] CID 12366: (#1 of 1): 데이터 경합 상태 (GUARDED_BY_VIOLATION) //2.missing_lock: 락 ACSIO._LockObject을(를) 유지하지 않고 this.RetryCount에 액세스합니다.다른 곳에서, ACSIO.RetryCount은(는) ACSIO._LockObject을(를) 유지한 상태에서 3회 중 2회 작성하였습니다. lock (_LockObject) { RetryCount = 0; } 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]; if (IsSimul) { SimulDIN = new int[_Config.IO_INPUT_BOARD_NUM]; SimulDOUT = new int[_Config.IO_OUTPUT_BOARD_NUM]; RunSimulationTask(); } RunMonitor(); return E1.OK; } private void RunSimulationTask() { Random r = new Random(); Task.Run(() => { while (IsActivated) { for (int i = 0; i < _Config.IO_INPUT_BOARD_NUM; i++) { var input = i * 100; var value = input + r.Next(0, 99); SimulDIN[i] = value; } Thread.Sleep(1000); } }); } private void RunMonitor() { // 실시간 update Task.Run(() => { BuffDIN = GetInputAll(); BuffDOUT = GetOutputAll(); #if DEBUG //Stopwatch sw = new Stopwatch(); //sw.Start(); #endif // Update while (IsActivated) { lock (_LockObject) { BuffDIN = GetInputAll(); if (BuffDIN == null) { RetryCount++; } BuffDOUT = GetOutputAll(); if (BuffDOUT == null) { RetryCount++; } } Thread.Sleep(10); } }); } protected override int DoDeactivate() { 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 (_LockObject) { // 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 (IsSimul) { int simulVal = SimulDIN[port]; return GetBitValue(bit, simulVal); } 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 (IsSimul) { return SimulDIN[port]; } 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 (IsSimul) { int simulVal = SimulDOUT[port]; return GetBitValue(bit, simulVal); } 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 (IsSimul) { return SimulDOUT[port]; } if (_Type == eIOType.Internal) { return _ACS.GetOutputPort(port); } return GetNodeInternal(EC_DOUT, port); } internal int[] GetInputAll() { if (!IsActivated) return null; if (IsSimul) { return SimulDIN; } 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] = GetInputPort(i); } return ret; } if (!_ACS.ReadVariableIntArr(EC_DIN, out int[] val)) return null; return val; } internal int[] GetOutputAll() { if (!IsActivated) return null; if (IsSimul) { return SimulDOUT; } 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 #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 } }