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

目录

项目基本流程

 Webserver服务器

Lighttpd服务器

运行lighttpd服务器并测试

1.运行

2.测试

 CGI特点

主要实现代码

1.Modbus 设备---Modbus slave

2.服务器程序<--ModbusTCP-->Modbus slave

 3.CGI程序

(1)main.c

(2)log_console.c

(3)log_console.h

 (4)custom_handle.c

 4.网页HTML

运行结果


项目基本流程

主要是完成网页对Modbus设备(寄存器)信息的读取(例如光照强度、加速度等)以及通过网页点击开关对Modbus设备(线圈)的控制,用Modbus slave来代替Modbus设备。

 Webserver服务器

Web Server中文名称叫网页服务器或web服务器。WEB服务器也称为WWW(WORLD WIDE WEB)服务器,主要功能是提供网上信息浏览服务。

 Web服务器可以解析HTTP协议。当Web服务器接收到一个HTTP请求,会返回一个HTTP响应,例如送回一个HTML页面。

在嵌入式中常见的轻量级的服务器有:Lighttpd、 Shttpd,、Thttpd、Boa、Mini_httpd、Appweb、Goahead

Lighttpd服务器

LigHttpd是一个开源的轻量级嵌入式Web server,是提供一个专门针对高性能网站,安全、快速、兼容性好并且灵活的web server环境。具有非常低的内存开销,cpu占用率低,效能好,以及丰富的模块等特点。

运行lighttpd服务器并测试

1.运行

cd ~/work/web

sudo sbin/lighttpd -f config/lighttpd.conf -m lib/

(结束进程为:pkill lighttpd

2.测试

将index.html文件放到www/htdocs目录下

打开浏览器,在地址栏输入服务器的IP地址(虚拟机IP)即可看到主页。

 CGI特点

CGI是Web服务器和一个独立的进程之间的协议,它通过环境变量及标准输入/输出和服务器之间进行数据交互。

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

主要实现代码

1.Modbus 设备---Modbus slave

创建两个Modbus设备,ID=1

功能码:03 表示:读保持寄存器

功能码:01表示:线圈

2.服务器程序<--ModbusTCP-->Modbus slave

创建两个进程

子进程每隔5s通过共享内存向cgi传递数据;

父进程使用消息队列接收cgi写线圈的命令。

#include <stdio.h>
#include <stdlib.h>
#include <modbus.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
//消息队列结构体
struct msgbuf
{
    long mtype;   //消息类型
    char buf[64]; //消息正文
} msg;
//编译时:gcc xx.c -lmodbus
int main(int argc, char const *argv[])
{
    modbus_t *ctx;
    uint16_t dest[64] = {};
    //1.以TCP方式创建Modbus实例,并初始化 主机ip地址和端口号502
    ctx = modbus_new_tcp(argv[1], atoi(argv[2]));
    if (ctx == NULL)
    {
        perror("modbus create err");
        return -1;
    }
    //2.设置从机ID
    int set_id = modbus_set_slave(ctx, 1);
    if (set_id != 0)
    {
        perror("set id err");
        return -1;
    }
    //3.和从机(slave)建立连接
    int con = modbus_connect(ctx);
    if (con != 0)
    {
        perror("connect err");
        return -1;
    }
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        while (1)
        {
            //1.创建key值
            key_t key = ftok("/home", '2');
            if (key < 0)
            {
                perror("ftok err");
                return -1;
            }
            //2.创建或打开共享内存
            int shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
            if (shmid <= 0)
            {
                if (errno == EEXIST)
                    shmid = shmget(key, 128, 0666);
                else
                {
                    perror("shmget err");
                    return -1;
                }
            }
            //3.映射共享内存和虚拟地址,NULL:由系统自动完成映射,0可读可写
            char *p = (char *)shmat(shmid, NULL, 0);
            if (p == (char *)-1)
            {
                perror("shmat err");
                return -1;
            }
            //4.寄存器操作 modbus实例 起始地址 寄存器个数 存储值
            modbus_read_registers(ctx, 0, 4, dest);
            sprintf(p, "%d X:%d Y:%d Z:%d\n", dest[0], dest[1], dest[2], dest[3]);
            printf("%s\n", p);
            sleep(5);
        }
    }
    else
    {
        while (1)
        {
            struct msgbuf msg;
            key_t key = ftok("/home", '1');
            if (key < 0)
            {
                perror("ftok err");
                return -1;
            }
            //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);
            if (msg.buf[4] == '0' && msg.buf[6] == '1')
            {
                modbus_write_bit(ctx, 0, 1);
            }
            else if (msg.buf[4] == '0' && msg.buf[6] == '0')
            {
                modbus_write_bit(ctx, 0, 0);
            }
            else if (msg.buf[4] == '1' && msg.buf[6] == '1')
            {
                modbus_write_bit(ctx, 1, 1);
            }
            else if (msg.buf[4] == '1' && msg.buf[6] == '0')
            {
                modbus_write_bit(ctx, 1, 0);
            }
        }
    }
    //5.关闭套接字
    modbus_close(ctx);
    //6.释放实例
    modbus_free(ctx);
    wait(NULL);
    exit(0);
    return 0;
}

 3.CGI程序

通过共享内存读取设备信息,向消息队列中发送网页控制线圈的命令

注意:服务程序与CGI程序中的共享内存和消息队列Key值要一致

(1)main.c

#include "req_handle.h"
#include "log_console.h"
int main(int argc, char *argv[])
{
   //先初始化log,标准输出已被重定向到网络
    int ret = log_console_init();
    if(ret < 0)
    {
        perror("open console err");
        system("echo open log err > err.log");
        exit(-1);
    }
    //argc和argv web server会自动传给cgi程序   
    //获取网页给服务器发送的数据中,请求头(环境变量)和请求正文(标准输入)信息
    handle_request(argc, argv);
	return 0;
}

(2)log_console.c


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include "log_console.h"
static int console_fd = -1;
/**
 * @brief 初始化log_console
 * @return 0 -1
 */
int log_console_init()
{
    console_fd = open(LOG_CONSOLE, O_WRONLY);//只写的方式打开小终端
    if (console_fd < 0)
    {
        printf("open %s error\n", LOG_CONSOLE);
        return -1;
    }
	return 0;
}
static ssize_t log_console_write(const void *buf, size_t count)
{
    int ret = write(console_fd, buf, count);//根据传过来的数据大小写到小终端
    if(ret < 0)
    {
        perror("write err");
    }
}
#define MAX_LOG_BUF_LEN (20*1024)  //打印内容不能超过这个
//自定义输出函数打印信息到小终端
//(因为标准输入标准输出已重定向,所以需要log_console函数将信息输出到小终端中)
int log_console(const char *format, ...)

{
    char str_tmp[MAX_LOG_BUF_LEN];//数组
    va_list arg;
    int len;
    va_start(arg, format);
    len = vsnprintf(str_tmp, MAX_LOG_BUF_LEN, format, arg);
    va_end(arg);
    str_tmp[len] = '\0';
    return log_console_write(str_tmp, strlen(str_tmp));

}

(3)log_console.h

 设置小终端路径,uxterm命令 

 

 

 (4)custom_handle.c

#include "req_handle.h"
#include "log_console.h"
#include <stdio.h>
#include <modbus.h>
#include <modbus-version.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#define KB 1024
#define HTML_SIZE (64 * KB)
//普通的文本回复需要增加html头部
#define HTML_HEAD "Content-Type: text/html\r\n" \

                  "Connection: close\r\n"

/**

 * @brief 处理自定义请求,在这里添加进程通信
 * @param input
 * @return
 */
//消息队列结构体
struct msgbuf
{
    long mtype;     //消息类型

    char buf[64]; //消息正文

}msg;
int parse_and_process(char *input)//input:网页发送的正文
{
    char val_buf[2048] = {0};//存放的是给服务器回复的正文响应的信息
    //这里可以根据接收的数据请求进行处理
    //例如:读传感器数据:get 共享内存
    //控制硬件设备:set=1 0 消息队列

    if (strcmp(input, "get") == 0) {
        //1.创建key值
        key_t key = ftok("/home", '2');
        if (key < 0){
            perror("ftok err");
            return -1;
        }
       //2.创建或打开共享内存
        int shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
        if (shmid <= 0){
            if (errno == EEXIST)
                shmid = shmget(key, 128, 0666);
            else{
                perror("shmget err");
                return -1;
            }
        }
        log_console("shmid:%d\n",shmid);
       //3.映射共享内存和虚拟地址,NULL:由系统自动完成映射,0可读可写

        char *p = (char *)shmat(shmid, NULL, 0);
        if (p == (char *)-1){
            perror("shmat err");
           return -1;
        }
        log_console("%s",p);
        strcpy(val_buf, p);
    }
    else{
        key_t key = ftok("/home", '1');
        if (key < 0) {
            perror("ftok err");
            return -1;
        }
        //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;

            }
        }
        msg.mtype = 1;
        strcpy(msg.buf, input);
        msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0);
        log_console("msgbuf:%s\n",msg.buf);
        strcpy(val_buf,input); 
    }
    //数据处理完成后,需要给服务器回复,回复内容按照http协议格式
    char reply_buf[HTML_SIZE] = {0};
    //将头部信息%s、正文长度Content-Length放到reply_buf中
    sprintf(reply_buf, "%sContent-Length: %d\r\n\r\n", HTML_HEAD, strlen(val_buf));
    strcat(reply_buf, val_buf); //把数据拼接到reply_buf后面 reply_buf:存放头部信息、正文
    log_console("post json_str = %s", reply_buf);
    //将reply_buf中的内容(头部信息、正文)写到标准输出中(即服务器)(标准输出服务器已做重定向)
    fputs(reply_buf, stdout);
    return 0;

}

 4.网页HTML

开发环境:vscode

1.在某路径下新建文件夹,打开VScode打开文件夹,新建文件,文件命名为index.html

2. 安装库 open in browser

库安装完成后,在编写文本位置右击->open in other browser->选择合适的浏览器即可在网页显示html标签内容

输入html,选择html:5进行框架搭建

 

<!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>Document</title>
    <script src="js/xhr.js"></script>
    <!--请求数据调用函数的路径-->
    <script>
        function get_info() {
            var v = document.getElementsByName("username");
            XHR.post('/cgi-bin/web.cgi', "get", function (x, info) {
                console.log(info);
                var val = info.split(" ");
                v[0].value = val[0];
                v[1].value = val[1] + " " + val[2] + " " + val[3];
            })
        }
        function set(obj) {
            XHR.post('/cgi-bin/web.cgi', obj, function (x, info) {
                if (obj == 'set=0 1') {
                    console.log("LED灯打开!");
                } else if (obj == 'set=0 0') {
                    console.log("LED灯关闭!");
                } else if (obj == 'set=1 1') {
                    console.log("蜂鸣器打开!");
                } else if (obj = 'set=1 0') {
                    console.log("蜂鸣器关闭!");
                }
            })
        }

    </script>
</head>

<body>
    <div style="color:chocolate;">
        <h1>基于Webserver的工业数据采集系统</h1>
    </div>
    <div style="color:black;background: cornsilk">
        <h2>信息采集</h2>
        get<input type="button" name="flash" onclick="get_info()">
        <h4>光照强度<input type="text" name="username" value="">
            加速度<input type="text" name="username" value=""></h4>

        <h2>设备控制</h2>
        <h4>LED灯
            <input type="radio" name="LED" id="set=0 1" onclick="set(id)">on
            <input type="radio" name="LED" id="set=0 0" onclick="set(id)">off</h4>
        <h4>蜂鸣器
            <input type="radio" name="BEEP" id="set=1 1" onclick="set(id)">on
            <input type="radio" name="BEEP" id="set=1 0" onclick="set(id)">off</h4>
    </div>

</body>

</html>

运行结果

猜你喜欢

转载自blog.csdn.net/m0_68672255/article/details/130427205
今日推荐