Ejemplo n.º 1
0
        /// <summary>
        /// Send the response
        /// </summary>
        /// <param name="send_buffer">Send buffer</param>
        /// <param name="flux">Object for flux gest</param>
        /// <param name="unit_id">Slave ID</param>
        void SendReply(List <byte> send_buffer, object flux, byte unit_id, ushort transaction_id)
        {
            switch (_connectionType)
            {
            case ConnectionType.SERIAL_ASCII:
                // Add unit ID
                send_buffer.Insert(0, unit_id);
                // Enqueue LRC
                send_buffer.Add(LRC.CalcLRC(send_buffer.ToArray(), 0, send_buffer.Count));
                // ASCII encoding
                send_buffer = GetASCIIBytesFromBinaryBuffer(send_buffer.ToArray()).ToList();
                // Add START character
                send_buffer.Insert(0, Encoding.ASCII.GetBytes(new char[] { ASCII_START_FRAME }).First());
                // Enqueue STOP chars
                send_buffer.AddRange(Encoding.ASCII.GetBytes(new char[] { ASCII_STOP_FRAME_1ST, ASCII_STOP_FRAME_2ND }));
                break;

            case ConnectionType.SERIAL_RTU:
                // Add unit ID
                send_buffer.Insert(0, unit_id);
                // Enqueue CRC
                send_buffer.AddRange(BitConverter.GetBytes(CRC16.CalcCRC16(send_buffer.ToArray(), 0, send_buffer.Count)));
                // Wait for interframe delay
                Thread.Sleep(_interframeDelay);
                break;

            case ConnectionType.UDP_IP:
            case ConnectionType.TCP_IP:
                // Build MBAP header
                send_buffer.InsertRange(0, GetBytes(transaction_id));
                send_buffer.InsertRange(2, GetBytes(PROTOCOL_ID));
                send_buffer.InsertRange(4, GetBytes((ushort)(1 + send_buffer.Count - 4)));
                send_buffer.Insert(6, unit_id);
                break;
            }
            // Send the buffer
            WriteBuffer(flux, send_buffer.ToArray(), 0, send_buffer.Count);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Execute a query to a destination master
        /// </summary>
        /// <param name="deviceAddress">Salve device address</param>
        /// <param name="messageLength">Message lenght</param>
        protected void Query(byte deviceAddress, ushort messageLength)
        {
            // Build the message according to the selected protocol.
            switch (_connectionType)
            {
            case ConnectionType.SerialASCII:
                // Insert device address in front of the message.
                _sendBuffer.Insert(0, deviceAddress);

                // Calculate message LCR.
                byte[] lrc = GetASCIIBytesFromBinaryBuffer(new byte[] { LRC.CalcLRC(_sendBuffer.ToArray(), 0, _sendBuffer.Count) });

                // Convert the message from binary to ASCII.
                _sendBuffer = GetASCIIBytesFromBinaryBuffer(_sendBuffer.ToArray()).ToList();

                // Add LRC at the end of the message.
                _sendBuffer.AddRange(lrc);

                // Insert the start frame chararacter at the start of the message.
                _sendBuffer.Insert(0, Encoding.ASCII.GetBytes(new char[] { AsciiStartFrame }).First());

                // Insert stop frame characters at the end of the message.
                char[] endFrame = new char[] { AsciiStopFrame1ST, AsciiStopFrame2ND };
                _sendBuffer.AddRange(Encoding.ASCII.GetBytes(endFrame));
                break;

            case ConnectionType.SerialRTU:
                // Insert device address in front of the message.
                _sendBuffer.Insert(0, deviceAddress);

                // Append CRC16 to end of message.
                _sendBuffer.AddRange(BitConverter.GetBytes(CRC16.CalcCRC16(_sendBuffer.ToArray(), 0, _sendBuffer.Count)));

                // Wait for interframe delay.
                Thread.Sleep(_interframeDelay);
                break;

            // For Modbus TCP/UDP, build MBAP header.
            case ConnectionType.UDPIP:
            case ConnectionType.TCPIP:
                // Transaction ID (incremented by 1 on each trasmission)
                _sendBuffer.InsertRange(0, GetBytes(_transactionID));

                // Protocol ID (fixed value)
                _sendBuffer.InsertRange(2, GetBytes(ProtocolID));

                // Message length
                _sendBuffer.InsertRange(4, GetBytes(messageLength));

                // Remote unit ID
                _sendBuffer.Insert(6, deviceAddress);
                break;
            }

            // Send the message.
            Send();

            // Clear the receive message buffer.
            _receiveBuffer.Clear();

            bool done = false;
            bool firstByteReceived = false;
            long elapsedTime;

            // Start a timeout stopwatch.
            Stopwatch sw = new Stopwatch();

            sw.Start();
            do
            {
                // Try to read a byte.
                int rcv = ReceiveByte();

                // If a byte was received...
                if (rcv > -1)
                {
                    // If it is the first byte...
                    if (firstByteReceived == false)
                    {
                        // Remember that at least one byte has been received.
                        firstByteReceived = true;
                    }

                    // If we're using MODBUS ASCII...
                    if (_connectionType == ConnectionType.SerialASCII)
                    {
                        // If the byte we received was a colon (the first byte of an ASCII message)...
                        if ((byte)rcv == Encoding.ASCII.GetBytes(new char[] { AsciiStartFrame }).First())
                        {
                            // Clear the receive message buffer of any previous partial message.
                            _receiveBuffer.Clear();
                        }
                    }

                    // Add the received byte to the receive message buffer.
                    _receiveBuffer.Add((byte)rcv);
                }
                // If we've stopped receiving bytes...
                else if ((rcv == -1) && firstByteReceived)
                {
                    // Consider the message finished.
                    done = true;
                }

                // Fetch how many milliseconds have elapsed.
                // Keep receiving until we stop receiving bytes.
                elapsedTime = sw.ElapsedMilliseconds;
            } while ((!done) && (RxTimeout > elapsedTime));

            // Stop the stopwatch.
            _timeoutStopwatch.Stop();
            sw.Stop();

            // If the response timed out...
            if (elapsedTime >= RxTimeout)
            {
                throw new ModbusTimeoutException("Timeout waiting for response.");
            }
            // If we got a response...
            else
            {
                // Fetch the minimum message length...
                int minFrameLength;
                switch (_connectionType)
                {
                default:
                case ConnectionType.SerialRTU:
                    minFrameLength = 5;
                    break;

                case ConnectionType.SerialASCII:
                    minFrameLength = 11;
                    break;

                case ConnectionType.UDPIP:
                case ConnectionType.TCPIP:
                    minFrameLength = 9;
                    break;
                }

                // If the message was incomplete...
                if (_receiveBuffer.Count < minFrameLength)
                {
                    throw new ModbusTimeoutException("Wrong message length.");
                }

                switch (_connectionType)
                {
                // If we're using MODBUS ASCII...
                case ConnectionType.SerialASCII:
                    // If we didn't get the correct start character...
                    if (_receiveBuffer[0] != _sendBuffer[0])
                    {
                        throw new ModbusTimeoutException("Start character not found.");
                    }

                    // Remove the start character (a colon).
                    _receiveBuffer.RemoveRange(0, 1);

                    // If we didn't get the correct stop characters...
                    char[] endFrameSent     = new char[] { AsciiStopFrame1ST, AsciiStopFrame2ND };
                    char[] endFrameReceived = Encoding.ASCII.GetChars(_receiveBuffer.GetRange(_receiveBuffer.Count - 2, 2).ToArray());
                    if (!endFrameSent.SequenceEqual(endFrameReceived))
                    {
                        throw new ModbusTimeoutException("End characters not found.");
                    }

                    // Remove the stop characters.
                    _receiveBuffer.RemoveRange(_receiveBuffer.Count - 2, 2);

                    // Convert receive buffer from ASCII to binary.
                    _receiveBuffer = GetBinaryBufferFromASCIIBytes(_receiveBuffer.ToArray()).ToList();

                    // If the LRC is incorrect...
                    byte lrc_calculated = LRC.CalcLRC(_receiveBuffer.ToArray(), 0, _receiveBuffer.Count - 1);
                    byte lrc_received   = _receiveBuffer[_receiveBuffer.Count - 1];
                    if (lrc_calculated != lrc_received)
                    {
                        throw new ModbusResponseException("Wrong LRC");
                    }

                    // Remove the LRC.
                    _receiveBuffer.RemoveRange(_receiveBuffer.Count - 1, 1);

                    // Remove address byte.
                    _receiveBuffer.RemoveRange(0, 1);
                    break;

                // If we're using MODBUS RTU...
                case ConnectionType.SerialRTU:
                    // If the 16-bit CRC is incorrect...
                    ushort crcCalculated = CRC16.CalcCRC16(_receiveBuffer.ToArray(), 0, _receiveBuffer.Count - 2);
                    ushort crcReceived   = BitConverter.ToUInt16(_receiveBuffer.ToArray(), _receiveBuffer.Count - 2);
                    if (crcReceived != crcCalculated)
                    {
                        // TODO:  Read until there's nothing left in the queue.

                        throw new ModbusResponseException("Wrong CRC.");
                    }

                    // If the device address is incorrect...
                    byte addr = _receiveBuffer[0];
                    if (addr != _sendBuffer[0])
                    {
                        throw new ModbusResponseException("Wrong response address.");
                    }

                    // Remove address.
                    _receiveBuffer.RemoveRange(0, 1);

                    // Remove CRC.
                    _receiveBuffer.RemoveRange(_receiveBuffer.Count - 2, 2);
                    break;

                // If we're using MODBUS IP...
                case ConnectionType.UDPIP:
                case ConnectionType.TCPIP:
                    // If the MBAP header is incorrect...
                    ushort tid = ToUInt16(_receiveBuffer.ToArray(), 0);
                    if (tid != _transactionID)
                    {
                        throw new ModbusResponseException("Wrong transaction ID.");
                    }

                    // If the protocol ID is incorect...
                    ushort pid = ToUInt16(_receiveBuffer.ToArray(), 2);
                    if (pid != ProtocolID)
                    {
                        throw new ModbusResponseException("Wrong transaction ID.");
                    }

                    // If the length is incorrect...
                    ushort len = ToUInt16(_receiveBuffer.ToArray(), 4);
                    if ((_receiveBuffer.Count - MbapHeaderLength + 1) < len)
                    {
                        throw new ModbusResponseException("Wrong message length.");
                    }

                    // If the device address is incorrect...
                    byte uid = _receiveBuffer[6];
                    if (uid != _sendBuffer[6])
                    {
                        throw new ModbusResponseException("Wrong device address.");
                    }

                    // Remove the header from the message.
                    _receiveBuffer.RemoveRange(0, MbapHeaderLength);
                    break;
                }

                // If an error message was received...
                if (_receiveBuffer[0] > 0x80)
                {
                    // An error has been reported.
                    // Throw an error according to the exception code.
                    switch (_receiveBuffer[1])
                    {
                    case 1:
                        throw new ModbusIllegalFunctionException();

                    case 2:
                        throw new ModbusIllegalDataAddressException();

                    case 3:
                        throw new ModbusIllegalDataValueException();

                    case 4:
                        throw new ModbusSlaveDeviceFailureException();
                    }
                }
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Modbus slave response manager
        /// </summary>
        /// <param name="send_buffer">Send buffer</param>
        /// <param name="receive_buffer">Receive buffer</param>
        /// <param name="flux">Object for flux manager</param>
        protected void IncomingMessagePolling(List <byte> send_buffer, List <byte> receive_buffer, object flux)
        {
            ushort    transaction_id = 0;
            long      elapsed_time;
            byte      unit_id = 0;
            Stopwatch sw      = new Stopwatch();

            // Empting reception buffer
            receive_buffer.Clear();
            // Start reception loop
            int data = -1;

            sw.Start();
            bool in_ric = false;
            bool done   = false;

            _charTimeout = 0;
            do
            {
                data = ReadByte(flux);
                if (data != -1)
                {
                    if (!in_ric)
                    {
                        in_ric = true;
                    }
                    if (_connectionType == ConnectionType.SERIAL_ASCII)
                    {
                        if ((byte)data == Encoding.ASCII.GetBytes(new char[] { ASCII_START_FRAME }).First())
                        {
                            receive_buffer.Clear();
                        }
                    }
                    receive_buffer.Add((byte)data);
                }
                else if ((data == -1) && in_ric)
                {
                    done = true;
                }
                else
                {
                    Thread.Sleep(1);
                }
                // Calc elapsed time since reception start
                elapsed_time = sw.ElapsedMilliseconds;
            } while ((elapsed_time < RxTimeout) && _run && (!done));
            _timeoutStopwatch.Stop();
            sw.Stop();
            // Check for a stop request
            if (!_run)
            {
                // TODO:  Not sure what type of exception is best to throw here; likely not a MODBUS one.
                throw new ModbusException("Thread block request.");
            }
            // Check for timeout error
            if (elapsed_time >= RxTimeout)
            {
                throw new ModbusTimeoutException("Timeout waiting for response.");
            }
            // Check message length
            if (receive_buffer.Count < 3)
            {
                throw new ModbusResponseException("Wrong message length.");
            }
            // Message received, start decoding...
            switch (_connectionType)
            {
            case ConnectionType.SERIAL_ASCII:
                // Check and delete start char
                if (Encoding.ASCII.GetChars(receive_buffer.ToArray()).FirstOrDefault() != ASCII_START_FRAME)
                {
                    throw new ModbusTimeoutException("Start character not found.");
                }
                receive_buffer.RemoveAt(0);
                // Check and delete stop chars
                char[] end_chars = new char[] { ASCII_STOP_FRAME_1ST, ASCII_STOP_FRAME_2ND };
                char[] last_two  = Encoding.ASCII.GetChars(receive_buffer.GetRange(receive_buffer.Count - 2, 2).ToArray());
                if (!end_chars.SequenceEqual(last_two))
                {
                    throw new ModbusTimeoutException("End characters not found.");
                }
                receive_buffer.RemoveRange(receive_buffer.Count - 2, 2);
                // Recode message in binary
                receive_buffer = GetBinaryBufferFromASCIIBytes(receive_buffer.ToArray()).ToList();
                // Calc and remove LRC
                byte msg_lrc  = receive_buffer[receive_buffer.Count - 1];
                byte calc_lrc = LRC.CalcLRC(receive_buffer.ToArray(), 0, receive_buffer.Count - 1);
                if (msg_lrc != calc_lrc)
                {
                    throw new ModbusResponseException("Wrong LRC");
                }
                receive_buffer.RemoveAt(receive_buffer.Count - 1);
                // Analize destination address, if not present in database, discard message and continue
                unit_id = receive_buffer[0];
                if (!ModbusDatabase.Any(x => x.UnitID == unit_id))
                {
                    return;
                }
                receive_buffer.RemoveAt(0);
                break;

            case ConnectionType.SERIAL_RTU:
                // Check CRC
                ushort msg_crc  = BitConverter.ToUInt16(receive_buffer.ToArray(), receive_buffer.Count - 2);
                ushort calc_crc = CRC16.CalcCRC16(receive_buffer.ToArray(), 0, receive_buffer.Count - 2);
                if (msg_crc != calc_crc)
                {
                    throw new ModbusResponseException("Wrong CRC.");
                }
                // Analize destination address, if not present in database, discard message and continue
                unit_id = receive_buffer[0];
                if (!ModbusDatabase.Any(x => x.UnitID == unit_id))
                {
                    return;
                }
                // Message is ok, remove unit_id and CRC
                receive_buffer.RemoveRange(0, 1);
                receive_buffer.RemoveRange(receive_buffer.Count - 2, 2);
                break;

            case ConnectionType.UDP_IP:
            case ConnectionType.TCP_IP:
                // Decode MBAP Header
                transaction_id = ToUInt16(receive_buffer.ToArray(), 0);

                // Check protocol ID
                ushort protocol_id = ToUInt16(receive_buffer.ToArray(), 2);
                if (protocol_id != PROTOCOL_ID)
                {
                    throw new ModbusResponseException("Wrong protocol ID.");
                }

                // Acquire data length and check it
                ushort len = ToUInt16(receive_buffer.ToArray(), 4);
                if ((receive_buffer.Count - 6) != len)
                {
                    throw new ModbusResponseException("Wrong message length.");
                }

                // Analize destination address, if not present in database, discard message and continue
                unit_id = receive_buffer[6];
                if (!ModbusDatabase.Any(x => x.UnitID == unit_id))
                {
                    return;
                }
                // Message is ok, remove MBAP header for reception buffer
                receive_buffer.RemoveRange(0, MBAP_HEADER_LEN);
                break;
            }
            // Adjust data and build response
            AdjAndReply(send_buffer, receive_buffer, flux, unit_id, transaction_id);
        }