2026-06-05 13:44:39 +09:00

464 lines
14 KiB
C#

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<AddrInfo> BitInputs { get; private set; }
public List<AddrInfo> BitOutputs { get; private set; }
public List<bool> InputBuffer { get; set; }
public List<bool> 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<bool>(_Config.IO_INPUT_BOARD_NUM * _Config.IO_COUNT);
OutputBuffer = new List<bool>(_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
}
}