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

344 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
};
}
}
}