2026-01-26 18:56:53 +09:00

402 lines
11 KiB
C#

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
}
}