网络高级项目( 基于webserver的工业数据采集和控制项目)

目录

一、项目要求:

二、演示效果:

设备端:

Modbus用户控制端:

服务器端:

网页端:

三、 项目代码:

Modbus用户控制端代码:

服务器端代码:

网页端代码:

四、B站讲解视频:


一、项目要求:

设备端:有温湿度传感器采集温度和湿度以及LED灯,和BEEP蜂鸣器(使用Modbus slave来模拟);

虚拟机端:通过modbus采集和控制信息的用户控制端和采集数据保存历史纪录的服务器端,利用进程间通信实现连接;

网页端:利用html5写一个网页,显示采集的温湿度信息和能够控制LED灯和BEEP蜂鸣器的开关,通过http协议与服务器连接。


二、演示效果:

设备端:

显示采集的温湿度和led,蜂鸣器的状态(0为关闭,1为开启)

Modbus用户控制端:

每隔两秒采集一次数据并将数据打印至终端

服务器端:

显示通过网页传递过来的指令和用户控制端进行联系,并且存入历史记录中

网页端:

按下温湿度采集按钮,显示采集到的温度和湿度,能够控制LED灯和蜂鸣器的开关,按下历史记录查询按钮可以显示历史记录


三、 项目代码:

Modbus用户控制端代码:

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

union val
{
    int i_val;
    float f_val;
};

typedef struct dev
{
    long op;
    char dev_name[32]; // 设备名称
    union val v;       // 存放具体数据
} DEV;

struct data
{
    char buf[32];
};

struct data *p = NULL;
key_t key, key1;
int msgid;
int shmid;

void *kongzhi(void *arg)
{
    modbus_t *ctx = (modbus_t *)arg;
    DEV dev;
    while (1)
    {
        msgrcv(msgid, &dev, sizeof(dev) - sizeof(dev.op), 100, 0);
        printf("%s %d\n", dev.dev_name, dev.v.i_val);
        if (strcmp(dev.dev_name, "led") == 0)
        {
            modbus_write_bit(ctx, 0, dev.v.i_val);
            printf("led灯%s\n", dev.v.i_val ? "开启" : "关闭");
        }
        else if (strcmp(dev.dev_name, "beep") == 0)
        {
            modbus_write_bit(ctx, 1, dev.v.i_val);
            printf("蜂鸣器%s\n", dev.v.i_val ? "开启" : "关闭");
        }
    }
}

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("用法 <ip>\n");
        return -1;
    }
    modbus_t *ctx;
    ctx = modbus_new_tcp(argv[1], 502);
    if (ctx == NULL)
    {
        perror("modbus new tcp失败");
        return -1;
    }

    // 设置从机ID
    modbus_set_slave(ctx, 1);

    // 建立连接
    if (modbus_connect(ctx) < 0)
    {
        printf("modbus connect失败\n");
        modbus_free(ctx);
        return -1;
    }
    printf("connect ok\n");
    key = ftok("./test", 'a');
    if (key < 0)
    {
        perror("创建key值失败");
        return -1;
    }

    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == EEXIST)
        {
            shmid = shmget(key, 128, 0666);
        }
        else
        {
            perror("创建共享内存失败");
            return -1;
        }
    }

    p = (struct data *)shmat(shmid, NULL, 0);
    if (p == (struct data *)-1)
    {
        perror("映射共享内存失败");
        return -1;
    }

    key1 = ftok("./test1", 'a');
    if (key1 < 0)
    {
        perror("创建key值失败");
        return -1;
    }
    msgid = msgget(key1, IPC_CREAT | IPC_EXCL | 0666);
    if (msgid <= 0)
    {
        if (errno == EEXIST)
        {
            msgid = msgget(key1, 0666);
        }
        else
        {
            perror("创建消息队列失败");
            return -1;
        }
    }

    pthread_t kongzhitid;
    // 创建线程
    if (pthread_create(&kongzhitid, NULL, kongzhi, ctx) != 0)
    {
        perror("创建控制线程失败");
        modbus_free(ctx);
        modbus_close(ctx);
        return -1;
    }
    uint16_t dest[4];
    DEV tem, hum;

    while (1)
    {
        int mrr = modbus_read_registers(ctx, 0, 4, dest);
        if (mrr <= 0)
        {
            perror("读保持寄存器的值失败");
            exit(0);
        }
        else
        {
            strcpy(tem.dev_name, "温度");
            tem.v.f_val = modbus_get_float_dcba(dest);
            strcpy(hum.dev_name, "温度");
            hum.v.f_val = modbus_get_float_dcba(dest + 2);
            sprintf(p->buf, "温度:%.2f℃ 湿度:%.2f\%\n", tem.v.f_val, hum.v.f_val);
            printf("%s",p->buf);
        }
        sleep(2); // 每2秒采集一次
    }
    modbus_free(ctx);
    modbus_close(ctx);
    return 0;
}

服务器端代码:

#include <sys/types.h>
#include <sys/socket.h>
#include "custom_handle.h"

struct data *p = NULL;
DEV dev;
extern int len;
sqlite3 *db;

#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
 */
int parse_and_process(int sock, const char *query_string, const char *input)
{
    // 打开数据库
    if (sqlite3_open("./history.db", &db) < 0)
    {
        printf("打开数据库失败: %s\n", sqlite3_errmsg(db));
        return -1;
    }

    key_t key, key1;
    int shmid, msgid;
    key = ftok("./test", 'a');
    if (key < 0)
    {
        perror("创建key值失败");
        return -1;
    }
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        if (errno == EEXIST)
        {
            shmid = shmget(key, 128, 0666);
        }
        else
        {
            perror("创建共享内存失败");
            return -1;
        }
    }
    p = (struct data *)shmat(shmid, NULL, 0);
    if (p == (struct data *)-1)
    {
        perror("映射共享内存失败");
        return -1;
    }

    key1 = ftok("./test1", 'a');
    if (key1 < 0)
    {
        perror("创建key值失败");
        return -1;
    }
    msgid = msgget(key1, IPC_CREAT | IPC_EXCL | 0666);
    if (msgid <= 0)
    {
        if (errno == EEXIST)
        {
            msgid = msgget(key1, 0666);
        }
        else
        {
            perror("创建消息队列失败");
            return -1;
        }
    }
    char sql[1024];
    char *errmsg = NULL;
    char **result = NULL;
    int rows, columns;
    time_t t;
    struct tm *timeinfo;
    char date[100];
    time(&t);
    timeinfo = localtime(&t);
    snprintf(date, sizeof(date), "%d-%d-%d %d:%d:%d", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);

    if (strstr(input, "get"))
    {
        send(sock, p->buf, strlen(p->buf), 0);
        snprintf(sql, sizeof(sql), "insert into history values ('%s','%s');", date, p->buf);
        if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != 0)
        {
            printf("放入历史记录失败: %s", errmsg);
            return -1;
        }
        memset(sql, 0, sizeof(sql));
        memset(p->buf, 0, sizeof(p->buf));
    }
    else if (strstr(input, "history"))
    {
        memset(p->buf, 0, sizeof(p->buf));
        snprintf(sql, sizeof(sql), "select * from history;");
        int rc = sqlite3_get_table(db, sql, &result, &rows, &columns, &errmsg);
        if (rc != 0)
        {
            printf("查询历史记录失败: %s", errmsg);
        }
        else
        {
            if (rows > 0)
            {
                for (int i = 1; i <= rows; ++i)
                {
                    for (int j = 0; j < columns; ++j)
                    {
                        strcat(p->buf, result[i * columns + j]);
                        strcat(p->buf, " ");
                    }
                    strcat(p->buf, "\n");
                }
            }
        }
        send(sock, p->buf, strlen(p->buf), 0);
        memset(sql, 0, sizeof(sql));
        memset(p->buf, 0, sizeof(p->buf));
    }
    else
    {
        memset(dev.dev_name, 0, sizeof(dev.dev_name));
        // for (int i = 0; i < len - 2; i++)
        // {
        //     dev.dev_name[i] = input[i];
        // }
        // char i = input[len - 1];
        // dev.v.i_val = i - '0';
        // printf("%s %d\n", dev.dev_name, dev.v.i_val);
        char i;
        sscanf(input, "%s %s", dev.dev_name, &i);
        dev.v.i_val = i - '0';
        printf("%s %d\n", dev.dev_name, dev.v.i_val);
        dev.op = 100;
        msgsnd(msgid, &dev, sizeof(dev) - sizeof(dev.op), 0);
        if (strcmp(dev.dev_name, "led") == 0)
        {
            sprintf(p->buf, "led灯%s\n", dev.v.i_val ? "开启" : "关闭");
            // send(sock, p->buf, strlen(p->buf), 0);
            snprintf(sql, sizeof(sql), "insert into history values ('%s','%s');", date, p->buf);
            if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != 0)
            {
                printf("放入历史记录失败: %s", errmsg);
                return -1;
            }
            printf("%s\n", sql);
            memset(sql, 0, sizeof(sql));
        }
        else if (strcmp(dev.dev_name, "beep") == 0)
        {
            sprintf(p->buf, "蜂鸣器%s\n", dev.v.i_val ? "开启" : "关闭");
            // send(sock, p->buf, strlen(p->buf), 0);
            snprintf(sql, sizeof(sql), "insert into history values ('%s','%s');", date, p->buf);
            if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != 0)
            {
                printf("放入历史记录失败: %s", errmsg);
                return -1;
            }
            printf("%s\n", sql);
            memset(sql, 0, sizeof(sql));
        }
        else
        {
            sprintf(p->buf, "输入错误,请重新输入\n");
            // send(sock, p->buf, strlen(p->buf), 0);
        }
    }

    return 0;
}

网页端代码:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>工业信息采集系统</title>
	<style>
		body {
			font-family: Arial, sans-serif;
			margin: 20px;
		}

		.container {
			max-width: 800px;
			margin: 0 auto;
		}

		.section {
			margin-bottom: 20px;
		}

		.button {
			padding: 10px 20px;
			font-size: 16px;
			cursor: pointer;
			background-color: #007bff;
			color: white;
			border: none;
			border-radius: 5px;
		}

		.button:disabled {
			background-color: #6c757d;
			cursor: not-allowed;
		}

		.status {
			margin-top: 10px;
			padding: 10px;
			background-color: #f8f9fa;
			border: 1px solid #ced4da;
			border-radius: 5px;
		}
	</style>
	<script>
		//     //设置定时器,定时5秒调用一次函数refreshPage
		//     setInterval(refreshPage, 5000);
		//     function refreshPage() {
		//         //TODO
		//     }
		function sendledon() {
			var buf = "led 1";
			var xhr = new XMLHttpRequest();
			var url = "";
			xhr.open("post", url, true);
			xhr.send(buf);
		}
		function sendledoff() {
			var buf = "led 0";
			var xhr = new XMLHttpRequest();
			var url = "";
			xhr.open("post", url, true);
			xhr.send(buf);
		}
		function sendbeepon() {
			var buf1 = "beep 1";
			var xhr = new XMLHttpRequest();
			var url = "";
			xhr.open("post", url, true);
			xhr.send(buf1);
		}
		function sendbeepoff() {
			var buf1 = "beep 0";
			var xhr = new XMLHttpRequest();
			var url = "";
			xhr.open("post", url, true);
			xhr.send(buf1);
		}
		function sendcaiji() {
			var buf = "get";
			var xhr = new XMLHttpRequest();
			var url = "";
			xhr.open("post", url, true);
			xhr.send(buf);
			xhr.onreadystatechange = function () {
				var response = xhr.responseText;
				document.getElementById('temhum').innerText = response;
			};
		}
		function sendhistory() {
			var buf = "history";
			var xhr = new XMLHttpRequest();
			var url = "";
			xhr.open("post", url, true);
			xhr.send(buf);
			xhr.onreadystatechange = function () {
				var response = xhr.responseText;
				document.getElementById('lishi').innerText = response;
			};
		}
	</script>


</head>

<body>
	<div class="container">
		<h1>工业信息采集系统</h1>

		<!-- 温湿度采集 -->
		<div class="section">
			<input class="button" type="button" name="caiji" value="温湿度采集" onclick="sendcaiji()" />
			<div class="status" id="wenshidu">
				<p><span id="temhum"></p>
			</div>
		</div>

		<!-- LED灯控制 -->
		<div class="section">
			<h2>LED灯控制</h2>
			<label for="ledon">
				开<input type="radio" name="led" id="ledon" value="led 1" onclick="sendledon()" />
			</label>
			<label for="ledoff">
				关<input type="radio" name="led" id="ledoff" checked="checked" value="led 0"
					onclick="sendledoff()" /><br />
			</label>
		</div>

		<!-- 蜂鸣器控制 -->
		<div class="section">
			<h2>蜂鸣器控制</h2>
			<label for="ledon">
				开<input type="radio" name="beep" id="beepon" value="beep 1" onclick="sendbeepon()" />
			</label>
			<label for="ledoff">
				关<input type="radio" name="beep" id="beepoff" checked="checked" value="beep 0"
					onclick="sendbeepoff()" /><br />
			</label>
		</div>

		<!-- 历史记录查询 -->
		<div class="section">
			<input type="button" class="button" name="history" value="历史记录查询" onclick="sendhistory()" />
			<div class="status" id="history-records">
				<p><strong>历史记录:</strong></p>
				<ul id="lishi"></ul>
			</div>
		</div>
	</div>
</body>

</html>

四、B站讲解视频:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VirtualBox Install MacOS

环境搭建 git clone https://github.com/myspaghetti/macos-virtualbox 脚本配置 修改macos-guest-virtualbox.sh部分内容为 vm_name"macOS" # name of the VirtualBox virtual machine macOS_release_name"Catalina" # install &quo…

股指期货的详细玩法功能与应用解析

股指期货作为一种重要的金融衍生工具&#xff0c;为投资者提供了多样化的投资和风险管理手段。本文将详细探讨股指期货的三大主要功能&#xff1a;风险规避、价格发现和资产配置。 第一&#xff0c;风险规避功能 1.套期保值&#xff1a;股指期货的风险规避功能主要通过套期保值…

外观模式详解:如何为复杂系统构建简洁的接口

&#x1f3af; 设计模式专栏&#xff0c;持续更新中 欢迎订阅&#xff1a;JAVA实现设计模式 &#x1f6e0;️ 希望小伙伴们一键三连&#xff0c;有问题私信都会回复&#xff0c;或者在评论区直接发言 外观模式 外观模式&#xff08;Facade Pattern&#xff09;为子系统中的一组…

智能 Uber 发票 PDF 合并工具

在现代商务出行中&#xff0c;尤其是在跨国出差中&#xff0c;处理和整合大量 Uber 发票已成为一项不小的挑战。手动整理和合并这些发票不仅耗时&#xff0c;还容易出错。作为开发者&#xff0c;为什么不开发一个自动化工具&#xff0c;将这些任务交给代码来完成呢&#xff1f;…

成型的程序

加一个提示信息 加上python 常用的包 整个程序打包完 250M 安装 960MB matplot numpy pandas scapy pysearial 常用的包 (pyvisa)… … 啥都有 Python 解释器组件构建 要比 lua 容易的多 &#xff08;C/Rust 的组件库)

钢材表面缺陷数据集以coco格式做好了数据集的划分,1200张训练集,600张验证集,对应的json文件也在里面

钢材表面缺陷数据集 以coco格式做好了数据集的划分&#xff0c;1200张训练集&#xff0c;600张验证集&#xff0c;对应的json文件也在里面。 钢材表面缺陷检测数据集营销介绍 项目背景&#xff1a; 钢材作为工业生产的重要原材料之一&#xff0c;其表面质量直接影响到成品的性…

MySQL之安装与基础知识

目录 一&#xff1a;在centos7上安装MySQL数据库 1.卸载默认存在的环境 2.配置mysql的yum源 3. 安装MySQL 4.登录mysql 5.设置MySQL的配置文件 二&#xff1a;MySQL基础知识 1.什么是数据库 2.主流数据库 3.服务器&#xff0c;数据库&#xff0c;表关系及使用案例 4…

预训练发展

预训练发展 1.ELMo2.GPT3.Bert3.1Ernie-baidu3.2Ernie- Tsinghua 4.GPT25.UNILM6.Transformer-XL & XLNet6.1方案一6.2方案三 7.Roberta8.SpanBert8.1SBO简介&#xff1a; 9.ALBERT9.1方案一9.2方案二9.3方案三 10.T511.GPT312.从"续写"到"回答"12.1SF…

基于51单片机的直流数字电流表proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1adZbhgOBvvg0KsCO6_ZiAw 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

MySQL——数据类型(一)

目录 一、前言 二、数值类型 2.1 tinyint [unsigned] 2.1.1 插入合法数据 2.1.2 插入边界数据 2.1.3 插入不合法数据 2.1.4 结论 2.2 bit [n] 2.3 float [(m, d)] [unsigned] 2.3.1 float 特性 2.3.2 插入整数部分大于 m-d 的数字 2.3.3 插入小数部分大于 d 的数字…

【贪心算法】贪心算法

贪心算法简介 1.什么是贪心算法2.贪心算法的特点3.学习贪心的方向 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.什么是贪心算法 与其说是…

MYSQL数据库——MYSQL管理

MYSQL数据库安装完成后&#xff0c;自带四个数据库&#xff0c;具体作用如下&#xff1a; 常用工具 1.mysql 不是指mysql服务&#xff0c;而是指mysql的客户端工具 例如&#xff1a; 2.mysqladmin 这是一个执行管理操作的客户端程序&#xff0c;可以用它来检查服务器的配置和…

vs2022快捷键异常解决办法

安装了新版本的vs2022&#xff0c;安装成功后&#xff0c;发现快捷键发生异常&#xff0c;之前常用的快捷键要么发生改变&#xff0c;要么无法使用&#xff0c;比如原来注释代码的快捷键是ctrlec&#xff0c;最新安装版本变成了ctrlkc&#xff0c;以前编译代码的快捷键是F6或者…

算法入门-贪心1

第八部分&#xff1a;贪心 409.最长回文串&#xff08;简单&#xff09; 给定一个包含大写字母和小写字母的字符串 s &#xff0c;返回通过这些字母构造成的最长的回文串 的长度。 在构造过程中&#xff0c;请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串…

记录小数点

记录data frame小数点后面省略掉0的问题 iloc得到的series .to_list() 0被省略掉 to_list() 可能会将浮点数转换为默认格式。先将数据转换为字符串以保留格式 df_2708.iloc[2,:].apply(lambda x: f{x:.3f}).to_list()自定义保留小数点后几位 def formatter(value):return &q…

自动驾驶自动泊车场景应用总结

自动泊车技术是当前智能驾驶技术的一个重要分支,其目标是通过车辆自身的感知、决策和控制系统,实现车辆在有限空间内的自主泊车操作。目前自动泊车可分为半自动泊车、全自动泊车、记忆泊车、自主代客泊车四种产品形态,其中, 根据搭载传感器和使用场景的不同,全自动泊车又可…

OpenGL笔记二十一之几何类设计

OpenGL笔记二十一之几何类设计 —— 2024-09-16 下午 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记二十一之几何类设计1.运行1.1.立方体运行1.2.球体运行 2.几何类搭建1.立方体分析2.球体分析3.图片资源文件4.关键实现4.1.geometry.h4.2.geometry.cpp…

vue3使用provide和inject传递异步请求数据子组件接收不到

前言 一般接口返回的格式是数组或对象&#xff0c;使用reactive定义共享变量 父组件传递 const data reactive([])// 使用settimout模拟接口返回 setTimeout(() > {// 将接口返回的数据赋值给变量Object.assign(data, [{ id: 10000 }]) }, 3000);provide(shareData, dat…

ip映射域名,一般用于mysql和redis的固定映射,方便快捷打包

举个例子 192.168.3.101mysql映射到mysql.smartlink.com 192.168.3.101redis redis.smartlink.com 要将IP地址映射到域名&#xff0c;可以通过几种方式实现&#xff0c;包括修改本地主机文件&#xff08;仅适用于本地开发环境&#xff09;、设置DNS解析&#xff08;适用于生产环…

一文入门生成式AI(理解ChatGPT的原理)

一、什么是生成式AI&#xff1f; 以ChatGPT为代表的生成式AI&#xff0c;是对已有的数据和知识进行向量化的归纳&#xff0c;总结出数据的联合概率。从而在生成内容时&#xff0c;根据用户需求&#xff0c;结合关联字词的概率&#xff0c;生成新的内容。 可以这么联想&#x…