基于WebServer的工业数据采集项目

文章介绍了如何使用ModbusTCP协议与CGI接口在Web服务器中处理动态数据请求。通过CGI,Web服务器能与外部应用程序交互,动态脚本通过CGI接口与Web服务器通信。服务程序通过ModbusTCP与Modbus设备交换数据,并利用共享内存和消息队列与CGI进行数据交互。网页端通过按钮触发CGI请求,实现对设备的控制,如LED灯和蜂鸣器的开关操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网页

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服务器和一个独立的进程之间的协议,它通过环境变量及标准输入/输出和服务器之间进行数据交互。

  1. 通过环境变量可以获得网页的请求方式、地址等
  2. 通过标准输入可以获取网页的消息正文
  3. 通过标准输出可以发送网页请求的数据 

常见的环境变量

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>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值