在C#中处理Modbus RTU框架 [英] Handling modbus rtu frame in c#

查看:293
本文介绍了在C#中处理Modbus RTU框架的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究


如上图所示,地址从 0000 0006 依次排列寄存器被跳过3次,下一个地址是 0009 ,之后它再次顺序执行,直到地址 003A 。然后再次跳过寄存器,依次从 0100 012E


我只想读取某些寄存器,即从 0000 0002 而不是从 000E 003A ,最后从 0100 012E 。一种方法是一帧一帧地发送,然后处理数据。但是我想在一个请求中发送它们。


我要执行以下操作。


  1. 组合请求并将其发送到一帧中


    var req1 = new byte [] {Convert.ToByte(iterations.hex),0x03、0x00、0x00、0x00 ,0x03}; // msn和电表ID
    var req2 = new byte [] {Convert.ToByte(iterations.hex),0x03、0x00、0x0E, 0x00,0x46}; //能量值
    var req3 = new byte [] {Convert.ToByte(iterations.hex),0x03、0x01、0x00、0x00、0x48}; / /其他能量值



var项目= req1.concat (req2).concat(req3);


我不确定这种方法是否行得通。我不知道该怎么做,但由于处理程序不断读取端口,所以我不想一一发送帧。


  1. 收集所有请求数据,然后保存数据。

任何帮助将不胜感激。

解决方案

假设


未提供详细的用例,因此我假设用例需要持续发送读取请求并处理响应。




如何发送请求?
-----
选择不多,要么一次阅读,要么将它们分成小块。
这取决于您的[波特率](https://en.wikipedia.org/wiki/Baud)和所需的探测间隔。

假定从0x000〜0x003A + 0x0100〜0x012E读取的批量读为T,从0x0000〜0x012E读取的批量读为3T。
您可以在此处计算通信时间。


  1. 如果所需的间隔远大于3T,则从0x0000〜0x012E进行读取似乎可以。

  2. 如果所需的间隔接近1〜 3T,然后增加波特率或使用多个批量读取都可以。

我的建议是一般读取两个区域0x000〜0x003A + 0x0100〜0x012E




如何设计
----
我的建议是不断重复发送和收集它。这是一所古老的学校,但对于以后的维护者来说很容易理解。

下面是抽象发送和接收状态机的伪代码。

 枚举EReadState {
首先,
第二个,
第三个
}
EReadState状态= EReadState.First; //如果需要读取两个以上的区域,则使用枚举
ManualResetEvent mre = new ManualResetEvent();
void StartPolling()
{
while(true){

switch(state){
case首先:
SendFirstPartRequest();
休息时间;
情况第二:
SendSecondPartRequest();
休息时间;
情况第三:
SendThirdPartRequest();
休息时间;
}
mre.WaitOne()/等待接收到一个数据
//不要忘记实现您的中断条件
}
}

void Receive()
{
var data = ... //获取数据
state = setNextState(data); //如果收到了第一部分,则将状态设置为第二。
mre.Set(); //转到下一个发送
}


I am working on modbus protocol in C#. I am able to send a single request and get a response from it. I am using a DataReceivedHandler to read from incoming port

 public ModBusEngine()
    {
        //serial port settings and opening it
        port.ReadTimeout = 500;

        port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

        port.Open();

        
       
    }

private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {


        //port.ReadTimeout = 200;
        

        // send request and waiting for response
        // the request needs: slaveId, dataAddress, registerCount 
        Thread.Sleep(200);
        Console.WriteLine("Bytes recieved are " + port.BytesToRead);
        try
        {
            receivedBytes = port.BaseStream.Read(buffer, 0, (int)buffer.Length);
            var receiveData = BitConverter.ToString(buffer, 0, receivedBytes);
            var finalData = receiveData.Replace("-", "");

            new Thread(() =>
            {
                SaveData(finalData);
            }).Start();
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message.ToString());
        }
       
    }

I am writing to a port in another method

 private void ModbusMethod(Iterations iterations)
    {
        Console.WriteLine("Starting Modbuss Scheduling data...");
        try
        {
            var items = new byte[]{ Convert.ToByte(iterations.hex), 0x03, 0x00, 0x00, 0x00, 0x03 };
            //iterations.hex is device id
            var crc = BitConverter.GetBytes(ModRTU_CRC(items, items.Length)); //ComputeChecksum(items);

            var dataItems = items.Concat(crc).ToArray();

            port.BaseStream.Write(dataItems, 0, dataItems.Length);

            
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message.ToString());
        }                      

    }

I am successfully able to send are receive a response.

What I want to do now?

There are multiple registers from which I can get data from the device. These addresses are not completely in sequential order. For example, see below pictures

As shown in the above images addresses from 0000 to 0006 are in a sequence then the register is skipped 3 times and the next address is 0009 after that it's again sequential till the address 003A. Then again registers are skipped and start from 0100 to 012E in sequence.

I only want to read certain registers i.e. from 0000 to 0002 than from 000E to 003A and finally from 0100 to 012E. One way is to send the frame one by one and then do the needful with the data. But I want to send them in a single request.

I want to do the following.

  1. Combining the request and send it in one frame

    var req1 = new byte[]{ Convert.ToByte(iterations.hex), 0x03, 0x00, 0x00, 0x00, 0x03 };// msn and meter id var req2 = new byte[]{ Convert.ToByte(iterations.hex), 0x03, 0x00, 0x0E, 0x00, 0x46 }; // energy values var req3 = new byte[]{ Convert.ToByte(iterations.hex), 0x03, 0x01, 0x00, 0x00, 0x48 };// energy values other

var items = req1.concat(req2).concat(req3);

I am not sure that this approach will work or not. I don't know how to do it but I don't want to send the frame one by one as the handler is continuously reading the port.

  1. Collect all the requests data and then save the data.

Any help would be highly appreciated.

解决方案

Assumption

Detail use case isn't provided, so I'll assume this use case needs to continuously sending read requests and process responses.


How to send request? ----- Not much choice, either read at once or split them into small areas. It depends on your [baudrate](https://en.wikipedia.org/wiki/Baud) and required probe interval.

Assume a bulk read from 0x000~0x003A + 0x0100~0x012E is T and read from 0x0000~0x012E is 3T. You may calculate communication time here.

  1. If required interval is much greater than 3T, then read from 0x0000~0x012E seems okay for this case.
  2. If required interval is near 1~3T, then either increasing baudrate or using multiple bulk read are okay.

My suggestion is to read two area 0x000~0x003A + 0x0100~0x012E as a general case, since at least reduce read time by T.


How to design ---- My suggestion is keep repeating send and collect it. It's old school but easy to understand for future maintainer.

Following are pseudo code for a state machine while abstracting send and receive.

enum EReadState{
    First,
    Second,
    Third
}
EReadState state = EReadState.First; // I use enum if you have more than two areas need to be read
ManualResetEvent mre = new ManualResetEvent();
void StartPolling()
{
    while(true){
        
        switch( state ){
            case First:
                SendFirstPartRequest();
                break;
            case Second:
                SendSecondPartRequest();
                break;
            case Third:
                SendThirdPartRequest();
                break;
        }
        mre.WaitOne() /  wait for one data received
        // Don't forget implement your break condition
    }
}

void Receive()
{
    var data = ... // get data
    state = setNextState( data ); // If first part is received, set state to second.
    mre.Set(); // Go to next send
}

这篇关于在C#中处理Modbus RTU框架的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆