添加 MX-PD-盘古 项目文件

将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
Shi.Ji
2026-05-18 11:43:09 +08:00
parent 03632a379d
commit e31d3560bb
739 changed files with 99783 additions and 0 deletions

View File

@@ -0,0 +1,253 @@
using MainShell.Filewritable;
using MainShell.Hardware;
using MainShell.PageCalib.OriginCalib.Service;
using MXJM.FileWritable;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
namespace MainShell.PageCalib.OriginCalib.Model
{
public static class OriginCalibModuleNames
{
public const string GantryX1Y1 = "X1Y1";
public const string GantryX2 = "X2";
public const string StageY2 = "StageY2";
public const string WSX3 = "WSX3";
public const string Z1 = "Z1";
public const string Z3 = "Z3";
public const string Z4 = "Z4";
public const string Z5 = "Z5";
public const string Z6 = "Z6";
}
public class AvoidanceAxisItem
{
public string AxisName { get; set; }
public double Position { get; set; }
}
public class CalibrationAxisItem
{
public string AxisName { get; set; }
public double TargetPosition { get; set; }
public double LastPosition { get; set; }
public double Offset { get; set; }
public bool IsCalibrated { get; set; }
}
public class OriginCalibModuleItem
{
public string ModuleName { get; set; }
public bool IsIndependent { get; set; }
public string TemplatePath { get; set; }
public List<AvoidanceAxisItem> AvoidanceAxes { get; set; } = new List<AvoidanceAxisItem>();
public List<CalibrationAxisItem> CalibrationAxes { get; set; } = new List<CalibrationAxisItem>();
[JsonIgnore]
public ICalibrationPostProcessor PostProcessor { get; set; }
public void EnsureCollections()
{
if (AvoidanceAxes == null)
{
AvoidanceAxes = new List<AvoidanceAxisItem>();
}
if (CalibrationAxes == null)
{
CalibrationAxes = new List<CalibrationAxisItem>();
}
if (string.IsNullOrWhiteSpace(TemplatePath))
{
TemplatePath = GetDefaultTemplatePath(ModuleName);
}
}
private static string GetDefaultTemplatePath(string moduleName)
{
switch (moduleName)
{
case OriginCalibModuleNames.GantryX1Y1:
return Paths.OriginCalibGantryX1Y1TemplatePath;
case OriginCalibModuleNames.GantryX2:
return Paths.OriginCalibGantryX2TemplatePath;
case OriginCalibModuleNames.StageY2:
return Paths.OriginCalibStageY2TemplatePath;
case OriginCalibModuleNames.WSX3:
return Paths.OriginCalibWSX3TemplatePath;
default:
return Paths.OriginCalibDefaultTemplatePath;
}
}
}
public class OriginCalibSetting : JsonFileWritableBase
{
[JsonIgnore]
public override string Dir => Paths.CalibSettingPath;
[JsonIgnore]
public override string FileName => "OriginCalibSetting.json";
public List<OriginCalibModuleItem> Modules { get; set; } = new List<OriginCalibModuleItem>();
public static OriginCalibSetting LoadOrCreate()
{
OriginCalibSetting setting = new OriginCalibSetting();
string path = Path.Combine(setting.Dir, setting.FileName);
if (File.Exists(path))
{
setting.Read(path);
setting.EnsureCollections();
return setting;
}
setting.Modules = CreateDefaultModules();
setting.Write(path);
return setting;
}
public void EnsureCollections()
{
if (Modules == null)
{
Modules = new List<OriginCalibModuleItem>();
}
foreach (OriginCalibModuleItem module in Modules)
{
if (module != null)
{
module.EnsureCollections();
}
}
}
private static List<OriginCalibModuleItem> CreateDefaultModules()
{
List<OriginCalibModuleItem> modules = new List<OriginCalibModuleItem>
{
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.GantryX1Y1,
IsIndependent = true,
TemplatePath = Paths.OriginCalibGantryX1Y1TemplatePath,
AvoidanceAxes = new List<AvoidanceAxisItem>
{
new AvoidanceAxisItem
{
AxisName=AxisName.Axis_Stage_Y3,
Position=0
}
},
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_X1, TargetPosition = 0.0 },
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_Y1, TargetPosition = 0.0 }
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.GantryX2,
IsIndependent = true,
TemplatePath = Paths.OriginCalibGantryX2TemplatePath,
AvoidanceAxes = new List<AvoidanceAxisItem>
{
new AvoidanceAxisItem{ AxisName=AxisName.Axis_Stage_Y3, Position= 0 },
},
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_X2, TargetPosition = 0.0 },
new CalibrationAxisItem { AxisName=AxisName.Axis_PHS_Y1, TargetPosition= 0.0 },
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.StageY2,
IsIndependent = true,
TemplatePath = Paths.OriginCalibStageY2TemplatePath,
AvoidanceAxes = new List<AvoidanceAxisItem>(),
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_Stage_Y3, TargetPosition = 0.0 }
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.WSX3,
IsIndependent = true,
TemplatePath = Paths.OriginCalibWSX3TemplatePath,
AvoidanceAxes = new List<AvoidanceAxisItem>(),
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_WS_X3, TargetPosition = 0.0 },
new CalibrationAxisItem { AxisName=AxisName.Axis_PHS_Y1, TargetPosition= 0.0 },
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.Z1,
IsIndependent = true,
TemplatePath = string.Empty,
AvoidanceAxes = new List<AvoidanceAxisItem>(),
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_Z1, TargetPosition = 0.0 },
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.Z3,
IsIndependent = true,
TemplatePath = string.Empty,
AvoidanceAxes = new List<AvoidanceAxisItem>(),
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_Z3, TargetPosition = 0.0 },
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.Z4,
IsIndependent = true,
TemplatePath = string.Empty,
AvoidanceAxes = new List<AvoidanceAxisItem>(),
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_Z4, TargetPosition = 0.0 },
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.Z5,
IsIndependent = true,
TemplatePath = string.Empty,
AvoidanceAxes = new List<AvoidanceAxisItem>(),
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_Z5, TargetPosition = 0.0 },
}
},
new OriginCalibModuleItem
{
ModuleName = OriginCalibModuleNames.Z6,
IsIndependent = true,
TemplatePath = string.Empty,
AvoidanceAxes = new List<AvoidanceAxisItem>(),
CalibrationAxes = new List<CalibrationAxisItem>
{
new CalibrationAxisItem { AxisName = AxisName.Axis_PHS_Z6, TargetPosition = 0.0 },
}
},
};
return modules;
}
}
}

View File

@@ -0,0 +1,182 @@
using MainShell.Common;
using MainShell.Log;
using MainShell.Motion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.PageCalib.OriginCalib.Service
{
/// <summary>
/// ʾ<><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڱ궨<DAB1><EAB6A8><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
public class VisionCapturePostProcessor : ICalibrationPostProcessor
{
public string Name => "VisionCapture";
public async Task ExecuteAsync(OriginCalibrationExecutionResult calibrationResult, CancellationToken cancellationToken,object pars= null)
{
<><D6B4><EFBFBD><EFBFBD><EFBFBD>պ<EFBFBD><D5BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD>...".LogInfo();
// <20>ڴ˴<DAB4><CBB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD>
// <20><><EFBFBD>磺await _visionService.CaptureAsync(cancellationToken);
await Task.Delay(100, cancellationToken);
"<22><><EFBFBD>պ<EFBFBD><D5BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɡ<EFBFBD>".LogInfo();
}
}
/// <summary>
/// <20>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڱ궨<DAB1><EAB6A8><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD>λ<EFBFBD><CEBB>ִ<EFBFBD>бƽ<D0B1><C6BD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
public class ApproachAlignmentPostProcessor : ICalibrationPostProcessor
{
private readonly ApproachAlignmentService _approachAlignmentService;
private readonly IReadOnlyList<ApproachAlignmentAxis> _alignmentAxes;
private readonly CameraType _camera;
/// <summary>
/// <20><><EFBFBD><EFBFBD><ECBAAF>
/// </summary>
/// <param name="approachAlignmentService"><3E>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
/// <param name="alignmentAxes"><3E><>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD></param>
/// <param name="camera"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͣ<EFBFBD>Ĭ<EFBFBD><C4AC>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
public ApproachAlignmentPostProcessor(
ApproachAlignmentService approachAlignmentService,
IEnumerable<ApproachAlignmentAxis> alignmentAxes,
CameraType camera = CameraType.TopPositionCamera)
{
_approachAlignmentService = approachAlignmentService ?? throw new ArgumentNullException(nameof(approachAlignmentService));
if (alignmentAxes == null)
{
throw new ArgumentNullException(nameof(alignmentAxes));
}
_alignmentAxes = alignmentAxes.ToList().AsReadOnly();
if (_alignmentAxes.Count == 0)
{
throw new ArgumentException("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫһ<D2AA><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ᡣ", nameof(alignmentAxes));
}
_camera = camera;
}
public string Name => "ApproachAlignment";
public async Task ExecuteAsync(OriginCalibrationExecutionResult calibrationResult, CancellationToken cancellationToken,object pars= null)
{
<>бƽ<D0B1><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD>...".LogInfo();
try
{
ApproachAlignmentRequest alignmentRequest = new ApproachAlignmentRequest(_alignmentAxes, _camera);
if(pars is CenterRecognitionParameters centerRecognitionParameters)
{
alignmentRequest.RecognitionParameters = centerRecognitionParameters;
}
ApproachAlignmentResult alignmentResult = await _approachAlignmentService.ApproachAlignmentAsync(
alignmentRequest,
cancellationToken).ConfigureAwait(false);
if (alignmentResult.Succeeded)
{
string.Format("<22>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɣ<EFBFBD><C9A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>{0}", alignmentResult.CompletedIterations).LogInfo();
}
else
{
string.Format("<22>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD>{0}", alignmentResult.Message).LogInfo();
throw alignmentResult.Exception ?? new InvalidOperationException(alignmentResult.Message);
}
}
catch (OperationCanceledException)
{
"<22>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>".LogInfo();
throw;
}
catch (Exception ex)
{
string.Format("<22>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><ECB3A3>{0}", ex.Message).LogInfo();
throw;
}
}
}
/// <summary>
/// <20><><EFBFBD>ζ<EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڱ궨<DAB1><EAB6A8><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD>λ<EFBFBD><CEBB>ִ<EFBFBD><D6B4>һ<EFBFBD><D2BB>ʶ<EFBFBD><CAB6><EFBFBD><EFBFBD>ת<EFBFBD><D7AA><EFBFBD><EFBFBD><EFBFBD>˶<EFBFBD>
/// </summary>
public class SingleAlignmentPostProcessor : ICalibrationPostProcessor
{
private readonly ApproachAlignmentService _approachAlignmentService;
private readonly IReadOnlyList<ApproachAlignmentAxis> _alignmentAxes;
private readonly CameraType _camera;
public SingleAlignmentPostProcessor(
ApproachAlignmentService approachAlignmentService,
IEnumerable<ApproachAlignmentAxis> alignmentAxes,
CameraType camera = CameraType.TopPositionCamera)
{
_approachAlignmentService = approachAlignmentService ?? throw new ArgumentNullException(nameof(approachAlignmentService));
if (alignmentAxes == null)
{
throw new ArgumentNullException(nameof(alignmentAxes));
}
_alignmentAxes = alignmentAxes.ToList().AsReadOnly();
if (_alignmentAxes.Count == 0)
{
throw new ArgumentException("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫһ<D2AA><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ᡣ", nameof(alignmentAxes));
}
_camera = camera;
}
public string Name => "SingleAlignment";
public async Task ExecuteAsync(OriginCalibrationExecutionResult calibrationResult, CancellationToken cancellationToken, object pars = null)
{
<>е<EFBFBD><D0B5>ζ<EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD>...".LogInfo();
try
{
ApproachAlignmentRequest alignmentRequest = new ApproachAlignmentRequest(_alignmentAxes, _camera);
if (pars is CenterRecognitionParameters centerRecognitionParameters)
{
alignmentRequest.RecognitionParameters = centerRecognitionParameters;
}
ApproachAlignmentResult alignmentResult = await _approachAlignmentService.SingleAlignmentAsync(
alignmentRequest,
cancellationToken).ConfigureAwait(false);
if (alignmentResult.Succeeded)
{
string.Format("<22><><EFBFBD>ζ<EFBFBD>λ<EFBFBD><CEBB><EFBFBD>ɣ<EFBFBD>ִ<EFBFBD>д<EFBFBD><D0B4><EFBFBD><EFBFBD><EFBFBD>{0}", alignmentResult.CompletedIterations).LogInfo();
}
else
{
string.Format("<22><><EFBFBD>ζ<EFBFBD>λʧ<CEBB>ܣ<EFBFBD>{0}", alignmentResult.Message).LogInfo();
throw alignmentResult.Exception ?? new InvalidOperationException(alignmentResult.Message);
}
}
catch (OperationCanceledException)
{
"<22><><EFBFBD>ζ<EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>".LogInfo();
throw;
}
catch (Exception ex)
{
string.Format("<22><><EFBFBD>ζ<EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><ECB3A3>{0}", ex.Message).LogInfo();
throw;
}
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.PageCalib.OriginCalib.Service
{
/// <summary>
/// 标定后处理接口,用于在避让轴和标定轴移动到位后执行模块特定的逻辑
/// </summary>
public interface ICalibrationPostProcessor
{
/// <summary>
/// 处理器名称,用于日志和诊断
/// </summary>
string Name { get; }
/// <summary>
/// 在运动完成后、标定前执行处理逻辑
/// </summary>
/// <param name="calibrationResult">运动执行结果,包含各轴的实际位置</param>
/// <param name="cancellationToken">取消令牌</param>
/// <param name="pars">可选参数</param>
/// <returns>异步任务</returns>
Task ExecuteAsync(OriginCalibrationExecutionResult calibrationResult, CancellationToken cancellationToken,object pars=null);
}
}

View File

@@ -0,0 +1,168 @@
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Motion;
using MainShell.PageCalib.OriginCalib.Model;
using MaxwellFramework.Core.Attributes;
using MwFramework.Device;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.PageCalib.OriginCalib.Service
{
public sealed class OriginCalibrationExecutionResult
{
public OriginCalibrationExecutionResult(IDictionary<string, double> actualPositions)
{
var positions = actualPositions ?? new Dictionary<string, double>(StringComparer.Ordinal);
ActualPositions = new ReadOnlyDictionary<string, double>(new Dictionary<string, double>(positions, StringComparer.Ordinal));
}
public IReadOnlyDictionary<string, double> ActualPositions { get; private set; }
}
[Singleton]
public class OriginCalibrationMotionService
{
private const int PositionSettleDelayMilliseconds = 200;
private readonly HardwareManager _hardware;
private readonly SafeAxisMotion _safeAxisMotion;
public OriginCalibrationMotionService(HardwareManager hardware, SafeAxisMotion safeAxisMotion)
{
_hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
_safeAxisMotion = safeAxisMotion ?? throw new ArgumentNullException(nameof(safeAxisMotion));
}
public async Task MoveAxisAsync(string axisName, double position, CancellationToken cancellationToken = default(CancellationToken))
{
string.Format("原点标定正在移动轴“{0}”到位置 {1:F3}。", axisName, position).LogInfo();
try
{
var result = await _safeAxisMotion.MoveAbsAsync(axisName, position, MotionController.DefaultTimeoutMilliseconds, cancellationToken).ConfigureAwait(false);
result.EnsureSuccess();
string.Format("原点标定轴“{0}”已到达目标位置 {1:F3}。", axisName, position).LogInfo();
}
catch (OperationCanceledException)
{
string.Format("原点标定轴“{0}”移动已取消。", axisName).LogInfo();
throw;
}
catch (Exception ex)
{
string.Format("原点标定轴“{0}”移动失败:{1}", axisName, ex.Message).LogSysError();
throw;
}
}
public async Task<OriginCalibrationExecutionResult> ExecuteAsync(IEnumerable<AvoidanceAxisItem> avoidanceAxes, IEnumerable<CalibrationAxisItem> calibrationAxes, CancellationToken cancellationToken = default(CancellationToken))
{
var avoidanceAxisList = avoidanceAxes == null ? new List<AvoidanceAxisItem>() : avoidanceAxes.Where(item => item != null).ToList();
var calibrationAxisList = calibrationAxes == null ? new List<CalibrationAxisItem>() : calibrationAxes.Where(item => item != null).ToList();
string.Format(
"原点标定执行开始,避让轴数量:{0},标定轴数量:{1}。",
avoidanceAxisList.Count,
calibrationAxisList.Count).LogInfo();
try
{
await ExecuteBatchAsync(CreateRequests(avoidanceAxisList, item => item.AxisName, item => item.Position), cancellationToken).ConfigureAwait(false);
await ExecuteBatchAsync(CreateRequests(calibrationAxisList, item => item.AxisName, item => item.TargetPosition), cancellationToken).ConfigureAwait(false);
string.Format(
"原点标定等待 {0} ms 以稳定轴反馈,再读取实际位置。",
PositionSettleDelayMilliseconds).LogInfo();
await Task.Delay(PositionSettleDelayMilliseconds, cancellationToken).ConfigureAwait(false);
var actualPositions = new Dictionary<string, double>(StringComparer.Ordinal);
foreach (var calibrationAxis in calibrationAxisList)
{
if (calibrationAxis == null || string.IsNullOrWhiteSpace(calibrationAxis.AxisName))
{
continue;
}
var axis = _hardware.GetAxisByName(calibrationAxis.AxisName);
if (axis == null)
{
continue;
}
actualPositions[calibrationAxis.AxisName] = Math.Round(GetActualPosition(axis), 3);
string.Format(
"原点标定已采集轴“{0}”的实际位置:{1:F3}。",
calibrationAxis.AxisName,
actualPositions[calibrationAxis.AxisName]).LogInfo();
}
string.Format("原点标定执行完成,共采集 {0} 个标定轴的实际位置。", actualPositions.Count).LogInfo();
return new OriginCalibrationExecutionResult(actualPositions);
}
catch (OperationCanceledException)
{
"原点标定执行已取消。".LogInfo();
throw;
}
catch (Exception ex)
{
string.Format("原点标定执行失败:{0}", ex.Message).LogSysError();
throw;
}
}
private async Task ExecuteBatchAsync(MotionMoveRequest[] requests, CancellationToken cancellationToken)
{
if (requests.Length == 0)
{
return;
}
var result = await _safeAxisMotion.SafeMoveAsync(cancellationToken, requests).ConfigureAwait(false);
result.EnsureSuccess();
}
private MotionMoveRequest[] CreateRequests<TItem>(IEnumerable<TItem> items, Func<TItem, string> axisNameAccessor, Func<TItem, double> positionAccessor)
{
if (items == null)
{
return new MotionMoveRequest[0];
}
var requests = new List<MotionMoveRequest>();
foreach (var item in items)
{
if (item == null)
{
continue;
}
var axisName = axisNameAccessor(item);
if (string.IsNullOrWhiteSpace(axisName))
{
continue;
}
if (_hardware.GetAxisByName(axisName) == null)
{
string.Format("原点标定轴“{0}”在硬件管理器中不存在。", axisName).LogSysError();
continue;
}
requests.Add(MotionMoveRequest.ForAxisName(axisName, positionAccessor(item)));
}
return requests.ToArray();
}
private static double GetActualPosition(IAxis axis)
{
return axis.State != null ? axis.State.ActualPos : axis.GetPositionImmediate();
}
}
}

View File

@@ -0,0 +1,428 @@
<UserControl x:Class="MainShell.PageCalib.OriginCalib.View.OriginCalibView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="800"
d:DesignWidth="1200">
<UserControl.Resources>
<Style x:Key="ModuleCardStyle" TargetType="Border">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#E0E4EA"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="6"/>
<Setter Property="Margin" Value="0,0,0,12"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="#000000" Opacity="0.06" BlurRadius="8" ShadowDepth="2" Direction="270"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="AvoidanceAxisNameStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="#E8821A"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="TableHeaderStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="11"/>
<Setter Property="Foreground" Value="#8892A0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style x:Key="CalibAxisNameStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="#2D3748"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="TargetValueStyle" TargetType="mw:NumberBox">
<Setter Property="Width" Value="85"/>
<Setter Property="Height" Value="28"/>
<Setter Property="Margin" Value="0,1"/>
<Setter Property="mw:NumericKeypadAttach.IsEnabled" Value="True"/>
<Setter Property="Minimum" Value="-99999"/>
<Setter Property="Maximum" Value="99999"/>
<Setter Property="DecimalPlaces" Value="3"/>
<Setter Property="Foreground" Value="#1568D5"/>
</Style>
<Style x:Key="SmallIconButtonStyle" TargetType="Button">
<Setter Property="Width" Value="28"/>
<Setter Property="Height" Value="24"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="1,0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1"
CornerRadius="4">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#F0F4F8"/>
<Setter TargetName="bd" Property="BorderBrush" Value="#CBD5E0"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="bd" Property="Background" Value="#E2E8F0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="AddAxisButtonStyle" TargetType="Button" BasedOn="{StaticResource SmallIconButtonStyle}">
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="24"/>
<Setter Property="Foreground" Value="#28A745"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<Style x:Key="ModuleActionButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseRoundedButtonStyle}">
<Setter Property="Width" Value="110"/>
<Setter Property="Height" Value="32"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Margin" Value="4,0"/>
</Style>
<Style x:Key="CalibStartButtonStyle" TargetType="Button" BasedOn="{StaticResource ModuleActionButtonStyle}">
<Setter Property="Background" Value="#1A202C"/>
<Setter Property="BorderBrush" Value="#1A202C"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#2D3748"/>
<Setter Property="BorderBrush" Value="#2D3748"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#0F141B"/>
<Setter Property="BorderBrush" Value="#0F141B"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{StaticResource DisabledBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource DisabledBackground}"/>
<Setter Property="Foreground" Value="{StaticResource DisabledForeground}"/>
<Setter Property="Cursor" Value="Arrow"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="BadgeStyle" TargetType="Border">
<Setter Property="CornerRadius" Value="3"/>
<Setter Property="Padding" Value="5,1"/>
<Setter Property="Margin" Value="3,0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="0,0,4,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Padding="10,0">
<Grid>
<TextBlock Text="{DynamicResource OriginCalibMonitorTitle}"
Foreground="#7A8FA6"
FontSize="11"
FontWeight="SemiBold"
VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<Ellipse Width="7" Height="7" Fill="#28A745" Margin="0,0,4,0"/>
<TextBlock Text="LIVE" Foreground="#28A745" FontSize="10" FontWeight="Bold"/>
</StackPanel>
</Grid>
</Border>
<ContentControl Grid.Row="1" Content="{Binding CameraAxisViewModel}"/>
</Grid>
</Border>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="White" BorderBrush="#E0E4EA" BorderThickness="0,0,0,1" Padding="8,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button Content="{DynamicResource OriginCalibSaveConfig}"
Click="{mw:Action SaveConfig}"
Style="{StaticResource SaveButtonStyle}"
Width="160"
Height="34"
Margin="5,0"/>
<Button Content="{DynamicResource OriginCalibResetAll}"
Click="{mw:Action ResetAll}"
Style="{StaticResource ResetButtonStyle}"
Width="130"
Height="34"
Margin="5,0"/>
</StackPanel>
</Border>
<ScrollViewer Grid.Row="1"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
CanContentScroll="False"
PanningMode="VerticalOnly"
Padding="8,8,8,0">
<ItemsControl ItemsSource="{Binding Modules}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource ModuleCardStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="36"/>
<RowDefinition Height="*"/>
<RowDefinition Height="46"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Background="#F7F9FC"
BorderBrush="#E0E4EA"
BorderThickness="0,0,0,1"
CornerRadius="6,6,0,0"
Padding="10,0">
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="[] " Foreground="#8892A0" FontSize="14" VerticalAlignment="Center"/>
<TextBlock FontWeight="SemiBold" FontSize="13" Foreground="#2D3748" VerticalAlignment="Center">
<Run Text="{DynamicResource OriginCalibModuleLabel}"/>
<Run Text=" "/>
<Run Text="{Binding Index, StringFormat=D2}"/>
<Run Text=": "/>
<Run Text="{Binding ModuleName}"/>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border Style="{StaticResource BadgeStyle}"
Background="#EDF2F7"
Visibility="{Binding ShowIndependentBadge}">
<TextBlock Text="{DynamicResource OriginCalibBadgeIndependent}" FontSize="10" Foreground="#718096"/>
</Border>
<Border Style="{StaticResource BadgeStyle}"
Background="#FFF5EB"
Visibility="{Binding ShowAvoidanceBadge}">
<TextBlock Text="{DynamicResource OriginCalibBadgeAvoidance}" FontSize="10" Foreground="#E8821A"/>
</Border>
</StackPanel>
</Grid>
</Border>
<Grid Grid.Row="1" MinHeight="170">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderBrush="#E0E4EA" BorderThickness="0,0,0,1" Padding="10,8">
<StackPanel>
<Grid Margin="0,0,0,6">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Ellipse Width="8" Height="8" Fill="#E8821A" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBlock Text="{DynamicResource OriginCalibAvoidanceHeader}"
Foreground="#E8821A"
FontWeight="SemiBold"
FontSize="12"
VerticalAlignment="Center"/>
</StackPanel>
<Button Content="+"
HorizontalAlignment="Right"
Style="{StaticResource AddAxisButtonStyle}"
IsEnabled="{Binding CanStartCalib}"
Click="{mw:Action AddAvoidanceAxis}"
mw:View.ActionTarget="{Binding}"
ToolTip="{DynamicResource OriginCalibAddAvoidanceAxis}"/>
</Grid>
<ItemsControl ItemsSource="{Binding AvoidanceAxes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding AxisName}"
Style="{StaticResource AvoidanceAxisNameStyle}"
TextTrimming="CharacterEllipsis"
ToolTip="{DynamicResource OriginCalibAxisName}"/>
<mw:NumberBox Grid.Column="1"
Value="{Binding Position, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
mw:NumericKeypadAttach.IsEnabled="True"
IsEnabled="{Binding CanTeach}"
Minimum="-99999"
Maximum="99999"
DecimalPlaces="2"
Width="68"
Height="26"
Margin="0,1"
HorizontalAlignment="Right"/>
<Button Grid.Column="2"
Style="{StaticResource SmallIconButtonStyle}"
IsEnabled="{Binding CanTeach}"
Click="{mw:Action TeachPosition}"
mw:View.ActionTarget="{Binding}"
ToolTip="{DynamicResource OriginCalibTeachAvoidancePosition}">
<TextBlock Text="T" FontSize="12" Foreground="#4A5568"/>
</Button>
<Button Grid.Column="3"
Style="{StaticResource SmallIconButtonStyle}"
IsEnabled="{Binding CanTeach}"
Click="{mw:Action Remove}" mw:View.ActionTarget="{Binding}"
ToolTip="{DynamicResource OriginCalibRemoveAxis}">
<TextBlock Text="X" FontSize="11" Foreground="#A0AEC0"/>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
<Grid Grid.Row="1" Margin="10,8">
<Grid.RowDefinitions>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="65"/>
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="5" Background="#F7F9FC" BorderBrush="#E0E4EA" BorderThickness="0,0,0,1"/>
<TextBlock Grid.Column="0" Text="{DynamicResource OriginCalibAxisHeader}" Style="{StaticResource TableHeaderStyle}" Margin="6,0"/>
<TextBlock Grid.Column="1" Text="{DynamicResource OriginCalibTargetHeader}" Style="{StaticResource TableHeaderStyle}" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{DynamicResource OriginCalibActualHeader}" Style="{StaticResource TableHeaderStyle}" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="3" Text="{DynamicResource OriginCalibOffsetHeader}" Style="{StaticResource TableHeaderStyle}" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="4" Text="{DynamicResource OriginCalibStatusHeader}" Style="{StaticResource TableHeaderStyle}" HorizontalAlignment="Center"/>
</Grid>
<ItemsControl Grid.Row="1" ItemsSource="{Binding CalibrationAxes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid MinHeight="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="65"/>
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="5" BorderBrush="#F0F4F8" BorderThickness="0,0,0,1"/>
<TextBlock Grid.Column="0"
Text="{Binding AxisName}"
Style="{StaticResource CalibAxisNameStyle}"
Margin="6,0"
TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Column="1"
Text="{Binding TargetPosition, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="12"
FontWeight="SemiBold"/>
<TextBlock Grid.Column="2"
Text="{Binding LastPosition, StringFormat=F3}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="12"
Foreground="#4A5568"/>
<TextBlock Grid.Column="3"
Text="{Binding OffsetText}"
Foreground="{Binding OffsetBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="12"
FontWeight="SemiBold"/>
<Ellipse Grid.Column="4"
Width="12"
Height="12"
Fill="{Binding StatusBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
<Border Grid.Row="2"
BorderBrush="#E0E4EA"
BorderThickness="0,1,0,0"
Background="#FAFBFC"
CornerRadius="0,0,6,6"
Padding="10,0">
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Button Content="{DynamicResource OriginCalibTeachTarget}"
Click="{mw:Action TeachCalibPositions}"
mw:View.ActionTarget="{Binding}"
Style="{StaticResource CommonButtonStyle}"
Width="110"
Height="32"
Margin="0,0,6,0"/>
<Button Content="{DynamicResource MakeModel}"
Click="{mw:Action MakeTemplate}"
mw:View.ActionTarget="{Binding}"
Style="{StaticResource CommonButtonStyle}"
Width="110"
Height="32"
Margin="0,0,6,0"/>
<Button Content="{DynamicResource OriginCalibStartCalib}"
Click="{mw:Action StartCalib}"
mw:View.ActionTarget="{Binding}"
Style="{StaticResource CalibStartButtonStyle}"
IsEnabled="{Binding CanStartCalib}"
Width="110"
Height="32"
Margin="0,0,6,0"/>
</StackPanel>
<Button HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{StaticResource SmallIconButtonStyle}"
ToolTip="{DynamicResource OriginCalibModuleSettings}">
<TextBlock Text="..." FontSize="13" Foreground="#718096"/>
</Button>
</Grid>
</Border>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,12 @@
using System.Windows.Controls;
namespace MainShell.PageCalib.OriginCalib.View
{
public partial class OriginCalibView : UserControl
{
public OriginCalibView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,111 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.PageCalib.OriginCalib.Model;
using MainShell.PageCalib.OriginCalib.Service;
using Stylet;
using System;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.PageCalib.OriginCalib.ViewModel
{
public class AvoidanceAxisViewModel : PropertyChangedBase
{
private readonly HardwareManager _hardware;
private readonly OriginCalibrationMotionService _motionService;
private readonly AvoidanceAxisItem _item;
private readonly Action<AvoidanceAxisViewModel> _removeCallback;
private bool _canTeach = true;
private const string DialogCaption = "原点标定";
public AvoidanceAxisItem Item => _item;
public string AxisName
{
get { return _item.AxisName; }
set { _item.AxisName = value; NotifyOfPropertyChange(nameof(AxisName)); }
}
public double Position
{
get { return _item.Position; }
set { _item.Position = value; NotifyOfPropertyChange(nameof(Position)); }
}
public bool CanTeach
{
get { return _canTeach; }
set { SetAndNotify(ref _canTeach, value); }
}
public AvoidanceAxisViewModel(AvoidanceAxisItem item, HardwareManager hardware, OriginCalibrationMotionService motionService, Action<AvoidanceAxisViewModel> removeCallback)
{
_item = item ?? throw new ArgumentNullException(nameof(item));
_hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
_motionService = motionService ?? throw new ArgumentNullException(nameof(motionService));
_removeCallback = removeCallback;
}
public void TeachPosition()
{
try
{
if (_hardware.AxesDic != null && _hardware.AxesDic.ContainsKey(AxisName))
{
Position = Math.Round(_hardware.AxesDic[AxisName].State.ActualPos, 3);
string.Format("原点标定避让轴“{0}”示教位置成功:{1}", AxisName, Position).LogInfo();
}
else
{
string.Format("原点标定示教避让位置时未找到轴“{0}”。", AxisName).LogSysError();
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibAxisNotFound, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning, AxisName);
}
}
catch (Exception ex)
{
string.Format("原点标定读取轴“{0}”位置失败:{1}", AxisName, ex.Message).LogSysError();
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibReadPositionFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
}
}
public async Task MoveToPosition()
{
try
{
if (_hardware.AxesDic != null && _hardware.AxesDic.ContainsKey(AxisName))
{
await _motionService.MoveAxisAsync(AxisName, Position);
}
else
{
string.Format("原点标定移动避让轴时未找到轴“{0}”。", AxisName).LogSysError();
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibAxisNotFound, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning, AxisName);
}
}
catch (Exception ex)
{
string.Format("原点标定移动避让轴“{0}”失败:{1}", AxisName, ex.Message).LogSysError();
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibMoveFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
}
}
public void Remove()
{
try
{
if (_removeCallback != null)
{
string.Format("原点标定正在删除避让轴“{0}”。", AxisName).LogInfo();
_removeCallback(this);
}
}
catch (Exception ex)
{
string.Format("原点标定删除避让轴“{0}”失败:{1}", AxisName, ex.Message).LogSysError();
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibDeleteAxisFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
}
}
}
}

View File

@@ -0,0 +1,123 @@
using MainShell.PageCalib.OriginCalib.Model;
using Stylet;
using System;
using System.Windows.Media;
namespace MainShell.PageCalib.OriginCalib.ViewModel
{
public class CalibrationAxisViewModel : PropertyChangedBase
{
private static readonly double OffsetTolerance = 0.05;
private static readonly Brush InactiveBrush = CreateFrozenBrush(0xCC, 0xCC, 0xCC);
private static readonly Brush InToleranceBrush = CreateFrozenBrush(0x28, 0xA7, 0x45);
private static readonly Brush WarningBrush = CreateFrozenBrush(0xFD, 0x7E, 0x14);
private readonly CalibrationAxisItem _item;
public CalibrationAxisItem Item => _item;
public string AxisName
{
get { return _item.AxisName; }
set { _item.AxisName = value; NotifyOfPropertyChange(nameof(AxisName)); }
}
public double TargetPosition
{
get { return _item.TargetPosition; }
set
{
_item.TargetPosition = value;
NotifyOfPropertyChange(nameof(TargetPosition));
UpdateOffset();
}
}
public double LastPosition
{
get { return _item.LastPosition; }
set
{
_item.LastPosition = value;
NotifyOfPropertyChange(nameof(LastPosition));
UpdateOffset();
}
}
public double Offset
{
get { return _item.Offset; }
private set
{
_item.Offset = value;
NotifyOfPropertyChange(nameof(Offset));
NotifyOfPropertyChange(nameof(OffsetText));
NotifyOfPropertyChange(nameof(OffsetBrush));
NotifyOfPropertyChange(nameof(StatusBrush));
}
}
public bool IsCalibrated
{
get { return _item.IsCalibrated; }
set
{
_item.IsCalibrated = value;
NotifyOfPropertyChange(nameof(IsCalibrated));
NotifyOfPropertyChange(nameof(OffsetText));
NotifyOfPropertyChange(nameof(OffsetBrush));
NotifyOfPropertyChange(nameof(StatusBrush));
}
}
public string OffsetText
{
get
{
if (!IsCalibrated) return "-";
return Offset >= 0 ? string.Format("+{0:F3}", Offset) : string.Format("{0:F3}", Offset);
}
}
public Brush OffsetBrush
{
get
{
if (!IsCalibrated) return Brushes.Gray;
return Math.Abs(Offset) <= OffsetTolerance ? InToleranceBrush : WarningBrush;
}
}
public Brush StatusBrush
{
get
{
if (!IsCalibrated) return InactiveBrush;
return Math.Abs(Offset) <= OffsetTolerance ? InToleranceBrush : WarningBrush;
}
}
public CalibrationAxisViewModel(CalibrationAxisItem item)
{
_item = item ?? throw new ArgumentNullException(nameof(item));
}
public void Reset()
{
LastPosition = 0;
IsCalibrated = false;
}
private void UpdateOffset()
{
Offset = Math.Round(_item.LastPosition, 3);
}
private static Brush CreateFrozenBrush(byte red, byte green, byte blue)
{
var brush = new SolidColorBrush(Color.FromRgb(red, green, blue));
brush.Freeze();
return brush;
}
}
}

View File

@@ -0,0 +1,348 @@
using MainShell.Common;
using MainShell.Common.VisionTemple.ViewModel;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Motion;
using MainShell.PageCalib.OriginCalib.Model;
using MainShell.PageCalib.OriginCalib.Service;
using MainShell.Resources.CustomControl;
using MwFramework.Device;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.PageCalib.OriginCalib.ViewModel
{
public class OriginCalibModuleViewModel : PropertyChangedBase
{
private readonly HardwareManager _hardware;
private readonly OriginCalibrationMotionService _motionService;
private readonly OriginCalibModuleItem _item;
private readonly VisionTempleWindowViewModel _visionTempleWindowViewModel;
private int _index;
private bool _isCalibrating;
public OriginCalibModuleItem Item => _item;
public int Index
{
get { return _index; }
set { SetAndNotify(ref _index, value); }
}
public string ModuleName
{
get { return _item.ModuleName; }
set { _item.ModuleName = value; NotifyOfPropertyChange(nameof(ModuleName)); }
}
public bool IsIndependent
{
get { return _item.IsIndependent; }
set
{
_item.IsIndependent = value;
NotifyOfPropertyChange(nameof(IsIndependent));
NotifyOfPropertyChange(nameof(ShowIndependentBadge));
}
}
public bool IsCalibrating
{
get { return _isCalibrating; }
set
{
SetAndNotify(ref _isCalibrating, value);
NotifyOfPropertyChange(nameof(CanStartCalib));
// 标定进行中禁用避让示教和删除操作
foreach (AvoidanceAxisViewModel axis in AvoidanceAxes)
{
axis.CanTeach = !value;
}
}
}
public bool CanStartCalib
{
get { return !IsCalibrating; }
}
public bool HasAvoidanceAxes
{
get { return AvoidanceAxes.Count > 0; }
}
public System.Windows.Visibility ShowIndependentBadge
{
get { return IsIndependent ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; }
}
public System.Windows.Visibility ShowAvoidanceBadge
{
get { return HasAvoidanceAxes ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; }
}
public ObservableCollection<AvoidanceAxisViewModel> AvoidanceAxes { get; } = new ObservableCollection<AvoidanceAxisViewModel>();
public ObservableCollection<CalibrationAxisViewModel> CalibrationAxes { get; } = new ObservableCollection<CalibrationAxisViewModel>();
public OriginCalibModuleViewModel(
OriginCalibModuleItem item,
HardwareManager hardware,
OriginCalibrationMotionService motionService,
VisionTempleWindowViewModel visionTempleWindowViewModel)
{
_item = item ?? throw new ArgumentNullException(nameof(item));
_hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
_motionService = motionService ?? throw new ArgumentNullException(nameof(motionService));
_visionTempleWindowViewModel = visionTempleWindowViewModel ?? throw new ArgumentNullException(nameof(visionTempleWindowViewModel));
foreach (AvoidanceAxisItem axisItem in item.AvoidanceAxes)
{
AvoidanceAxes.Add(new AvoidanceAxisViewModel(axisItem, hardware, motionService, OnRemoveAvoidanceAxis));
}
foreach (CalibrationAxisItem calibItem in item.CalibrationAxes)
{
CalibrationAxes.Add(new CalibrationAxisViewModel(calibItem));
}
}
public void AddAvoidanceAxis()
{
AvoidanceAxisItem newItem = new AvoidanceAxisItem { AxisName = "NewAxis", Position = 0.0 };
_item.AvoidanceAxes.Add(newItem);
AvoidanceAxisViewModel newVm = new AvoidanceAxisViewModel(newItem, _hardware, _motionService, OnRemoveAvoidanceAxis);
newVm.CanTeach = !_isCalibrating;
AvoidanceAxes.Add(newVm);
NotifyOfPropertyChange(nameof(HasAvoidanceAxes));
NotifyOfPropertyChange(nameof(ShowAvoidanceBadge));
}
private void OnRemoveAvoidanceAxis(AvoidanceAxisViewModel vm)
{
if (_isCalibrating)
{
"原点标定进行中,无法删除避让轴。".LogSysError();
LocalizedMessageBox.Show(MessageKey.OriginCalibDeleteAxisDuringCalibration, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
try
{
string.Format("原点标定模块“{1}”准备删除避让轴“{0}”。", vm.AxisName, ModuleName).LogInfo();
_item.AvoidanceAxes.Remove(vm.Item);
AvoidanceAxes.Remove(vm);
NotifyOfPropertyChange(nameof(HasAvoidanceAxes));
NotifyOfPropertyChange(nameof(ShowAvoidanceBadge));
string.Format("原点标定删除避让轴成功,当前剩余 {0} 个避让轴。", AvoidanceAxes.Count).LogInfo();
}
catch (Exception ex)
{
string.Format("原点标定删除避让轴“{0}”失败:{1}", vm.AxisName, ex.Message).LogSysError();
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibDeleteAxisFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
}
}
public void TeachCalibPositions()
{
foreach (CalibrationAxisViewModel calibAxis in CalibrationAxes)
{
if (_hardware.AxesDic != null && _hardware.AxesDic.ContainsKey(calibAxis.AxisName))
{
calibAxis.TargetPosition = Math.Round(_hardware.AxesDic[calibAxis.AxisName].State.ActualPos, 3);
}
}
}
public void MakeTemplate()
{
try
{
if(Item.ModuleName== OriginCalibModuleNames.GantryX2)
{
_visionTempleWindowViewModel.SetCamera(CameraType.TopWideCamera);
}
else
{
_visionTempleWindowViewModel.SetCamera(CameraType.TopPositionCamera);
}
_visionTempleWindowViewModel.TemplatePath = _item.TemplatePath;
$"原点标定模块“{ModuleName}”打开视觉模板制作窗口。".LogInfo();
_visionTempleWindowViewModel.ShowWindow();
}
catch (Exception ex)
{
$"原点标定模块“{ModuleName}”打开视觉模板制作窗口失败:{ex.Message}".LogSysError();
LocalizedMessageBox.ShowFormat(
MessageKey.OriginCalibOpenVisionTemplateFailed,
MessageKey.TitleError,
MessageBoxButton.OK,
MessageBoxImage.Error,
ex.Message);
}
}
public async Task StartCalib()
{
if (IsCalibrating)
{
return;
}
IsCalibrating = true;
LoadingService.Instance.Show(string.Format("模块 {0} 正在标定...", ModuleName), true, true);
try
{
string.Format(
"原点标定模块“{0}”开始标定,避让轴数量:{1},标定轴数量:{2}。",
ModuleName,
AvoidanceAxes.Count,
CalibrationAxes.Count).LogInfo();
var calibrationResult = await _motionService.ExecuteAsync(
AvoidanceAxes.Select(x => x.Item),
CalibrationAxes.Select(x => x.Item),
LoadingService.Instance.Token);
// 执行模块特定的后处理逻辑,通常在运动完成后、标定写入前
if (_item.PostProcessor != null)
{
string.Format(
"原点标定模块“{0}”执行后处理“{1}”。",
ModuleName,
_item.PostProcessor.Name).LogInfo();
try
{
await _item.PostProcessor.ExecuteAsync(calibrationResult, LoadingService.Instance.Token, new CenterRecognitionParameters
{
TemplatePath = _item.TemplatePath,
Type = CenterRecognitionType.Template
});
string.Format(
"原点标定模块“{0}”后处理“{1}”完成。",
ModuleName,
_item.PostProcessor.Name).LogInfo();
}
catch (OperationCanceledException)
{
string.Format(
"原点标定模块“{0}”后处理“{1}”被取消。",
ModuleName,
_item.PostProcessor.Name).LogInfo();
throw;
}
catch (Exception ex)
{
string.Format(
"原点标定模块“{0}”后处理“{1}”失败:{2}",
ModuleName,
_item.PostProcessor.Name,
ex.Message).LogSysError();
throw;
}
}
foreach (CalibrationAxisViewModel calibAxis in CalibrationAxes)
{
IAxis axis = ResolveCalibrationAxis(calibAxis.AxisName);
if (axis != null)
{
calibAxis.LastPosition = Math.Round(GetActualPosition(axis), 3);
calibAxis.IsCalibrated = true;
continue;
}
double lastPosition;
if (calibrationResult.ActualPositions.TryGetValue(calibAxis.AxisName, out lastPosition))
{
calibAxis.LastPosition = lastPosition;
calibAxis.IsCalibrated = true;
}
}
string.Format("原点标定模块“{0}”标定完成。", ModuleName).LogInfo();
}
catch (OperationCanceledException)
{
string.Format("原点标定模块“{0}”取消标定。", ModuleName).LogInfo();
LocalizedMessageBox.Show(MessageKey.OriginCalibCalibrationCanceled, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (Exception ex)
{
string.Format("原点标定模块“{0}”标定失败:{1}", ModuleName, ex.Message).LogSysError();
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibCalibrationFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
}
finally
{
LoadingService.Instance.Hide();
IsCalibrating = false;
}
}
public void ResetModule()
{
foreach (CalibrationAxisViewModel calibAxis in CalibrationAxes)
{
calibAxis.Reset();
}
string.Format("原点标定模块“{0}”已复位。", ModuleName).LogInfo();
}
private IAxis ResolveCalibrationAxis(string axisName)
{
if (string.IsNullOrWhiteSpace(axisName))
{
return null;
}
if (_hardware.AxesDic != null && _hardware.AxesDic.ContainsKey(axisName))
{
return _hardware.AxesDic[axisName];
}
if (string.Equals(axisName, "X1", StringComparison.OrdinalIgnoreCase))
{
return _hardware.Axis_X1;
}
if (string.Equals(axisName, "Y1", StringComparison.OrdinalIgnoreCase))
{
return _hardware.Axis_Y1;
}
if (string.Equals(axisName, "X2", StringComparison.OrdinalIgnoreCase))
{
return _hardware.Axis_X2;
}
if (string.Equals(axisName, "Y2", StringComparison.OrdinalIgnoreCase))
{
return _hardware.Axis_Y2;
}
if (string.Equals(axisName, "WSX3", StringComparison.OrdinalIgnoreCase))
{
return _hardware.Axis_WS_X3;
}
return null;
}
private static double GetActualPosition(IAxis axis)
{
if (axis == null)
{
return 0;
}
return axis.State != null ? axis.State.ActualPos : axis.GetPositionImmediate();
}
}
}

View File

@@ -0,0 +1,182 @@
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Common.VisionTemple.ViewModel;
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Models;
using MainShell.Motion;
using MainShell.PageCalib.OriginCalib.Model;
using MainShell.PageCalib.OriginCalib.Service;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Device;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
namespace MainShell.PageCalib.OriginCalib.ViewModel
{
public class OriginCalibViewModel : BaseScreen, IPage
{
public string Name { get; set; } = "OriginCalib";
private readonly OriginCalibSetting _setting;
private readonly HardwareManager _hardware;
private readonly OriginCalibrationMotionService _originCalibrationMotionService;
private readonly ApproachAlignmentService _approachAlignmentService;
private readonly VisionTempleWindowViewModel _visionTempleWindowViewModel;
private readonly IEventAggregator _eventAggregator;
private bool _isInitialized;
private const string DialogCaption = "原点标定";
public ObservableCollection<OriginCalibModuleViewModel> Modules { get; } = new ObservableCollection<OriginCalibModuleViewModel>();
private CameraAxisViewModel _cameraAxisViewModel;
public CameraAxisViewModel CameraAxisViewModel
{
get { return _cameraAxisViewModel; }
private set { SetAndNotify(ref _cameraAxisViewModel, value); }
}
public OriginCalibViewModel(
HardwareManager hardware,
OriginCalibrationMotionService originCalibrationMotionService,
ApproachAlignmentService approachAlignmentService,
VisionTempleWindowViewModel visionTempleWindowViewModel,
IEventAggregator eventAggregator)
{
_hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
_originCalibrationMotionService = originCalibrationMotionService ?? throw new ArgumentNullException(nameof(originCalibrationMotionService));
_approachAlignmentService = approachAlignmentService ?? throw new ArgumentNullException(nameof(approachAlignmentService));
_visionTempleWindowViewModel = visionTempleWindowViewModel ?? throw new ArgumentNullException(nameof(visionTempleWindowViewModel));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_setting = OriginCalibSetting.LoadOrCreate();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_isInitialized)
{
return;
}
CameraAxisViewModel = IoC.Get<CameraAxisViewModel>();
CameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = _hardware.CameraAxisManager.TopPositionAndWideCameraAxisDevices;
InitializePostProcessors();
RebuildModuleViewModels();
_isInitialized = true;
}
private void InitializePostProcessors()
{
foreach (OriginCalibModuleItem module in _setting.Modules)
{
if (module == null)
{
continue;
}
module.PostProcessor = CreateDefaultPostProcessor(module);
}
}
private ICalibrationPostProcessor CreateDefaultPostProcessor(OriginCalibModuleItem module)
{
if (module == null)
{
return null;
}
if (string.IsNullOrWhiteSpace(module.TemplatePath) || module.CalibrationAxes == null)
{
return null;
}
List<ApproachAlignmentAxis> alignmentAxes = module.CalibrationAxes
.Where(item => item != null)
.Select(item => item.AxisName)
.Where(axisName => !string.IsNullOrWhiteSpace(axisName))
.Select(axisName => new ApproachAlignmentAxis(axisName, 0.005))
.ToList();
if (alignmentAxes.Count == 0)
{
return null;
}
return new SingleAlignmentPostProcessor(
_approachAlignmentService,
alignmentAxes,
CameraType.TopPositionCamera);
}
private void RebuildModuleViewModels()
{
Modules.Clear();
for (int i = 0; i < _setting.Modules.Count; i++)
{
OriginCalibModuleViewModel moduleVm = new OriginCalibModuleViewModel(
_setting.Modules[i],
_hardware,
_originCalibrationMotionService,
_visionTempleWindowViewModel);
moduleVm.Index = i + 1;
Modules.Add(moduleVm);
}
}
public void SaveConfig()
{
try
{
_setting.Write();
_eventAggregator.Publish(CreateOriginCalibDataChangedEventArgs());
LocalizedMessageBox.Show(MessageKey.OriginCalibConfigSaved, MessageKey.TitleInfo, MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibConfigSaveFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
}
}
private OriginCalibDataChangedEventArgs CreateOriginCalibDataChangedEventArgs()
{
List<OriginCalibAxisOffsetChangedItem> axisOffsets = new List<OriginCalibAxisOffsetChangedItem>();
foreach (OriginCalibModuleItem module in _setting.Modules)
{
if (module == null || module.CalibrationAxes == null)
{
continue;
}
foreach (CalibrationAxisItem calibrationAxis in module.CalibrationAxes)
{
if (calibrationAxis == null || string.IsNullOrWhiteSpace(calibrationAxis.AxisName))
{
continue;
}
axisOffsets.Add(new OriginCalibAxisOffsetChangedItem(calibrationAxis.AxisName, calibrationAxis.Offset));
}
}
return new OriginCalibDataChangedEventArgs(axisOffsets);
}
public void ResetAll()
{
foreach (OriginCalibModuleViewModel module in Modules)
{
module.ResetModule();
}
}
}
}