添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
@@ -0,0 +1,343 @@
|
||||
using MainShell.DeviceMaintance.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MainShell.Hardware
|
||||
{
|
||||
public interface IDeviceCylinderService
|
||||
{
|
||||
IReadOnlyList<CylinderDefinition> Definitions { get; }
|
||||
bool TryGetDefinition(string name, out CylinderDefinition definition);
|
||||
bool TryExtend(string name);
|
||||
bool TryExtend(string name, out string failureReason);
|
||||
bool TryRetract(string name);
|
||||
bool TryRetract(string name, out string failureReason);
|
||||
Task<bool> TryExtendAndWaitAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
|
||||
Task<bool> TryRetractAndWaitAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
|
||||
Task<CylinderExecutionResult> TryExtendAndWaitResultAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
|
||||
Task<CylinderExecutionResult> TryRetractAndWaitResultAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
|
||||
public class DeviceCylinderService : IDeviceCylinderService
|
||||
{
|
||||
private readonly IDeviceIoMonitorService _ioMonitorService;
|
||||
private readonly Dictionary<string, CylinderDefinition> _definitionsByName;
|
||||
private static readonly StringComparer PointReferenceComparer = StringComparer.OrdinalIgnoreCase;
|
||||
private static readonly TimeSpan ActiveRefreshTimeout = TimeSpan.FromMilliseconds(120);
|
||||
private static readonly TimeSpan FeedbackRetryInterval = TimeSpan.FromMilliseconds(50);
|
||||
|
||||
public IReadOnlyList<CylinderDefinition> Definitions { get; }
|
||||
|
||||
public DeviceCylinderService(IDeviceIoMonitorService ioMonitorService)
|
||||
{
|
||||
_ioMonitorService = ioMonitorService;
|
||||
Definitions = CylinderDefinitionLoader.LoadDefinitions();
|
||||
_definitionsByName = Definitions.ToDictionary(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public bool TryGetDefinition(string name, out CylinderDefinition definition)
|
||||
{
|
||||
return _definitionsByName.TryGetValue(name, out definition);
|
||||
}
|
||||
|
||||
public bool TryExtend(string name)
|
||||
{
|
||||
string failureReason;
|
||||
return TryExtend(name, out failureReason);
|
||||
}
|
||||
|
||||
public bool TryExtend(string name, out string failureReason)
|
||||
{
|
||||
CylinderDefinition definition;
|
||||
if (!TryGetDefinition(name, out definition))
|
||||
{
|
||||
failureReason = "未找到对应气缸定义。";
|
||||
return false;
|
||||
}
|
||||
|
||||
return Execute(definition, true, out failureReason);
|
||||
}
|
||||
|
||||
public bool TryRetract(string name)
|
||||
{
|
||||
string failureReason;
|
||||
return TryRetract(name, out failureReason);
|
||||
}
|
||||
|
||||
public bool TryRetract(string name, out string failureReason)
|
||||
{
|
||||
CylinderDefinition definition;
|
||||
if (!TryGetDefinition(name, out definition))
|
||||
{
|
||||
failureReason = "未找到对应气缸定义。";
|
||||
return false;
|
||||
}
|
||||
|
||||
return Execute(definition, false, out failureReason);
|
||||
}
|
||||
|
||||
public async Task<bool> TryExtendAndWaitAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var result = await TryExtendAndWaitResultAsync(name, timeout, cancellationToken);
|
||||
return result.Success;
|
||||
}
|
||||
|
||||
public async Task<bool> TryRetractAndWaitAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var result = await TryRetractAndWaitResultAsync(name, timeout, cancellationToken);
|
||||
return result.Success;
|
||||
}
|
||||
|
||||
public async Task<CylinderExecutionResult> TryExtendAndWaitResultAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
CylinderDefinition definition;
|
||||
string failureReason;
|
||||
if (!TryGetDefinition(name, out definition))
|
||||
{
|
||||
return CreateFailureResult("未找到对应气缸定义。");
|
||||
}
|
||||
|
||||
if (!Execute(definition, true, out failureReason))
|
||||
{
|
||||
return CreateFailureResult(failureReason);
|
||||
}
|
||||
|
||||
return await WaitForFeedbackResultAsync(definition.ExtendedFeedbackPoints, definition.RetractedFeedbackPoints, timeout, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<CylinderExecutionResult> TryRetractAndWaitResultAsync(string name, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
CylinderDefinition definition;
|
||||
string failureReason;
|
||||
if (!TryGetDefinition(name, out definition))
|
||||
{
|
||||
return CreateFailureResult("未找到对应气缸定义。");
|
||||
}
|
||||
|
||||
if (!Execute(definition, false, out failureReason))
|
||||
{
|
||||
return CreateFailureResult(failureReason);
|
||||
}
|
||||
|
||||
return await WaitForFeedbackResultAsync(definition.RetractedFeedbackPoints, definition.ExtendedFeedbackPoints, timeout, cancellationToken);
|
||||
}
|
||||
|
||||
private bool Execute(CylinderDefinition definition, bool extend, out string failureReason)
|
||||
{
|
||||
if (!TryValidateConditions(definition, extend, out failureReason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<DeviceIoOutputWriteRequest> requests;
|
||||
if (!TryBuildWriteRequests(definition, extend, out requests, out failureReason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string failurePointReference;
|
||||
var result = _ioMonitorService.TrySetOutputStates(requests, out failurePointReference);
|
||||
failureReason = result
|
||||
? string.Empty
|
||||
: string.IsNullOrWhiteSpace(failurePointReference)
|
||||
? "输出控制执行失败。"
|
||||
: $"输出控制执行失败:{failurePointReference}";
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TryValidateConditions(CylinderDefinition definition, bool extend, out string failureReason)
|
||||
{
|
||||
failureReason = string.Empty;
|
||||
var conditions = extend ? definition.ExtendConditions : definition.RetractConditions;
|
||||
if (conditions == null || conditions.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
_ioMonitorService.TryRefreshNow(ActiveRefreshTimeout);
|
||||
|
||||
foreach (var condition in conditions)
|
||||
{
|
||||
DeviceIoPointState point;
|
||||
if (!TryGetPoint(condition.PointReference, out point))
|
||||
{
|
||||
failureReason = !string.IsNullOrWhiteSpace(condition.Message)
|
||||
? condition.Message
|
||||
: $"条件 IO {condition.PointReference} 状态不可用。";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (point.Value != condition.ExpectedState)
|
||||
{
|
||||
failureReason = !string.IsNullOrWhiteSpace(condition.Message)
|
||||
? condition.Message
|
||||
: $"条件 IO {point.Name} 未满足,要求 {(condition.ExpectedState ? "ON" : "OFF")}。";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<CylinderExecutionResult> WaitForFeedbackResultAsync(IReadOnlyList<string> requiredOnPoints, IReadOnlyList<string> requiredOffPoints, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
var normalizedOnPoints = NormalizePointReferences(requiredOnPoints);
|
||||
var normalizedOffPoints = NormalizePointReferences(requiredOffPoints);
|
||||
if (normalizedOnPoints.Count == 0 && normalizedOffPoints.Count == 0)
|
||||
{
|
||||
return CreateSuccessResult();
|
||||
}
|
||||
|
||||
var start = DateTime.UtcNow;
|
||||
|
||||
while (DateTime.UtcNow - start < timeout)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var remaining = timeout - (DateTime.UtcNow - start);
|
||||
var refreshTimeout = remaining < ActiveRefreshTimeout ? remaining : ActiveRefreshTimeout;
|
||||
if (refreshTimeout > TimeSpan.Zero)
|
||||
{
|
||||
await _ioMonitorService.TryRefreshNowAsync(refreshTimeout, cancellationToken);
|
||||
}
|
||||
|
||||
var onReady = normalizedOnPoints.All(IsPointOn);
|
||||
var offReady = normalizedOffPoints.All(pointReference => !IsPointOn(pointReference));
|
||||
if (onReady && offReady)
|
||||
{
|
||||
return CreateSuccessResult();
|
||||
}
|
||||
|
||||
await Task.Delay(FeedbackRetryInterval, cancellationToken);
|
||||
}
|
||||
|
||||
return CreateFeedbackTimeoutResult(normalizedOnPoints, normalizedOffPoints);
|
||||
}
|
||||
|
||||
private bool TryBuildWriteRequests(CylinderDefinition definition, bool extend, out List<DeviceIoOutputWriteRequest> requests, out string failureReason)
|
||||
{
|
||||
requests = new List<DeviceIoOutputWriteRequest>();
|
||||
failureReason = string.Empty;
|
||||
|
||||
var desiredStates = new Dictionary<string, bool>(PointReferenceComparer);
|
||||
if (!AddOutputRequests(desiredStates, definition.ExtendOutputPoints, extend, out failureReason) ||
|
||||
!AddOutputRequests(desiredStates, definition.RetractOutputPoints, !extend, out failureReason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
requests.AddRange(desiredStates.Select(x => new DeviceIoOutputWriteRequest
|
||||
{
|
||||
PointReference = x.Key,
|
||||
OutputOn = x.Value
|
||||
}));
|
||||
|
||||
if (requests.Count > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
failureReason = "未配置输出控制点。";
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool AddOutputRequests(IDictionary<string, bool> desiredStates, IEnumerable<string> pointReferences, bool outputOn, out string failureReason)
|
||||
{
|
||||
failureReason = string.Empty;
|
||||
if (pointReferences == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var pointReference in pointReferences.Where(x => !string.IsNullOrWhiteSpace(x)))
|
||||
{
|
||||
bool existingState;
|
||||
if (desiredStates.TryGetValue(pointReference, out existingState))
|
||||
{
|
||||
if (existingState != outputOn)
|
||||
{
|
||||
failureReason = $"气缸输出配置冲突:{pointReference} 同时要求为 {(existingState ? "ON" : "OFF")} 和 {(outputOn ? "ON" : "OFF")}。";
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
desiredStates[pointReference] = outputOn;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<string> NormalizePointReferences(IEnumerable<string> pointReferences)
|
||||
{
|
||||
return (pointReferences ?? Enumerable.Empty<string>())
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(PointReferenceComparer)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private bool TryGetPoint(string pointReference, out DeviceIoPointState point)
|
||||
{
|
||||
return _ioMonitorService.TryGetPoint(pointReference, out point)
|
||||
|| _ioMonitorService.TryGetPointByPointKey(pointReference, out point);
|
||||
}
|
||||
|
||||
private bool IsPointOn(string pointReference)
|
||||
{
|
||||
return _ioMonitorService.IsPointOn(pointReference)
|
||||
|| _ioMonitorService.IsPointOnByPointKey(pointReference);
|
||||
}
|
||||
|
||||
private CylinderExecutionResult CreateFeedbackTimeoutResult(IReadOnlyList<string> requiredOnPoints, IReadOnlyList<string> requiredOffPoints)
|
||||
{
|
||||
var missingOnPoints = requiredOnPoints.Where(pointReference => !IsPointOn(pointReference)).ToList();
|
||||
var missingOffPoints = requiredOffPoints.Where(IsPointOn).ToList();
|
||||
var messages = new List<string>();
|
||||
if (missingOnPoints.Count > 0)
|
||||
{
|
||||
messages.Add("以下反馈未到位(期望ON):" + string.Join(",", missingOnPoints.Select(GetPointDisplayName)));
|
||||
}
|
||||
|
||||
if (missingOffPoints.Count > 0)
|
||||
{
|
||||
messages.Add("以下反馈未复位(期望OFF):" + string.Join(",", missingOffPoints.Select(GetPointDisplayName)));
|
||||
}
|
||||
|
||||
var failurePoints = missingOnPoints.Concat(missingOffPoints).Distinct(PointReferenceComparer).ToList();
|
||||
return new CylinderExecutionResult
|
||||
{
|
||||
Success = false,
|
||||
FailureReason = messages.Count > 0 ? string.Join(";", messages) : "等待气缸反馈超时。",
|
||||
FailurePointReferences = failurePoints
|
||||
};
|
||||
}
|
||||
|
||||
private string GetPointDisplayName(string pointReference)
|
||||
{
|
||||
DeviceIoPointState point;
|
||||
return TryGetPoint(pointReference, out point) ? point.Name : pointReference;
|
||||
}
|
||||
|
||||
private static CylinderExecutionResult CreateSuccessResult()
|
||||
{
|
||||
return new CylinderExecutionResult
|
||||
{
|
||||
Success = true,
|
||||
FailureReason = string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
private static CylinderExecutionResult CreateFailureResult(string failureReason)
|
||||
{
|
||||
return new CylinderExecutionResult
|
||||
{
|
||||
Success = false,
|
||||
FailureReason = failureReason ?? string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user