[Modbus] 如何 用 C# 開發 Modbus Master Protocol - (04) 實作 TcpModbusRequest

[Modbus] 如何 用 C# 開發 Modbus Master Protocol - (04) 實作 TcpModbusRequest

續上篇,接下來,我要實作Modbus TCP 的 Request Protocol

image

 

這也沒有太大的困難,只要建立符合標準的通訊協定就可以了,在這裡我利用 MemoryStream 來建立我想要組合的通訊協定,若對組合通訊協定有問題,請參考

[C#.NET] 開發通訊協定必須要會的技巧

[C#.NET] 處理通訊協定的事前準備

另外我在功能裡自動累加 TransactionID ,它是一個身份識別的位置,可有可無,若不需要的話自行拿掉。

程式碼如下:


internal class TcpModbusRequest : AbsModbusRequest
{
    private ushort? _transaction;
    private readonly ushort MODBUS_PROTOCOL = 0;
    private readonly ushort MODBUS_DEFAULT_LENGTH = 6;

    private byte[] createReadMessage(byte Unit, EnumModbusFunctionCode FunctionCode, ushort StartAddress, ushort Quantity)
    {
        if (this.TransactionID != null)
        {
            this._transaction = TransactionID;
        }
        if (this._transaction == null)
        {
            this._transaction = 0;
        }

        using (MemoryStream memory = new MemoryStream())
        {
            memory.WriteByte((byte)(this._transaction >> 8));
            memory.WriteByte((byte)this._transaction);
            memory.WriteByte((byte)(MODBUS_PROTOCOL >> 8));
            memory.WriteByte((byte)MODBUS_PROTOCOL);
            memory.WriteByte((byte)(MODBUS_DEFAULT_LENGTH >> 8));
            memory.WriteByte((byte)MODBUS_DEFAULT_LENGTH);
            memory.WriteByte((byte)Unit);
            memory.WriteByte((byte)FunctionCode);
            memory.WriteByte((byte)(StartAddress >> 8));
            memory.WriteByte((byte)(StartAddress));
            memory.WriteByte((byte)(Quantity >> 8));
            memory.WriteByte((byte)(Quantity));
            this._transaction++;

            return memory.ToArray();
        }
    }

    public override byte[] ReadCoils(byte Unit, ushort StartAddress, ushort Quantity)
    {
        this.QuantityValidate(StartAddress, Quantity, 1, 2000);
        var requestArray = this.createReadMessage(Unit, EnumModbusFunctionCode.ReadCoils, StartAddress, Quantity);
        return requestArray;
    }

    public override byte[] ReadDiscreteInputs(byte Unit, ushort StartAddress, ushort Quantity)
    {
        this.QuantityValidate(StartAddress, Quantity, 1, 2000);
        var requestArray = this.createReadMessage(Unit, EnumModbusFunctionCode.ReadDiscreteInputs, StartAddress, Quantity);
        return requestArray;
    }

    public override byte[] ReadHoldingRegisters(byte Unit, ushort StartAddress, ushort Quantity)
    {
        this.QuantityValidate(StartAddress, Quantity, 1, 175);
        var requestArray = this.createReadMessage(Unit, EnumModbusFunctionCode.ReadHoldingRegisters, StartAddress, Quantity);
        return requestArray;
    }

    public override byte[] ReadInputRegisters(byte Unit, ushort StartAddress, ushort Quantity)
    {
        this.QuantityValidate(StartAddress, Quantity, 1, 175);
        var requestArray = this.createReadMessage(Unit, EnumModbusFunctionCode.ReadInputRegisters, StartAddress, Quantity);
        return requestArray;
    }

    public override byte[] WriteSingleCoil(byte Unit, ushort OutputAddress, bool OutputValue)
    {
        ushort outputValue = 0x0000;
        if (OutputValue)
        {
            outputValue = 0xFF00;
        }
        if (this.TransactionID != null)
        {
            this._transaction = TransactionID;
        }
        if (this._transaction == null)
        {
            this._transaction = 0;
        }

        using (MemoryStream memory = new MemoryStream())
        {
            memory.WriteByte((byte)(this._transaction >> 8));
            memory.WriteByte((byte)this._transaction);
            memory.WriteByte((byte)(MODBUS_PROTOCOL >> 8));
            memory.WriteByte((byte)MODBUS_PROTOCOL);
            memory.WriteByte((byte)(MODBUS_DEFAULT_LENGTH >> 8));
            memory.WriteByte((byte)MODBUS_DEFAULT_LENGTH);
            memory.WriteByte((byte)Unit);
            memory.WriteByte((byte)EnumModbusFunctionCode.WriteSingleCoil);
            memory.WriteByte((byte)(OutputAddress >> 8));
            memory.WriteByte((byte)(OutputAddress));
            memory.WriteByte((byte)(outputValue >> 8));
            memory.WriteByte((byte)(outputValue));
            this._transaction++;

            return memory.ToArray();
        }
    }

    public override byte[] WriteSingleRegister(byte Unit, ushort OutputAddress, short OutputValue)
    {
        if (this.TransactionID != null)
        {
            this._transaction = TransactionID;
        }
        if (this._transaction == null)
        {
            this._transaction = 0;
        }

        using (MemoryStream memory = new MemoryStream())
        {
            memory.WriteByte((byte)(this._transaction >> 8));
            memory.WriteByte((byte)this._transaction);
            memory.WriteByte((byte)(MODBUS_PROTOCOL >> 8));
            memory.WriteByte((byte)MODBUS_PROTOCOL);
            memory.WriteByte((byte)(MODBUS_DEFAULT_LENGTH >> 8));
            memory.WriteByte((byte)MODBUS_DEFAULT_LENGTH);
            memory.WriteByte((byte)Unit);
            memory.WriteByte((byte)EnumModbusFunctionCode.WriteSingleRegister);
            memory.WriteByte((byte)(OutputAddress >> 8));
            memory.WriteByte((byte)(OutputAddress));
            memory.WriteByte((byte)(OutputValue >> 8));
            memory.WriteByte((byte)(OutputValue));
            this._transaction++;

            return memory.ToArray();
        }
    }

    public override byte[] WriteMultipleCoils(byte Unit, ushort StartAddress, ushort Quantity, byte[] OutputValues)
    {
        this.QuantityValidate(StartAddress, Quantity, 1, 0x07B0);

        byte counter = base.GetByteCount(Quantity);

        if (counter != OutputValues.Length)
        {
            ModbusException.GetModbusException(0x03);
        }

        if (this.TransactionID != null)
        {
            this._transaction = TransactionID;
        }
        if (this._transaction == null)
        {
            this._transaction = 0;
        }

        ushort dataLength = (ushort)(MODBUS_DEFAULT_LENGTH + OutputValues.Length + 1);
        using (MemoryStream memory = new MemoryStream())
        {
            memory.WriteByte((byte)(this._transaction >> 8));
            memory.WriteByte((byte)this._transaction);
            memory.WriteByte((byte)(MODBUS_PROTOCOL >> 8));
            memory.WriteByte((byte)MODBUS_PROTOCOL);

            memory.WriteByte((byte)(dataLength >> 8));
            memory.WriteByte((byte)dataLength);

            memory.WriteByte((byte)Unit);
            memory.WriteByte((byte)EnumModbusFunctionCode.WriteMultipleCoils);
            memory.WriteByte((byte)(StartAddress >> 8));
            memory.WriteByte((byte)(StartAddress));
            memory.WriteByte((byte)(Quantity >> 8));
            memory.WriteByte((byte)(Quantity));
            memory.WriteByte((byte)(counter));
            memory.Write(OutputValues, 0, OutputValues.Length);
            this._transaction++;

            return memory.ToArray();
        }
    }

    public override byte[] WriteMultipleRegisters(byte Unit, ushort StartAddress, ushort Quantity, short[] OutputValues)
    {
        this.QuantityValidate(StartAddress, Quantity, 1, 0x007B);

        byte[] outputArray = base.GetByteCount(OutputValues);
        byte counter = (byte)outputArray.Length;

        if (Quantity * 2 != outputArray.Length)
        {
            ModbusException.GetModbusException(0x02);
        }
        if (this.TransactionID != null)
        {
            this._transaction = TransactionID;
        }
        if (this._transaction == null)
        {
            this._transaction = 0;
        }
        ushort dataLength = (ushort)(MODBUS_DEFAULT_LENGTH + counter + 1);
        using (MemoryStream memory = new MemoryStream())
        {
            memory.WriteByte((byte)(this._transaction >> 8));
            memory.WriteByte((byte)this._transaction);
            memory.WriteByte((byte)(MODBUS_PROTOCOL >> 8));
            memory.WriteByte((byte)MODBUS_PROTOCOL);

            memory.WriteByte((byte)(dataLength >> 8));
            memory.WriteByte((byte)dataLength);

            memory.WriteByte((byte)Unit);
            memory.WriteByte((byte)EnumModbusFunctionCode.WriteMultipleRegisters);
            memory.WriteByte((byte)(StartAddress >> 8));
            memory.WriteByte((byte)(StartAddress));
            memory.WriteByte((byte)(Quantity >> 8));
            memory.WriteByte((byte)(Quantity));
            memory.WriteByte((byte)(counter));
            memory.Write(outputArray, 0, outputArray.Length);

            this._transaction++;

            return memory.ToArray();
        }
    }
}

 


建立單元測試,確保方法如期執行。

那驗証如何取得?取了手動建立外,還可以由其他工具取得,請參考上篇,[Modbus] 如何 用 C# 開發 Modbus Protocol - (1) 事前準備

SNAGHTML513a1ad5

 


private ModbusUtility _modbusUtility = new ModbusUtility();
[TestMethod()]
public void ReadCoilsTest()
{
    TcpModbusRequest target = new TcpModbusRequest();

    byte Unit = 1;
    ushort StartAddress = 0;
    ushort Quantity = 10;
    ushort Transaction = 0;
    byte[] expected = _modbusUtility.HexStringToBytes("00 00 00 00 00 06 01 01 00 00 00 0A");
    target.TransactionID = Transaction;
    byte[] actual;
    actual = target.ReadCoils(Unit, StartAddress, Quantity);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

[TestMethod()]
public void ReadDiscreteInputsTest()
{
    TcpModbusRequest target = new TcpModbusRequest();

    byte Unit = 1;
    ushort StartAddress = 0;
    ushort Quantity = 10;
    ushort Transaction = 0;
    byte[] expected = _modbusUtility.HexStringToBytes("00 00 00 00 00 06 01 02 00 00 00 0A");
    byte[] actual;
    target.TransactionID = Transaction;
    actual = target.ReadDiscreteInputs(Unit, StartAddress, Quantity);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

[TestMethod()]
public void ReadHoldingRegistersTest()
{
    TcpModbusRequest target = new TcpModbusRequest();

    byte Unit = 1;
    ushort StartAddress = 0;
    ushort Quantity = 10;
    ushort Transaction = 0;
    byte[] expected = _modbusUtility.HexStringToBytes("00 00 00 00 00 06 01 03 00 00 00 0A");
    byte[] actual;
    target.TransactionID = Transaction;
    actual = target.ReadHoldingRegisters(Unit, StartAddress, Quantity);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

[TestMethod()]
public void ReadInputRegistersTest()
{
    TcpModbusRequest target = new TcpModbusRequest();

    byte Unit = 1;
    ushort StartAddress = 0;
    ushort Quantity = 100;
    ushort Transaction = 934;
    byte[] expected = _modbusUtility.HexStringToBytes("03 A6 00 00 00 06 01 04 00 00 00 64");
    byte[] actual;
    target.TransactionID = Transaction;
    actual = target.ReadInputRegisters(Unit, StartAddress, Quantity);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

[TestMethod()]
public void WriteSingleCoilTest()
{
    TcpModbusRequest target = new TcpModbusRequest();

    byte Unit = 1;
    ushort OutputAddress = 0;
    bool OutputValue = true;
    ushort Transaction = 1106;
    byte[] expected = _modbusUtility.HexStringToBytes("04 52 00 00 00 06 01 05 00 00 FF 00");
    byte[] actual;
    target.TransactionID = Transaction;
    actual = target.WriteSingleCoil(Unit, OutputAddress, OutputValue);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

[TestMethod()]
public void WriteSingleRegisterTest()
{
    TcpModbusRequest target = new TcpModbusRequest();
    byte Unit = 1;
    ushort OutputAddress = 2;
    short OutputValue = 234;
    ushort Transaction = 18;
    byte[] expected = _modbusUtility.HexStringToBytes("00 12 00 00 00 06 01 06 00 02 00 EA");
    byte[] actual;
    target.TransactionID = Transaction;
    actual = target.WriteSingleRegister(Unit, OutputAddress, OutputValue);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

[TestMethod()]
public void WriteMultipleCoilsTest()
{
    TcpModbusRequest target = new TcpModbusRequest();
    byte Unit = 1;
    ushort StartAddress = 0;
    ushort Quantity = 9;
    byte[] OutputValues = new byte[] { 0x0E, 0x00 };
    ushort Transaction = 895;
    byte[] expected = _modbusUtility.HexStringToBytes("03 7F 00 00 00 09 01 0F 00 00 00 09, 02 0E 00");
    byte[] actual;
    target.TransactionID = Transaction;
    actual = target.WriteMultipleCoils(Unit, StartAddress, Quantity, OutputValues);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

[TestMethod()]
public void WriteMultipleRegistersTest()
{
    TcpModbusRequest target = new TcpModbusRequest();
    byte Unit = 1;
    ushort StartAddress = 0;
    ushort Quantity = 10;
    short[] OutputValues = new short[] { 78, 87, -78, -87, 23, 35, 123, 12, 33, 6 };
    ushort Transaction = 0x03DA;
    byte[] expected = _modbusUtility.HexStringToBytes("03 DA 00 00 00 1B 01 10 00 00 00 0A 14 00 4E 00 57 FF B2 FF A9 00 17 00 23 00 7B 00 0C 00 21 00 06");
    byte[] actual;
    target.TransactionID = Transaction;
    actual = target.WriteMultipleRegisters(Unit, StartAddress, Quantity, OutputValues);
    Assert.IsTrue(expected.SequenceEqual(actual));
}

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo