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

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

續上篇,接下來,來實作做後一個模型Modbus ASCII Request

如下圖紅框:

image

 

下圖為 ASCII 框架

image

 

以下是我實作LRC演算法的心得:

  1. Start 符號為":"
  2. End符號為"\r\n"
  3. LRC 在計算時不包含 Start 和 End 符號
  4. 計算過程中,先用 byte[] 取得 LRC byte[],再把整個 byte[] 轉成 Hex,再轉成 ASCII byte[],最後再加上 Start 和 End,整個過程花費了我相當多的時間。

如下段程式碼

{
    var lrc = ModbusUtility.CalculateLRC(OriginalArray);
    var pdu = ModbusUtility.BytesToHexString(OriginalArray);
    var adu = Encoding.ASCII.GetBytes(ModbusUtility.ASCII_START_SYMBOL + pdu + lrc + ModbusUtility.ASCII_END_SYMBOL);
    return adu;
}

 

完整程式碼如下:

{
    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;
        }
        using (MemoryStream memory = new MemoryStream())
        {
            memory.WriteByte(Unit);
            memory.WriteByte((byte)EnumModbusFunctionCode.WriteSingleCoil);
            memory.WriteByte((byte)(OutputAddress >> 8));
            memory.WriteByte((byte)OutputAddress);
            memory.WriteByte((byte)(outputValue >> 8));
            memory.WriteByte((byte)(outputValue));
            var aduArray = toAsciiData(memory.ToArray());
            return aduArray;
        }
    }

    public override byte[] WriteSingleRegister(byte Unit, ushort OutputAddress, short OutputValue)
    {
        using (MemoryStream memory = new MemoryStream())
        {
            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));

            var aduArray = toAsciiData(memory.ToArray());
            return aduArray;
        }
    }

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

        byte counter = base.GetByteCount(Quantity);

        using (MemoryStream memory = new MemoryStream())
        {
            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);

            var lrc = ModbusUtility.CalculateLRC(memory.ToArray());
            var pdu = ModbusUtility.BytesToHexString(memory.ToArray());
            var adu = Encoding.ASCII.GetBytes(ModbusUtility.ASCII_START_SYMBOL + pdu + lrc + ModbusUtility.ASCII_END_SYMBOL);
            return adu;
        }
    }

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

        var outputArray = base.GetByteCount(OutputValues);
        byte counter = (byte)outputArray.Length;
        if (Quantity * 2 != outputArray.Length)
        {
            ModbusException.GetModbusException(0x02);
        }

        using (MemoryStream memory = new MemoryStream())
        {
            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);
            var aduArray = toAsciiData(memory.ToArray());
            return aduArray;
        }
    }

    private byte[] createReadMessage(byte Unit, EnumModbusFunctionCode FunctionCode, ushort StartAddress, ushort Quantity)
    {
        using (MemoryStream memory = new MemoryStream())
        {
            memory.WriteByte(Unit);
            memory.WriteByte((byte)FunctionCode);
            memory.WriteByte((byte)(StartAddress >> 8));
            memory.WriteByte((byte)StartAddress);
            memory.WriteByte((byte)(Quantity >> 8));
            memory.WriteByte((byte)(Quantity));
            var aduArray = toAsciiData(memory.ToArray());
            return aduArray;
        }
    }

    private byte[] toAsciiData(byte[] OriginalArray)
    {
        var lrc = ModbusUtility.CalculateLRC(OriginalArray);
        var pdu = ModbusUtility.BytesToHexString(OriginalArray);
        var adu = Encoding.ASCII.GetBytes(ModbusUtility.ASCII_START_SYMBOL + pdu + lrc + ModbusUtility.ASCII_END_SYMBOL);
        return adu;
    }
}

 


建立單元測試,同樣的期望資料仍是藉由工具取得。

 [TestMethod()]
 public void ReadCoilsTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     byte Unit = 1;
     ushort StartAddress = 0;
     ushort Quantity = 10;
     byte[] expected = _modbusUtility.HexStringToBytes("3A 30 31 30 31 30 30 30 30 30 30 30 41 46 34 0D 0A");
     byte[] actual;
     actual = target.ReadCoils(Unit, StartAddress, Quantity);

     Assert.IsTrue(expected.SequenceEqual(actual));
 }

 [TestMethod()]
 public void ReadDiscreteInputsTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     byte Unit = 1;
     ushort StartAddress = 0;
     ushort Quantity = 10;
     byte[] expected = _modbusUtility.HexStringToBytes("3A 30 31 30 32 30 30 30 30 30 30 30 41 46 33 0D 0A");
     byte[] actual;
     actual = target.ReadDiscreteInputs(Unit, StartAddress, Quantity);
     Assert.IsTrue(expected.SequenceEqual(actual));
 }

 [TestMethod()]
 public void ReadHoldingRegistersTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     byte Unit = 1;
     ushort StartAddress = 0;
     ushort Quantity = 10;
     byte[] expected = _modbusUtility.HexStringToBytes("3A 30 31 30 33 30 30 30 30 30 30 30 41 46 32 0D 0A");
     byte[] actual;
     actual = target.ReadHoldingRegisters(Unit, StartAddress, Quantity);
     Assert.IsTrue(expected.SequenceEqual(actual));
 }

 [TestMethod()]
 public void ReadInputRegistersTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     byte Unit = 1;
     ushort StartAddress = 0;
     ushort Quantity = 10;
     byte[] expected = _modbusUtility.HexStringToBytes("3A 30 31 30 34 30 30 30 30 30 30 30 41 46 31 0D 0A");
     byte[] actual;
     actual = target.ReadInputRegisters(Unit, StartAddress, Quantity);
     Assert.IsTrue(expected.SequenceEqual(actual));
 }

 [TestMethod()]
 public void WriteSingleCoilTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     byte Unit = 1;
     ushort OutputAddress = 1;
     bool OutputValue = true;
     byte[] expected = _modbusUtility.HexStringToBytes("3A 30 31 30 35 30 30 30 31 46 46 30 30 46 41 0D 0A");
     byte[] actual;
     actual = target.WriteSingleCoil(Unit, OutputAddress, OutputValue);
     Assert.IsTrue(expected.SequenceEqual(actual));
 }

 [TestMethod()]
 public void WriteSingleRegisterTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     byte Unit = 1;
     ushort OutputAddress = 2;
     short OutputValue = 123;
     byte[] expected = _modbusUtility.HexStringToBytes("3A 30 31 30 36 30 30 30 32 30 30 37 42 37 43 0D 0A");
     byte[] actual;
     actual = target.WriteSingleRegister(Unit, OutputAddress, OutputValue);
     Assert.IsTrue(expected.SequenceEqual(actual));
 }

 [TestMethod()]
 public void WriteMultipleCoilsTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     ModbusUtility ModbusUtility = new ModbusUtility();
     byte Unit = 1;
     ushort StartAddress = 0;
     ushort Quantity = 3;
     byte[] OutputValues = new byte[] { 3 };

     byte[] expected = ModbusUtility.HexStringToBytes("3A 30 31 30 46 30 30 30 30 30 30 30 33 30 31 30 33 45 39 0D 0A");

     byte[] actual;
     actual = target.WriteMultipleCoils(Unit, StartAddress, Quantity, OutputValues);
     Assert.IsTrue(expected.SequenceEqual(actual));
 }

 [TestMethod()]
 public void WriteMultipleRegistersTest()
 {
     AsciiModbusRequest target = new AsciiModbusRequest();
     byte Unit = 1;
     ushort StartAddress = 0;
     ushort Quantity = 24;
     short[] OutputValues = new short[] { 12, 0, 0, 987, -98, 1, 456, 0, 123, -8, 0, 0, 0, 234, 112, 0, 3458, -89, 0, 0, 0, 988, 0, 0 };
     byte[] expected = _modbusUtility.HexStringToBytes("3A 30 31 31 30 30 30 30 30 30 30 31 38 33 30 30 30 30 43 30 30 30 30 30 30 30 30 30 33 44 42 46 46 39 45 30 30 30 31 30 31 43 38 30 30 30 30 30 30 37 42 46 46 46 38 30 30 30 30 30 30 30 30 30 30 30 30 30 30 45 41 30 30 37 30 30 30 30 30 30 44 38 32 46 46 41 37 30 30 30 30 30 30 30 30 30 30 30 30 30 33 44 43 30 30 30 30 30 30 30 30 37 36 0D 0A");
     byte[] actual;
     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