网页
Modbus TCP协议
工具:Modbus Slave/Poll Wireshark postman
整体框架
WebServer服务器
Web Server中文名称叫网页服务器或web服务器。WEB服务器也称为WWW(WORLD WIDE WEB)服务器,主要功能是提供网上信息浏览服务。
Web server的分类:Kangle、Nginx、apache等等
在嵌入式中常见的轻量级的服务器有:Lighttpd、 Shttpd,、Thttpd、Boa、Mini_httpd、Appweb、Goahead
CGI简介
早期的Web服务器,只能响应浏览器发来的HTTP静态资源的请求,并将存储在服务器中的静态资源返回给浏览器。
随着Web技术的发展,逐渐出现了动态技术,但是Web服务器并不能够直接运行动态脚本,为了解决Web服务器与外部应用程序之间数据互通,于是出现了CGI通用网关接口。
简单理解,可以认为CGI是Web服务器和运行其上的应用程序进行“交流”的一种约定。
CGI(Common Gateway Interface)通用网关接口,是外部扩展应用程序与 Web 服务器交互的一个标准接口。
CGI特点
CGI是Web服务器和一个独立的进程之间的协议,它通过环境变量及标准输入/输出和服务器之间进行数据交互。
- 通过环境变量可以获得网页的请求方式、地址等
- 通过标准输入可以获取网页的消息正文
- 通过标准输出可以发送网页请求的数据
常见的环境变量
REQUEST_URI:访问此页面需要的URL,比如:“/index.html”
REQUEST_METHOD:获取客户端请求数据的方式:POST或GET
CONTENT_LENGTH:获取用户数据的长度
CONTENT_TYPE:网页中存在的 Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件
CGI工作原理
当浏览器向web服务器发送动态数据请求时,Web服务器主进程就会Fork创建出一个新的进程来启动CGI程序,也就是将动态脚本交给CGI程序来处理。
当CGI程序启动后会去解析动态脚本,然后将结果返回给Web服务器,最后由Web服务器将结果返回给客户端,之前Fork出来的进程也随之关闭。
这样,每次用户请求动态脚本,Web服务器都要重新Fork创建一个新进程去启动CGI程序,由CGI程序来处理动态脚本,处理完成后进程随之关闭。
对于一个CGI程序,主要的工作是从环境变量和标准输入中读取数据,然后处理数据,最后向标准输出中输出数据。(这里服务器将标准输入和标准输出做了重定向)
对于整体项目可以分为两大块,个人认为。
网页与CGI
CGI对网页发来的消息进行分类,进行不同的操作,获取数据时通过共享内存来获取传感器的数据,当是控制IO设备时通过消息队列控制IO设备。
Modbus设备与服务程序
服务程序通过Modbus TCP与Modbus设备通信,获取的数据存储到共享内存,控制IO设备的命令从消息队列中获取。
CGI与服务程序
CGI与服务程序通过共享内存和消息队列进行数据的交互。
特别注意!
创建共享内存是容易出错,最好是根目录下的已存在的文件来当做key。!!!!
服务程序
实现服务程序数据的获取和控制 。
#include <stdio.h>
#include <modbus.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct mbuf
{
long mtype; //消息类型
char buf[128]; //消息正文
} msg;
int main(int argc, char const *argv[])
{
//创建实例
modbus_t *mode;
mode = modbus_new_tcp("ip", port);
if (mode == NULL)
{
perror("modbus_new_tcp");
return -1;
}
//设置从机ID
modbus_set_slave(mode, 1);
//建立连接
modbus_connect(mode);
uint16_t dest[32];
//创建进程
pid_t pid = fork();
if (pid < 0)
{
perror("fork err");
return -1;
}
//子进程
else if (pid == 0)
{
while (1)
{
//创建key
key_t key;
key = ftok("/home", '1');
if (key < 0)
{
perror("ftok err");
return -1;
}
//创建或打开共享内存
int shmid = shmget(key, 256, IPC_CREAT | IPC_EXCL | 0666);
if (shmid <= 0)
{
if (errno == EEXIST)
{
shmid = shmget(key, 256, 0666);
}
else
{
perror("shmget err");
return -1;
}
}
//映射共享内存到用户空间shmat
char *p = shmat(shmid, NULL, 0);
if (p == (void *)-1)
{
perror("shmat err");
return -1;
}
while (1)
{
//读取数据
modbus_read_registers(mode, 0, 4, dest);
//将数据转入buf
char buf[32] = {0};
sprintf(buf, "%#.2x %#.2x %#.2x %#.2x",dest[0], dest[1], dest[2], dest[3]);
//将数据转入共享内存
strcpy(p, buf);
//printf("%s %s\n", buf, p);
sleep(3);
}
}
exit(0);
}
else
{
while (1)
{
//创建key
key_t key = ftok("/home", '2');
if (key < 0)
{
perror("ftok err");
return -1;
}
//创建或打开消息队列
int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msgid <= 0)
{
if (errno == EEXIST)
msgid = msgget(key, 0666);
else
{
perror("msgget err");
return -1;
}
}
//读取消息
msgrcv(msgid, &msg, sizeof(msg) - sizeof(long), 1, 0);
printf("msgbuf:%s\n", msg.buf);
//LED灯
if (msg.buf[3] == '0' && msg.buf[4] == '1')
{
modbus_write_bit(mode, 0, 1);
}
else if (msg.buf[3] == '0' && msg.buf[4] == '0')
{
modbus_write_bit(mode, 0, 0);
}
//蜂鸣器
else if (msg.buf[3] == '1' && msg.buf[4] == '1')
{
modbus_write_bit(mode, 1, 1);
}
else if (msg.buf[3] == '1' && msg.buf[4] == '0')
{
modbus_write_bit(mode, 1, 0);
}
}
}
modbus_close(mode);
modbus_free(mode);
return 0;
}
CGI
对网页数据的处理,实现不同功能。
struct mbuf
{
long mtype; //消息类型
char buf[128]; //消息正文
} msg;
if (strcmp(input, "get") == 0)
{
//创建key
key_t key;
key = ftok("/home", '1');
if (key < 0)
{
perror("ftok err");
return -1;
}
//创建或打开共享内存
int shmid = shmget(key, 256, IPC_CREAT | IPC_EXCL | 0777);
if (shmid <= 0)
{
if (errno == EEXIST)
{
shmid = shmget(key, 256, 0666);
}
else
{
perror("shmget err");
return -1;
}
}
//映射共享内存到用户空间shmat
char *p = shmat(shmid, NULL, 0);
if (p == (void *)-1)
{
perror("shmat err");
return -1;
}
//将共享内存中的数据写到val_buf
strcpy(val_buf, p);
}
else
{
//创建key
key_t key = ftok("/home", '2');
if (key < 0)
{
perror("ftok err");
return -1;
}
//创建或打开消息队列
int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msgid <= 0)
{
if (errno == EEXIST)
msgid = msgget(key, 0666);
else
{
perror("msgget err");
return -1;
}
printf("msgid:%d\n", msgid);
//消息类型
msg.mtype = 1;
//数据转到msg.buf中
strcpy(msg.buf, input);
//添加消息到消息队列
msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0);
log_console("msgbuf:%s\n", msg.buf);
//正文
strcpy(val_buf, input);
}
}
网页
页面的简单实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>基于WebServer工业设备的数据采集</title>
<script src="js/xhr.js"></script>
<script>
function get_info() {
var v = document.getElementsByName("t1");
XHR.post('/cgi-bin/web.cgi', "get", function (x, info) {
console.log(info);
v[0].value = info;
});
}
function set1() {
XHR.post('/cgi-bin/web.cgi', "set01", function (x, info) {
console.log(info);
});
}
function set2() {
XHR.post('/cgi-bin/web.cgi', "set00", function (x, info) {
console.log(info);
});
}
function set3() {
XHR.post('/cgi-bin/web.cgi', "set11", function (x, info) {
console.log(info);
});
}
function set4() {
XHR.post('/cgi-bin/web.cgi', "set10", function (x, info) {
console.log(info);
});
}
</script>
</head>
<body>
<body background="./dog.jpg">
</body>
数据:
<input type="text" name="t1">
<input type="button" name="b1" value="获取" onclick="get_info()">
<br />
LED灯:
<input type="button" name="B1" value="开" onclick="set1()">
<input type="button" name="B2" value="关" onclick="set2()">
<br />
蜂鸣器:
<input type="button" name="B3" value="开" onclick="set3()">
<input type="button" name="B4" value="关" onclick="set4()">
</body>
</html>