208 lines
7.7 KiB
C#
208 lines
7.7 KiB
C#
using MaxwellFramework.Core.Attributes;
|
|
using MainShell.Alarm;
|
|
using MainShell.Hardware;
|
|
using MainShell.Log;
|
|
using MainShell.Motion.Safety;
|
|
using MwFramework.Device;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace MainShell.Motion
|
|
{
|
|
[Singleton]
|
|
public sealed class MotionAlarmReporter
|
|
{
|
|
private readonly AlarmOperate _alarmOperate;
|
|
|
|
public MotionAlarmReporter(AlarmOperate alarmOperate)
|
|
{
|
|
_alarmOperate = alarmOperate ?? throw new ArgumentNullException(nameof(alarmOperate));
|
|
}
|
|
|
|
public async Task<bool> ReportAlarmAsync(int? alarmId)
|
|
{
|
|
if (!alarmId.HasValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
await _alarmOperate.AlertAsync(alarmId.Value).ConfigureAwait(false);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[Singleton]
|
|
public sealed class StagePlatformSafetyOptionsProvider
|
|
{
|
|
private const double DefaultMaxPlaneSpread = 5.0d;
|
|
private const double DefaultMaxTravelPerStep = 10.0d;
|
|
|
|
public StagePlatformSafetyOptions GetOptions()
|
|
{
|
|
return new StagePlatformSafetyOptions(DefaultMaxPlaneSpread, DefaultMaxTravelPerStep);
|
|
}
|
|
}
|
|
|
|
[Singleton]
|
|
public sealed class MotionPrecheckService
|
|
{
|
|
private readonly MotionAlarmReporter _motionAlarmReporter;
|
|
private readonly IReadOnlyList<IMotionSafetyCheck> _motionSafetyChecks;
|
|
|
|
public MotionPrecheckService(HardwareManager hardware, MotionSafetyStateProvider motionSafetyStateProvider, MotionAlarmReporter motionAlarmReporter, StagePlatformSafetyOptionsProvider stagePlatformSafetyOptionsProvider)
|
|
{
|
|
if (hardware == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(hardware));
|
|
}
|
|
|
|
if (motionSafetyStateProvider == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(motionSafetyStateProvider));
|
|
}
|
|
|
|
_motionAlarmReporter = motionAlarmReporter ?? throw new ArgumentNullException(nameof(motionAlarmReporter));
|
|
var stagePlatformSafetyOptions = (stagePlatformSafetyOptionsProvider ?? throw new ArgumentNullException(nameof(stagePlatformSafetyOptionsProvider))).GetOptions();
|
|
_motionSafetyChecks = new IMotionSafetyCheck[]
|
|
{
|
|
new MotionRequestDuplicateAxisCheck(),
|
|
new MotionBatchSameSourceCheck(),
|
|
new EmergencyStopReleasedCheck(motionSafetyStateProvider),
|
|
new SafetyDoorClosedCheck(motionSafetyStateProvider),
|
|
new AutoFlowModeCheck(motionSafetyStateProvider),
|
|
new StageVacuumReadyCheck(motionSafetyStateProvider),
|
|
new BondHeadSafePositionCheck(motionSafetyStateProvider),
|
|
new StagePlatformPlaneMoveSafetyCheck(hardware, stagePlatformSafetyOptions)
|
|
};
|
|
}
|
|
|
|
public async Task ValidateAsync(MotionSafetyContext context, CancellationToken cancellationToken, int? fallbackAlarmId)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
foreach (var check in _motionSafetyChecks)
|
|
{
|
|
var result = check.Check(context);
|
|
if (result == null || result.IsPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var failureMessage = string.Format("Motion precheck failed. Rule:{0} Kind:{1} Source:{2} Message:{3}", result.RuleName, context.RequestKind, context.Source ?? "N/A", result.Message);
|
|
failureMessage.LogSysError();
|
|
var alarmId = result.AlarmId ?? fallbackAlarmId;
|
|
if (alarmId.HasValue)
|
|
{
|
|
await _motionAlarmReporter.ReportAlarmAsync(alarmId).ConfigureAwait(false);
|
|
}
|
|
|
|
throw new InvalidOperationException(failureMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Singleton]
|
|
public sealed class MotionExecutionLogger
|
|
{
|
|
public void HandleMotionStarted(object sender, MotionStartedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
string.Format("Axis:{0} Motion:{1} Started StartPos:{2:F3} Target:{3}", e.AxisName, e.Operation, e.StartPosition, e.TargetPosition.HasValue ? e.TargetPosition.Value.ToString("F3") : "N/A").LogInfo();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
string.Format("Axis:{0} motion start log failed. Error:{1}", e.AxisName, ex.Message).LogSysError();
|
|
}
|
|
}
|
|
|
|
public void HandleMotionFinished(object sender, MotionFinishedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
var result = e.Result;
|
|
var message = string.Format("Axis:{0} Motion:{1} Finished Success:{2} Cancelled:{3} EndPos:{4:F3} Duration:{5}ms FailureStage:{6}", result.AxisName, result.Operation, result.Succeeded, result.Cancelled, result.EndPosition, result.DurationMilliseconds, result.FailureStage ?? "N/A");
|
|
if (result.Succeeded)
|
|
{
|
|
message.LogInfo();
|
|
return;
|
|
}
|
|
|
|
var error = string.Format("{0} Error:{1}", message, result.Exception);
|
|
error.LogSysError();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
string.Format("Axis:{0} motion finish log failed. Error:{1}", e.AxisName, ex.Message).LogSysError();
|
|
}
|
|
}
|
|
}
|
|
|
|
[Singleton]
|
|
public sealed class MotionControllerRegistry
|
|
{
|
|
private readonly Dictionary<string, ControllerEntry> _controllers = new Dictionary<string, ControllerEntry>(StringComparer.OrdinalIgnoreCase);
|
|
private readonly object _syncRoot = new object();
|
|
private readonly MotionExecutionLogger _motionExecutionLogger;
|
|
|
|
private sealed class ControllerEntry
|
|
{
|
|
public ControllerEntry(IAxis axis, MotionController controller)
|
|
{
|
|
Axis = axis ?? throw new ArgumentNullException(nameof(axis));
|
|
Controller = controller ?? throw new ArgumentNullException(nameof(controller));
|
|
}
|
|
|
|
public IAxis Axis { get; }
|
|
public MotionController Controller { get; }
|
|
}
|
|
|
|
public MotionControllerRegistry(MotionExecutionLogger motionExecutionLogger)
|
|
{
|
|
_motionExecutionLogger = motionExecutionLogger ?? throw new ArgumentNullException(nameof(motionExecutionLogger));
|
|
}
|
|
|
|
public MotionController GetController(IAxis axis)
|
|
{
|
|
if (axis == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(axis));
|
|
}
|
|
|
|
var axisName = axis.Name;
|
|
if (string.IsNullOrWhiteSpace(axisName))
|
|
{
|
|
return CreateController(axis);
|
|
}
|
|
|
|
lock (_syncRoot)
|
|
{
|
|
ControllerEntry entry;
|
|
if (_controllers.TryGetValue(axisName, out entry) && ReferenceEquals(entry.Axis, axis))
|
|
{
|
|
return entry.Controller;
|
|
}
|
|
|
|
var controller = CreateController(axis);
|
|
_controllers[axisName] = new ControllerEntry(axis, controller);
|
|
return controller;
|
|
}
|
|
}
|
|
|
|
private MotionController CreateController(IAxis axis)
|
|
{
|
|
var controller = new MotionController(axis);
|
|
controller.MotionStarted += _motionExecutionLogger.HandleMotionStarted;
|
|
controller.MotionFinished += _motionExecutionLogger.HandleMotionFinished;
|
|
return controller;
|
|
}
|
|
}
|
|
}
|