工业控制器、Linux 和纯 C++。第一部分

大家好!我是大聪明-PLUS

我想和大家分享一些我们在日常工作中总结的最佳实践。

如果谈到工业自动化,我们就会明白编程将与 IEC 61131-3 标准的语言相关联。

我找到了一台运行Ubuntu 12的Icp-Das LP-8x21控制器,还有几块来自KontrAvt的MDS DIO-16BD和AIO-4 I/O模块,可以用来测试。现在我有东西可以做实验了。

测试版本

为了连接控制器和 I/O 模块,我们将使用最常用的物理通信标准之一------RS-485。这些模块使用 Modbus 协议。

要为该控制器创建可执行文件,您需要为其处理器安装编译器:

复制代码
sudo` apt install gcc-arm-linux-gnueabihf g`++-arm-linux-gnueabihf

安装构建文件和源代码libmodbus-dev

复制代码
sudo` apt install libmodbus-dev
apt `source` libmodbus-dev`

我们进入已创建的包含源代码的文件夹,并为以下编译器编译库arm-linux-gnueabihf

复制代码
`./autogen.sh
./configure `--host=`arm-linux-gnueabihf `--enable-shared` `--enable-static`
`make

文件夹中./src/.libs/ 包含了所有已编译好的库,可以添加到你的项目中。我们需要用到这些库libmodbus.a

从 I/O 模块文档中,我们可以找到需要读取的寄存器。对于 DIO-16BD,输入寄存器是 258;对于 AIO-4,我们需要从地址 207 读取四个模拟输入。由于有四个浮点值,我们需要同时读取八个寄存器。为了测试这一点,我们来构建一个简单的 C++ 项目:

文件main.cpp

复制代码
#include <modbus/modbus.h>`
`#include <iostream>`
`#include <errno.h>`
`#include "util.h"`

`using` `namespace` `std`;

`bool` `_stop=false`;

`void` `handle_signal`(`int` `i`) {
   `printf`(`"[MAIN] Terminating\n"`); 
   `_stop=true`;
}

`int` `main`() {
   `signal`(`SIGINT`, `handle_signal`);
   `signal`(`SIGTERM`, `handle_signal`);
   `cout<<"Start..."<<endl`;
   `uint16_t` `buffer`[`8`];
   `modbus_t` `*ctx=modbus_new_rtu`(`"/dev/ttyS2"`,`115200`,`'N'`,`8`,`1`);
   `if` (`modbus_connect`(`ctx`) `==` `-1`)
       `cout<<"Connection failed: "<<modbus_strerror`(`errno`)`<<endl`;
   `modbus_set_response_timeout`(`ctx`, `1`,`0`);
   `while`(`!_stop`) {
       `modbus_set_slave`(`ctx`,`1`);
       `int` `rc=modbus_read_registers`(`ctx`,`258`,`1`,`buffer`);
       `if` (`rc` `==` `-1`)
           `cout<<"#1 MDS_DIO_16BD::Read() "<<modbus_strerror`(`errno`)`<<endl`;
       `else`
           `cout<<"#1 "<<buffer`[`0`]`<<endl`;
       `modbus_set_slave`(`ctx`,`2`);
       `nsleep`(`10`);
       `rc=modbus_read_registers`(`ctx`,`258`,`1`,`buffer`);
       `if` (`rc` `==` `-1`)
           `cout<<"#2 MDS_DIO_16BD::Read() "<<modbus_strerror`(`errno`)`<<endl`;
       `else`
           `cout<<"#2 "<<buffer`[`0`]`<<endl`;
       `modbus_set_slave`(`ctx`,`3`);
       `nsleep`(`10`);
       `rc` `=` `modbus_read_input_registers`(`ctx`, `207`, `8`, `buffer`);
       `if` (`rc` `==` `-1`)
           `cout<<"#3 MDS_AIO_4::Read() "<<modbus_strerror`(`errno`)`<<endl`;
       `else`
           `cout<<"#3 "<<modbus_get_float_badc`(`buffer`)`<<" "<<modbus_get_float_badc`(`buffer+2`)`<<" "<<modbus_get_float_badc`(`buffer+4`)`<<" "<<modbus_get_float_badc`(`buffer+6`)`<<endl`;
       `nsleep`(`10`);
   }  
   `modbus_close`(`ctx`);
   `modbus_free`(`ctx`);
   `cout<<"Stop..."<<endl`;
}`

文件util.cpp

复制代码
int` `nsleep`(`long` `miliseconds`) {
   `struct` `timespec` `req`, `rem`;
   `if`(`miliseconds` `>` `999`) {
       `req`.`tv_sec` `=` (`int`)(`miliseconds` `/` `1000`);
       `req`.`tv_nsec` `=` (`miliseconds` `-` ((`long`)`req`.`tv_sec` `*` `1000`)) `*` `1000000`;
   }
   `else` {
       `req`.`tv_sec` `=` `0`;
       `req`.`tv_nsec` `=` `miliseconds` `*` `1000000`;
   }
   `return` `nanosleep`(`&req` , `&rem`);
}`

我们使用以下命令编译并发送可执行文件:

复制代码
`arm-linux-gnueabihf-g`++` `-std=`c`++11` `-I` ./libmodbus `-L`. `-o` ./main ./*.cpp `-lmodbus`
sshpass `-p` `"icpdas"` scp main root@192.168.0.2:/root/`

所有部件组装完毕后会发送到PLC。如果组装和配置都正确,终端会显示以下结果:

复制代码
#1 0`
`#2 0`
`#3 4.000000 4.000001 3.999998 4.000000

一切运行正常,循环显示了所需的数据。因此,我们已经学会了如何从"字段"中检索数据。

如果我们根据这些时间节点来观察 PLC 处理器的负载情况,就会发现情况相当糟糕:三个模块就占用了控制器 10% 的负载。在生产条件下,这是一个非常非常高的负载,因为目前有些项目有十几条生产线,每条生产线都有 7-10 个模块。

我们来看一下系统启动数据。运行以下命令top

复制代码
`top - 10:47:15 up  5:28,  2 users,  load average: 1.00, 1.01, 1.03
Tasks: 108 total,   1 running, 107 sleeping,   0 stopped,   0 zombie
Cpu(s):  1.3%us, 14.4%sy,  0.0%ni, 84.0%id,  0.0%wa,  0.0%hi,  0.3%si,  0.0%st
Mem:	506968k total,	95152k used,   411816k free,    	0k buffers
Swap:    	0k total,    	0k used,    	0k free,	41316k cached

  PID USER  	PR  NI  VIRT  RES  SHR S %CPU %MEM	TIME+  COMMAND
 2079 root  	20   0  2660 1004  860 S  9.5  0.2 0:09.09 main...`

为了降低系统负载,您可以增加轮询所有 I/O 模块后的延迟。将延迟设置为 300 毫秒,可将此任务的 CPU 负载降低至 1.6%。

如何改进和规范大量不同设备的使用?我们需要采用建造者模式。让我们创建一个 Modbus 设备的基类:

文件ModbusModule.h

复制代码
class` `ModbusModule`
{
`public`:
    `ModbusModule`(`modbus_t` `*ctx`, `int` `addr`);
    `virtual` `~ModbusModule`();
    `virtual` `void` `Read`() {};
    `virtual` `void` `Write`() {};
    `bool` `isError=true`,`isChanged=true`;
    `int` `error=0`;
`protected`:
    `bool` `oldIsError=false`;
    `int` `rc`,`olderror`;
`protected`:
    `void` `Set`();
    `modbus_t` `*ctx`;
    `int` `address`;
};`

文件ModbusModule.cpp

复制代码
ModbusModule::ModbusModule`(`modbus_t` `*ctx`, `int` `addr`) :
    `ctx`{`ctx`},`address`{`addr`} { }

`ModbusModule::~ModbusModule`() { }

`void` `ModbusModule::Set`() {
    `modbus_flush`(`ctx`);
    `modbus_set_slave`(`ctx`,`address`);
    `nsleep`(`5`);
}`

现在我们以MDS DIO-16BD为例来描述一下这个设备本身。其原理是将需要从中获取值或向设备写入数据的变量地址传递给它。其他线程随后就可以访问这些变量了。

文件MDS-DIO-16BD.h

复制代码
class` `MDS_DIO_16BD` : `public` `ModbusModule`
{
`public`:
    `MDS_DIO_16BD`(`modbus_t` `*ctx`, `int` `addr`,
        `volatile` `bool` `*rB1=NULL`,`volatile` `bool` `*rB2=NULL`,`volatile` `bool` `*rB3=NULL`,`volatile` `bool` `*rB4=NULL`,
        `volatile` `bool` `*rB5=NULL`,`volatile` `bool` `*rB6=NULL`,`volatile` `bool` `*rB7=NULL`,`volatile` `bool` `*rB8=NULL`,
        `volatile` `bool` `*rB9=NULL`,`volatile` `bool` `*rB10=NULL`,`volatile` `bool` `*rB11=NULL`,`volatile` `bool` `*rB12=NULL`,
        `volatile` `bool` `*rB13=NULL`,`volatile` `bool` `*rB14=NULL`,`volatile` `bool` `*rB15=NULL`,`volatile` `bool` `*rB16=NULL`,
        `volatile` `bool` `*wB1=NULL`,`volatile` `bool` `*wB2=NULL`,`volatile` `bool` `*wB3=NULL`,`volatile` `bool` `*wB4=NULL`,
        `volatile` `bool` `*wB5=NULL`,`volatile` `bool` `*wB6=NULL`,`volatile` `bool` `*wB7=NULL`,`volatile` `bool` `*wB8=NULL`,
        `volatile` `bool` `*wB9=NULL`,`volatile` `bool` `*wB10=NULL`,`volatile` `bool` `*wB11=NULL`,`volatile` `bool` `*wB12=NULL`,
        `volatile` `bool` `*wB13=NULL`,`volatile` `bool` `*wB14=NULL`,`volatile` `bool` `*wB15=NULL`,`volatile` `bool` `*wB16=NULL`,
        `volatile` `uint16_t` `*rC1=NULL`,`volatile` `uint16_t` `*rC2=NULL`,`volatile` `uint16_t` `*rC3=NULL`,`volatile` `uint16_t` `*rC4=NULL`,
        `volatile` `bool` `*wReset1=NULL`,`volatile` `bool` `*wReset2=NULL`,`volatile` `bool` `*wReset3=NULL`,`volatile` `bool` `*wReset4=NULL`);
    `~MDS_DIO_16BD`() `override`;
    `void` `Write`() `override`;
    `void` `Read`() `override`;
    `void` `setRev`(`int` `index`);


`private`:
    `uint16_t` `*buffer`;
    `volatile` `bool` `*rB1`,`*rB2`,`*rB3`,`*rB4`,`*rB5`,`*rB6`,`*rB7`,`*rB8`,`*rB9`,`*rB10`,`*rB11`,`*rB12`,`*rB13`,`*rB14`,`*rB15`,`*rB16`,`*wB1`,`*wB2`,`*wB3`,`*wB4`,`*wB5`,`*wB6`,`*wB7`,`*wB8`,`*wB9`,`*wB10`,`*wB11`,`*wB12`,`*wB13`,`*wB14`,`*wB15`,`*wB16`;
    `volatile` `uint16_t` `*rC1`,`*rC2`,`*rC3`,`*rC4`;
    `volatile` `bool` `*wReset1`,`*wReset2`,`*wReset3`,`*wReset4`;
    `bool` `rev`[`16`];
};`

文件MDS-DIO-16BD.cpp

复制代码
MDS_DIO_16BD::MDS_DIO_16BD`(`modbus_t` `*ctx`, `int` `addr`,
    `volatile` `bool` `*rB1`,`volatile` `bool` `*rB2`,`volatile` `bool` `*rB3`,`volatile` `bool` `*rB4`,
    `volatile` `bool` `*rB5`,`volatile` `bool` `*rB6`,`volatile` `bool` `*rB7`,`volatile` `bool` `*rB8`,
    `volatile` `bool` `*rB9`,`volatile` `bool` `*rB10`,`volatile` `bool` `*rB11`,`volatile` `bool` `*rB12`,
    `volatile` `bool` `*rB13`,`volatile` `bool` `*rB14`,`volatile` `bool` `*rB15`,`volatile` `bool` `*rB16`,
    `volatile` `bool` `*wB1`,`volatile` `bool` `*wB2`,`volatile` `bool` `*wB3`,`volatile` `bool` `*wB4`,
    `volatile` `bool` `*wB5`,`volatile` `bool` `*wB6`,`volatile` `bool` `*wB7`,`volatile` `bool` `*wB8`,
    `volatile` `bool` `*wB9`,`volatile` `bool` `*wB10`,`volatile` `bool` `*wB11`,`volatile` `bool` `*wB12`,
    `volatile` `bool` `*wB13`,`volatile` `bool` `*wB14`,`volatile` `bool` `*wB15`,`volatile` `bool` `*wB16`,
    `volatile` `uint16_t` `*rC1`,`volatile` `uint16_t` `*rC2`,`volatile` `uint16_t` `*rC3`,`volatile` `uint16_t` `*rC4`,
    `volatile` `bool` `*wReset1`,`volatile` `bool` `*wReset2`,`volatile` `bool` `*wReset3`,`volatile` `bool` `*wReset4`)
    : `rB1`{`rB1`},`rB2`{`rB2`},`rB3`{`rB3`},`rB4`{`rB4`},`rB5`{`rB5`},`rB6`{`rB6`},`rB7`{`rB7`},`rB8`{`rB8`},
    `rB9`{`rB9`},`rB10`{`rB10`},`rB11`{`rB11`},`rB12`{`rB12`},`rB13`{`rB13`},`rB14`{`rB14`},`rB15`{`rB15`},`rB16`{`rB16`},
    `wB1`{`wB1`},`wB2`{`wB2`},`wB3`{`wB3`},`wB4`{`wB4`},`wB5`{`wB5`},`wB6`{`wB6`},`wB7`{`wB7`},`wB8`{`wB8`},
    `wB9`{`wB9`},`wB10`{`wB10`},`wB11`{`wB11`},`wB12`{`wB12`},`wB13`{`wB13`},`wB14`{`wB14`},`wB15`{`wB15`},`wB16`{`wB16`},
    `rC1`{`rC1`},`rC2`{`rC2`},`rC3`{`rC3`},`rC4`{`rC4`},
    `wReset1`{`wReset1`},`wReset2`{`wReset2`},`wReset3`{`wReset3`},`wReset4`{`wReset4`},`ModbusModule`(`ctx`,`addr`) {
    `buffer` `=` `new` `uint16_t`[`16`];
    `memset`(`buffer`, `0`, `16` `*` `sizeof`(`uint16_t`));
    `for`(`int` `i=0`;`i<16`;`i++`) `rev`[`i`]`=false`;
}
`MDS_DIO_16BD::~MDS_DIO_16BD`() {
    `delete`[] `buffer`;
}

`void` `MDS_DIO_16BD::Write`() {
    `uint16_t` `temp=0`;
    `if`(`wB1!=NULL||wB2!=NULL||wB3!=NULL||wB4!=NULL||wB5!=NULL||wB6!=NULL||wB7!=NULL||wB8!=NULL||`
        `wB9!=NULL||wB10!=NULL||wB11!=NULL||wB12!=NULL||wB13!=NULL||wB14!=NULL||wB15!=NULL||wB16!=NULL`) {
        `temp=`((`wB1==NULL`)`?0`:`rev`[`0`]`?!`(`*wB1<<0`):(`*wB1<<0`))`|`
            ((`wB2==NULL`)`?0`:`rev`[`1`]`?!`(`*wB2<<1`):(`*wB2<<1`))`|`
            ((`wB3==NULL`)`?0`:`rev`[`2`]`?!`(`*wB3<<2`):(`*wB3<<2`))`|`
            ((`wB4==NULL`)`?0`:`rev`[`3`]`?!`(`*wB4<<3`):(`*wB4<<3`))`|`
            ((`wB5==NULL`)`?0`:`rev`[`4`]`?!`(`*wB5<<4`):(`*wB5<<4`))`|`
            ((`wB6==NULL`)`?0`:`rev`[`5`]`?!`(`*wB6<<5`):(`*wB6<<5`))`|`
            ((`wB7==NULL`)`?0`:`rev`[`6`]`?!`(`*wB7<<6`):(`*wB7<<6`))`|`
            ((`wB8==NULL`)`?0`:`rev`[`7`]`?!`(`*wB8<<7`):(`*wB8<<7`))`|`
            ((`wB9==NULL`)`?0`:`rev`[`8`]`?!`(`*wB9<<8`):(`*wB9<<8`))`|`
            ((`wB10==NULL`)`?0`:`rev`[`9`]`?!`(`*wB10<<9`):(`*wB10<<9`))`|`
            ((`wB11==NULL`)`?0`:`rev`[`10`]`?!`(`*wB11<<10`):(`*wB11<<10`))`|`
            ((`wB12==NULL`)`?0`:`rev`[`11`]`?!`(`*wB12<<11`):(`*wB12<<11`))`|`
            ((`wB13==NULL`)`?0`:`rev`[`12`]`?!`(`*wB13<<12`):(`*wB13<<12`))`|`
            ((`wB14==NULL`)`?0`:`rev`[`13`]`?!`(`*wB14<<13`):(`*wB14<<13`))`|`
            ((`wB15==NULL`)`?0`:`rev`[`14`]`?!`(`*wB15<<14`):(`*wB15<<14`))`|`
            ((`wB16==NULL`)`?0`:`rev`[`15`]`?!`(`*wB16<<15`):(`*wB16<<15`));
        `buffer`[`1`]`=temp`;
        `Set`();
        `rc` `=` `modbus_write_registers`(`ctx`, `267`, `1`, `buffer+1`);
        `isError=`(`rc==-1`);
        `if` (`rc` `==` `-1`)
            `fprintf`(`stderr`, `"#%d MDS_DIO_16BD::Write(... %s\n"`, `address`, `modbus_strerror`(`errno`));
    }
    `if`(`wReset1!=NULL||wReset2!=NULL||wReset3!=NULL||wReset4!=NULL`) {
        `temp=`((`wReset1==NULL`)`?0`:(`*wReset1<<0`))`|`
            ((`wReset2==NULL`)`?0`:(`*wReset2<<1`))`|`
            ((`wReset3==NULL`)`?0`:(`*wReset3<<2`))`|`
            ((`wReset4==NULL`)`?0`:(`*wReset4<<3`));
        `rc` `=` `modbus_write_register`(`ctx`, `276`, `temp`);
        `isError&=`(`rc==-1`);
        `if` (`rc` `==` `-1`)
            `fprintf`(`stderr`, `"#%d MDS_DIO_16BD::Write(... %s\n"`,`address`, `modbus_strerror`(`errno`));
    }
}

`void` `MDS_DIO_16BD::Read`() {
    `if`(`rB1!=NULL||rB2!=NULL||rB3!=NULL||rB4!=NULL||rB5!=NULL||rB6!=NULL||rB7!=NULL||rB8!=NULL||`
        `rB9!=NULL||rB10!=NULL||rB11!=NULL||rB12!=NULL||rB13!=NULL||rB14!=NULL||rB15!=NULL||rB16!=NULL`) {
        `Set`();
        `rc` `=` `modbus_read_registers`(`ctx`, `258`, `1`, `buffer`);
        `isError=`(`rc==-1`);
        `if` (`rc` `==` `-1`)
            `fprintf`(`stderr`, `"#%d MDS_DIO_16BD::Read() %s\n"`,`address`, `modbus_strerror`(`errno`));
        `else` {
            `if`(`rB1!=NULL`) `*rB1=`(`buffer`[`0`]`>>0`)`&1`;
            `if`(`rB2!=NULL`) `*rB2=`(`buffer`[`0`]`>>1`)`&1`;
            `if`(`rB3!=NULL`) `*rB3=`(`buffer`[`0`]`>>2`)`&1`;
            `if`(`rB4!=NULL`) `*rB4=`(`buffer`[`0`]`>>3`)`&1`;
            `if`(`rB5!=NULL`) `*rB5=`(`buffer`[`0`]`>>4`)`&1`;
            `if`(`rB6!=NULL`) `*rB6=`(`buffer`[`0`]`>>5`)`&1`;
            `if`(`rB7!=NULL`) `*rB7=`(`buffer`[`0`]`>>6`)`&1`;
            `if`(`rB8!=NULL`) `*rB8=`(`buffer`[`0`]`>>7`)`&1`;
            `if`(`rB9!=NULL`) `*rB9=`(`buffer`[`0`]`>>8`)`&1`;
            `if`(`rB10!=NULL`) `*rB10=`(`buffer`[`0`]`>>9`)`&1`;
            `if`(`rB11!=NULL`) `*rB11=`(`buffer`[`0`]`>>10`)`&1`;
            `if`(`rB12!=NULL`) `*rB12=`(`buffer`[`0`]`>>11`)`&1`;
            `if`(`rB13!=NULL`) `*rB13=`(`buffer`[`0`]`>>12`)`&1`;
            `if`(`rB14!=NULL`) `*rB14=`(`buffer`[`0`]`>>13`)`&1`;
            `if`(`rB15!=NULL`) `*rB15=`(`buffer`[`0`]`>>14`)`&1`;
            `if`(`rB16!=NULL`) `*rB16=`(`buffer`[`0`]`>>15`)`&1`;
        }
    }
    `if`(`rC1!=NULL||rC2!=NULL||rC3!=NULL||rC4!=NULL`) {
        `rc` `=` `modbus_read_registers`(`ctx`, `278`, `4`, `buffer`);
        `isError&=`(`rc==-1`);
        `if` (`rc` `==` `-1`)
            `fprintf`(`stderr`, `"#%d MDS_DIO_16BD::Read() %s\n"`,`address`, `modbus_strerror`(`errno`));
        `else` {
            `if`(`rC1!=NULL`) `*rC1=buffer`[`0`];
            `if`(`rC2!=NULL`) `*rC2=buffer`[`1`];
            `if`(`rC3!=NULL`) `*rC3=buffer`[`2`];
            `if`(`rC4!=NULL`) `*rC4=buffer`[`3`];
        }
    }
}

`void` `MDS_DIO_16BD::setRev`(`int` `index`) {
    `if`(`index<0` `||` `index>15`) `return`;
    `rev`[`index`]`=!rev`[`index`];
}`

我们将使用生成模式来创建用于轮询单个 RS-485 总线上的设备的类。

文件ModbusLine.h

复制代码
class` `ModbusLine` {
`public`:
    `ModbusLine`(`modbus_t` `*ctx`, `long` `sleepTime=300`);
    `~ModbusLine`();
    `MDS_DIO_16BD*` `addMDS_DIO_16BD`(`int` `address`,
        `volatile` `bool` `*rB1=NULL`,`volatile` `bool` `*rB2=NULL`,`volatile` `bool` `*rB3=NULL`,`volatile` `bool` `*rB4=NULL`,
        `volatile` `bool` `*rB5=NULL`,`volatile` `bool` `*rB6=NULL`,`volatile` `bool` `*rB7=NULL`,`volatile` `bool` `*rB8=NULL`,
        `volatile` `bool` `*rB9=NULL`,`volatile` `bool` `*rB10=NULL`,`volatile` `bool` `*rB11=NULL`,`volatile` `bool` `*rB12=NULL`,
        `volatile` `bool` `*rB13=NULL`,`volatile` `bool` `*rB14=NULL`,`volatile` `bool` `*rB15=NULL`,`volatile` `bool` `*rB16=NULL`,
        `volatile` `bool` `*wB1=NULL`,`volatile` `bool` `*wB2=NULL`,`volatile` `bool` `*wB3=NULL`,`volatile` `bool` `*wB4=NULL`,
        `volatile` `bool` `*wB5=NULL`,`volatile` `bool` `*wB6=NULL`,`volatile` `bool` `*wB7=NULL`,`volatile` `bool` `*wB8=NULL`,
        `volatile` `bool` `*wB9=NULL`,`volatile` `bool` `*wB10=NULL`,`volatile` `bool` `*wB11=NULL`,`volatile` `bool` `*wB12=NULL`,
        `volatile` `bool` `*wB13=NULL`,`volatile` `bool` `*wB14=NULL`,`volatile` `bool` `*wB15=NULL`,`volatile` `bool` `*wB16=NULL`,
        `volatile` `uint16_t` `*rC1=NULL`,`volatile` `uint16_t` `*rC2=NULL`,`volatile` `uint16_t` `*rC3=NULL`,`volatile` `uint16_t` `*rC4=NULL`,
        `volatile` `bool` `*wReset1=NULL`,`volatile` `bool` `*wReset2=NULL`,`volatile` `bool` `*wReset3=NULL`,`volatile` `bool` `*wReset4=NULL`);
`protected`:
    `void` `Thread`();
    `modbus_t` `*ctx`;
    `long` `sleepTime`;
    `std::thread` `_t`;
    `bool` `_stop=false`;
    `std::vector<ModbusModule*>` `devices`;
};
`

文件ModbusLine.cpp

复制代码
ModbusLine::ModbusLine`(`modbus_t` `*ctx`, `long` `sleepTime`) : `ctx`{`ctx`},`sleepTime`{`sleepTime`} {
    `_t=std::thread`(`&ModbusLine::Thread`,`this`);
    `printf`(`"ModbusLine create\n"`);
}
`ModbusLine::~ModbusLine`() {
    `_stop=true`;
    `_t`.`join`();
    `for`(`auto&` `dev`:`devices`) `delete` `dev`;
    `devices`.`clear`();
    `modbus_close`(`ctx`);
    `modbus_free`(`ctx`);
    `printf`(`"ModbusLine delete\n"`);
}

`void` `ModbusLine::Thread`() {
    `int` `isOpen=-1`;
    `auto` `olderrno=errno`;
    `while`(`!_stop`) {
        `if` (`isOpen==-1`) {
            `cout<<"Try open port..."<<endl`;
            `if` ((`isOpen` `=` `modbus_connect`(`ctx`)) `==` `-1`) {
                `if`(`errno!=olderrno`) {
                    `fprintf`(`stderr`, `"Connection failed: %s\n"`,`modbus_strerror`(`errno`));
                }
            }
            `modbus_set_error_recovery`(`ctx`,  (`modbus_error_recovery_mode`)`6`);
            `modbus_set_response_timeout`(`ctx`, `0`,`500`);
            `olderrno=errno`;
            `nsleep`(`1000`);
            `continue`;
        }
        `bool` `err=true`;
        `for`(`auto&` `dev`:`devices`) {
            `dev->Read`();
            `if`(`dev->isError==false`) `err=false`;
            `nsleep`(`10`);
            `dev->Write`();
            `if`(`dev->isError==false`) `err=false`;
            `nsleep`(`10`);
        }
        `if`(`err`) {
            `isOpen=-1`;
        }
        `nsleep`(`sleepTime`);
    }
}

`MDS_DIO_16BD*` `ModbusLine::addMDS_DIO_16BD`(`int` `address`,
    `volatile` `bool` `*rB1`,`volatile` `bool` `*rB2`,`volatile` `bool` `*rB3`,`volatile` `bool` `*rB4`,
    `volatile` `bool` `*rB5`,`volatile` `bool` `*rB6`,`volatile` `bool` `*rB7`,`volatile` `bool` `*rB8`,
    `volatile` `bool` `*rB9`,`volatile` `bool` `*rB10`,`volatile` `bool` `*rB11`,`volatile` `bool` `*rB12`,
    `volatile` `bool` `*rB13`,`volatile` `bool` `*rB14`,`volatile` `bool` `*rB15`,`volatile` `bool` `*rB16`,
    `volatile` `bool` `*wB1`,`volatile` `bool` `*wB2`,`volatile` `bool` `*wB3`,`volatile` `bool` `*wB4`,
    `volatile` `bool` `*wB5`,`volatile` `bool` `*wB6`,`volatile` `bool` `*wB7`,`volatile` `bool` `*wB8`,
    `volatile` `bool` `*wB9`,`volatile` `bool` `*wB10`,`volatile` `bool` `*wB11`,`volatile` `bool` `*wB12`,
    `volatile` `bool` `*wB13`,`volatile` `bool` `*wB14`,`volatile` `bool` `*wB15`,`volatile` `bool` `*wB16`,
    `volatile` `uint16_t` `*rC1`,`volatile` `uint16_t` `*rC2`,`volatile` `uint16_t` `*rC3`,`volatile` `uint16_t` `*rC4`,
    `volatile` `bool` `*wReset1`,`volatile` `bool` `*wReset2`,`volatile` `bool` `*wReset3`,`volatile` `bool` `*wReset4`) {
    `auto` `out` `=` `new` `MDS_DIO_16BD`(`ctx`,`address`,`rB1`,`rB2`,`rB3`,`rB4`,
        `rB5`,`rB6`,`rB7`,`rB8`,`rB9`,`rB10`,`rB11`,`rB12`,`rB13`,`rB14`,`rB15`,`rB16`,
        `wB1`,`wB2`,`wB3`,`wB4`,`wB5`,`wB6`,`wB7`,`wB8`,`wB9`,`wB10`,`wB11`,`wB12`,`wB13`,`wB14`,`wB15`,`wB16`,
        `rC1`,`rC2`,`rC3`,`rC4`,`wReset1`,`wReset2`,`wReset3`,`wReset4`);
    `devices`.`push_back`((`ModbusModule*`)`out`);
    `return` `out`;
}`

RS-485总线的描述如下所示:

复制代码
volatile` `bool` `b1=false`,`b2=false`,`b3=false`;
`ModbusLine` `line1`(`modbus_new_rtu`(`"/dev/ttyS2"`, `115200`, `'N'`, `8`, `1`));
`line1`.`addMDS_DIO_16BD`(`1`,`&b1`,`&b2`,`&b3`);`

我们将为每一行代码创建一个单独的线程,主应用程序不会依赖设备轮询。由于使用了 volatile 关键字,变量始终包含最新参数。volatile 关键字会告知编译器变量的值可以被外部更改,并且不会对其进行缓存。

下一篇文章,我们将探讨如何使用 MQTT 在 PLC 应用程序和 MNE 之间进行通信。

相关推荐
碎碎思2 小时前
从 JTAG 启动 Zynq-7000 嵌入式 Linux:使用 XSCT 全流程教程
linux·运维·服务器·fpga开发
一颗青果2 小时前
单例模式 | 死锁
linux·服务器·单例模式·1024程序员节
路西法012 小时前
# CentOS系统yum方式安装MySQL
linux·mysql·centos
CAU界编程小白2 小时前
Linux编程系列之进程概念(上)
linux
yangn02 小时前
ysu-527科研服务器使用指南
linux·运维·服务器
迷人的星空2 小时前
SPI 只是个接口?揭秘芯片间的高速通道!
物联网·嵌入式
ydswin3 小时前
Sidecar不就是在Pod里多跑一个容器吗!
linux·kubernetes
QT 小鲜肉3 小时前
【Linux命令大全】001.文件管理之mdu命令(实操篇)
linux·运维·服务器·chrome·笔记·microsoft
旖旎夜光3 小时前
Linux(6)(上)
linux·学习