public static bool DetectFrame(byte unitIdentifier, Memory <byte> frame) { byte newUnitIdentifier; /* Correct response frame (min. 6 bytes) * 00 Unit Identifier * 01 Function Code * 02 Byte count * 03 Minimum of 1 byte * 04 CRC Byte 1 * 05 CRC Byte 2 */ /* Error response frame (5 bytes) * 00 Unit Identifier * 01 Function Code + 0x80 * 02 Exception Code * 03 CRC Byte 1 * 04 CRC Byte 2 */ var span = frame.Span; if (span.Length < 5) { return(false); } if (unitIdentifier != 255) // 255 means "skip unit identifier check" { newUnitIdentifier = span[0]; if (newUnitIdentifier != unitIdentifier) { return(false); } } // CRC check var crcBytes = span.Slice(span.Length - 2, 2); var actualCRC = unchecked ((ushort)((crcBytes[1] << 8) + crcBytes[0])); var expectedCRC = ModbusUtils.CalculateCRC(frame.Slice(0, frame.Length - 2)); if (actualCRC != expectedCRC) { return(false); } return(true); }
protected override int WriteFrame(Action extendFrame) { int frameLength; ushort crc; this.FrameBuffer.Writer.Seek(0, SeekOrigin.Begin); // add unit identifier this.FrameBuffer.Writer.Write(this.UnitIdentifier); // add PDU extendFrame(); // add CRC frameLength = unchecked ((int)this.FrameBuffer.Writer.BaseStream.Position); crc = ModbusUtils.CalculateCRC(this.FrameBuffer.Buffer.AsMemory(0, frameLength)); this.FrameBuffer.Writer.Write(crc); return(frameLength + 2); }
private protected override Span <byte> TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action <ExtendedBinaryWriter> extendFrame) { int messageLength; int frameLength; byte rawFunctionCode; ushort crc; int mbapHeaderLength = 4; // build request if (!(0 <= unitIdentifier && unitIdentifier <= 247)) { throw new ModbusException(ErrorMessage.ModbusClient_InvalidUnitIdentifier); } // special case: broadcast (only for write commands) if (unitIdentifier == 0) { switch (functionCode) { case ModbusFunctionCode.WriteMultipleRegisters: case ModbusFunctionCode.WriteSingleCoil: case ModbusFunctionCode.WriteSingleRegister: case ModbusFunctionCode.WriteMultipleCoils: case ModbusFunctionCode.WriteFileRecord: case ModbusFunctionCode.MaskWriteRegister: break; default: throw new ModbusException(ErrorMessage.Modbus_InvalidUseOfBroadcast); } } _frameBuffer.Writer.Seek(0, SeekOrigin.Begin); _frameBuffer.Writer.Write(unitIdentifier); // 00 Unit Identifier extendFrame(_frameBuffer.Writer); frameLength = (int)_frameBuffer.Writer.BaseStream.Position; // add CRC crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.AsMemory().Slice(0, frameLength)); _frameBuffer.Writer.Write(crc); frameLength = (int)_frameBuffer.Writer.BaseStream.Position; // build message _messageBuffer.Writer.Seek(0, SeekOrigin.Begin); if (!SwapBytes) { _messageBuffer.Writer.WriteReverse(ProtocolIdentifier); //2b _messageBuffer.Writer.WriteReverse((ushort)frameLength); //2b } else { _messageBuffer.Writer.Write(ProtocolIdentifier); //2b _messageBuffer.Writer.Write((ushort)frameLength); //2b } _messageBuffer.Writer.Write(_frameBuffer.Buffer, 0, frameLength); //framelength messageLength = frameLength + mbapHeaderLength; // send request _serialPort.Write(_messageBuffer.Buffer, 0, messageLength); // special case: broadcast (only for write commands) if (unitIdentifier == 0) { return(_messageBuffer.Buffer.AsSpan(0, 0)); } // wait for and process response messageLength = 0; _messageBuffer.Reader.BaseStream.Seek(0, SeekOrigin.Begin); while (true) { messageLength += _serialPort.Read(_messageBuffer.Buffer, messageLength, _messageBuffer.Buffer.Length - messageLength); if (ModbusUtils.DetectFrame(unitIdentifier, _messageBuffer.Buffer.AsMemory().Slice(mbapHeaderLength, messageLength - mbapHeaderLength))) { break; } else { // reset length because one or more chunks of data were received and written to // the buffer, but no valid Modbus frame could be detected and now the buffer is full if (messageLength == _messageBuffer.Buffer.Length) { messageLength = 0; } } } //write message content to framebuffer _frameBuffer.Writer.BaseStream.Seek(0, SeekOrigin.Begin); _frameBuffer.Writer.Write(_messageBuffer.Buffer.AsSpan().Slice(mbapHeaderLength).ToArray(), 0, messageLength - mbapHeaderLength); frameLength = messageLength - mbapHeaderLength; unitIdentifier = _frameBuffer.Reader.ReadByte(); rawFunctionCode = _frameBuffer.Reader.ReadByte(); if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode) { this.ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer[2]); } else if (rawFunctionCode != (byte)functionCode) { throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode); } return(_frameBuffer.Buffer.AsSpan(1, frameLength - 3)); }
private protected override async Task <Memory <byte> > TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action <ExtendedBinaryWriter> extendFrame, CancellationToken cancellationToken = default) { int frameLength; byte rawFunctionCode; ushort crc; // build request if (!(0 <= unitIdentifier && unitIdentifier <= 247)) { throw new ModbusException(ErrorMessage.ModbusClient_InvalidUnitIdentifier); } // special case: broadcast (only for write commands) if (unitIdentifier == 0) { switch (functionCode) { case ModbusFunctionCode.WriteMultipleRegisters: case ModbusFunctionCode.WriteSingleCoil: case ModbusFunctionCode.WriteSingleRegister: case ModbusFunctionCode.WriteMultipleCoils: case ModbusFunctionCode.WriteFileRecord: case ModbusFunctionCode.MaskWriteRegister: break; default: throw new ModbusException(ErrorMessage.Modbus_InvalidUseOfBroadcast); } } _frameBuffer.Writer.Seek(0, SeekOrigin.Begin); _frameBuffer.Writer.Write(unitIdentifier); // 00 Unit Identifier extendFrame(_frameBuffer.Writer); frameLength = (int)_frameBuffer.Writer.BaseStream.Position; // add CRC crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.AsMemory().Slice(0, frameLength)); _frameBuffer.Writer.Write(crc); frameLength = (int)_frameBuffer.Writer.BaseStream.Position; // send request await _serialPort.WriteAsync(_frameBuffer.Buffer, 0, frameLength, cancellationToken).ConfigureAwait(false); // special case: broadcast (only for write commands) if (unitIdentifier == 0) { return(_frameBuffer.Buffer.AsMemory(0, 0)); } // wait for and process response frameLength = 0; _frameBuffer.Reader.BaseStream.Seek(0, SeekOrigin.Begin); while (true) { frameLength += await _serialPort.ReadAsync(_frameBuffer.Buffer, frameLength, _frameBuffer.Buffer.Length - frameLength, cancellationToken).ConfigureAwait(false); if (ModbusUtils.DetectFrame(unitIdentifier, _frameBuffer.Buffer.AsMemory().Slice(0, frameLength))) { break; } else { // reset length because one or more chunks of data were received and written to // the buffer, but no valid Modbus frame could be detected and now the buffer is full if (frameLength == _frameBuffer.Buffer.Length) { frameLength = 0; } } } unitIdentifier = _frameBuffer.Reader.ReadByte(); rawFunctionCode = _frameBuffer.Reader.ReadByte(); if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode) { this.ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer[2]); } else if (rawFunctionCode != (byte)functionCode) { throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode); } return(_frameBuffer.Buffer.AsMemory(1, frameLength - 3)); }