13.4 后台程序(控制中心)开发
源码为:
13.4.1 程序流程
后台程序是核心,它主要有2个操作:
①等待别的程序发来RPC请求
②处理RPC请求
main函数代码如下:
19 int main(int argc, char **argv)
20 {
21 if (argc != 2)
22 {
23 test(argc, argv);
24 }
25 else
26 {
27 read_cfg();
28
29 RPC_Server_Init();
30 }
31
32 return 0;
33 }
第27行:读取“/etc/control.cfg”配置文件,解析、记录MQTT信息、“点”信息。
第29行:注册各类RPC接口,等待、处理RPC请求。
13.4.2 配置文件的操作
main函数调用的“read_cfg”函数在“cfg.c”中实现。“/etc/control.cfg”
配置文件的内容示例如下:
{"port_info": "/dev/ttySC4,115200,8N1", "dev_addr": 3, "reg_addr": 0, "reg_type": "0x
", "period": 300}
{"port_info": "/dev/ttySC4,115200,8N1", "dev_addr": 3, "reg_addr": 1, "reg_type": "3x
", "period": 300}
{"port_info": "/dev/ttySC4,115200,8N1", "dev_addr": 3, "reg_addr": 0, "reg_type": "3x
", "period": 300}
{"broker": "192.168.5.10", "port": 1883, "client_id": "dev_019999", "user": "100ask99
", "password": "100asktech99", "publish": "/iot/up", "subcribe": "
/iot/down"}
里面含有2类信息:
①port_info开头:表示“点”的信息,可能有多个点
②broker开头:MQTT信息,只有1个
1.读取操作
“read_cfg”函数就是解析配置文件,把“点”的信息记录在数组里;把MQTT信息也记录起来。后续的RPC调用,将会使用到这些信息。
“read_cfg”函数代码如下:
78 /* 读取配置文件
79 * 配置文件里有如下多行信息:
80 * {"port_info": "/dev/ttyUSB0,115200,8n1", "dev_addr": 3, "reg_addr": 0, "reg_typ
e": "0x", "period": 100}
81 * {"broker": "192.168.5.10", "port": 1833, "client_id": "device_01", "user": "100
ask", "password": "www.100ask.net", "publish":"/iot/up", "subcribe
":"/iot/down"}
82 */
83 int read_cfg(void)
84 {
85 FILE *fp;
86 char line[400];
87 PointInfo tPointInfo;
88 MQTTInfo tMQTTInfo;
89
90 fp = fopen(CFG_FILE, "r");
91
92 if (fp)
93 {
94 while (fgets(line, 400, fp))
95 {
96 tPointInfo.dev_addr = 0;
97
98 cJSON *params = cJSON_Parse(line);
99
100 cJSON *port_info = cJSON_GetObjectItem(params, "port_info");
101 if (port_info)
102 {
103 strcpy(tPointInfo.port_info, port_info->valuestring);
104
105 cJSON *dev_addr = cJSON_GetObjectItem(params, "dev_addr");
106 if (dev_addr)
107 {
108 tPointInfo.dev_addr = dev_addr->valueint;
109 }
110
/* 省略 */
129 if (tPointInfo.dev_addr)
130 {
131 local_add_point(&tPointInfo);
132 }
133 }
134 else
135 {
136 cJSON *broker = cJSON_GetObjectItem(params, "broker");
137 if (broker)
138 {
139 strcpy(tMQTTInfo.broker, broker->valuestring);
140
141 cJSON *port = cJSON_GetObjectItem(params, "port");
142 if (port)
143 {
144 tMQTTInfo.port = port->valueint;
145 }
/* 省略 */
176 local_set_mqttinfo(&tMQTTInfo);
177 }
178 }
179
180 cJSON_Delete(params);
181 }
182 fclose(fp);
183 return 0;
184 }
185
186 return -1;
187 }
第94行:读取配置文件的一行信息。
第98行:把它转换为一个cJSON结构体。
第100~132行:如果它表示的是“点”,提取里面的信息,然后使用第132行在数组里记录下来。
第134~176行:如果它表示的是“MQTT信息”,提取里面的信息,然后使用第176行把信息保存起来。
2.保存操作
用户在LVGL前台界面操作时,可能会增加、删除、修改“点”,会设置MQTT信息。这些信息要保存下来,cfg.c里提供了写配置文件的操作(仅供后台程序使用,前台通过 RPC调用通知后台程序保存配置文件)。
“write_cfg”函数代码如下:
191 /* {"port_info": "/dev/ttyUSB0,115200,8n1", "dev_addr": 3, "reg_addr": 0, "reg_ty
pe": "0x", "period": 100}
192 * {"broker": "192.168.5.10", "port": 1833, "client_id": "device_01", "user": "10
0ask", "password": "www.100ask.net", "publish":"/iot/up", "subcrib
e":"/iot/down"} */
193 /* 写配置文件
194 */
195 int write_cfg(void)
196 {
197 FILE *fp;
198 char *line;
199 PPointInfo Points;
200 int i = 0;
201
202 line = malloc(1024);
203
204 fp = fopen(CFG_FILE, "w");
205
206 Points = local_get_points();
207 if (fp)
208 {
209 while (1)
210 {
211 if (Points[i].dev_addr == 0)
212 break;
213
214 sprintf(line, "{\"port_info\": \"%s\", \"dev_addr\": %d, \"reg_addr\":
%d, \"reg_type\": \"%s\", \"period\": %d}\r\n", \
215 Points[i].port_info, Points[i].dev_addr, Points[i].reg_addr, Point
s[i].reg_type, Points[i].period);
216 fwrite(line, strlen(line), 1, fp);
217 i++;
218 }
219
220 /* {"broker": "192.168.5.10", "port": 1833, "client_id": "device_01", "us
er": "100ask", "password": "www.100ask.net", "publish":"/iot/up",
"subcribe":"/iot/down"} */
221 MQTTInfo tMQTTInfo;
222 local_get_mqttinfo(&tMQTTInfo);
223 sprintf(line, "{\"broker\": \"%s\", \"port\": %d, \"client_id\": \"%s\",
\"user\": \"%s\", \"password\": \"%s\", \"publish\": \"%s\", \"sub
cribe\": \"%s\"}\r\n", \
224 tMQTTInfo.broker, tMQTTInfo.port, tMQTTInfo.client_id, tMQTTInfo.use
r, tMQTTInfo.password, tMQTTInfo.publish, tMQTTInfo.subcribe);
225 fwrite(line, strlen(line), 1, fp);
226
227 fclose(fp);
228 }
229
230 free(line);
231 return 0;
232 }
第206~218行,把所有“点”的信息,写入配置文件。
第221~225行,把MQTT信息写入配置文件。
3.变化监测
前台程序会修改点的信息、设置MQTT信息,最终会通过RPC调用保存配置文件。mqtt_client_app程序如何及时知道配置文件被修改了?可以使用inotify机制。
初始化inotify,代码如下:
18
19
20 static int fd_inotify;
21
22 /* 初始化: 监测配置文件
23 * 内部创建一个 inotify 文件句柄, 以后可以使用 poll 函数查询这个句柄
24 * 返回值: 0-成功, (-1)-失败
25 */
26 int cfg_inotify_init(void)
27 {
28 int ret;
29
30 fd_inotify = inotify_init();
31 if (fd_inotify < 0)
32 {
33 printf("inotify_init err\n");
34 return -1;
35 }
36 else
37 {
38 ret = inotify_add_watch(fd_inotify, CFG_FILE, IN_CREATE | IN_MODIFY);
39 if (ret > 0)
40 return 0;
41 else
42 {
43 printf("inotify_add_watch err\n");
44 return -1;
45 }
46 }
47 }
第30行:得到一个inotify句柄。
第38行:这个句柄跟配置文件挂钩,监测它的“创建、修改”操作。
监测配置文件有没有被修改,代码如下:
49 /* 监测配置文件
50 * timeout_ms - 超时时间(ms)
51 * 返回值: 0-文件被创建或被修改了, (-1)-文件没有被创建或修改
52 */
53 int cfg_inotify_poll(int timeout_ms)
54 {
55 int ret;
56 struct pollfd fds[1];
57
58 fds[0].fd = fd_inotify;
59 fds[0].events = POLLIN;
60 fds[0].revents = 0;
61
62 ret = poll(fds, 1, timeout_ms);
63 if (ret > 0)
64 {
65 if (fds[0].revents | POLLIN)
66 return 0;
67 else
68 return -1;
69 }
70 else
71 {
72 return -1;
73 }
使用poll函数监测inotify句柄有没有数据可供读入,有数据供读入,表示它挂钩的文件被修改了。
13.4.3 实现“点”的RPC操作
“点”的RPC调用很多,本节讲解几个关键函数。
1.点的保存与引用
在读取配置文件时,配置文件中各个“点”的信息都保存在 rpc.c的如下数组中:
对于这些点,后台程序(控制中心)在rpc.c里提供了如下RPC调用:
注意,在后台程序(控制中心)里,使用如下结构体表示“点”:
但是前台程序、mqtt_client_app等程序更后台程序(控制中心)通信时,总是传输这个完整的结构体的话,效率不高。所以这个结构体里有一个成员“ponit”,它是一个整数,在后台程序运行的整个周期里,每个点的“point”是唯一的、不变的。所以,程序就可以使用“point”这个整数表示“点”、表示对应的“PointInfo”结构体。
2.获取点的个数
前台等程序使用rpc.c中如下代码发起RPC调用:
后台程序(控制中心)对应的代码如下(统计数组中有效结构体的个数、返回):
3.获取点的信息
前台等程序使用rpc.c中如下代码发起RPC调用:
后台程序(控制中心)对应的代码如下(根据point值找到数组项):
4.读点
前台等程序使用rpc.c中如下代码发起RPC调用:
后台程序(控制中心)对应的代码如下(根据point值发起modbus读操作):
需要产品及方案支持
请扫码登记
如您在使用瑞萨MCU/MPU产品中有任何问题,可识别下方二维码或复制网址到浏览器中打开,进入瑞萨技术论坛寻找答案或获取在线技术支持。
https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/
未完待续
推荐阅读
Modbus RTU客户端及服务器端的编程与实验 - RZ MPU工业控制教程连载(43)
Modbus TCP编程与实验 - RZ MPU工业控制教程连载(44)
LVGL开发入门 - RZ MPU工业控制教程连载(45)
需要产品及方案支持
请扫码登记