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

目录

一,项目框架

二,LigHttpd 服务器(常用,以此为例)

1.服务器安装配置

2.目录创建及文件移动

3.修改配置文件

三,CGI

CGI特点

 CGI工作原理

 源码分析

四,主要代码

1.modbus tcp 通信主程序

2.CGI主程序(custom_handle.c)



一,项目框架

 1.服务程序(代码)通过ModbusTCP协议实现Modbus设备之间的通信
2.要实现的是将Modbus设备上的数据采集信息到网页中,通过网页点击开关控制Modbus设备在网页中点击一个链接相当于给服务器发起请求,网页不能直接和程序进行通信,一定是放到服务器上,所以需要有一个Webserver服务器,通过http协议进行通信
3.服务器会去处理网页的请求,因为需要动态的去处理,所以当网页给Webserver发请求时,服务器创建一个子进程,启动CGI程序,Webserver和CGI之间是fork出来的。
4.CGI是一个进程,写的服务程序也是一个进程,两者可以通过进程间通信的方式进行通信,因为采集信息需要是最新的、实时的,所以两个进程间可以通过共享内存(最高效、实时)读取信息或者通过消息队列(控制设备)。

二,LigHttpd 服务器(常用,以此为例)

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

1.服务器安装配置

1)解压

tar -xvf lighttpd-1.4.54.tar.gz

2)进入源码目录,创建文件夹web

cd lighttpd-1.4.54

mkdir web

执行configure脚本文件
./configure --prefix=$PWD/web

执行Makefile文件
make

make install

2.目录创建及文件移动

1)将源码目录lighttpd-1.4.54下web文件夹移动到某个路径下

mv lighttpd-1.4.54/web ~/work

2)在web目录下创建文件夹(config、log、run、www)

cd ~/work/web

mkdir config log run www

3)将源码目录lighttpd-1.4.54/doc/config下的conf.d lighttpd.conf modules.conf

cp conf.d lighttpd.conf modules.conf ~/work/web/config -r

4)修改log文件夹权限,并在log目录下创建error.log文件修改权限

chmod 777 log

touch log/error.log

chmod 777 log/error.log

5) 在www目录下创建htdocs文件夹存放网页文件

mkdir www/htdocs
 

3.修改配置文件

1) vi ~/work/web/config/lighttpd.conf

##
var.home_dir    = "/home/hq/work/web"   #lighttpd操作的主目录
var.log_root    = home_dir + "/log"			#日志文件目录(程序执行中出现的错误信息)
var.server_root = home_dir + "/www"			#存放html、cgi代码目录
var.state_dir   = home_dir + "/run"			#存放pid文件服务运行起来后自动创建
var.conf_dir    = home_dir + "/config"  #存放配置文件
##
var.vhosts_dir  = home_dir + "/vhosts"
##
var.cache_dir   = home_dir + "/cache"
##
var.socket_dir  = home_dir + "/sockets"
##
server.port = 80    #端口号为80
##
server.use-ipv6 = "disable"	  #设置为禁用
##
#server.bind = "localhost"		#默认即可
##
server.username  = "hq"		#修改为当前用户,nobody为任何人都可以访问
#server.groupname = "nobody"		#将其注释即可
##
server.document-root = server_root + "/htdocs"		#存放html网页的文件
##
server.pid-file = state_dir + "/lighttpd.pid"
##
server.errorlog             = log_root + "/error.log"		#错误日志文件

2) vi ~/work/web/config/modules.conf

include "conf.d/cgi.conf" #将此行注释打开

3) vi ~/work/web/config/conf.d/cgi.conf 

$HTTP["url"] =~ "^/cgi-bin" {
   cgi.assign = ( "" => "" )
}			将这三行注释打开28-30行

 4.运行

运行

cd ~/work/web

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

(结束进程为:pkill lighttpd)

三,CGI

CGI特点

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

  1. 通过环境变量可以获得网页的请求方式、地址等    (头部信息)
  2. 通过标准输入可以获取网页的消息正文     (判断采集信息还是控制设备)
  3. 通过标准输出可以发送网页请求的数据  (共享内存采集数据,再写到 基于网络的标准输出进行通信)

 CGI工作原理

 源码分析

 在main.c函数中通过handle_requst获取网页给服务器发送的数据中,请求头(环境变量)和请求正文(标准输入)信息,在函数中调用parse_and_process函数,在函数中根据请求正文判断网页需要执行什么操作(获取传感器数据还是控制硬件设备),根据请求完成数据采集和硬件控制,最终给网页回复(标准输出)数据(遵循http协议格式)
 

四,主要代码

1.modbus tcp 通信主程序

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <modbus.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf
{
    long mtype;    /* message type, must be > 0 */
    char buf[128]; /* message data */
} msg;
int sockfd;
uint16_t dest[64] = {};
 
int main()
{
 
    modbus_t *ctx;
    ctx = modbus_new_tcp("192.168.50.112", 502);
    if (ctx == NULL)
    {
        perror("error\n");
        return -1;
    }
    int id = modbus_set_slave(ctx, 1);
    if (id < 0)
    {
        perror("id err");
        return -1;
    }
    int x = modbus_connect(ctx);
    if (x < 0)
    {
        perror("connect err");
        return -1;
    }
 
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0) //子进程
    {
        while (1)
        {
            //创建key值
            key_t key = ftok("/home", 'a');
            if (key < 0)
            {
                perror("ftok err");
                return -1;
            }
            printf("key:%#x\n", key);
            //创建或打开共享内存
            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;
                }
            }
            printf("shmid:%d\n", shmid);
            //映射共享内存,NULL:由系统自动完成映射,0可读可写
            char *p = (char *)shmat(shmid, NULL, 0);
 
            if (p == (char *)-1)
            {
                perror("shmat err");
                return -1;
            }
            modbus_read_registers(ctx, 0, 4, dest);
            sprintf(p, "光传感器:%#x 加速度传感器x:%#x 加速度传感器y:%#x 加速度传感器z:%#x\n", dest[0], dest[1], dest[2], dest[3]);
            printf("%s\n", p);
            sleep(5);
        }
        exit(0);
    }
    else
    {
        int a, b;
        while (1)
        {
            key_t key = ftok("/home", '1');
            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);
            if (msg.buf[4] == '0' && msg.buf[6] == '1')
            {
                printf("led灯打开\n");
                modbus_write_bit(ctx, 0, 1);
            }
            else if (msg.buf[4] == '0' && msg.buf[6] == '0')
            {
                printf("led灯关闭\n");
                modbus_write_bit(ctx, 0, 0);
            }
            else if (msg.buf[4] == '1' && msg.buf[6] == '1')
            {
                printf("蜂鸣器开\n");
                modbus_write_bit(ctx, 1, 1);
            }
            else if (msg.buf[4] == '1' && msg.buf[6] == '0')
            {
                printf("蜂鸣器关\n");
                modbus_write_bit(ctx, 1, 0);
            }
        }
    }
    modbus_close(ctx);
    modbus_free(ctx);
    wait(NULL);
    exit(0);
    return 0;
}

2.CGI主程序(custom_handle.c)

#include "req_handle.h"
#include "log_console.h"
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.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[128]; //消息正文
} msg;
 
int parse_and_process(char *input)
{
    char val_buf[2048] = {0};
    //这里可以根据接收的数据请求进行处理
    if (strcmp(input, "get") == 0)
    {
        //创建key值
        key_t key = ftok("/home", 'a');
        if (key < 0)
        {
            perror("ftok err");
            return -1;
        }
        printf("key:%#x\n", key);
        //创建或打开共享内存
        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;
            }
        }
        printf("shmid:%d\n", shmid);
        //映射共享内存,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); //读取共享内存的数据  //将数据写道标准输出val_buf
    }
    else
    {
        key_t key = ftok("/home", '1');
        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;
            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};
    sprintf(reply_buf, "%sContent-Length: %ld\r\n\r\n", HTML_HEAD, strlen(val_buf));
    strcat(reply_buf, val_buf);
    log_console("post json_str = %s", reply_buf);
    //向标准输出写内容(标准输出服务器已做重定向)
    fputs(reply_buf, stdout);
    return 0;
}
 

运行结果:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值