464 lines
14 KiB
C#
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
|
|
}
|
|
}
|