Files
test_demo/MX-PD-盘古 - new/PanGu.DieBonderApp/MainShell/Hardware/DeviceIoMonitorService.cs
Shi.Ji e31d3560bb 添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
2026-05-18 11:43:09 +08:00

951 lines
33 KiB
C#

using MainShell.EventArgsFolder;
using Stylet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.Hardware
{
public interface IDeviceIoMonitorService
{
event EventHandler<DeviceIoChangedEventArgs> IoChanged;
IReadOnlyList<DeviceIoPointDefinition> Definitions { get; }
IReadOnlyDictionary<string, List<DeviceIoPointDefinition>> DefinitionsByModule { get; }
IReadOnlyDictionary<string, DeviceIoPointState> LatestPoints { get; }
bool IsOnline { get; }
bool TryGetPoint(int id, out DeviceIoPointState point);
bool TryGetPoint(string name, out DeviceIoPointState point);
bool TryGetPointByPointKey(string pointKey, out DeviceIoPointState point);
bool IsPointOn(int id);
bool IsPointOn(string name);
bool IsPointOnByPointKey(string pointKey);
Task<bool> WaitForPointStateAsync(int id, bool expectedState, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
Task<bool> WaitForPointStateAsync(string name, bool expectedState, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
Task<bool> WaitForPointStateByPointKeyAsync(string pointKey, bool expectedState, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
IReadOnlyList<DeviceIoPointState> GetPointsByModule(string module);
void Start();
void Stop();
void RequestRefresh();
bool TryRefreshNow(TimeSpan timeout);
Task<bool> TryRefreshNowAsync(TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
bool TrySetOutputState(int id, bool outputOn);
bool TrySetOutputState(string name, bool outputOn);
bool TrySetOutputStateByPointKey(string pointKey, bool outputOn);
bool TrySetOutputStates(IReadOnlyList<DeviceIoOutputWriteRequest> requests, out string failurePointReference);
bool TrySetOutputStatesByPointKey(IReadOnlyDictionary<string, bool> outputStates, out string failurePointKey);
}
public class DeviceIoMonitorService : IDeviceIoMonitorService
{
private readonly HardwareManager _hardwareManager;
private readonly AutoResetEvent _refreshSignal = new AutoResetEvent(false);
private readonly TimeSpan _pollInterval = TimeSpan.FromMilliseconds(100);
private readonly object _syncRoot = new object();
private readonly List<DeviceIoPointDefinition> _definitions = new List<DeviceIoPointDefinition>();
private readonly Dictionary<string, List<DeviceIoPointDefinition>> _definitionsByModule = new Dictionary<string, List<DeviceIoPointDefinition>>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, DeviceIoPointDefinition> _definitionsByName = new Dictionary<string, DeviceIoPointDefinition>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, DeviceIoPointDefinition> _definitionsByPointKey = new Dictionary<string, DeviceIoPointDefinition>(StringComparer.OrdinalIgnoreCase);
private readonly object _refreshWaitersSyncRoot = new object();
private readonly List<TaskCompletionSource<bool>> _refreshWaiters = new List<TaskCompletionSource<bool>>();
private const string DeviceIoConfigFileName = "DeviceIoPoints.csv";
private readonly Dictionary<string, DeviceIoPointState> _latestPoints = new Dictionary<string, DeviceIoPointState>(StringComparer.OrdinalIgnoreCase);
private CancellationTokenSource _cts;
public event EventHandler<DeviceIoChangedEventArgs> IoChanged;
public IReadOnlyList<DeviceIoPointDefinition> Definitions => _definitions;
public IReadOnlyDictionary<string, List<DeviceIoPointDefinition>> DefinitionsByModule => _definitionsByModule;
public IReadOnlyDictionary<string, DeviceIoPointState> LatestPoints => GetLatestPointsSnapshot();
public bool IsOnline => _hardwareManager.TdCard != null || _hardwareManager.AcsCard != null;
public DeviceIoMonitorService(HardwareManager hardwareManager)
{
_hardwareManager = hardwareManager;
LoadIoPointDefinitions();
}
public void Start()
{
if (_cts != null)
{
return;
}
LoadIoPointDefinitions();
_cts = new CancellationTokenSource();
var token = _cts.Token;
Task.Run(async () =>
{
while (!token.IsCancellationRequested)
{
_refreshSignal.WaitOne(_pollInterval);
if (token.IsCancellationRequested)
{
break;
}
IList<DeviceIoPointState> readPoints;
if (!TryReadIoPoints(out readPoints) || readPoints == null || readPoints.Count == 0)
{
CompleteRefreshWaiters(false);
await Task.Yield();
continue;
}
var changed = CollectChangedPoints(readPoints);
CompleteRefreshWaiters(true);
if (changed.Count == 0)
{
await Task.Yield();
continue;
}
var handler = IoChanged;
if (handler != null)
{
var changedCopy = changed.Select(x => x.Clone()).ToList();
var allCopy = GetLatestPointsSnapshot();
Execute.OnUIThread(() => handler(this, new DeviceIoChangedEventArgs(changedCopy, allCopy)));
}
await Task.Yield();
}
}, token);
}
public void Stop()
{
if (_cts == null)
{
return;
}
_cts.Cancel();
_refreshSignal.Set();
CompleteRefreshWaiters(false);
_cts.Dispose();
_cts = null;
}
public void RequestRefresh()
{
_refreshSignal.Set();
}
public bool TryRefreshNow(TimeSpan timeout)
{
return TryRefreshNowAsync(timeout).GetAwaiter().GetResult();
}
public async Task<bool> TryRefreshNowAsync(TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
{
Start();
var waiter = new TaskCompletionSource<bool>();
CancellationTokenRegistration registration = default(CancellationTokenRegistration);
lock (_refreshWaitersSyncRoot)
{
_refreshWaiters.Add(waiter);
}
if (cancellationToken.CanBeCanceled)
{
registration = cancellationToken.Register(() => waiter.TrySetCanceled());
}
try
{
RequestRefresh();
if (timeout <= TimeSpan.Zero)
{
timeout = _pollInterval;
}
var completedTask = await Task.WhenAny(waiter.Task, Task.Delay(timeout, cancellationToken));
if (completedTask != waiter.Task)
{
RemoveRefreshWaiter(waiter);
return false;
}
return await waiter.Task;
}
finally
{
registration.Dispose();
}
}
public bool TrySetOutputState(int id, bool outputOn)
{
var definition = ResolveUniqueDefinitionById(id, IoPointType.Output);
if (definition == null)
{
return false;
}
var result = _hardwareManager.TryWriteIoPointValue(definition, outputOn);
if (result)
{
RequestRefresh();
}
return result;
}
public bool TrySetOutputStateByPointKey(string pointKey, bool outputOn)
{
DeviceIoPointDefinition definition;
if (!_definitionsByPointKey.TryGetValue(pointKey ?? string.Empty, out definition) || definition.Type != IoPointType.Output || !definition.Enabled)
{
return false;
}
var result = _hardwareManager.TryWriteIoPointValue(definition, outputOn);
if (result)
{
RequestRefresh();
}
return result;
}
public bool TrySetOutputStates(IReadOnlyList<DeviceIoOutputWriteRequest> requests, out string failurePointReference)
{
failurePointReference = string.Empty;
if (requests == null || requests.Count == 0)
{
return true;
}
var definitions = new List<Tuple<DeviceIoPointDefinition, bool, string>>();
foreach (var request in requests)
{
if (request == null || string.IsNullOrWhiteSpace(request.PointReference))
{
failurePointReference = string.Empty;
return false;
}
DeviceIoPointDefinition definition;
if (!TryResolveOutputDefinition(request.PointReference, out definition))
{
failurePointReference = request.PointReference;
return false;
}
definitions.Add(Tuple.Create(definition, request.OutputOn, request.PointReference));
}
foreach (var item in definitions)
{
if (!_hardwareManager.TryWriteIoPointValue(item.Item1, item.Item2))
{
failurePointReference = item.Item3;
return false;
}
}
RequestRefresh();
return true;
}
public bool TrySetOutputStatesByPointKey(IReadOnlyDictionary<string, bool> outputStates, out string failurePointKey)
{
failurePointKey = string.Empty;
if (outputStates == null || outputStates.Count == 0)
{
return true;
}
var definitions = new List<Tuple<DeviceIoPointDefinition, bool, string>>();
foreach (var item in outputStates)
{
DeviceIoPointDefinition definition;
if (!_definitionsByPointKey.TryGetValue(item.Key ?? string.Empty, out definition) || definition.Type != IoPointType.Output || !definition.Enabled)
{
failurePointKey = item.Key;
return false;
}
definitions.Add(Tuple.Create(definition, item.Value, item.Key));
}
foreach (var item in definitions)
{
if (!_hardwareManager.TryWriteIoPointValue(item.Item1, item.Item2))
{
failurePointKey = item.Item3;
return false;
}
}
RequestRefresh();
return true;
}
public bool TrySetOutputState(string name, bool outputOn)
{
DeviceIoPointDefinition definition;
if (!_definitionsByName.TryGetValue(name ?? string.Empty, out definition) || definition.Type != IoPointType.Output || !definition.Enabled)
{
return false;
}
var result = _hardwareManager.TryWriteIoPointValue(definition, outputOn);
if (result)
{
RequestRefresh();
}
return result;
}
public bool TryGetPoint(int id, out DeviceIoPointState point)
{
var definition = ResolveUniqueDefinitionById(id, null);
if (definition == null)
{
point = null;
return false;
}
lock (_syncRoot)
{
if (_latestPoints.TryGetValue(definition.PointKey, out point))
{
point = point.Clone();
return true;
}
}
point = null;
return false;
}
public bool TryGetPointByPointKey(string pointKey, out DeviceIoPointState point)
{
lock (_syncRoot)
{
if (_latestPoints.TryGetValue(pointKey ?? string.Empty, out point))
{
point = point.Clone();
return true;
}
}
point = null;
return false;
}
public bool IsPointOn(int id)
{
DeviceIoPointState point;
return TryGetPoint(id, out point) && point.Value;
}
public bool IsPointOn(string name)
{
DeviceIoPointState point;
return TryGetPoint(name, out point) && point.Value;
}
public bool IsPointOnByPointKey(string pointKey)
{
DeviceIoPointState point;
return TryGetPointByPointKey(pointKey, out point) && point.Value;
}
public async Task<bool> WaitForPointStateAsync(int id, bool expectedState, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
{
var start = DateTime.UtcNow;
while (DateTime.UtcNow - start < timeout)
{
cancellationToken.ThrowIfCancellationRequested();
var remaining = timeout - (DateTime.UtcNow - start);
var refreshTimeout = remaining < _pollInterval ? remaining : _pollInterval;
if (refreshTimeout > TimeSpan.Zero)
{
await TryRefreshNowAsync(refreshTimeout, cancellationToken);
}
if (IsPointOn(id) == expectedState)
{
return true;
}
await Task.Delay(50, cancellationToken);
}
return false;
}
public async Task<bool> WaitForPointStateByPointKeyAsync(string pointKey, bool expectedState, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
{
var start = DateTime.UtcNow;
while (DateTime.UtcNow - start < timeout)
{
cancellationToken.ThrowIfCancellationRequested();
var remaining = timeout - (DateTime.UtcNow - start);
var refreshTimeout = remaining < _pollInterval ? remaining : _pollInterval;
if (refreshTimeout > TimeSpan.Zero)
{
await TryRefreshNowAsync(refreshTimeout, cancellationToken);
}
if (IsPointOnByPointKey(pointKey) == expectedState)
{
return true;
}
await Task.Delay(50, cancellationToken);
}
return false;
}
public async Task<bool> WaitForPointStateAsync(string name, bool expectedState, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
{
var start = DateTime.UtcNow;
while (DateTime.UtcNow - start < timeout)
{
cancellationToken.ThrowIfCancellationRequested();
var remaining = timeout - (DateTime.UtcNow - start);
var refreshTimeout = remaining < _pollInterval ? remaining : _pollInterval;
if (refreshTimeout > TimeSpan.Zero)
{
await TryRefreshNowAsync(refreshTimeout, cancellationToken);
}
if (IsPointOn(name) == expectedState)
{
return true;
}
await Task.Delay(50, cancellationToken);
}
return false;
}
public bool TryGetPoint(string name, out DeviceIoPointState point)
{
DeviceIoPointDefinition definition;
if (!_definitionsByName.TryGetValue(name ?? string.Empty, out definition) && !_definitionsByPointKey.TryGetValue(name ?? string.Empty, out definition))
{
point = null;
return false;
}
lock (_syncRoot)
{
if (_latestPoints.TryGetValue(definition.PointKey, out point))
{
point = point.Clone();
return true;
}
}
point = null;
return false;
}
public IReadOnlyList<DeviceIoPointState> GetPointsByModule(string module)
{
lock (_syncRoot)
{
return _latestPoints.Values
.Where(x => string.Equals(x.Module, module, StringComparison.OrdinalIgnoreCase))
.Select(x => x.Clone())
.ToList();
}
}
private bool TryReadIoPoints(out IList<DeviceIoPointState> points)
{
points = new List<DeviceIoPointState>();
if (Definitions == null || Definitions.Count == 0)
{
return false;
}
Dictionary<string, bool> readValues;
bool hasBatchValues = _hardwareManager.TryReadIoPointValues(Definitions, out readValues);
foreach (DeviceIoPointDefinition definition in Definitions)
{
bool value;
if (!definition.Enabled)
{
value = definition.DefaultValue;
}
else if (hasBatchValues && readValues != null && readValues.TryGetValue(definition.PointKey, out value))
{
}
else if (!_hardwareManager.TryReadIoPointValue(definition, out value))
{
value = definition.DefaultValue;
}
DeviceIoPointState state;
if (definition.Type == IoPointType.Output)
{
state = new DeviceOutputPointState
{
Id = definition.Id,
PointKey = definition.PointKey,
Module = definition.Module,
Name = definition.Name,
Card = definition.Card,
StationNo = definition.StationNo,
LineNo = definition.LineNo,
Value = value,
IsInverse = definition.IsInverse,
CommandValue = value
};
}
else
{
state = new DeviceInputPointState
{
Id = definition.Id,
PointKey = definition.PointKey,
Module = definition.Module,
Name = definition.Name,
Card = definition.Card,
StationNo = definition.StationNo,
LineNo = definition.LineNo,
Value = value,
IsInverse = definition.IsInverse
};
}
points.Add(state);
}
return points.Count > 0;
}
private void LoadIoPointDefinitions()
{
_definitions.Clear();
_definitionsByModule.Clear();
_definitionsByName.Clear();
_definitionsByPointKey.Clear();
var pointKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var path = ResolveConfigPath();
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
return;
}
var lines = File.ReadAllLines(path, Encoding.UTF8);
for (var i = 0; i < lines.Length; i++)
{
var raw = lines[i];
if (string.IsNullOrWhiteSpace(raw))
{
continue;
}
var line = raw.Trim();
if (line.StartsWith("#"))
{
continue;
}
if (i == 0 && line.IndexOf("Id", StringComparison.OrdinalIgnoreCase) >= 0)
{
continue;
}
var cols = line.Split(',');
if (cols.Length < 11)
{
continue;
}
int id;
if (!int.TryParse(cols[0].Trim(), out id))
{
continue;
}
var moduleIndex = 1;
var nameIndex = 2;
var typeIndex = 3;
var cardIndex = 4;
var stationIndex = 5;
var indexColumnIndex = 6;
var subIndexColumnIndex = 7;
var lineNoIndex = 8;
var enabledIndex = 9;
var defaultValueIndex = 10;
var isInverseIndex = cols.Length >= 12 ? 11 : -1;
var descriptionIndex = isInverseIndex >= 0 ? 12 : 11;
var name = cols[nameIndex].Trim();
if (string.IsNullOrWhiteSpace(name))
{
continue;
}
var module = cols[moduleIndex].Trim();
if (string.IsNullOrWhiteSpace(module))
{
module = "Default";
}
var type = ParseIoPointType(cols[typeIndex]);
var card = ParseIoCardType(cols[cardIndex]);
var stationNo = ParseInt(cols[stationIndex]);
var index = ParseInt(cols[indexColumnIndex]);
var subIndex = ParseInt(cols[subIndexColumnIndex]);
var lineNo = cols[lineNoIndex].Trim();
var pointKey = DeviceIoPointDefinition.BuildPointKey(type, card, stationNo, index, subIndex, lineNo);
if (!pointKeys.Add(pointKey) || !names.Add(name))
{
continue;
}
var definition = new DeviceIoPointDefinition
{
Id = id,
PointKey = pointKey,
Module = module,
Name = name,
Type = type,
Card = card,
StationNo = stationNo,
Index = index,
SubIndex = subIndex,
LineNo = lineNo,
Enabled = ParseBoolean(cols[enabledIndex], true),
DefaultValue = ParseBoolean(cols[defaultValueIndex], false),
IsInverse = isInverseIndex >= 0 && isInverseIndex < cols.Length && ParseBoolean(cols[isInverseIndex], false),
Description = cols.Length > descriptionIndex ? string.Join(",", cols.Skip(descriptionIndex)).Trim() : string.Empty
};
_definitions.Add(definition);
_definitionsByName[definition.Name] = definition;
_definitionsByPointKey[definition.PointKey] = definition;
List<DeviceIoPointDefinition> moduleDefinitions;
if (!_definitionsByModule.TryGetValue(module, out moduleDefinitions))
{
moduleDefinitions = new List<DeviceIoPointDefinition>();
_definitionsByModule[module] = moduleDefinitions;
}
moduleDefinitions.Add(definition);
}
}
private static int ParseInt(string raw)
{
int value;
return int.TryParse((raw ?? string.Empty).Trim(), out value) ? value : 0;
}
private static IoPointType ParseIoPointType(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
{
return IoPointType.Input;
}
var text = raw.Trim();
if (string.Equals(text, "Output", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "OUT", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "DO", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "O", StringComparison.OrdinalIgnoreCase))
{
return IoPointType.Output;
}
return IoPointType.Input;
}
private static IoCardType ParseIoCardType(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
{
return IoCardType.Td;
}
var text = raw.Trim();
if (string.Equals(text, "Acs", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "ACS", StringComparison.OrdinalIgnoreCase))
{
return IoCardType.Acs;
}
return IoCardType.Td;
}
private static bool ParseBoolean(string raw, bool defaultValue)
{
if (string.IsNullOrWhiteSpace(raw))
{
return defaultValue;
}
var text = raw.Trim();
if (string.Equals(text, "1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "true", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "yes", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "y", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (string.Equals(text, "0", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "false", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "no", StringComparison.OrdinalIgnoreCase) ||
string.Equals(text, "n", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return defaultValue;
}
private DeviceIoPointDefinition ResolveUniqueDefinitionById(int id, IoPointType? pointType)
{
var matches = _definitions
.Where(x => x.Id == id && (!pointType.HasValue || x.Type == pointType.Value))
.Take(2)
.ToList();
return matches.Count == 1 ? matches[0] : null;
}
private void CompleteRefreshWaiters(bool result)
{
List<TaskCompletionSource<bool>> waiters;
lock (_refreshWaitersSyncRoot)
{
if (_refreshWaiters.Count == 0)
{
return;
}
waiters = _refreshWaiters.ToList();
_refreshWaiters.Clear();
}
foreach (var waiter in waiters)
{
waiter.TrySetResult(result);
}
}
private void RemoveRefreshWaiter(TaskCompletionSource<bool> waiter)
{
lock (_refreshWaitersSyncRoot)
{
_refreshWaiters.Remove(waiter);
}
}
private bool TryResolveOutputDefinition(string pointReference, out DeviceIoPointDefinition definition)
{
definition = null;
if (string.IsNullOrWhiteSpace(pointReference))
{
return false;
}
if (_definitionsByPointKey.TryGetValue(pointReference, out definition))
{
return definition.Type == IoPointType.Output && definition.Enabled;
}
if (_definitionsByName.TryGetValue(pointReference, out definition))
{
return definition.Type == IoPointType.Output && definition.Enabled;
}
int id;
if (int.TryParse(pointReference, out id))
{
definition = ResolveUniqueDefinitionById(id, IoPointType.Output);
return definition != null && definition.Enabled;
}
return false;
}
private static string ResolveConfigPath()
{
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
var localPath = Path.Combine(baseDir, "Configuration", DeviceIoConfigFileName);
var sourcePath = ResolveRootConfigPath(baseDir);
if (TrySyncConfigToLocal(sourcePath, localPath))
{
return localPath;
}
return File.Exists(localPath) ? localPath : null;
}
private static string ResolveRootConfigPath(string baseDir)
{
var current = new DirectoryInfo(baseDir);
while (current != null)
{
var rootConfigDir = Path.Combine(current.FullName, "Configuration");
if (Directory.Exists(rootConfigDir) && Directory.Exists(Path.Combine(current.FullName, "MainShell")))
{
var path = Path.Combine(rootConfigDir, DeviceIoConfigFileName);
if (File.Exists(path))
{
return path;
}
}
current = current.Parent;
}
return null;
}
private static bool TrySyncConfigToLocal(string sourcePath, string localPath)
{
if (string.IsNullOrWhiteSpace(sourcePath) || !File.Exists(sourcePath))
{
return false;
}
if (!string.Equals(Path.GetFullPath(sourcePath), Path.GetFullPath(localPath), StringComparison.OrdinalIgnoreCase))
{
var localDir = Path.GetDirectoryName(localPath);
if (!string.IsNullOrWhiteSpace(localDir))
{
Directory.CreateDirectory(localDir);
}
if (!File.Exists(localPath) || File.GetLastWriteTimeUtc(localPath) < File.GetLastWriteTimeUtc(sourcePath))
{
File.Copy(sourcePath, localPath, true);
}
}
return true;
}
private List<DeviceIoPointState> CollectChangedPoints(IList<DeviceIoPointState> readPoints)
{
var changed = new List<DeviceIoPointState>();
lock (_syncRoot)
{
if (_latestPoints.Count == 0)
{
foreach (var point in readPoints)
{
if (point == null)
{
continue;
}
_latestPoints[point.PointKey] = PreparePoint(point.Clone());
}
return changed;
}
foreach (var point in readPoints)
{
if (point == null)
{
continue;
}
if (!_latestPoints.TryGetValue(point.PointKey, out DeviceIoPointState old))
{
_latestPoints[point.PointKey] = PreparePoint(point.Clone());
changed.Add(PreparePoint(point.Clone()));
continue;
}
if (HasPointChanged(old, point))
{
_latestPoints[point.PointKey] = PreparePoint(point.Clone());
changed.Add(PreparePoint(point.Clone()));
}
}
}
return changed;
}
private IReadOnlyDictionary<string, DeviceIoPointState> GetLatestPointsSnapshot()
{
lock (_syncRoot)
{
return _latestPoints.ToDictionary(x => x.Key, x => PreparePoint(x.Value.Clone()));
}
}
private DeviceIoPointState PreparePoint(DeviceIoPointState point)
{
var outputPoint = point as DeviceOutputPointState;
DeviceIoPointDefinition definition;
_definitionsByPointKey.TryGetValue(point.PointKey ?? string.Empty, out definition);
if (outputPoint != null && definition != null && definition.Enabled)
{
outputPoint.BindWriteAction(outputOn => TrySetOutputState(definition.Name, outputOn));
}
return point;
}
private static bool HasPointChanged(DeviceIoPointState oldPoint, DeviceIoPointState newPoint)
{
if (oldPoint == null || newPoint == null)
{
return true;
}
if (oldPoint.Value != newPoint.Value ||
oldPoint.PointType != newPoint.PointType ||
!string.Equals(oldPoint.PointKey, newPoint.PointKey, StringComparison.OrdinalIgnoreCase) ||
!string.Equals(oldPoint.Name, newPoint.Name, StringComparison.OrdinalIgnoreCase) ||
!string.Equals(oldPoint.Module, newPoint.Module, StringComparison.OrdinalIgnoreCase) ||
oldPoint.Card != newPoint.Card)
{
return true;
}
var oldOutput = oldPoint as DeviceOutputPointState;
var newOutput = newPoint as DeviceOutputPointState;
if (oldOutput != null && newOutput != null)
{
return oldOutput.CommandValue != newOutput.CommandValue;
}
return false;
}
}
}