2026-01-26 14:51:28 +09:00

232 lines
7.9 KiB
C#

using Aurora;
using Aurora.Api;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace AuroraACS
{
class IOMonitor
{
private readonly Logger Log = new Logger("IOMonitor");
private static IOMonitor _instance;
private IOMonitor()
{
_ioInSubject = new Subject<IOItem>();
_ioOutSubject = new Subject<IOOutItem>();
BitInputs = DIN.GetAllInfo().ToArray();
BitOutputs = DOUT.GetAllInfo().ToArray();
}
public static IOMonitor GetInstance()
{
if (_instance == null)
_instance = new IOMonitor();
return _instance;
}
private AddrInfo[] BitInputs;
private AddrInfo[] BitOutputs;
private ACSIOEx _dio;
private IDisposable _ioMonitor;
private IDisposable _ioOutMonitor;
private Subject<IOItem> _ioInSubject; // Address, value
private Subject<IOOutItem> _ioOutSubject; // Address, value
public IObservable<IOItem> IO_Observable => _ioInSubject.AsObservable();
public IObservable<IOOutItem> IOOut_Observable => _ioOutSubject.AsObservable();
private int[] _prevInputPorts = new int[IOItem.NUM_PORTS];
private int[] _prevOutputPorts = new int[IOItem.NUM_PORTS];
private IOItem _ioInItem = new IOItem();
private IOOutItem _ioOutItem = new IOOutItem();
public void Init(ACSIOEx dio)
{
_dio = dio;
var active = Application.Current.AuroraEnv().EventObservable.AsObservable(AuroraSysEvent.SYS_DEVICE_ACTIVATED);
var deactive = Application.Current.AuroraEnv().EventObservable.AsObservable(AuroraSysEvent.SYS_DEVICE_DEACTIVATED);
Observable.Merge(active, deactive)
.Where(msg => msg.From == dio.DeviceId)
.Subscribe(msg => DevActivated(msg));
_ioMonitor = Observable.Interval(TimeSpan.FromMilliseconds(1))
.Where(x => _dio.IsActivated)
.Where(tick => GetIO())
.Subscribe(_ => { _ioInItem.IsInit = false; _ioInSubject.OnNext(_ioInItem); });
_ioOutMonitor = Observable.Interval(TimeSpan.FromMilliseconds(1))
.Where(x => _dio.IsActivated)
.Where(tick => GetIOOut())
.Subscribe(_ => { _ioOutItem.IsInit = false; _ioOutSubject.OnNext(_ioOutItem); });
}
private void DevActivated(EventMessage msg)
{
if (msg.From == _dio.DeviceId) {
if (msg.Subject == AuroraSysEvent.SYS_DEVICE_ACTIVATED) {
Log.D("Init start.");
_initializing = true;
if (GetIOAll()) {
_ioInItem.IsInit = true;
_ioInSubject.OnNext(_ioInItem);
_ioOutItem.IsInit = true;
_ioOutSubject.OnNext(_ioOutItem);
//System.Threading.Thread.Sleep(50);
}
_initializing = false;
Log.D("Init end.");
}
}
}
bool _initializing = false;
private bool GetIO()
{
if (_initializing == true)
return false;
//int k = 0;
bool changed = false;
int[] val = _dio.GetInputAll();
if (val == null)
return false;
for (int i = 0; i < IOItem.NUM_PORTS; i++) {
int nodeAddr = (i << 4);
CalcIO(ref _prevInputPorts[i], val[i], nodeAddr, _ioInItem.IsChanged, _ioInItem.ChangedVaslues, ref changed, BitInputs);
}
return changed;
}
private bool GetIOOut()
{
if (_initializing == true)
return false;
//int k = 0;
bool changed = false;
int[] outVal = _dio.GetOutputAll();
if (outVal == null)
return false;
for (int i = 0; i < IOItem.NUM_PORTS; i++) {
int nodeAddr = (i << 4);
CalcIO(ref _prevOutputPorts[i], outVal[i], nodeAddr, _ioOutItem.IsOutChanged, _ioOutItem.ChangedOutVaslues, ref changed, BitOutputs);
}
return changed;
}
private void CalcIO(ref int _prevport, int val, int nodeAddr, bool[] isChanged, byte[] changedVals, ref bool changed, AddrInfo[] addrInfos)
{
if (_prevport != val) { // Port보고 다르면... 세부적으로 언 놈 다른지 보고 날리기...
var diff = _prevport ^ val;
for (int j = 0; j < 8; j++) {
int addr = nodeAddr + j;
isChanged[addr] = false;
if ((diff & (1 << j)) != 0) { // 참이면
changed = true;
isChanged[addr] = true;
changedVals[addr] = (byte)((val >> j) & 1);
}
}
_prevport = val;
} else {
for (int j = 0; j < 8; j++) {
int addr = nodeAddr + j;
isChanged[addr] = false;
}
_prevport = val;
}
if (changed) {
UpdateValues(isChanged, changedVals, addrInfos);
}
}
private unsafe void UpdateValues(bool[] isChanged, byte[] changedVals, AddrInfo[] addrInfos)
{
fixed (bool* ptrIsChanged = isChanged)
fixed (byte* ptrChangedVals = changedVals) {
for (int i = 0; i < addrInfos.Length; i++) {
if (isChanged[addrInfos[i].Idx])
addrInfos[i].Value = changedVals[addrInfos[i].Idx];
}
}
}
private bool GetIOAll()
{
try {
_ioInItem.Data = _dio.GetInputAll();
_ioOutItem.Data = _dio.GetOutputAll();
if (_ioInItem.Data == null || _ioOutItem.Data == null)
return false;
for (int i = 0; i < IOItem.NUM_PORTS; i++) {
int nodeAddr = (i << 4);
for (int j = 0; j < 8; j++) {
int addr = nodeAddr + j;
_ioInItem.IsChanged[addr] = true;
_ioInItem.ChangedVaslues[addr] = (byte)((_ioInItem.Data[i] >> j) & 1);
_ioOutItem.IsOutChanged[addr] = true;
_ioOutItem.ChangedOutVaslues[addr] = (byte)((_ioOutItem.Data[i] >> j) & 1);
}
}
return true;
} catch (Exception exc) {
return false;
}
}
}
public class IOItem
{
private object lockObj = new object();
public bool IsInit = false;
public const int NUM_PORTS = 64;
public byte[] ChangedVaslues = new byte[1040];
public bool[] IsChanged = new bool[1040];
private int[] data = new int[NUM_PORTS];
public int[] Data
{
get => data;
set
{
lock (lockObj)
{
data = value;
}
}
}
}
public class IOOutItem
{
private object lockObj = new object();
public bool IsInit = false;
public const int NUM_PORTS = 64;
public byte[] ChangedOutVaslues = new byte[1040];
public bool[] IsOutChanged = new bool[1040];
private int[] data = new int[NUM_PORTS];
public int[] Data {
get => data;
set {
lock (lockObj)
{
data = value;
}
}
}
}
}