本帖最后由 story_xjj 于 2018-6-28 13:12 编辑
基于实时数据库的多串口多协议通讯管理机
本项目是基于米尔科技MYS-6ULX-IOT主板为基础,开发的具有实时数据库功能的多串口多协议通讯管理机项目。
由于时间的原因,目前完成的项目内容包括2个串口和2种协议的支持。
项目主要完成目标:
l 基于以太网接口的实时数据库访问;
l 基于串口(可扩展为RS485接口)的modbus-RTU设备通讯管理功能;
l 基于串口(可扩展为RS485接口)的CDT设备通讯管理功能;
注:由于个人能力、时间等原因,所实现的内容难免出现错误,敬请指正。
注:本次软件所实现的功能用于参与米尔科技产品试用活动,不可用于商业用途。
1. 实时数据库
为了达成本次项目的需求,选择了较为常用的免费开源实时数据库redis。Redis是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,包括Java,C/C++,C#,PHP,Python等,使用很方便。提供多种语言API这个非常重要,可以基于这个数据库开发丰富的上位软件功能。
1.1. 实时数据库安装
为了可以使用Redis数据库,我们必须下载源码,编译后,在IOT板上运行。
1)源代码下载
我这里选择的是redis-stable版本。
2)编译
编译过程还是比较顺利的。我是在ubuntu16.04 64位系统上完成的。
第一步:解压源码
tar xzf redis-stalbe.tar.gz
第二步:编译准备
为了可以正确的编译redis,首先必须先要编译一下目录下的deps文件夹下的几个组件,要不编译的时候可能提示缺少静态库的支持。
编译的命令,因为要支持imx6ull,所以我用linaro编译器。
进入deps目录中,执行
make CC=arm-linux-gnueabihf-gcc hiredis linenoiselua
编译结束后回到redis-stable的主目录下,执行真正的编译过程
make CC=arm-linux-gnueabihf-gcc MALLOC=libc
正常情况下可以编译我们需要的所有文件了。
第三步:导出执行文件
make install PREFIX=~/redis-install
执行完后,在redis-install目录中就是我们准备在imx6ull上使用的所有可执行文件了。
3)安装
将ubuntu系统中我们准备好的redis-install目录下的所有文件复制到imx6ull系统中/usr/local/bin目录下,这个目录不是必须的,什么目录都可以,因为这个目录是redis官方推荐的,另外在PATH中默认有这个路径,所以比较省事。
4)修改执行属性
通常情况下,这些文件都不是可执行文件,必须增加可执行属性。
进入/usr/local/bin目录中,执行
chmod a+x *
这样,整个安装工作就基本结束了。
5) 测试
为了验证所编译的redis是否可以在目标机器上使用,我们进行简单的测试。测试方法:
在终端中执行命令
redis-server &
在执行
redis-cli
使用“PING”的命令测试服务器是否已经进入就绪状态,如果正确的回复“PONG”的话,恭喜,安装完成,服务器正确的启动了。
1.2. 实时数据库基本配置
l 绑定的IP地址,指定可以访问数据库的网络。
bind 127.0.0.1 192.168.7.2
l 端口指定
port 6379
l 默认redis不是以后台进程的方式启动,如果需要在后台运行,需要将这个值设置成yes,以后台方式启动的时候,redis会写入默认的进程文件/var/run/redis.pid
daemonize yes
l 数据库固化时间
save "" 表示不进行固化,即不写入硬盘
还有其它的可配置参数,如果需要大家可以进一步研究。
1.3. 实时数据库基本使用方法
这里不详细说明数据库的具体使用方法,请大家上网搜索,资料超级多。
1.4. 实时数据库C语言接口
为了通过我们自己的应用程序操作数据库,必须使用数据库的API接口,前面提到了,Redis的API接口又多又全。我的程序为C语言的,所以我也选择C语言接口API——hiredis。
Hiredis是Redis数据库的简约C客户端库。它是简约的,因为它只是增加了对协议的最小支持,但是同时它使用了一个高级别的printf-likeAPI,所以对于习惯了printf风格的C编程用户来说,其非常容易使用,而且API中没有明确的绑定每个Redis命令。除了支持发送命令和接收回复之外,它还附带了一个与I/O层分离的回复解析器。为了方便复用,其设计成为一个流解析器,例如可以在更高级别的语言 绑定中使用,以实现高效的回复解析。
Hiredis仅支持二进制安全(binary-safe)的Redis协议,因此您可以将其与任何 Redis版本(> = 1.2.0)配合使用。该库带有多个API。 有同步API,异步API和答复解析API。
简单的介绍在程序中使用的几个hiredis的使用方法。
1) 建立连接
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
if (c) {
printf("Error: %s\n", c->errstr);
// handle error
} else {
printf("Can't allocate redis context\n");
}
}
2) 发送命令
void(redisAsyncContext *c, void *reply, void*privdata);
3) 使用事务型处理
这个很重要。我的所有数据写入数据库都是采用此种方法实现的。实际测试效果还是挺好的。
Redis 事务可以一次执行多个命令,并且带有以下两个重要的保证:
n 批量操作在发送 EXEC 命令前被放入队列缓存。
n 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
n 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
n 一个事务从开始到执行会经历以下三个阶段:
开始事务 MULTI。
命令入队。
执行事务 EXEC。
例子:
reply = redisCommand(edis_con,"MULTI");
for (i=0; i<nb; i++)
{
redisCommand(edis_con,"SET %s %d", key_name, tab_rp_registers);
}
reply = redisCommand(edis_con,"EXEC");
freeReplyObject(reply);
4) 关闭连接
/* Disconnects and frees thecontext */
redisFree(redisContext *c);
1.5. 实时数据库windows管理器
通过在网上查找,我发现一个国内设计的管理器挺好用的,软件名叫treeNMS。使用挺简单,是基于浏览器的,不用安装一堆的软件,我喜欢。
关于软件的设置和使用请大家上网去搜索一下,其实挺容易的,至少我现在用的内容足够了。
2. Modbus-RTU协议的实现
2.1. 协议栈描述
通过网络搜索,linux下比较推崇的modbus库是libmodbus,他支持主站和子站两种模式,我决定选用这个源码开源的libmodbus作为Modbus-RTU的开发基础。
2.2. libmodbus安装
要想正确的使用这个库,可以直接编译源码方式,也可以采用库的方式。
本次采用稳定版libmodbus-3.0.6.tar.gz
1) 拷贝到相应目录,解压
tar -zxvf libmodbus-3.0.6.tar.gz
2) 创建安装目录
mkdirinstall
3) 进入解压的目录
cd libmodbus-3.0.6
4) 配置编译选项(注:--prefix选项是安装目录,根据自己环境确定)
./configure --host=arm-fsl-linux-gnueabi--enable-static --prefix=[安装路径]/install/
5) 编译
make
6) 安装
makeinstall
7) 在install生成三个目录:include lib share
2.3. libmodbus的应用
1) 按要求打开端口
/* RTU */
ctx = modbus_new_rtu("/dev/ttymxc1", 19200, 'N', 8, 1);
2) 建立连接
modbus_set_slave(ctx, Modbus_scan_str[Modbus_scan_No].sta_no);
if(modbus_connect(ctx) == -1) {
fprintf(stderr, "Connection failed:%s\n",
modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
在打开端口后,为了可以巡检到我们需要的子站,必须首先调用这个函数将准备管理的子站ID提供给协议栈。
这个很重要,要不然你不会读取到正确的子站数据。
3) 读取数据
针对命令读保持寄存器-3
连接 地址 数量 返回数据
rc= modbus_read_registers(ctx, addr, nb, tab_rp_registers);
命令执行后,tab_rp_registers[]中将保存我读取到的子站保持寄存器中的数据。我们可以针对这个数组进行操作。
还有其它命令,此处不一一介绍,请大家看库函数或我前面的帖子中又一些说明。
4) 关闭连接
/* Close the connection */
modbus_free(ctx);
2.4. 针对多个子站的组态
为了方便管理多个子站,我创建了一个结构体,用来存储子站信息。
typedef struct
{
unsigned char sta_no; /*子站ID*/
unsigned char modbus_cmd; /*巡检指令*/
unsigned int src_addr; /*起始地址*/
unsigned char get_num; /*读取数量*/
char data_type[10];/*数据类型*/
unsigned long dest_addr;/*目的地址*/
}MODBUS_SCAN_STR;
通过组态这个结构体的内容,我们可以读取多个子站数据,和单个子站中的多种数据。
MODBUS_SCAN_STR Modbus_scan_str[MODBUS_SCAN_NUM] =
{
{1,3, 0,10,"int",0},
{1,3,10,10,"int",0},
{2,3, 0,10,"int",0},
{2,3,10,20,"int",0},
{2,3,50,10,"int",0}
};
3. CDT协议的实现
3.1. 协议栈描述
CDT的全名为循环式远动规约。该标准为电力行业通讯标准之一,规定了电网数据采集和监控系统之间数据交换的传输规则。
CDT协议采用可变帧长度、多种帧类别循环传送、变位遥信优先传递,重要遥测量更新循环时间较短,区分循环量、随机量和插入量采用不同形式传递信息,以满足电网调度安全监控系统对远动信息的实时性和可靠性的要求。
CDT协议帧的基本结构为:
同步字 | 控制字 | 信息字 | 信息字… … | EB90EB90EB90 | 71610A0101CRC8 | 00xxxxxxxxCRC8 | 01xxxxxxxxCRC8… … |
3.2. CRC校验
CRC校验的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去。在接收端,则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。
CDT协议采用CRC8校验形式。
校验码实现代码:
unsignedchar CalCRC(unsigned char *data, int len)
{
int i;
unsigned char result = 0;
for(i=0; i<len; i++)
{
Crcget(data, result);
}
result = (unsigned char)~result;
}
voidCrcget(unsigned char data, unsigned char *result)
{
*result= crctable[*result ^ data];
}
//CRC8表,X8+X2+X+1
unsigned char crctable[] =
{
file:///C:/Users/jiyong/AppData/Local/Temp/msohtmlclip1/01/clip_image003.jpg 3.3. 针对多个子站的组态
为了方便管理多个子站,通过创建一个结构体来存储子站信息。主要包括每个子站上送的遥测量、遥信量和电度量。
typedef struct
{
unsigned char sta_no; /*子站ID*/
unsigned char YC_num; /*遥测量数量*/
unsigned char YX_num; /*遥信量数量*/
unsigned char DD_num; /*电度量数量*/
}CDT_SCAN_STR;
通过组态这个结构体的内容,我们可以获取每个子站上送信息的内容和数量。这里以两个子站为例,列举了子站信息的配置方法。
#define CDT_SCAN_NUM 2
CDT_SCAN_STR cdt_scan_str[CDT_SCAN_NUM]=
{
{.sta_no = 1,
.YC_num = 20,
.YX_num = 128,
.DD_num = 4},
{.sta_no = 2,
.YC_num = 20,
.YX_num = 64,
.DD_num = 0}
};
4. 运行效果检验
4.1. 仿真环境建立
根据组态内容,建立仿真环境,基本硬件实现结构如下图所示:
目前使用仅实现了2个串行总线和两种协议的转换,不过可以扩展更多总线和协议的转换,例如CAN总线、其它行业标准协议或自定义协议等。
串口2对应设备号为/dev/ttymxc1;
串口3对应设备号为/dev/ttymxc2。
注:关于使能IOT板卡多个串口功能,请参考我的贴子。
【我的项目666】+通讯管理(4)使能串口功能
4.2. 实际运行效果
1) 启动redis的服务器
命令:
redis-server redis.conf
运行效果检查 2) 启动通讯管理应用程序
命令:
./commser
运行效果 3) 启动模拟子站软件
命令:
模拟子站采用两个windows下的软件,一个是Modsim32,一个是PMA。Modsim32模拟两个子站,PMA分别模拟两个子站。
运行效果: file:///C:/Users/jiyong/AppData/Local/Temp/msohtmlclip1/01/clip_image012.jpg file:///C:/Users/jiyong/AppData/Local/Temp/msohtmlclip1/01/clip_image014.jpg 4) 启动实时数据库监视软件
命令:
启动TreeNMS,打开浏览器并输入127.0.0.1:8085打开实时数据库监视。
运行效果:
|