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

 一、项目框架及流程

二、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.xdnf.cn/news/149258.html

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

相关文章

C/C++语言基础--C++构造函数、析构函数、深拷贝与浅拷贝等等相关知识讲解

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 周末休息了&#xff0c;没有更新&#xff0c;请大家见谅哈&#xff1b;构造函数、析构函数可以说便随着C每一个程序&#xff0c;故学构造函数、析构函数是必要的&#xff1b;C语言后面也会继续更新知识点&am…

计算机的错误计算(一百零二)

摘要 探讨 的计算精度问题。 从计算机的错误计算&#xff08;九十九&#xff09;可知&#xff0c; 在IEEE 754-2019的列表中。因此&#xff0c;有必要分析其计算准确度。 例1. 已知 计算 若利用 Python的SciPy库中函数计算&#xff0c;则有&#xff1a; 若用Java的pow函…

通过 LabVIEW 正则表达式读取数值(整数或小数)

在LabVIEW开发中&#xff0c;字符串处理是一个非常常见的需求&#xff0c;尤其是在处理包含复杂格式的数字时。本文通过一个具体的例子来说明如何利用 Match Regular Expression Function 和 Match Pattern Function 读取并解析字符串中的数字&#xff0c;并重点探讨这两个函数…

毕业设计选题:基于ssm+vue+uniapp的英语学习激励系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

达梦-华为鲲鹏ARM架构下性能测试最佳实践

一、测试综述 1.1 测试目的 本次测试的目的是验证达梦数据库&#xff0c;在鲲鹏服务器下&#xff0c;不同服务器参数基于sysbench性能压力测试的表现。本次参数是根据为华为鲲鹏arm服务器调优十板斧内建议值调整 成长地图-鲲鹏开发套件开发文档-鲲鹏社区 1.2 通用指标 指标…

虚幻蓝图Ai随机点移动

主要函数: AI MoveTo 想要AI移动必须要有 导航网格体边界体积 (Nav Mesh Bounds Volume) , 放到地上放大 , 然后按P键 , 可以查看范围 然后创建一个character类 这样连上 AI就会随机运动了 为了AI移动更自然 , 取消使用控制器旋转Yaw 取消角色移动组件 的 使用控制器所需的…

关于Cursor使用的小白第一视角

最近看破局感觉洋哥总是提到cursor&#xff0c;感觉好火&#xff0c;所以打算学习一下怎么用Cursor&#xff0c;如果可以希望能做一个我自己的网站。 之前从来没用过Cursor。所以&#xff0c;这是一篇小白视角的Cursor使用教程。 如果你也是一个小白&#xff0c;并且对Cursor…

ArcGIS Desktop使用入门(三)图层右键工具——拓扑(上篇:地图拓扑)

系列文章目录 ArcGIS Desktop使用入门&#xff08;一&#xff09;软件初认识 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——标准工具 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——编辑器 ArcGIS Desktop使用入门&#xff08;二&#x…

Maven配置及使用

1. Maven简介和安装 1.1. Maven是一个依赖管理工具 问题&#xff1a; jar包的规模 随着使用框架越来越多&#xff0c;或框架的封装程度越来越高&#xff0c;项目中使用的jar包也越来越多。项目中&#xff0c;一个模块里用到上百个jar包是非常正常的jar包的来源 jar包所属技术…

LeetCode 面试经典150题 201.数字范围按位与

题目&#xff1a;给你两个整数 left 和 right &#xff0c;表示区间 [left, right] &#xff0c;返回此区间内所有数字 按位与 的结果&#xff08;包含 left 、right 端点&#xff09;。 提示&#xff1a;0 < left < right < 2^31 - 1 思路&#xff1a; 位与的特性…

《一本书讲透Elasticsearch》读书笔记(二)

Elasticsearch集群部署 Elastic Stack集群部署基础知识 Elasticsearch、Logstash、Beats、Kibana全部都支持跨平台部署 集群部署平台及操作系统的选型 可供选择的部署平台包括实体服务器、虚拟机&#xff08;VMWare、OpenStack等&#xff09;​、容器化平台&#xff08;Doc…

链式队列操作

文章目录 &#x1f34a;自我介绍&#x1f34a;概述&#x1f34a;链式队列代码linkstack.clinkstack.hmain.c 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&…

课程表-LeetCode100

现在你总共有 numCourses 门课需要选&#xff0c;记为 0 到 numCourses - 1。给你一个数组 prerequisites &#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示在选修课程 ai 前 必须 先选修 bi 。 例如&#xff0c;想要学习课程 0 &#xff0c;你需要先完成课程 1…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23 本期&#xff0c;我们对大语言模型在表情推荐, 软件安全和 自动化软件漏洞检测等方面如何应用&#xff0c;提供几篇最新的参考文章。 1 Semantics Preserving Emoji Recommendation with Large Language Mod…

机器学习中分类问题的各类评估指标总结

机器学习中分类问题的各类评估指标总结 在机器学习的世界里&#xff0c;分类问题占据了半壁江山。从垃圾邮件检测到疾病诊断&#xff0c;从用户行为分析到市场趋势预测&#xff0c;分类算法的应用无处不在。然而&#xff0c;如何评价一个分类模型的性能&#xff0c;却是一门大…

MovieLife 电影生活

MovieLife 电影生活 今天看到一个很有意思的项目&#xff1a;https://www.lampysecurity.com/post/the-infinite-audio-book “我有一个看似愚蠢的想法。通常&#xff0c;这类想法只是一闪而过&#xff0c;很少会付诸实践。但这次有所不同。假如你的生活是一部电影&#xff0c…

Stable Diffusion 使用详解(13)--- 3D纹理增强

目录 背景 Normal Map 描述 原理 使用心得 例子 描述 原图 参数设置 底模 ​编辑 正负相关性提示词 其他参数 controlnet 效果 还能做点啥 调整 效果 背景 实际上&#xff0c;在stable diffusion 中&#xff0c;你获取发现很多controlnet 其实功能有点类似&…

webview2加载本地页面

加载方式 通过导航到文件 URL 加载本地内容 使用方式&#xff1a; webView->Navigate( L"file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html"); 但是这种方式存在一些问题&#xff0c;比如&#xff1a; 存在跨域问题&#xff08;我加载…

Java语言程序设计基础篇_编程练习题***18.33 (游戏:骑士旅途的动画)

目录 ***18.33 (游戏:骑士旅途的动画) 习题思路 代码示例 动画演示 ***18.33 (游戏:骑士旅途的动画) 为骑士旅途的问题编写一个程序&#xff0c;该程序应该允许用户将骑士放到任何一个起始正方形&#xff0c;并单击Solve按钮&#xff0c;用动画展示骑士沿着路径的移动&…

9.5HSV体系进行颜色分割

基本概念 inRange() 函数是 OpenCV 中用于图像处理的一个非常有用的函数&#xff0c;即从图像中提取出介于指定范围内的像素值。这个函数在图像处理中特别有用&#xff0c;比如颜色检测、背景去除等应用。它主要用于图像的阈值处理&#xff0c;但与其他阈值方法&#xff08;如…