using MainShell.Common; using MainShell.PageCalib.OriginCalib.Model; using MaxwellFramework.Core.Attributes; using MwFramework.Device; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Reflection; using System.Threading; using System.Xml.Serialization; namespace MainShell.Hardware.Acs { public interface IAcsStartupInitializationService { bool IsInitialized { get; } void Initialize(); void WriteOriginCalibrationHomeOffsets(); } public sealed class AcsStartupInitializationService : IAcsStartupInitializationService { private const int CorrectionArrayLength = 100; private const int StartupPollingIntervalMilliseconds = 100; private const int StartupTimeoutMilliseconds = 10000; private const string InterferometerConfigRelativePath = @"Configuration\Card\Acs\Interferometer.xml"; private const string WorkspaceInterferometerConfigRelativePath = @"Bin\Configuration\Card\Acs\Interferometer.xml"; private readonly object _syncRoot = new object(); private readonly IAcsAddressAccessService _acsAddressAccessService; private readonly AcsAddressRepository _acsAddressRepository; private readonly HardwareManager _hardwareManager; private bool _isInitialized; public AcsStartupInitializationService( IAcsAddressAccessService acsAddressAccessService, AcsAddressRepository acsAddressRepository, HardwareManager hardwareManager) { _acsAddressAccessService = acsAddressAccessService ?? throw new ArgumentNullException(nameof(acsAddressAccessService)); _acsAddressRepository = acsAddressRepository ?? throw new ArgumentNullException(nameof(acsAddressRepository)); _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager)); } public bool IsInitialized { get { return _isInitialized; } } public void Initialize() { lock (_syncRoot) { WriteInterferometerStartupParameters(); WriteOriginCalibrationHomeOffsetsCore(); _isInitialized = true; } } public void WriteOriginCalibrationHomeOffsets() { WriteOriginCalibrationHomeOffsetsCore(); } private static void ValidateAxisIndex(int axisIndex, int axisCount) { if (axisIndex < 0 || axisIndex >= axisCount) { throw new ArgumentOutOfRangeException(nameof(axisIndex), axisIndex, "Axis index is outside the loaded ACS startup parameter range."); } } private void WriteInterferometerStartupParameters() { ObservableCollection corrections = LoadInterferometerCorrections(); AcsCommonAdress commonData = _acsAddressRepository.GetCommonData(); AcsInterferometerCompensationAdress interferometerData = _acsAddressRepository.GetInterferometerCompensationData(); AcsHomeSettingAdress homeSettingData = _acsAddressRepository.GetHomeSettingData(); int axisCount = homeSettingData.PA_HomeMode.Length; double[] startPositions = new double[axisCount]; double[] stepLengths = new double[axisCount]; int[] correctionOpens = new int[axisCount]; int[] homeModes = new int[axisCount]; double[] homeVelocities = new double[axisCount]; int[] homeOrders = new int[axisCount]; foreach (CorrectionItem correctionItem in corrections) { if (correctionItem == null) { continue; } ValidateAxisIndex(correctionItem.AxisIndex, axisCount); double[] correctionValues = BuildCorrectionArray(correctionItem); startPositions[correctionItem.AxisIndex] = correctionItem.StartPos; stepLengths[correctionItem.AxisIndex] = correctionItem.StepLength; correctionOpens[correctionItem.AxisIndex] = correctionItem.Enable ? 1 : 0; homeModes[correctionItem.AxisIndex] = correctionItem.HomeMode; homeVelocities[correctionItem.AxisIndex] = correctionItem.HomeSpeed; homeOrders[correctionItem.AxisIndex] = correctionItem.HomeOrder; AcsAddressDefinition correctionBufferAddress; if (TryGetCorrectionBufferAddress(correctionItem.AxisName, interferometerData, out correctionBufferAddress)) { _acsAddressAccessService.Write(correctionBufferAddress, correctionValues); } } _acsAddressAccessService.Write(interferometerData.PA_StartPos, startPositions); _acsAddressAccessService.Write(interferometerData.PA_Step_len, stepLengths); _acsAddressAccessService.Write(interferometerData.PA_CorrectionOpen, correctionOpens); _acsAddressAccessService.Write(homeSettingData.PA_HomeMode, homeModes); _acsAddressAccessService.Write(homeSettingData.PA_HomeVel, homeVelocities); _acsAddressAccessService.Write(homeSettingData.PA_HomeOrder, homeOrders); _acsAddressAccessService.Write(commonData.PA_ComSupMotionType, (int)ComSupMotionType.Interfere); WaitForStartupWriteResult(commonData); } private ObservableCollection LoadInterferometerCorrections() { string configPath = ResolveInterferometerConfigPath(); XmlSerializer xmlSerializer = new XmlSerializer(typeof(ObservableCollection)); using (FileStream fileStream = File.OpenRead(configPath)) { ObservableCollection corrections = xmlSerializer.Deserialize(fileStream) as ObservableCollection; if (corrections == null) { throw new InvalidOperationException("The interferometer configuration could not be deserialized."); } return corrections; } } private static double[] BuildCorrectionArray(CorrectionItem correctionItem) { double[] correctionValues = new double[CorrectionArrayLength]; if (correctionItem.CorrectionDatas == null) { return correctionValues; } int count = Math.Min(correctionItem.CorrectionDatas.Count, correctionValues.Length); for (int index = 0; index < count; index++) { CorrectionData correctionData = correctionItem.CorrectionDatas[index]; if (correctionData == null) { continue; } correctionValues[index] = correctionData.ScaleData; } return correctionValues; } private void WriteOriginCalibrationHomeOffsetsCore() { OriginCalibSetting setting = OriginCalibSetting.LoadOrCreate(); AcsHomeOffsetAdress homeOffsetData = _acsAddressRepository.GetHomeOffsetData(); double[] homeOffsets = new double[homeOffsetData.PA_HomeOffset.Length]; WriteHomeOffset(homeOffsets, _hardwareManager.Axis_X1, GetCalibrationOffset(setting, OriginCalibModuleNames.GantryX1Y1, AxisName.Axis_PHS_X1)); WriteHomeOffset(homeOffsets, _hardwareManager.Axis_Y1, GetCalibrationOffset(setting, OriginCalibModuleNames.GantryX1Y1, AxisName.Axis_PHS_Y1)); WriteHomeOffset(homeOffsets, _hardwareManager.Axis_X2, GetCalibrationOffset(setting, OriginCalibModuleNames.GantryX2, AxisName.Axis_PHS_X2)); WriteHomeOffset(homeOffsets, _hardwareManager.Axis_Stage_Y3, GetCalibrationOffset(setting, OriginCalibModuleNames.StageY2, AxisName.Axis_Stage_Y3)); WriteHomeOffset(homeOffsets, _hardwareManager.Axis_WS_X3, GetCalibrationOffset(setting, OriginCalibModuleNames.WSX3, AxisName.Axis_WS_X3)); _acsAddressAccessService.Write(homeOffsetData.PA_HomeOffset, homeOffsets); } private static double GetCalibrationOffset(OriginCalibSetting setting, string moduleName, string axisName) { if (setting == null) { throw new ArgumentNullException(nameof(setting)); } OriginCalibModuleItem module = setting.Modules.Find(item => item != null && string.Equals(item.ModuleName, moduleName, StringComparison.OrdinalIgnoreCase)); if (module == null || module.CalibrationAxes == null) { return 0d; } CalibrationAxisItem calibrationAxis = module.CalibrationAxes.Find(item => item != null && string.Equals(item.AxisName, axisName, StringComparison.OrdinalIgnoreCase)); if (calibrationAxis == null) { return 0d; } return calibrationAxis.Offset; } private static void WriteHomeOffset(double[] homeOffsets, IAxis axis, double offset) { if (homeOffsets == null) { throw new ArgumentNullException(nameof(homeOffsets)); } if (axis == null) { throw new InvalidOperationException("The target axis for origin calibration home offset was not found."); } int axisIndex = axis.AxisIndex; ValidateAxisIndex(axisIndex, homeOffsets.Length); homeOffsets[axisIndex] = offset; } private void WaitForStartupWriteResult(AcsCommonAdress commonData) { if (commonData == null) { throw new ArgumentNullException(nameof(commonData)); } DateTime deadline = DateTime.Now.AddMilliseconds(StartupTimeoutMilliseconds); int result = 0; while (DateTime.Now <= deadline) { object rawResult = _acsAddressAccessService.Read(commonData.AP_ComSupMotionRes); if (rawResult != null) { result = Convert.ToInt32(rawResult); if (result == -1) { throw new InvalidOperationException("Writing interferometer startup parameters to ACS failed."); } if (result == 1) { return; } } Thread.Sleep(StartupPollingIntervalMilliseconds); } throw new TimeoutException("Writing interferometer startup parameters to ACS timed out."); } private static string ResolveInterferometerConfigPath() { List candidatePaths = new List { Path.Combine(AppDomain.CurrentDomain.BaseDirectory, InterferometerConfigRelativePath), Path.Combine(Environment.CurrentDirectory, WorkspaceInterferometerConfigRelativePath), Path.Combine(Environment.CurrentDirectory, InterferometerConfigRelativePath) }; foreach (string candidatePath in candidatePaths) { if (File.Exists(candidatePath)) { return candidatePath; } } throw new FileNotFoundException("The interferometer startup configuration file was not found."); } private static bool TryGetCorrectionBufferAddress( string axisName, AcsInterferometerCompensationAdress interferometerData, out AcsAddressDefinition correctionBufferAddress) { if (interferometerData == null) { throw new ArgumentNullException(nameof(interferometerData)); } correctionBufferAddress = null; if (string.IsNullOrWhiteSpace(axisName)) { return false; } string normalizedAxisName = axisName.Trim().ToUpperInvariant(); switch (normalizedAxisName) { case "X1": case "X11": correctionBufferAddress = interferometerData.PA_Correction_X1; return true; case "Y1": case "Y11": correctionBufferAddress = interferometerData.PA_Correction_Y1; return true; case "X2": case "X21": correctionBufferAddress = interferometerData.PA_Correction_X2; return true; case "Y2": case "Y21": correctionBufferAddress = interferometerData.PA_Correction_Y2; return true; default: return false; } } private static int ResolveInterfereMotionTypeValue() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type[] types; try { types = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { types = ex.Types; } foreach (Type type in types) { if (type == null || !type.IsEnum || !string.Equals(type.Name, "ComSupMotionType", StringComparison.Ordinal)) { continue; } if (!Enum.IsDefined(type, "Interfere")) { continue; } object enumValue = Enum.Parse(type, "Interfere"); return Convert.ToInt32(enumValue); } } throw new InvalidOperationException("The ComSupMotionType.Interfere enumeration value could not be resolved."); } } }