using MaxwellFramework.Core.Attributes; using MainShell.Hardware; using MwFramework.Device; using System; namespace MainShell.Hardware.Acs { public interface IAcsAddressAccessService { bool IsReady { get; } object Read(AcsAddressDefinition addressDefinition); object Read(AcsAddressDefinition addressDefinition, int index1, int index2); object ReadRange(AcsAddressDefinition addressDefinition, int from1, int to1, int from2, int to2); T Read(AcsAddressDefinition addressDefinition); bool Write(AcsAddressDefinition addressDefinition, object value); bool Write(AcsAddressDefinition addressDefinition, object value, int index1, int index2); bool WriteRange(AcsAddressDefinition addressDefinition, object value, int from1, int to1, int from2, int to2); } [Singleton] public sealed class AcsAddressAccessService : IAcsAddressAccessService { private const int DefaultBufferId = -1; private readonly HardwareManager _hardwareManager; public AcsAddressAccessService(HardwareManager hardwareManager) { _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager)); } public bool IsReady { get { return _hardwareManager.AcsCard != null && _hardwareManager.AcsCard.Controller != null && _hardwareManager.AcsCard.IsOpen; } } public object Read(AcsAddressDefinition addressDefinition) { AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition); IMotionController controller = GetController(); string variableName = ResolveVariableName(validatedAddress); bool result= controller.ReadVariableBuffer(DefaultBufferId, variableName,out object val); if (!result) { throw CreateReadWriteException("Read", validatedAddress); } return val; } public object Read(AcsAddressDefinition addressDefinition, int index1, int index2) { AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition); ValidateIndex(index1, nameof(index1)); ValidateIndex(index2, nameof(index2)); IMotionController controller = GetController(); string variableName = ResolveVariableName(validatedAddress); bool result = controller.ReadVariableBuffer(DefaultBufferId, variableName, out object val, index1, index2); if (!result) { throw CreateReadWriteException("Read", validatedAddress); } return val; } public object ReadRange(AcsAddressDefinition addressDefinition, int from1, int to1, int from2, int to2) { AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition); ValidateRange(from1, to1, nameof(from1), nameof(to1)); ValidateRange(from2, to2, nameof(from2), nameof(to2)); IMotionController controller = GetController(); string variableName = ResolveVariableName(validatedAddress); object value; bool result = controller.ReadVariableBuffer(DefaultBufferId, variableName, out value, from1, to1, from2, to2); if (!result) { throw CreateReadWriteException("Read", validatedAddress); } return value; } public T Read(AcsAddressDefinition addressDefinition) { AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition); ValidateTypeCompatibility(validatedAddress, typeof(T)); object value = Read(validatedAddress); return ConvertValue(value, validatedAddress); } public bool Write(AcsAddressDefinition addressDefinition, object value) { AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition); ValidateWriteValue(validatedAddress, value); IMotionController controller = GetController(); string variableName = ResolveVariableName(validatedAddress); bool result = controller.WriteVariableBuffer(DefaultBufferId, variableName, value); if (!result) { throw CreateReadWriteException("Write", validatedAddress); } return true; } public bool Write(AcsAddressDefinition addressDefinition, object value, int index1, int index2) { AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition); ValidateWriteValue(validatedAddress, value); ValidateIndex(index1, nameof(index1)); ValidateIndex(index2, nameof(index2)); IMotionController controller = GetController(); string variableName = ResolveVariableName(validatedAddress); bool result = controller.WriteVariableBuffer(DefaultBufferId, variableName, value, index1, index2); if (!result) { throw CreateReadWriteException("Write", validatedAddress); } return true; } public bool WriteRange(AcsAddressDefinition addressDefinition, object value, int from1, int to1, int from2, int to2) { AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition); ValidateWriteValue(validatedAddress, value); ValidateRange(from1, to1, nameof(from1), nameof(to1)); ValidateRange(from2, to2, nameof(from2), nameof(to2)); IMotionController controller = GetController(); string variableName = ResolveVariableName(validatedAddress); bool result = controller.WriteVariableBuffer(DefaultBufferId, variableName, value, from1, to1, from2, to2); if (!result) { throw CreateReadWriteException("Write", validatedAddress); } return true; } private IMotionController GetController() { if (!IsReady) { throw new InvalidOperationException("ACS controller is not ready. Ensure AcsCard is initialized and opened."); } return _hardwareManager.AcsCard.Controller; } private static AcsAddressDefinition ValidateAddress(AcsAddressDefinition addressDefinition) { if (addressDefinition == null) { throw new ArgumentNullException(nameof(addressDefinition)); } if (!addressDefinition.HasNumericAddress && !addressDefinition.HasSymbolicAddress) { throw new ArgumentException("ACS address definition does not contain a usable address.", nameof(addressDefinition)); } return addressDefinition; } private static string ResolveVariableName(AcsAddressDefinition addressDefinition) { if (addressDefinition.HasSymbolicAddress) { return addressDefinition.SymbolicAddress; } return addressDefinition.Address.Value.ToString(); } private static void ValidateTypeCompatibility(AcsAddressDefinition addressDefinition, Type targetType) { if (targetType == null) { throw new ArgumentNullException(nameof(targetType)); } Type nonNullableTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType; if (nonNullableTargetType == typeof(object)) { return; } if (addressDefinition.DataType == nonNullableTargetType) { return; } if (addressDefinition.IsInteger && IsIntegerType(nonNullableTargetType)) { return; } if (addressDefinition.IsFloatingPoint && IsFloatingPointType(nonNullableTargetType)) { return; } throw new InvalidOperationException($"Address {addressDefinition.Name} is defined as {addressDefinition.DataType.Name} and cannot be read as {nonNullableTargetType.Name}."); } private static T ConvertValue(object value, AcsAddressDefinition addressDefinition) { if (value == null) { return default(T); } if (value is T typedValue) { return typedValue; } Type targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); try { if (targetType == typeof(bool)) { return (T)(object)ConvertToBoolean(value); } object convertedValue = Convert.ChangeType(value, targetType); return (T)convertedValue; } catch (Exception ex) { throw new InvalidCastException($"The value of address {addressDefinition.Name} cannot be converted to {targetType.Name}.", ex); } } private static bool ConvertToBoolean(object value) { if (value is bool boolValue) { return boolValue; } if (value is string stringValue) { if (string.Equals(stringValue, "1", StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(stringValue, "0", StringComparison.OrdinalIgnoreCase)) { return false; } } return Convert.ToDouble(value) != 0d; } private static void ValidateWriteValue(AcsAddressDefinition addressDefinition, object value) { if (value == null) { throw new ArgumentNullException(nameof(value)); } if (addressDefinition.Length > 1) { return; } Type valueType = value.GetType(); if (addressDefinition.DataType.IsAssignableFrom(valueType)) { return; } if (addressDefinition.IsBoolean && IsBooleanCompatibleType(valueType)) { return; } if (addressDefinition.IsInteger && IsIntegerType(valueType)) { return; } if (addressDefinition.IsFloatingPoint && (IsFloatingPointType(valueType) || IsIntegerType(valueType))) { return; } throw new InvalidOperationException($"Address {addressDefinition.Name} is defined as {addressDefinition.DataType.Name}, but the current value type is {valueType.Name}."); } private static bool IsBooleanCompatibleType(Type type) { return type == typeof(bool) || IsIntegerType(type); } private static bool IsIntegerType(Type type) { return type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort) || type == typeof(int) || type == typeof(uint) || type == typeof(long) || type == typeof(ulong); } private static bool IsFloatingPointType(Type type) { return type == typeof(float) || type == typeof(double) || type == typeof(decimal); } private static void ValidateIndex(int index, string parameterName) { if (index < 0) { throw new ArgumentOutOfRangeException(parameterName, index, "ACS index cannot be less than 0."); } } private static void ValidateRange(int from, int to, string fromName, string toName) { ValidateIndex(from, fromName); ValidateIndex(to, toName); if (to < from) { throw new ArgumentException($"Parameter {toName} cannot be less than {fromName}.", toName); } } private static Exception CreateReadWriteException(string operationName, AcsAddressDefinition addressDefinition) { string addressText = addressDefinition.HasSymbolicAddress ? addressDefinition.SymbolicAddress : addressDefinition.Address.Value.ToString(); return new InvalidOperationException($"{operationName} ACS address failed: {addressDefinition.Name}, address={addressText}."); } } }