356 lines
13 KiB
C#
356 lines
13 KiB
C#
|
|
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<T>(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<T>(AcsAddressDefinition addressDefinition)
|
||
|
|
{
|
||
|
|
AcsAddressDefinition validatedAddress = ValidateAddress(addressDefinition);
|
||
|
|
ValidateTypeCompatibility(validatedAddress, typeof(T));
|
||
|
|
object value = Read(validatedAddress);
|
||
|
|
return ConvertValue<T>(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<T>(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}.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|