예제 #1
0
        /// <summary>
        /// Reads the specified number of values of type <typeparamref name="TRead"/> from and writes the provided array of type <typeparamref name="TWrite"/> to the holding registers. The write operation is performed before the read.
        /// </summary>
        /// <typeparam name="TRead">Determines the type of the returned data.</typeparam>
        /// <typeparam name="TWrite">Determines the type of the provided data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="readStartingAddress">The holding register start address for the read operation.</param>
        /// <param name="readCount">The number of elements of type <typeparamref name="TRead"/> to read.</param>
        /// <param name="writeStartingAddress">The holding register start address for the write operation.</param>
        /// <param name="dataset">The data of type <typeparamref name="TWrite"/> to write to the server.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        public async Task <Memory <TRead> > ReadWriteMultipleRegistersAsync <TRead, TWrite>(int unitIdentifier, int readStartingAddress, int readCount, int writeStartingAddress, TWrite[] dataset, CancellationToken cancellationToken = default) where TRead : unmanaged
            where TWrite : unmanaged
        {
            var unitIdentifier_converted       = this.ConvertUnitIdentifier(unitIdentifier);
            var readStartingAddress_converted  = this.ConvertUshort(readStartingAddress);
            var readCount_converted            = this.ConvertUshort(readCount);
            var writeStartingAddress_converted = this.ConvertUshort(writeStartingAddress);

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset.AsSpan());
            }

            var readQuantity = this.ConvertSize <TRead>(readCount_converted);
            var byteData     = MemoryMarshal.Cast <TWrite, byte>(dataset).ToArray();

            var dataset2 = SpanExtensions.Cast <byte, TRead>(await this.ReadWriteMultipleRegistersAsync(unitIdentifier_converted, readStartingAddress_converted, readQuantity, writeStartingAddress_converted, byteData).ConfigureAwait(false));

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset2);
            }

            return(dataset2);
        }
예제 #2
0
        /// <summary>
        /// Reads the specified number of values of type <typeparamref name="TRead"/> from and writes the provided array of type <typeparamref name="TWrite"/> to the holding registers. The write operation is performed before the read.
        /// </summary>
        /// <typeparam name="TRead">Determines the type of the returned data.</typeparam>
        /// <typeparam name="TWrite">Determines the type of the provided data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="readStartingAddress">The holding register start address for the read operation.</param>
        /// <param name="readCount">The number of elements of type <typeparamref name="TRead"/> to read.</param>
        /// <param name="writeStartingAddress">The holding register start address for the write operation.</param>
        /// <param name="dataset">The data of type <typeparamref name="TWrite"/> to write to the server.</param>
        public Span <TRead> ReadWriteMultipleRegisters <TRead, TWrite>(int unitIdentifier, int readStartingAddress, int readCount, int writeStartingAddress, TWrite[] dataset) where TRead : unmanaged
            where TWrite : unmanaged
        {
            var unitIdentifier_converted       = this.ConvertUnitIdentifier(unitIdentifier);
            var readStartingAddress_converted  = this.ConvertUshort(readStartingAddress);
            var readCount_converted            = this.ConvertUshort(readCount);
            var writeStartingAddress_converted = this.ConvertUshort(writeStartingAddress);

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset.AsSpan());
            }

            var readQuantity = this.ConvertSize <TRead>(readCount_converted);
            var byteData     = MemoryMarshal.Cast <TWrite, byte>(dataset).ToArray();

            var dataset2 = MemoryMarshal.Cast <byte, TRead>(this.ReadWriteMultipleRegisters(unitIdentifier_converted, readStartingAddress_converted, readQuantity, writeStartingAddress_converted, byteData));

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset2);
            }

            return(dataset2);
        }
예제 #3
0
        public static T SwitchEndianness <T>(T value) where T : unmanaged
        {
            var data = new T[] { value };

            ModbusUtils.SwitchEndianness(data.AsSpan());

            return(data[0]);
        }
예제 #4
0
        public static T SwitchEndianness <T>(T value) where T : unmanaged
        {
            Span <T> data = stackalloc T[] { value };

            ModbusUtils.SwitchEndianness(data);

            return(data[0]);
        }
예제 #5
0
        /// <summary>
        /// Writes the provided array of type <typeparamref name="T"/> to the holding registers.
        /// </summary>
        /// <typeparam name="T">Determines the type of the provided data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="startingAddress">The holding register start address for the write operation.</param>
        /// <param name="dataset">The data of type <typeparamref name="T"/> to write to the server.</param>
        public void WriteMultipleRegisters <T>(byte unitIdentifier, ushort startingAddress, T[] dataset) where T : unmanaged
        {
            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset.AsSpan());
            }

            this.WriteMultipleRegisters(unitIdentifier, startingAddress, MemoryMarshal.Cast <T, byte>(dataset).ToArray());
        }
예제 #6
0
        /// <summary>
        /// Connect to the specified <paramref name="remoteEndpoint"/>.
        /// </summary>
        /// <param name="remoteEndpoint">The IP address and optional port of the end unit. Examples: "192.168.0.1", "192.168.0.1:502", "::1", "[::1]:502". The default port is 502.</param>
        /// <param name="endianness">Specifies the endianness of the data exchanged with the Modbus server.</param>
        public void Connect(string remoteEndpoint, ModbusEndianness endianness)
        {
            if (!ModbusUtils.TryParseEndpoint(remoteEndpoint.AsSpan(), out var parsedRemoteEndpoint))
            {
                throw new FormatException("An invalid IPEndPoint was specified.");
            }

            this.Connect(parsedRemoteEndpoint, endianness);
        }
예제 #7
0
        /// <summary>
        /// Writes the provided <paramref name="value"/> to the holding registers.
        /// </summary>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="registerAddress">The holding register address for the write operation.</param>
        /// <param name="value">The value to write to the server.</param>
        public void WriteSingleRegister(byte unitIdentifier, ushort registerAddress, ushort value)
        {
            if (this.SwapBytes)
            {
                value = ModbusUtils.SwitchEndianness(value);
            }

            this.WriteSingleRegister(unitIdentifier, registerAddress, MemoryMarshal.Cast <ushort, byte>(new[] { value }).ToArray());
        }
예제 #8
0
        /// <summary>
        /// Reads the specified number of values of type <typeparamref name="T"/> from the input registers.
        /// </summary>
        /// <typeparam name="T">Determines the type of the returned data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="startingAddress">The input register start address for the read operation.</param>
        /// <param name="count">The number of elements of type <typeparamref name="T"/> to read.</param>
        public Span <T> ReadInputRegisters <T>(byte unitIdentifier, ushort startingAddress, ushort count) where T : unmanaged
        {
            var dataset = MemoryMarshal.Cast <byte, T>(this.ReadInputRegisters(unitIdentifier, startingAddress, this.ConvertSize <T>(count)));

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset);
            }

            return(dataset);
        }
예제 #9
0
        /// <summary>
        /// Writes the provided array of type <typeparamref name="T"/> to the holding registers.
        /// </summary>
        /// <typeparam name="T">Determines the type of the provided data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="startingAddress">The holding register start address for the write operation.</param>
        /// <param name="dataset">The data of type <typeparamref name="T"/> to write to the server.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        public async Task WriteMultipleRegistersAsync <T>(int unitIdentifier, int startingAddress, T[] dataset, CancellationToken cancellationToken = default) where T : unmanaged
        {
            var unitIdentifier_converted  = this.ConvertUnitIdentifier(unitIdentifier);
            var startingAddress_converted = this.ConvertUshort(startingAddress);

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset.AsSpan());
            }

            await this.WriteMultipleRegistersAsync(unitIdentifier_converted, startingAddress_converted, MemoryMarshal.Cast <T, byte>(dataset).ToArray()).ConfigureAwait(false);
        }
예제 #10
0
        /// <summary>
        /// Writes the provided <paramref name="value"/> to the holding registers.
        /// </summary>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="registerAddress">The holding register address for the write operation.</param>
        /// <param name="value">The value to write to the server.</param>
        public void WriteSingleRegister(int unitIdentifier, int registerAddress, ushort value)
        {
            var unitIdentifier_converted  = this.ConvertUnitIdentifier(unitIdentifier);
            var registerAddress_converted = this.ConvertUshort(registerAddress);

            if (this.SwapBytes)
            {
                value = ModbusUtils.SwitchEndianness(value);
            }

            this.WriteSingleRegister(unitIdentifier_converted, registerAddress_converted, MemoryMarshal.Cast <ushort, byte>(new[] { value }).ToArray());
        }
예제 #11
0
        /// <summary>
        /// Writes the provided array of type <typeparamref name="T"/> to the holding registers.
        /// </summary>
        /// <typeparam name="T">Determines the type of the provided data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="startingAddress">The holding register start address for the write operation.</param>
        /// <param name="dataset">The data of type <typeparamref name="T"/> to write to the server.</param>
        public void WriteMultipleRegisters <T>(int unitIdentifier, int startingAddress, T[] dataset) where T : unmanaged
        {
            var unitIdentifier_converted  = this.ConvertUnitIdentifier(unitIdentifier);
            var startingAddress_converted = this.ConvertUshort(startingAddress);

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset.AsSpan());
            }

            this.WriteMultipleRegisters(unitIdentifier_converted, startingAddress_converted, MemoryMarshal.Cast <T, byte>(dataset).ToArray());
        }
예제 #12
0
        /// <summary>
        /// Writes the provided <paramref name="value"/> to the holding registers.
        /// </summary>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="registerAddress">The holding register address for the write operation.</param>
        /// <param name="value">The value to write to the server.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        public async Task WriteSingleRegisterAsync(int unitIdentifier, int registerAddress, ushort value, CancellationToken cancellationToken = default)
        {
            var unitIdentifier_converted  = this.ConvertUnitIdentifier(unitIdentifier);
            var registerAddress_converted = this.ConvertUshort(registerAddress);

            if (this.SwapBytes)
            {
                value = ModbusUtils.SwitchEndianness(value);
            }

            await this.WriteSingleRegisterAsync(unitIdentifier_converted, registerAddress_converted, MemoryMarshal.Cast <ushort, byte>(new[] { value }).ToArray()).ConfigureAwait(false);
        }
예제 #13
0
        public static void SetBigEndian <T>(this Span <short> buffer, ushort address, T value)
            where T : unmanaged
        {
            var byteBuffer = MemoryMarshal
                             .AsBytes(buffer)
                             .Slice(address * 2);

            if (BitConverter.IsLittleEndian)
            {
                value = ModbusUtils.SwitchEndianness(value);
            }

            Unsafe.WriteUnaligned(ref byteBuffer.GetPinnableReference(), value);
        }
예제 #14
0
        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);
        }
예제 #15
0
        /// <summary>
        /// Reads the specified number of values of type <typeparamref name="T"/> from the holding registers.
        /// </summary>
        /// <typeparam name="T">Determines the type of the returned data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="startingAddress">The holding register start address for the read operation.</param>
        /// <param name="count">The number of elements of type <typeparamref name="T"/> to read.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        public async Task <Memory <T> > ReadHoldingRegistersAsync <T>(int unitIdentifier, int startingAddress, int count, CancellationToken cancellationToken = default) where T : unmanaged
        {
            var unitIdentifier_converted  = this.ConvertUnitIdentifier(unitIdentifier);
            var startingAddress_converted = this.ConvertUshort(startingAddress);
            var count_converted           = this.ConvertUshort(count);

            var dataset = SpanExtensions.Cast <byte, T>(await
                                                        this.ReadHoldingRegistersAsync(unitIdentifier_converted, startingAddress_converted, this.ConvertSize <T>(count_converted)).ConfigureAwait(false));

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset);
            }

            return(dataset);
        }
예제 #16
0
        /// <summary>
        /// Reads the specified number of values of type <typeparamref name="T"/> from the input registers.
        /// </summary>
        /// <typeparam name="T">Determines the type of the returned data.</typeparam>
        /// <param name="unitIdentifier">The unit identifier is used to communicate via devices such as bridges, routers and gateways that use a single IP address to support multiple independent Modbus end units. Thus, the unit identifier is the address of a remote slave connected on a serial line or on other buses. Use the default values 0x00 or 0xFF when communicating to a Modbus server that is directly connected to a TCP/IP network.</param>
        /// <param name="startingAddress">The input register start address for the read operation.</param>
        /// <param name="count">The number of elements of type <typeparamref name="T"/> to read.</param>
        public Span <T> ReadInputRegisters <T>(int unitIdentifier, int startingAddress, int count) where T : unmanaged
        {
            var unitIdentifier_converted  = this.ConvertUnitIdentifier(unitIdentifier);
            var startingAddress_converted = this.ConvertUshort(startingAddress);
            var count_converted           = this.ConvertUshort(count);

            var dataset = MemoryMarshal.Cast <byte, T>(
                this.ReadInputRegisters(unitIdentifier_converted, startingAddress_converted, this.ConvertSize <T>(count_converted)));

            if (this.SwapBytes)
            {
                ModbusUtils.SwitchEndianness(dataset);
            }

            return(dataset);
        }
예제 #17
0
        public static T GetBigEndian <T>(this Span <short> buffer, ushort address)
            where T : unmanaged
        {
            var byteBuffer = MemoryMarshal
                             .AsBytes(buffer)
                             .Slice(address * 2);

            var value = Unsafe.ReadUnaligned <T>(ref byteBuffer.GetPinnableReference());

            if (BitConverter.IsLittleEndian)
            {
                value = ModbusUtils.SwitchEndianness(value);
            }

            return(value);
        }
예제 #18
0
        private async Task <bool> InternalReceiveRequestAsync()
        {
            this.Length = 0;

            try
            {
                while (true)
                {
                    this.Length += await _serialPort.ReadAsync(this.FrameBuffer.Buffer, this.Length, this.FrameBuffer.Buffer.Length - this.Length, this.CTS.Token);

                    // full frame received
                    if (ModbusUtils.DetectFrame(255, this.FrameBuffer.Buffer.AsMemory(0, this.Length)))
                    {
                        this.FrameBuffer.Reader.BaseStream.Seek(0, SeekOrigin.Begin);

                        // read unit identifier
                        this.UnitIdentifier = this.FrameBuffer.Reader.ReadByte();

                        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 (this.Length == this.FrameBuffer.Buffer.Length)
                        {
                            this.Length = 0;
                        }
                    }
                }
            }
            catch (TimeoutException)
            {
                this.Length = 0;
            }

            // make sure that the incoming frame is actually adressed to this server
            if (this.UnitIdentifier == this.ModbusRtuServer.UnitIdentifier)
            {
                this.LastRequest.Restart();
                return(true);
            }
            else
            {
                return(false);
            }
        }
예제 #19
0
        public static void SetBigEndian <T>(this Span <short> buffer, int address, T value)
            where T : unmanaged
        {
            if (!(0 <= address && address <= ushort.MaxValue))
            {
                throw new Exception(ErrorMessage.Modbus_InvalidValueUShort);
            }

            var byteBuffer = MemoryMarshal
                             .AsBytes(buffer)
                             .Slice(address * 2);

            if (BitConverter.IsLittleEndian)
            {
                value = ModbusUtils.SwitchEndianness(value);
            }

            Unsafe.WriteUnaligned(ref byteBuffer.GetPinnableReference(), value);
        }
예제 #20
0
        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);
        }
예제 #21
0
        public static T GetMidLittleEndian <T>(this Span <short> buffer, int address)
            where T : unmanaged
        {
            if (!(0 <= address && address <= ushort.MaxValue))
            {
                throw new Exception(ErrorMessage.Modbus_InvalidValueUShort);
            }

            var byteBuffer = MemoryMarshal
                             .AsBytes(buffer)
                             .Slice(address * 2);

            var value = Unsafe.ReadUnaligned <T>(ref byteBuffer.GetPinnableReference());

            value = ModbusUtils.ConvertBetweenLittleEndianAndMidLittleEndian(value);

            if (!BitConverter.IsLittleEndian)
            {
                value = ModbusUtils.SwitchEndianness(value);
            }

            return(value);
        }
예제 #22
0
        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));
        }
예제 #23
0
 public static void SwitchEndianness <T>(Memory <T> dataset) where T : unmanaged
 {
     ModbusUtils.SwitchEndianness(dataset.Span);
 }
        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));
        }