基于WebServer的工业数据采集系统

news2024/9/24 1:37:42

 一、项目框架及流程

二、http简介

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于Web Browser(浏览器)到Web Server(服务器)进行数据交互的传输协议。

HTTP是应用层协议

HTTP是一个基于TCP通信协议传输来传递数据(HTML 文件, 图片文件, 查询结果等)

HTTP协议工作于B/S架构上,浏览器作为HTTP客户端通过URL主动向HTTP服务端即WEB服务器发送所有请求,Web服务器根据接收到的请求后,向客户端发送响应信息。

HTTP默认端口号为80,但是你也可以改为8080或者其他端口

三、http特点

HTTP是短连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。

HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

四、代码实现

   采用多线程模式,一个线程用来循环读取传感器数据,一个线程用来发送控制命令。

//数据采集代码

#include <modbus.h>
#include <modbus-tcp.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/msg.h>
#include <errno.h>

modbus_t *ctx;
struct msgbuf
{
    long type; // 必须有,在第一个,表示消息的类型,值>0!
    int num1;  // 消息正文,自己定义
    int num2;
};

void *handler(void *arg);

int main(int argc, char const *argv[])
{
    int shmid;
    key_t key;
    uint16_t *p;
    key = ftok("00.c", 'a');
    if (key < 0)
    {
        perror("key err");
        return -1;
    }
    printf("key: %#x\n", key);

    // 打开或创建共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0777); // 如果共享内存不存在则创建,存在则返回-1
    if (shmid <= 0)
    {
        if (errno == EEXIST)                // 如果共享内存已存在则,直接打开
            shmid = shmget(key, 128, 0777); // 直接打开已有的共享内存并且获得共享内存id
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid: %d\n", shmid);

    // 映射共享内存
    p = (uint16_t *)shmat(shmid, NULL, 0);
    if (p == (uint16_t *)-1)
    {
        perror("shmat err");
        return -1;
    }

    // 1.创建实例
    ctx = modbus_new_tcp(argv[1], 502);
    // 3.设置从机id(主从线程)
    modbus_set_slave(ctx, 0x01);
    // 4.创建连接(主从线程)
    modbus_connect(ctx);
    // 2.创建新线程
    pthread_t tid;
    pthread_create(&tid, NULL, handler, NULL);
    // 5.主线程:04功能码读输入寄存器循环
    uint16_t dest[32] = {0};
    while (1)
    {
        modbus_read_input_registers(ctx, 0x00, 2, p);
        printf("temp:%02x\nhumi:%02x\n", p[0], p[1]);
        sleep(1);
    }
    pthread_join(tid, NULL);
    modbus_free(ctx);
    modbus_close(ctx);
    // 取消映射
    shmdt(p);

    // 删除共享内存
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}
// 6.从线程:05功能码写单个线圈
void *handler(void *arg)
{
    int a, b;
    while (1)
    {
        key_t key;
        int msgid;

        if ((key = ftok("00.c", 'a')) < 0)
        {
            perror("ftok err");
        }
        printf("key: %#x\n", key);

        // 打开或创建消息队列
        msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0777);
        if (msgid <= 0)
        {
            if (errno == EEXIST)
                msgid = msgget(key, 0777); // 如果已经存在消息队列那直接打开该消息队列
            else
            {
                perror("msgget err");
            }
        }
        printf("msgid: %d\n", msgid);
        struct msgbuf m;
        msgrcv(msgid, &m, sizeof(m) - sizeof(long), 20, 0); // 0:阻塞,读完消息再返回
        modbus_write_bit(ctx, m.num1, m.num2);
    }
    pthread_exit(NULL);
}
 

//服务代码

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <modbus.h>
#include <modbus-tcp.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/msg.h>
#include <errno.h>
#include "custom_handle.h"

#define KB 1024
#define HTML_SIZE (64 * KB)
struct msgbuf
{
    long type; // 必须有,在第一个,表示消息的类型,值>0!
    int num1;  // 消息正文,自己定义
    int num2;
};

// 普通的文本回复需要增加html头部
#define HTML_HEAD "Content-Type: text/html\r\n" \
                  "Connection: close\r\n"

static int handle_login(int sock, const char *input)
{
    char reply_buf[HTML_SIZE] = {0};
    char *uname = strstr(input, "username=");
    uname += strlen("username=");
    char *p = strstr(input, "password");
    *(p - 1) = '\0';
    printf("username = %s\n", uname);

    char *passwd = p + strlen("password=");
    printf("passwd = %s\n", passwd);

    if (strcmp(uname, "admin") == 0 && strcmp(passwd, "admin") == 0)
    {
        sprintf(reply_buf, "<script>localStorage.setItem('usr_user_name', '%s');</script>", uname);
        strcat(reply_buf, "<script>window.location.href = '/index.html';</script>");
        send(sock, reply_buf, strlen(reply_buf), 0);
    }
    else
    {
        printf("web login failed\n");

        //"用户名或密码错误"提示,chrome浏览器直接输送utf-8字符流乱码,没有找到太好解决方案,先过渡
        char out[128] = {0xd3, 0xc3, 0xbb, 0xa7, 0xc3, 0xfb, 0xbb, 0xf2, 0xc3, 0xdc, 0xc2, 0xeb, 0xb4, 0xed, 0xce, 0xf3};
        sprintf(reply_buf, "<script charset='gb2312'>alert('%s');</script>", out);
        strcat(reply_buf, "<script>window.location.href = '/login.html';</script>");
        send(sock, reply_buf, strlen(reply_buf), 0);
    }

    return 0;
}

static int handle_add(int sock, const char
                                    *input)
{
    int number1, number2;

    // input必须是"data1=1data2=6"类似的格式,注意前端过来的字符串会有双引号
    sscanf(input, "data1=%ddata2=%d", &number1, &number2);
    printf("num1 = %d\n", number1);

    char reply_buf[HTML_SIZE] = {0};
    printf("num = %d\n", number1 + number2);
    sprintf(reply_buf, "%d", number1 + number2);
    printf("resp = %s\n", reply_buf);
    send(sock, reply_buf, strlen(reply_buf), 0);

    return 0;
}

static int handle_collect(int sock, const char *input)
{
    int shmid;
    key_t key;
    uint16_t *p;
    key = ftok("00.c", 'a');
    if (key < 0)
    {
        perror("key err");
        return -1;
    }
    printf("key: %#x\n", key);

    // 打开或创建共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0777); // 如果共享内存不存在则创建,存在则返回-1
    if (shmid <= 0)
    {
        if (errno == EEXIST)                // 如果共享内存已存在则,直接打开
            shmid = shmget(key, 128, 0777); // 直接打开已有的共享内存并且获得共享内存id
        else
        {
            perror("shmget err");
            return -1;
        }
    }
    printf("shmid: %d\n", shmid);

    // 映射共享内存
    p = (uint16_t *)shmat(shmid, NULL, 0);
    if (p == (uint16_t *)-1)
    {
        perror("shmat err");
        return -1;
    }

    char reply_buf[HTML_SIZE];
    printf("temp:%02x\nhumi:%02x\n", p[0], p[1]);
    sprintf(reply_buf, "温度:%d 湿度:%d", p[0], p[1]);
    printf("%s\n", reply_buf);
    send(sock, reply_buf, strlen(reply_buf), 0);
    return -1;
}

static int handle_set(int sock, const char *input)
{
    key_t key;
    int msgid;

    if ((key = ftok("00.c", 'a')) < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key: %#x\n", key);

    // 打开或创建消息队列
    msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0777);
    if (msgid <= 0)
    {
        if (errno == EEXIST)
            msgid = msgget(key, 0777); // 如果已经存在消息队列那直接打开该消息队列
        else
        {
            perror("msgget err");
            return -1;
        }
    }
    printf("msgid: %d\n", msgid);

    int number1, number2;

    // input必须是"data1=1data2=6"类似的格式,注意前端过来的字符串会有双引号
    sscanf(input, "set %d %d", &number1, &number2);
    printf("num1 = %d\n", number1);
    printf("num2 = %d\n", number2);

    // 添加消息
    struct msgbuf msg;
    msg.type = 20;
    msg.num1 = number1;
    msg.num2 = number2;
    msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0); // 0:发完消息再返回,而不是立即返回函数
    return -1;
}

/**
 * @brief 处理自定义请求,在这里添加进程通信
 * @param input
 * @return
 */
int parse_and_process(int sock, const char *query_string, const char *input)
{
    // query_string不一定能用的到

    // 先处理登录操作
    if (strstr(input, "username=") && strstr(input, "password="))
    {
        return handle_login(sock, input);
    }
    // 处理求和请求
    else if (strstr(input, "data1=") && strstr(input, "data2="))
    {
        return handle_add(sock, input);
    }
    else if (strstr(input, "get"))
    {
        return handle_collect(sock, input);
    }
    else if (strstr(input, "set"))
    {
        return handle_set(sock, input);
    }

    else // 剩下的都是json请求,这个和协议有关了
    {
        // 构建要回复的JSON数据
        const char *json_response = "{\"message\": \"Hello, client!\"}";

        // 发送HTTP响应给客户端
        send(sock, json_response, strlen(json_response), 0);
    }

    return 0;
}

//.h文件


#ifndef CUSTOM_HANDLE_H
#define CUSTOM_HANDLE_H

#include <stdio.h>
#include <string.h>

int parse_and_process(int sock, const char *query_string, const char *input);

#endif  // CUSTOM_HANDLE_H
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2159008.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

poi生成的ppt,powerPoint打开提示内容错误解决方案

poi生成的ppt&#xff0c;powerPoint打开提示内容错误解决方案 最近做了ppt的生成&#xff0c;使用poi制作ppt&#xff0c;出现一个问题。微软的powerPoint打不开&#xff0c;提示错误信息 通过xml对比工具发现只需要删除幻灯片的某些标签即可解决。 用的是XML Notepand 分…

ai写论文哪个平台好?7款AI写论文软件帮你撰写论文

在当今学术研究和写作领域&#xff0c;AI论文写作工具的出现极大地提高了写作效率和质量。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。以下是七款推荐的AI写论文软件&#xff0c;其中特别推荐千笔-AIPassPaper。 一、千笔…

Java集合(Map篇)

一.Map a.使用Map i.键值&#xff08;key-value&#xff09;映射表的数据结构&#xff0c;能高效通过key快速查找value&#xff08;元素&#xff09;。 ii.Map是一个接口&#xff0c;最常用的实现类是HashMap。 iii.重复放入k-v不会有问题&#xff0c;但是一个…

盈科巴黎办公室开业典礼暨盈科全球一小时法律服务生态圈·法国中心揭牌仪式圆满举办

国际化建设是盈科律师事务所发展的重要战略目标之一&#xff0c;随着中国对外投资、跨境贸易、“一带一路”高质量共建等迅速发展&#xff0c;越来越多中国企业和公民“走出去”&#xff0c;寻找海外市场。今年是中法建交60周年。建交以来&#xff0c;中法坚持做真诚的朋友、共…

【Java】JVM基本组成

一、JDK、JRE、JVM JDK&#xff1a;全称 “Java Development Kit” Java 开发工具包&#xff0c;提供 javac编译器、jheap、jconsole 等监控工具; JRE&#xff1a;全称 “Java Runtime Environment” Java 运行环境&#xff0c;提供 class Library 核心类库JVM; …

文件服务器FastDFS 消息队列中间件RabbitMQ

新标签页 (chinaunix.net) FastDFS - Browse Files at SourceForge.net 一、FastDFS Tracker和Storage&#xff1a; tracker用来管理所有的storage&#xff0c;只是管理服务器&#xff0c;负责负载均衡。 storage是存储服务器&#xff0c;每一个storage服务器都是一个单独的个…

计算机毕业设计之:基于微信小程序的疫苗预约系统的设计与实现(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Shiro rememberMe反序列化漏洞(Shiro-550)

开启环境抓包验证Shiro框架 使⽤BurpSuite进⾏抓包&#xff0c;在请求包中的cookie字段中添加 rememberMe123; &#xff0c;看响应包 header中是否返回 rememberMedeleteMe 值&#xff0c;若有&#xff0c;则证明该系统使⽤了Shiro框架&#xff1a; 使用工具爆破密钥爆破利用…

论文大杀器!分享4款ai论文写作工具软件

在当今学术研究和论文写作领域&#xff0c;AI技术的应用已经变得越来越普遍。这些工具不仅能够提高写作效率&#xff0c;还能帮助研究人员生成高质量的论文内容。本文将重点介绍四款优秀的AI论文写作工具&#xff0c;并特别推荐千笔-AIPassPaper。 一、千笔-AIPassPaper 传送门…

2024寻找那些能精准修改PDF内容的工具

如今&#xff0c;我们使用 PDF 文档的频率不断攀升&#xff0c;很多时候收到的表格等资料都是 PDF 格式。若先进行格式转换后编辑&#xff0c;再转换回 PDF 格式&#xff0c;着实有些麻烦。那么&#xff0c;pdf怎么编辑修改内容呢&#xff1f;在这篇文章中&#xff0c;我将为大…

python爬虫案例——抓取链家租房信息

文章目录 1、任务目标2、分析网页3、编写代码1、任务目标 目标站点:链家租房版块(https://bj.lianjia.com/zufang/) 要求:抓取该链接下前5页所有的租房信息,包括:标题、详情信息、详情链接、价格 如: 2、分析网页 用浏览器打开链接,按F12或右键检查,进入开发者模式;因…

防火墙--NAT技术,基于源NAT,NAT服务器,双向NAT

文章目录 防火墙--NAT技术一、基于源NAT**方式**&#xff1a;NAT No-PATNAPT出接口地址方式Smart NAT三元组 NAT 二、基于服务器的NAT多出口场景下的NAT Server 三、双向NAT 防火墙–NAT技术 基于源NAT&#xff1a;用于将内部网络的私有IP地址转换为公共IP地址&#xff0c;以便…

使用 IntelliJ IDEA 连接到达梦数据库(DM)

前言 达梦数据库是一款国产的关系型数据库管理系统&#xff0c;因其高性能和稳定性而被广泛应用于政府、金融等多个领域。本文将详细介绍如何在 IntelliJ IDEA 中配置并连接到达梦数据库。 准备工作 获取达梦JDBC驱动&#xff1a; 访问达梦在线服务平台网站或通过其他官方渠道…

Matlab|基于遗传模拟退火算法的风电功率聚类分析

目录 主要内容 部分代码 结果一览 下载链接 主要内容 模糊C-均值聚类&#xff0c;也称FCM&#xff0c;是比较常用的一种聚类算法&#xff0c;该算法利用几何贴进度的概念将不同数据分配到不同聚类群中&#xff0c;但是作为局部搜索优化算法&#xff0c;初值选择不…

Conda虚拟环境配置常见问题记录

搞深度学习的&#xff0c;总有被虚拟环境搞得头大的时候&#xff0c;特别是涉及到CUDA&#xff0c;Torch &#xff0c;Torchvision 版本适配的问题。这两天因为在原来的环境中装了几个包&#xff0c;导致原来的环境崩了&#xff0c;搞了一天的时间又重新配了环境&#xff0c;中…

塑料瓶回收标志分级检测系统源码分享

塑料瓶回收标志分级检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Co…

Etcd权限认证管理

1 查看是否开启权限认证 ctl auth status 2 开启权限认证 ctl auth enable。开启后每一条命令都要加上用户 --userroot:root(root默认最高权限) 3 创建其他用户 ctl user add user1 --user用户名:密码 4 创建角色 ctl role add testR --user 5 为角色添加权限 ctl role g…

python和pyqt-tools安装位置

一.python的安装位置 1.查询安装的python的位置 先查询python&#xff0c;然后输入import sys和sys.path 二.python-tools的安装位置 找到python的文件后按下图路径即可查到tools的文件

OpenAI o1团队突破性论文:『过程推理』中数学推理能力大幅提升,从正确中学习的新方法

原创 超 近年来&#xff0c;大型语言模型(LLMs)在复杂的多步推理任务中取得了令人瞩目的进展。这些模型能够生成逐步的思维链&#xff0c;解决从小学数学到高等微积分的各种问题。然而&#xff0c;即使是最先进的模型也常常陷入逻辑陷阱&#xff0c;产生看似合理但实际错误的推…

PHP校园外卖跑腿小程序带后台(商业版)

有需要请加文章底部Q哦 可远程调试 PHP校园外卖跑腿小程序带后台(商业版) 一 介绍 此校园外卖跑腿小程序端基于原生开发&#xff0c;后端基于ThinkPHP5框架开发&#xff0c;数据库mysql&#xff0c;系统角色分为用户&#xff0c;商家(自配送)&#xff0c;跑腿员&#xff0c;管…