diff --git a/src/FluentModbus/Client/ModbusClient.cs b/src/FluentModbus/Client/ModbusClient.cs
index 48cdbf6..f7120c0 100755
--- a/src/FluentModbus/Client/ModbusClient.cs
+++ b/src/FluentModbus/Client/ModbusClient.cs
@@ -27,7 +27,7 @@ public abstract partial class ModbusClient
/// The unit identifier.
/// The function code.
/// An action to be called to extend the prepared Modbus frame with function code specific data.
- protected abstract Span TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame);
+ protected abstract Span TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame);
internal void ProcessError(ModbusFunctionCode functionCode, ModbusExceptionCode exceptionCode)
{
@@ -214,7 +214,7 @@ public void WriteMultipleRegisters(byte unitIdentifier, ushort startingAddress,
writer.Write((byte)(quantity * 2)); // 12 Byte Count = Quantity of Registers * 2
- writer.Write(dataset, 0, dataset.Length);
+ writer.Write(dataset);
});
}
@@ -422,7 +422,10 @@ public void WriteSingleRegister(byte unitIdentifier, ushort registerAddress, byt
else
writer.Write(registerAddress); // 08-09 Starting Address
- writer.Write(value); // 10-11 Value
+ foreach (var b in value)
+ {
+ writer.Write(b);
+ }
});
}
@@ -463,7 +466,10 @@ public void WriteMultipleCoils(int unitIdentifier, int startingAddress, bool[] v
writer.Write((byte)byteCount); // 12 Byte Count = Outputs
- writer.Write(convertedData);
+ foreach (var b in convertedData)
+ {
+ writer.Write(b);
+ }
});
}
@@ -562,7 +568,10 @@ public Span ReadWriteMultipleRegisters(byte unitIdentifier, ushort readSta
writer.Write((byte)(writeQuantity * 2)); // 16 Byte Count = Quantity to Write * 2
- writer.Write(dataset, 0, dataset.Length);
+ foreach (var b in dataset)
+ {
+ writer.Write(b);
+ }
});
if (buffer.Length < readQuantity * 2 + 2)
diff --git a/src/FluentModbus/Client/ModbusClientAsync.cs b/src/FluentModbus/Client/ModbusClientAsync.cs
index 790ec66..a0716a7 100755
--- a/src/FluentModbus/Client/ModbusClientAsync.cs
+++ b/src/FluentModbus/Client/ModbusClientAsync.cs
@@ -17,7 +17,7 @@ public abstract partial class ModbusClient
/// The function code.
/// An action to be called to extend the prepared Modbus frame with function code specific data.
/// The token to monitor for cancellation requests. The default value is .
- protected abstract Task> TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame, CancellationToken cancellationToken = default);
+ protected abstract Task> TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame, CancellationToken cancellationToken = default);
///
/// Reads the specified number of values of type from the holding registers.
@@ -33,8 +33,8 @@ public async Task> ReadHoldingRegistersAsync(int unitIdentifier, in
var startingAddress_converted = ConvertUshort(startingAddress);
var count_converted = ConvertUshort(count);
- var dataset = SpanExtensions.Cast(await
- ReadHoldingRegistersAsync(unitIdentifier_converted, startingAddress_converted, ConvertSize(count_converted), cancellationToken).ConfigureAwait(false));
+ var dataset = MemoryMarshal.Cast((await
+ ReadHoldingRegistersAsync(unitIdentifier_converted, startingAddress_converted, ConvertSize(count_converted), cancellationToken).ConfigureAwait(false)).Span);
if (SwapBytes)
ModbusUtils.SwitchEndianness(dataset);
@@ -125,7 +125,7 @@ await TransceiveFrameAsync(unitIdentifier, ModbusFunctionCode.WriteMultipleRegis
writer.Write((byte)(quantity * 2)); // 12 Byte Count = Quantity of Registers * 2
- writer.Write(dataset, 0, dataset.Length);
+ writer.Write(dataset);
}, cancellationToken).ConfigureAwait(false);
}
@@ -213,8 +213,8 @@ public async Task> ReadInputRegistersAsync(int unitIdentifier, int
var startingAddress_converted = ConvertUshort(startingAddress);
var count_converted = ConvertUshort(count);
- var dataset = SpanExtensions.Cast(await
- ReadInputRegistersAsync(unitIdentifier_converted, startingAddress_converted, ConvertSize(count_converted), cancellationToken).ConfigureAwait(false));
+ var dataset = MemoryMarshal.Cast((await
+ ReadInputRegistersAsync(unitIdentifier_converted, startingAddress_converted, ConvertSize(count_converted), cancellationToken).ConfigureAwait(false)).Span);
if (SwapBytes)
ModbusUtils.SwitchEndianness(dataset);
@@ -339,7 +339,7 @@ await TransceiveFrameAsync(unitIdentifier, ModbusFunctionCode.WriteSingleRegiste
else
writer.Write(registerAddress); // 08-09 Starting Address
- writer.Write(value); // 10-11 Value
+ writer.Write(value);
}, cancellationToken).ConfigureAwait(false);
}
@@ -350,7 +350,7 @@ await TransceiveFrameAsync(unitIdentifier, ModbusFunctionCode.WriteSingleRegiste
/// The coil register start address for the write operation.
/// The values to write to the server.
/// The token to monitor for cancellation requests. The default value is .
- public void WriteMultipleCoilsAsync(int unitIdentifier, int startingAddress, bool[] values, CancellationToken cancellationToken = default)
+ public async Task WriteMultipleCoilsAsync(int unitIdentifier, int startingAddress, bool[] values, CancellationToken cancellationToken = default)
{
var unitIdentifier_converted = ConvertUnitIdentifier(unitIdentifier);
var startingAddress_converted = ConvertUshort(startingAddress);
@@ -361,7 +361,7 @@ public void WriteMultipleCoilsAsync(int unitIdentifier, int startingAddress, boo
new BitArray(values)
.CopyTo(convertedData, 0);
- TransceiveFrameAsync(unitIdentifier_converted, ModbusFunctionCode.WriteMultipleCoils, writer =>
+ await TransceiveFrameAsync(unitIdentifier_converted, ModbusFunctionCode.WriteMultipleCoils, writer =>
{
writer.Write((byte)ModbusFunctionCode.WriteMultipleCoils); // 07 Function Code
@@ -380,7 +380,7 @@ public void WriteMultipleCoilsAsync(int unitIdentifier, int startingAddress, boo
writer.Write((byte)byteCount); // 12 Byte Count = Outputs
writer.Write(convertedData);
- }, cancellationToken);
+ }, cancellationToken).ConfigureAwait(false);
}
///
@@ -435,7 +435,7 @@ public async Task> ReadWriteMultipleRegistersAsync(
var readQuantity = ConvertSize(readCount_converted);
var byteData = MemoryMarshal.Cast(dataset).ToArray();
- var dataset2 = SpanExtensions.Cast(await ReadWriteMultipleRegistersAsync(unitIdentifier_converted, readStartingAddress_converted, readQuantity, writeStartingAddress_converted, byteData, cancellationToken).ConfigureAwait(false));
+ var dataset2 = MemoryMarshal.Cast((await ReadWriteMultipleRegistersAsync(unitIdentifier_converted, readStartingAddress_converted, readQuantity, writeStartingAddress_converted, byteData, cancellationToken).ConfigureAwait(false)).Span);
if (SwapBytes)
ModbusUtils.SwitchEndianness(dataset2);
@@ -480,7 +480,7 @@ public async Task> ReadWriteMultipleRegistersAsync(byte unitIdentif
writer.Write((byte)(writeQuantity * 2)); // 16 Byte Count = Quantity to Write * 2
- writer.Write(dataset, 0, dataset.Length);
+ writer.Write(dataset);
}, cancellationToken).ConfigureAwait(false);
if (buffer.Length < readQuantity * 2 + 2)
diff --git a/src/FluentModbus/Client/ModbusRtuClient.cs b/src/FluentModbus/Client/ModbusRtuClient.cs
index c6f46d2..0f92f37 100755
--- a/src/FluentModbus/Client/ModbusRtuClient.cs
+++ b/src/FluentModbus/Client/ModbusRtuClient.cs
@@ -145,7 +145,7 @@ public void Close()
}
///
- protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame)
+ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame)
{
// WARNING: IF YOU EDIT THIS METHOD, REFLECT ALL CHANGES ALSO IN TransceiveFrameAsync!
@@ -174,32 +174,31 @@ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctio
}
}
- _frameBuffer.Writer.Seek(0, SeekOrigin.Begin);
- _frameBuffer.Writer.Write(unitIdentifier); // 00 Unit Identifier
- extendFrame(_frameBuffer.Writer);
- frameLength = (int)_frameBuffer.Writer.BaseStream.Position;
+ var writer = new SpanWriter(_frameBuffer.Buffer.Span);
+ writer.Write(unitIdentifier); // 00 Unit Identifier
+ extendFrame(writer);
+ frameLength = writer.Position;
// add CRC
- crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.AsMemory()[..frameLength]);
- _frameBuffer.Writer.Write(crc);
- frameLength = (int)_frameBuffer.Writer.BaseStream.Position;
+ crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.Slice(0, frameLength));
+ writer.Write(crc);
+ frameLength = writer.Position;
// send request
- _serialPort!.Value.Value.Write(_frameBuffer.Buffer, 0, frameLength);
+ _serialPort!.Value.Value.Write(_frameBuffer.Buffer.Slice(0, frameLength));
// special case: broadcast (only for write commands)
if (unitIdentifier == 0)
- return _frameBuffer.Buffer.AsSpan(0, 0);
+ return _frameBuffer.Buffer.Span.Slice(0, 0);
// wait for and process response
frameLength = 0;
- _frameBuffer.Reader.BaseStream.Seek(0, SeekOrigin.Begin);
while (true)
{
- frameLength += _serialPort!.Value.Value.Read(_frameBuffer.Buffer, frameLength, _frameBuffer.Buffer.Length - frameLength);
+ frameLength += _serialPort!.Value.Value.Read(_frameBuffer.Buffer.Slice(frameLength));
- if (ModbusUtils.DetectResponseFrame(unitIdentifier, _frameBuffer.Buffer.AsMemory()[..frameLength]))
+ if (ModbusUtils.DetectResponseFrame(unitIdentifier, _frameBuffer.Buffer.Slice(0, frameLength)))
{
break;
}
@@ -213,16 +212,17 @@ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctio
}
}
- _ = _frameBuffer.Reader.ReadByte();
- rawFunctionCode = _frameBuffer.Reader.ReadByte();
+ var reader = new SpanReader(_frameBuffer.Buffer.Span);
+ _ = reader.ReadByte();
+ rawFunctionCode = reader.ReadByte();
if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode)
- ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer[2]);
+ ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer.Span[2]);
else if (rawFunctionCode != (byte)functionCode)
throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode);
- return _frameBuffer.Buffer.AsSpan(1, frameLength - 3);
+ return _frameBuffer.Buffer.Span.Slice(1, frameLength - 3);
}
#endregion
diff --git a/src/FluentModbus/Client/ModbusRtuClientAsync.cs b/src/FluentModbus/Client/ModbusRtuClientAsync.cs
index c376418..cf44937 100755
--- a/src/FluentModbus/Client/ModbusRtuClientAsync.cs
+++ b/src/FluentModbus/Client/ModbusRtuClientAsync.cs
@@ -6,7 +6,7 @@ namespace FluentModbus
public partial class ModbusRtuClient
{
///
- protected override async Task> TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame, CancellationToken cancellationToken = default)
+ protected override async Task> TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame, CancellationToken cancellationToken = default)
{
// WARNING: IF YOU EDIT THIS METHOD, REFLECT ALL CHANGES ALSO IN TransceiveFrameAsync!
@@ -35,32 +35,31 @@ protected override async Task> TransceiveFrameAsync(byte unitIdenti
}
}
- _frameBuffer.Writer.Seek(0, SeekOrigin.Begin);
- _frameBuffer.Writer.Write(unitIdentifier); // 00 Unit Identifier
- extendFrame(_frameBuffer.Writer);
- frameLength = (int)_frameBuffer.Writer.BaseStream.Position;
+ var writer = new SpanWriter(_frameBuffer.Buffer.Span);
+ writer.Write(unitIdentifier); // 00 Unit Identifier
+ extendFrame(writer);
+ frameLength = writer.Position;
// add CRC
- crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.AsMemory()[..frameLength]);
- _frameBuffer.Writer.Write(crc);
- frameLength = (int)_frameBuffer.Writer.BaseStream.Position;
+ crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.Slice(0, frameLength));
+ writer.Write(crc);
+ frameLength = writer.Position;
// send request
- await _serialPort!.Value.Value.WriteAsync(_frameBuffer.Buffer, 0, frameLength, cancellationToken).ConfigureAwait(false);
+ await _serialPort!.Value.Value.WriteAsync(_frameBuffer.Buffer.Slice(0, frameLength), cancellationToken).ConfigureAwait(false);
// special case: broadcast (only for write commands)
if (unitIdentifier == 0)
- return _frameBuffer.Buffer.AsMemory(0, 0);
+ return _frameBuffer.Buffer.Slice(0, 0);
// wait for and process response
frameLength = 0;
- _frameBuffer.Reader.BaseStream.Seek(0, SeekOrigin.Begin);
while (true)
{
- frameLength += await _serialPort!.Value.Value.ReadAsync(_frameBuffer.Buffer, frameLength, _frameBuffer.Buffer.Length - frameLength, cancellationToken).ConfigureAwait(false);
+ frameLength += await _serialPort!.Value.Value.ReadAsync(_frameBuffer.Buffer.Slice(frameLength), cancellationToken).ConfigureAwait(false);
- if (ModbusUtils.DetectResponseFrame(unitIdentifier, _frameBuffer.Buffer.AsMemory()[..frameLength]))
+ if (ModbusUtils.DetectResponseFrame(unitIdentifier, _frameBuffer.Buffer.Slice(0, frameLength)))
{
break;
}
@@ -74,16 +73,17 @@ protected override async Task> TransceiveFrameAsync(byte unitIdenti
}
}
- _ = _frameBuffer.Reader.ReadByte();
- rawFunctionCode = _frameBuffer.Reader.ReadByte();
+ var reader = new SpanReader(_frameBuffer.Buffer.Span);
+ _ = reader.ReadByte();
+ rawFunctionCode = reader.ReadByte();
if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode)
- ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer[2]);
+ ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer.Span[2]);
else if (rawFunctionCode != (byte)functionCode)
throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode);
- return _frameBuffer.Buffer.AsMemory(1, frameLength - 3);
+ return _frameBuffer.Buffer.Slice(1, frameLength - 3);
}
}
}
\ No newline at end of file
diff --git a/src/FluentModbus/Client/ModbusTcpClient.cs b/src/FluentModbus/Client/ModbusTcpClient.cs
index a103a54..9239c31 100755
--- a/src/FluentModbus/Client/ModbusTcpClient.cs
+++ b/src/FluentModbus/Client/ModbusTcpClient.cs
@@ -202,7 +202,7 @@ public void Disconnect()
}
///
- protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame)
+ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame)
{
// WARNING: IF YOU EDIT THIS METHOD, REFLECT ALL CHANGES ALSO IN TransceiveFrameAsync!
@@ -216,21 +216,15 @@ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctio
bool isParsed;
- ModbusFrameBuffer frameBuffer;
- ExtendedBinaryWriter writer;
- ExtendedBinaryReader reader;
-
bytesFollowing = 0;
- frameBuffer = _frameBuffer;
- writer = _frameBuffer.Writer;
- reader = _frameBuffer.Reader;
// build request
- writer.Seek(7, SeekOrigin.Begin);
+ var writer = new SpanWriter(_frameBuffer.Buffer.Span);
+ writer.Seek(7);
extendFrame(writer);
- frameLength = (int)writer.BaseStream.Position;
+ frameLength = writer.Position;
- writer.Seek(0, SeekOrigin.Begin);
+ writer.Seek(0);
if (BitConverter.IsLittleEndian)
{
@@ -248,12 +242,11 @@ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctio
writer.Write(unitIdentifier); // 06 Unit Identifier
// send request
- _networkStream.Write(frameBuffer.Buffer, 0, frameLength);
+ _networkStream.Write(_frameBuffer.Buffer.Span.Slice(0, frameLength));
// wait for and process response
frameLength = 0;
isParsed = false;
- reader.BaseStream.Seek(0, SeekOrigin.Begin);
while (true)
{
@@ -266,7 +259,7 @@ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctio
// ASYNC-ONLY: {
// ASYNC-ONLY: try
// ASYNC-ONLY: {
- partialLength = _networkStream.Read(frameBuffer.Buffer, frameLength, frameBuffer.Buffer.Length - frameLength);
+ partialLength = _networkStream.Read(_frameBuffer.Buffer.Span.Slice(frameLength));
// ASYNC-ONLY: }
// ASYNC-ONLY: catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
// ASYNC-ONLY: {
@@ -300,6 +293,8 @@ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctio
{
if (!isParsed) // read MBAP header only once
{
+ var reader = new SpanReader(_frameBuffer.Buffer.Span);
+
// read MBAP header
_ = reader.ReadUInt16Reverse(); // 00-01 Transaction Identifier
protocolIdentifier = reader.ReadUInt16Reverse(); // 02-03 Protocol Identifier
@@ -318,15 +313,17 @@ protected override Span TransceiveFrame(byte unitIdentifier, ModbusFunctio
}
}
- rawFunctionCode = reader.ReadByte();
+ var finalReader = new SpanReader(_frameBuffer.Buffer.Span);
+ finalReader.Seek(7);
+ rawFunctionCode = finalReader.ReadByte();
if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode)
- ProcessError(functionCode, (ModbusExceptionCode)frameBuffer.Buffer[8]);
+ ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer.Span[8]);
else if (rawFunctionCode != (byte)functionCode)
throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode);
- return frameBuffer.Buffer.AsSpan(7, frameLength - 7);
+ return _frameBuffer.Buffer.Span.Slice(7, frameLength - 7);
}
private ushort GetTransactionIdentifier()
diff --git a/src/FluentModbus/Client/ModbusTcpClientAsync.cs b/src/FluentModbus/Client/ModbusTcpClientAsync.cs
index bb17428..e6de14e 100755
--- a/src/FluentModbus/Client/ModbusTcpClientAsync.cs
+++ b/src/FluentModbus/Client/ModbusTcpClientAsync.cs
@@ -6,7 +6,7 @@ namespace FluentModbus
public partial class ModbusTcpClient
{
///
- protected override async Task> TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame, CancellationToken cancellationToken = default)
+ protected override async Task> TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action extendFrame, CancellationToken cancellationToken = default)
{
// WARNING: IF YOU EDIT THIS METHOD, REFLECT ALL CHANGES ALSO IN TransceiveFrameAsync!
@@ -20,44 +20,37 @@ protected override async Task> TransceiveFrameAsync(byte unitIdenti
bool isParsed;
- ModbusFrameBuffer frameBuffer;
- ExtendedBinaryWriter writer;
- ExtendedBinaryReader reader;
-
bytesFollowing = 0;
- frameBuffer = _frameBuffer;
- writer = _frameBuffer.Writer;
- reader = _frameBuffer.Reader;
// build request
- writer.Seek(7, SeekOrigin.Begin);
+ var writer = new SpanWriter(_frameBuffer.Buffer.Span);
+ writer.Seek(7);
extendFrame(writer);
- frameLength = (int)writer.BaseStream.Position;
+ frameLength = writer.Position;
- writer.Seek(0, SeekOrigin.Begin);
+ writer.Seek(0);
if (BitConverter.IsLittleEndian)
{
- writer.WriteReverse(GetTransactionIdentifier()); // 00-01 Transaction Identifier
- writer.WriteReverse((ushort)0); // 02-03 Protocol Identifier
- writer.WriteReverse((ushort)(frameLength - 6)); // 04-05 Length
+ writer.WriteReverse(GetTransactionIdentifier()); // 00-01 Transaction Identifier
+ writer.WriteReverse((ushort)0); // 02-03 Protocol Identifier
+ writer.WriteReverse((ushort)(frameLength - 6)); // 04-05 Length
}
else
{
- writer.Write(GetTransactionIdentifier()); // 00-01 Transaction Identifier
- writer.Write((ushort)0); // 02-03 Protocol Identifier
- writer.Write((ushort)(frameLength - 6)); // 04-05 Length
+ writer.Write(GetTransactionIdentifier()); // 00-01 Transaction Identifier
+ writer.Write((ushort)0); // 02-03 Protocol Identifier
+ writer.Write((ushort)(frameLength - 6)); // 04-05 Length
}
- writer.Write(unitIdentifier); // 06 Unit Identifier
+ writer.Write(unitIdentifier); // 06 Unit Identifier
// send request
- await _networkStream.WriteAsync(frameBuffer.Buffer, 0, frameLength, cancellationToken).ConfigureAwait(false);
+ await _networkStream.WriteAsync(_frameBuffer.Buffer.Slice(0, frameLength), cancellationToken).ConfigureAwait(false);
// wait for and process response
frameLength = 0;
isParsed = false;
- reader.BaseStream.Seek(0, SeekOrigin.Begin);
while (true)
{
@@ -70,7 +63,7 @@ protected override async Task> TransceiveFrameAsync(byte unitIdenti
{
try
{
- partialLength = await _networkStream.ReadAsync(frameBuffer.Buffer, frameLength, frameBuffer.Buffer.Length - frameLength, cancellationToken).ConfigureAwait(false);
+ partialLength = await _networkStream.ReadAsync(_frameBuffer.Buffer.Slice(frameLength), cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
@@ -104,6 +97,8 @@ protected override async Task> TransceiveFrameAsync(byte unitIdenti
{
if (!isParsed) // read MBAP header only once
{
+ var reader = new SpanReader(_frameBuffer.Buffer.Span);
+
// read MBAP header
_ = reader.ReadUInt16Reverse(); // 00-01 Transaction Identifier
protocolIdentifier = reader.ReadUInt16Reverse(); // 02-03 Protocol Identifier
@@ -122,15 +117,17 @@ protected override async Task> TransceiveFrameAsync(byte unitIdenti
}
}
- rawFunctionCode = reader.ReadByte();
+ var finalReader = new SpanReader(_frameBuffer.Buffer.Span);
+ finalReader.Seek(7);
+ rawFunctionCode = finalReader.ReadByte();
if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode)
- ProcessError(functionCode, (ModbusExceptionCode)frameBuffer.Buffer[8]);
+ ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer.Span[8]);
else if (rawFunctionCode != (byte)functionCode)
throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode);
- return frameBuffer.Buffer.AsMemory(7, frameLength - 7);
+ return _frameBuffer.Buffer.Slice(7, frameLength - 7);
}
}
}
\ No newline at end of file
diff --git a/src/FluentModbus/IModbusRtuSerialPort.cs b/src/FluentModbus/IModbusRtuSerialPort.cs
index 5871fd7..f2f9180 100644
--- a/src/FluentModbus/IModbusRtuSerialPort.cs
+++ b/src/FluentModbus/IModbusRtuSerialPort.cs
@@ -16,6 +16,13 @@ public interface IModbusRtuSerialPort
/// The number of bytes read.
int Read(byte[] buffer, int offset, int count);
+ ///
+ /// Reads from the serial port input buffer and writes the bytes into a memory block.
+ ///
+ /// The memory block to write the input to.
+ /// The number of bytes read.
+ int Read(Memory buffer);
+
///
/// Asynchronously reads a number of bytes from the serial port input buffer and writes those bytes into a byte array at the specified offset.
///
@@ -26,6 +33,14 @@ public interface IModbusRtuSerialPort
/// The number of bytes read.
Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken token);
+ ///
+ /// Asynchronously reads from the serial port input buffer and writes the bytes into a memory block.
+ ///
+ /// The memory block to write the input to.
+ /// A token to cancel the current operation.
+ /// The number of bytes read.
+ ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken);
+
///
/// Writes a specified number of bytes to the serial port using data from a buffer.
///
@@ -34,6 +49,12 @@ public interface IModbusRtuSerialPort
/// The number of bytes to write.
void Write(byte[] buffer, int offset, int count);
+ ///
+ /// Writes the provided data to the serial port.
+ ///
+ /// The data to write to the port.
+ void Write(ReadOnlyMemory buffer);
+
///
/// Asynchronously writes a specified number of bytes to the serial port using data from a buffer.
///
@@ -43,6 +64,14 @@ public interface IModbusRtuSerialPort
/// A token to cancel the current operation.
Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token);
+ ///
+ //
+ /// Asynchronously writes the provided data to the serial port.
+ ///
+ /// The data to write to the port.
+ /// A token to cancel the current operation.
+ ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken);
+
///
/// Opens a new serial port connection.
///
diff --git a/src/FluentModbus/ModbusFrameBuffer.cs b/src/FluentModbus/ModbusFrameBuffer.cs
index c19d605..bc6bee7 100644
--- a/src/FluentModbus/ModbusFrameBuffer.cs
+++ b/src/FluentModbus/ModbusFrameBuffer.cs
@@ -4,30 +4,15 @@ namespace FluentModbus;
internal class ModbusFrameBuffer : IDisposable
{
- #region Constructors
+ private IMemoryOwner _memoryOwner;
+ private bool _disposedValue = false;
public ModbusFrameBuffer(int size)
{
- Buffer = ArrayPool.Shared.Rent(size);
-
- Writer = new ExtendedBinaryWriter(new MemoryStream(Buffer));
- Reader = new ExtendedBinaryReader(new MemoryStream(Buffer));
+ _memoryOwner = MemoryPool.Shared.Rent(size);
}
- #endregion
-
- #region Properties
-
- public byte[] Buffer { get; }
-
- public ExtendedBinaryWriter Writer { get; }
- public ExtendedBinaryReader Reader { get; }
-
- #endregion
-
- #region IDisposable Support
-
- private bool _disposedValue = false;
+ public Memory Buffer => _memoryOwner.Memory;
protected virtual void Dispose(bool disposing)
{
@@ -35,10 +20,7 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
- Writer.Dispose();
- Reader.Dispose();
-
- ArrayPool.Shared.Return(Buffer);
+ _memoryOwner.Dispose();
}
_disposedValue = true;
@@ -48,7 +30,6 @@ protected virtual void Dispose(bool disposing)
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
-
- #endregion
}
\ No newline at end of file
diff --git a/src/FluentModbus/ModbusRtuSerialPort.cs b/src/FluentModbus/ModbusRtuSerialPort.cs
index 3e9ff0f..902920e 100644
--- a/src/FluentModbus/ModbusRtuSerialPort.cs
+++ b/src/FluentModbus/ModbusRtuSerialPort.cs
@@ -70,6 +70,22 @@ public int Read(byte[] buffer, int offset, int count)
return _serialPort.Read(buffer, offset, count);
}
+ ///
+ public int Read(Memory buffer)
+ {
+ if (MemoryMarshal.TryGetArray(buffer, out var segment))
+ {
+ return Read(segment.Array, segment.Offset, segment.Count);
+ }
+ else
+ {
+ var array = buffer.ToArray();
+ var result = Read(array, 0, array.Length);
+ array.CopyTo(buffer);
+ return result;
+ }
+ }
+
///
/// Asynchronously reads from the input buffer.
///
@@ -113,6 +129,38 @@ public async Task ReadAsync(byte[] buffer, int offset, int count, Cancellat
}
}
+ ///
+ public async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken)
+ {
+ using var timeoutCts = new CancellationTokenSource(_serialPort.ReadTimeout);
+
+ /* _serialPort.DiscardInBuffer is essential here to cancel the operation */
+ using (timeoutCts.Token.Register(() =>
+ {
+ if (IsOpen)
+ _serialPort.DiscardInBuffer();
+ }))
+ using (cancellationToken.Register(() => timeoutCts.Cancel()))
+ {
+ try
+ {
+ return await _serialPort.BaseStream.ReadAsync(buffer, timeoutCts.Token);
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ throw;
+ }
+ catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
+ {
+ throw new TimeoutException("The asynchronous read operation timed out.");
+ }
+ catch (IOException) when (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
+ {
+ throw new TimeoutException("The asynchronous read operation timed out.");
+ }
+ }
+ }
+
///
/// Writes data to the serial port output buffer.
///
@@ -124,6 +172,20 @@ public void Write(byte[] buffer, int offset, int count)
_serialPort.Write(buffer, offset, count);
}
+ ///
+ public void Write(ReadOnlyMemory buffer)
+ {
+ if (MemoryMarshal.TryGetArray(buffer, out var segment))
+ {
+ Write(segment.Array, segment.Offset, segment.Count);
+ }
+ else
+ {
+ var array = buffer.ToArray();
+ Write(array, 0, array.Length);
+ }
+ }
+
///
/// Asynchronously writes data to the serial port output buffer.
///
@@ -161,5 +223,33 @@ public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationT
}
}
+ ///
+ public async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken)
+ {
+ using var timeoutCts = new CancellationTokenSource(_serialPort.WriteTimeout);
+
+ /* _serialPort.DiscardInBuffer is essential here to cancel the operation */
+ using (timeoutCts.Token.Register(() => _serialPort.DiscardOutBuffer()))
+ using (cancellationToken.Register(() => timeoutCts.Cancel()))
+ {
+ try
+ {
+ await _serialPort.BaseStream.WriteAsync(buffer, timeoutCts.Token);
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ throw;
+ }
+ catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
+ {
+ throw new TimeoutException("The asynchronous write operation timed out.");
+ }
+ catch (IOException) when (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
+ {
+ throw new TimeoutException("The asynchronous write operation timed out.");
+ }
+ }
+ }
+
#endregion
}
diff --git a/src/FluentModbus/SpanReader.cs b/src/FluentModbus/SpanReader.cs
new file mode 100644
index 0000000..8062d2d
--- /dev/null
+++ b/src/FluentModbus/SpanReader.cs
@@ -0,0 +1,184 @@
+using System.Runtime.InteropServices;
+
+namespace FluentModbus;
+
+internal ref struct SpanReader
+{
+ private ReadOnlySpan _span;
+ private int _position;
+
+ public SpanReader(ReadOnlySpan span)
+ {
+ _span = span;
+ _position = 0;
+ }
+
+ public int Position => _position;
+
+ public void Seek(int offset)
+ {
+ _position = offset;
+ }
+
+ public byte ReadByte()
+ {
+ var value = _span[_position];
+ _position += 1;
+ return value;
+ }
+
+ public short ReadInt16()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 2;
+ return value;
+ }
+
+ public ushort ReadUInt16()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 2;
+ return value;
+ }
+
+ public int ReadInt32()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 4;
+ return value;
+ }
+
+ public uint ReadUInt32()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 4;
+ return value;
+ }
+
+ public long ReadInt64()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 8;
+ return value;
+ }
+
+ public ulong ReadUInt64()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 8;
+ return value;
+ }
+
+ public float ReadFloat32()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 4;
+ return value;
+ }
+
+ public double ReadFloat64()
+ {
+ var value = MemoryMarshal.Read(_span.Slice(_position));
+ _position += 8;
+ return value;
+ }
+
+ public short ReadInt16Reverse()
+ {
+ var value = ReadInt16();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ public ushort ReadUInt16Reverse()
+ {
+ var value = ReadUInt16();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ public int ReadInt32Reverse()
+ {
+ var value = ReadInt32();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ public uint ReadUInt32Reverse()
+ {
+ var value = ReadUInt32();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ public long ReadInt64Reverse()
+ {
+ var value = ReadInt64();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ public ulong ReadUInt64Reverse()
+ {
+ var value = ReadUInt64();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ public float ReadFloat32Reverse()
+ {
+ var value = ReadFloat32();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ public double ReadFloat64Reverse()
+ {
+ var value = ReadFloat64();
+ return BitConverter.IsLittleEndian ? Reverse(value) : value;
+ }
+
+ private T ReadReverse(byte[] data) where T : struct
+ {
+ data.AsSpan().Reverse();
+ return MemoryMarshal.Cast(data)[0];
+ }
+
+ private static unsafe short Reverse(short value)
+ {
+ return (short)((value & 0xFF00) >> 8 | (value & 0x00FF) << 8);
+ }
+
+ private static unsafe ushort Reverse(ushort value)
+ {
+ return (ushort)((value & 0xFF00) >> 8 | (value & 0x00FF) << 8);
+ }
+
+ private static unsafe int Reverse(int value)
+ {
+ return (int)Reverse((uint)value);
+ }
+
+ private static unsafe uint Reverse(uint value)
+ {
+ value = (value >> 16) | (value << 16);
+ return ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8);
+ }
+
+ private static unsafe long Reverse(long value)
+ {
+ return (long)Reverse((ulong)value);
+ }
+
+ private static unsafe ulong Reverse(ulong value)
+ {
+ value = (value >> 32) | (value << 32);
+ value = ((value & 0xFFFF0000FFFF0000) >> 16) | ((value & 0x0000FFFF0000FFFF) << 16);
+ return ((value & 0xFF00FF00FF00FF00) >> 8) | ((value & 0x00FF00FF00FF00FF) << 8);
+ }
+
+ private static unsafe float Reverse(float value)
+ {
+ uint temp = Reverse(*(uint*)&value);
+ return *(float*)&temp;
+ }
+
+ private static unsafe double Reverse(double value)
+ {
+ ulong temp = Reverse(*(ulong*)&value);
+ return *(double*)&temp;
+ }
+}
diff --git a/src/FluentModbus/SpanWriter.cs b/src/FluentModbus/SpanWriter.cs
new file mode 100644
index 0000000..331fb9f
--- /dev/null
+++ b/src/FluentModbus/SpanWriter.cs
@@ -0,0 +1,129 @@
+using System.Runtime.InteropServices;
+
+namespace FluentModbus;
+
+internal ref struct SpanWriter
+{
+ private Span _span;
+ private int _position;
+
+ public SpanWriter(Span span)
+ {
+ _span = span;
+ _position = 0;
+ }
+
+ public int Position => _position;
+
+ public void Seek(int offset)
+ {
+ _position = offset;
+ }
+
+ public void Write(byte value)
+ {
+ _span[_position] = value;
+ _position += 1;
+ }
+
+ public void Write(short value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 2;
+ }
+
+ public void Write(ushort value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 2;
+ }
+
+ public void Write(int value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 4;
+ }
+
+ public void Write(uint value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 4;
+ }
+
+ public void Write(long value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 8;
+ }
+
+ public void Write(ulong value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 8;
+ }
+
+ public void Write(float value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 4;
+ }
+
+ public void Write(double value)
+ {
+ MemoryMarshal.Write(_span.Slice(_position), ref value);
+ _position += 8;
+ }
+
+ public void WriteReverse(short value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void WriteReverse(ushort value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void WriteReverse(int value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void WriteReverse(uint value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void WriteReverse(long value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void WriteReverse(ulong value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void WriteReverse(float value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void WriteReverse(double value)
+ {
+ WriteReverse(BitConverter.GetBytes(value));
+ }
+
+ public void Write(ReadOnlySpan data)
+ {
+ data.CopyTo(_span.Slice(_position));
+ _position += data.Length;
+ }
+
+ private void WriteReverse(byte[] data)
+ {
+ Array.Reverse(data);
+ data.CopyTo(_span.Slice(_position));
+ _position += data.Length;
+ }
+}