1using Dreamine.IO.Abstractions.Models;
2using Dreamine.IO.Abstractions.Results;
173 if (!payload.IsSuccess || payload.Value is
null)
175 return IoResult<string>.Failure(payload.Message ??
"Failed to parse Fastech slave information response.", payload.ErrorCode);
178 var slaveType = payload.Value[0];
179 var name = payload.Value.Length > 1
180 ? System.Text.Encoding.ASCII.GetString(payload.Value, 1, payload.Value.Length - 1).TrimEnd(
'\0')
183 return IoResult<string>.Success($
"SlaveType={slaveType}, Name={name}");
220 ArgumentNullException.ThrowIfNull(points);
260 if (!payload.IsSuccess || payload.Value is
null)
262 return IoResult<bool[]>.Failure(payload.Message ??
"Failed to parse Fastech input response.", payload.ErrorCode);
265 return IoResult<bool[]>.Success(
ToInputBits(payload.Value, count));
302 ArgumentNullException.ThrowIfNull(points);
342 if (!payload.IsSuccess || payload.Value is
null)
344 return IoResult<bool[]>.Failure(payload.Message ??
"Failed to parse Fastech output response.", payload.ErrorCode);
347 return IoResult<bool[]>.Success(
ToOutputBits(payload.Value, count));
392 ArgumentNullException.ThrowIfNull(values);
397 foreach (var value
in values)
399 if (value.Key.Channel is < 0 or > 15)
401 throw new ArgumentOutOfRangeException(nameof(values), value.Key.Channel,
"Channel must be between 0 and 15.");
412 var data =
new byte[8];
631 return payload.IsSuccess
633 : IoResult.Failure(payload.Message ??
"Failed to parse Fastech write response.", payload.ErrorCode);
676 private byte[]
BuildFrame(
byte frameType, IReadOnlyList<byte> data)
678 if (data.Count > 252)
680 throw new ArgumentOutOfRangeException(nameof(data), data.Count,
"Fastech frame data must be 252 bytes or less.");
683 var frame =
new byte[5 + data.Count];
685 frame[1] = checked((
byte)(3 + data.Count));
688 frame[4] = frameType;
690 for (var i = 0; i < data.Count; i++)
692 frame[5 + i] = data[i];
724 return new NotSupportedException(message);
753 return IoResult<double[]>.Failure(message);
804 private IoResult<byte[]>
ParseReply(IReadOnlyList<byte> responseFrame,
byte expectedFrameType,
int minimumPayloadLength)
806 ArgumentNullException.ThrowIfNull(responseFrame);
808 if (responseFrame.Count < 6)
810 return IoResult<byte[]>.Failure(
"The Fastech response frame is too short.");
813 if (responseFrame[0] !=
Header)
815 return IoResult<byte[]>.Failure($
"Invalid Fastech response header: 0x{responseFrame[0]:X2}.");
818 if (responseFrame[1] != responseFrame.Count - 2)
820 return IoResult<byte[]>.Failure($
"Invalid Fastech response length: {responseFrame[1]}.");
825 return IoResult<byte[]>.Failure($
"Invalid Fastech response reserved byte: 0x{responseFrame[3]:X2}.");
828 if (responseFrame[4] != expectedFrameType)
830 return IoResult<byte[]>.Failure($
"Unexpected Fastech response frame type: 0x{responseFrame[4]:X2}.");
833 var status = responseFrame[5];
836 return IoResult<byte[]>.Failure($
"Fastech communication status error: 0x{status:X2}.", status);
839 var payloadLength = responseFrame.Count - 6;
840 if (payloadLength < minimumPayloadLength)
842 return IoResult<byte[]>.Failure($
"The Fastech response payload length is {payloadLength}, expected at least {minimumPayloadLength}.");
845 var payload =
new byte[payloadLength];
846 for (var i = 0; i < payload.Length; i++)
848 payload[i] = responseFrame[6 + i];
851 return IoResult<byte[]>.Success(payload);
911 private static bool[]
ToInputBits(IReadOnlyList<byte> payload,
int count)
913 count = Math.Clamp(count, 0, 16);
914 var values =
new bool[count];
916 for (var i = 0; i < values.Length; i++)
918 var byteOffset = i < 8 ? 0 : 1;
919 var bitIndex = i % 8;
920 values[i] = (payload[byteOffset] & (1 << bitIndex)) != 0;
958 private static bool[]
ToOutputBits(IReadOnlyList<byte> payload,
int count)
960 count = Math.Clamp(count, 0, 16);
961 var values =
new bool[count];
963 for (var i = 0; i < values.Length; i++)
965 var byteOffset = i < 8 ? 2 : 3;
966 var bitIndex = i % 8;
967 values[i] = (payload[byteOffset] & (1 << bitIndex)) != 0;
999 var normalizedChannel = Math.Clamp(channel, 0, 15);
1000 var bitIndex = normalizedChannel < 8
1001 ? 8 + normalizedChannel
1002 : normalizedChannel - 8;
1004 return 1u << bitIndex;
1041 return ((uint)bytes[offset] << 24)
1042 | ((uint)bytes[offset + 1] << 16)
1043 | ((uint)bytes[offset + 2] << 8)
1044 | bytes[offset + 3];
1081 bytes[offset] = (byte)(value >> 24);
1082 bytes[offset + 1] = (byte)(value >> 16);
1083 bytes[offset + 2] = (byte)(value >> 8);
1084 bytes[offset + 3] = (byte)value;
byte[] BuildReadDigitalOutputs(IReadOnlyList< IoPoint > points)
IoResult< byte[]> ParseReply(IReadOnlyList< byte > responseFrame, byte expectedFrameType, int minimumPayloadLength)
const byte GetSlaveInfoFrameType
byte[] BuildReadDigitalInputs(IReadOnlyList< IoPoint > points)
static uint ReadUInt32BigEndian(IReadOnlyList< byte > bytes, int offset)
static void WriteUInt32BigEndian(uint value, byte[] bytes, int offset)
const byte GetInputFrameType
const byte SetOutputFrameType
IoResult< bool[]> ParseDigitalInputs(IReadOnlyList< byte > responseFrame, int count)
byte[] BuildReadAnalogInputs(IReadOnlyList< AnalogIoPoint > points)
byte[] BuildWriteAnalogOutputs(IReadOnlyDictionary< AnalogIoPoint, double > values)
byte[] BuildWriteDigitalOutputs(IReadOnlyDictionary< IoPoint, bool > values)
const string AnalogInputNotSupportedMessage
byte[] BuildFrame(byte frameType, IReadOnlyList< byte > data)
readonly object _syncLock
IoResult< double[]> ParseAnalogOutputs(IReadOnlyList< byte > responseFrame, int count)
byte[] BuildGetSlaveInfo()
IoResult< string > ParseSlaveInfo(IReadOnlyList< byte > responseFrame)
byte[] BuildReadAnalogOutputs(IReadOnlyList< AnalogIoPoint > points)
const byte GetOutputFrameType
IoResult< double[]> ParseAnalogInputs(IReadOnlyList< byte > responseFrame, int count)
IoResult< bool[]> ParseDigitalOutputs(IReadOnlyList< byte > responseFrame, int count)
IoResult ParseWriteResponse(IReadOnlyList< byte > responseFrame)
static uint GetDigital16PointOutputWriteBit(int channel)
static NotSupportedException CreateNotSupportedException(string message)
static IoResult< double[]> CreateUnsupportedAnalogResult(string message)
const string AnalogOutputNotSupportedMessage
static bool[] ToOutputBits(IReadOnlyList< byte > payload, int count)
const uint Digital16PointResetMask
static bool[] ToInputBits(IReadOnlyList< byte > payload, int count)