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

news2024/9/20 22:35:43

目录

一、项目要求:

二、演示效果:

设备端:

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.coloradmin.cn/o/2142249.html

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

相关文章

C++3D迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #include <iostream> using namespace std; void printmaze(char strmaze[5][5][5]) {cout << "-----" << endl;int i 0;int ia 0…

pdf去水印怎么去掉免费?6个pdf去除水印的方法快码住,超级好用!

pdf去水印怎么去掉免费&#xff1f;您是否有一些带有水印的pdf文档&#xff0c;让您感觉到头疼&#xff1f;您又是否希望能够去除这些水印&#xff0c;或者想用其他水印来替换现有的水印&#xff1f;如果是这样的话&#xff0c;我非常推荐您继续阅读本篇文章。本文将为您提供一…

如何在Linux下升级R版本和RStudio

一、升级R版本 在Linux上&#xff0c;R的安装通常通过包管理器完成。不同的Linux发行版&#xff08;如Ubuntu、Debian、Fedora等&#xff09;可能略有不同。下面以Ubuntu为例&#xff0c;介绍如何升级R版本。如果你使用其他发行版&#xff0c;步骤可能类似。 二.更新步骤 2.…

Git:版本控制工具介绍

目录 全文概要版本控制工具介绍版本控制系统的概念**版本控制系统的历史****版本控制系统的分类****本地版本控制系统****集中式版本控制****分布式版本控制系统** Git 介绍Git 概念Git 与 SVN 对比**SVN的记录方式****Git 的记录快照** Git 安装Git 安装Bash、CMD与GUIGit 的配…

黑神话悟空黄凤岭

黑神话悟空黄风岭支线任务大全 第二关“黄风寨”的难度可以说是飙升&#xff1a; 虎先锋从名字来看只是一般妖王&#xff0c;但是他的天命人“猴头下酒”怕是吃到撑也吃不完&#xff0c;关底的黄风大圣二阶段放法宝之后的飓风更是重量级&#xff0c;从视线和移动能力对玩家会造…

Git提交类型

说明&#xff1a;Git提交类型指的是代码commit时&#xff0c;写在comment前面的标志&#xff0c;表示此次commit的提交类型&#xff0c;如下&#xff1a; Git提交类型 常见的Git提交类型有&#xff1a; feat&#xff1a;新特性、新功能或优化&#xff1b; fix&#xff1a;修复…

再临TSC原创24年CSP-J全真模拟卷-阅读程序篇(1)

没有看基础题的可以点击我的主页查看基础题部分 阅读程序题&#xff1a; 1. 16. 如果删除第6行&#xff0c;程序仍然能正确运行 A正确B错误 答案&#xff1a;B 17.输入负数时&#xff0c;程序什么也不会输出&#xff0c;并正常结束程序 A正确B错误 答案&#xff1a;A …

Linux内核(Kernel)启动过程分析

文章目录 Linux内核&#xff08;Kernel&#xff09;启动过程一、内核启动的基本流程1. 启动加载程序 (Bootloader)2. 内核解压阶段3. 内核启动&#xff08;Kernel Startup&#xff09;4. start_kernel函数5. 启动初始进程 二、内核文件加载及解压缩1.为什么是压缩文件2.文件类型…

磁盘写入缓存区太大,如何清理C盘缓存

针对“磁盘写入缓存区太大&#xff0c;如何清理C盘缓存”的问题&#xff0c;我们可以从多个角度进行专业解答。首先&#xff0c;需要明确的是&#xff0c;“磁盘写入缓存区太大”这一表述可能涉及硬盘缓存的设置或系统缓存管理&#xff0c;但通常用户面对的问题更多是关于C盘空…

变量常量标识符 Day5

1. 变量 1.1 变量的概念 变量是计算机内存中的一块存储单元&#xff0c;是存储数据的基本单元变量的组成包括&#xff1a;数据类型、变量名、值&#xff0c;后文会具体描述变量的本质作用就是去记录数据的&#xff0c;比如说记录一个人的身高、体重、年龄&#xff0c;就需要去…

Vue接入高德地图并实现基本的路线规划功能

目录 一、申请密钥 二、安装依赖 三、代码实现 四、运行截图 五、官方文档 一、申请密钥 登录高德开放平台&#xff0c;点击我的应用&#xff0c;先添加新应用&#xff0c;然后再添加Key。 如图所示填写对应的信息&#xff0c;系统就会自动生成。 二、安装依赖 npm i am…

Java调用数据库 笔记05

一. 数据库&#xff08;通过各种驱动来实现调用&#xff09;&#xff1a; &#xff08;应用程序通过接口控制的各种数据库驱动来调用数据库-->jdbc方法&#xff09; 1.创建Java的普通class类 2.加载驱动 Class.forName("com.mysql.jdbc.Driver"); 3.驱动管理类…

中国空间计算产业链发展分析

2024中国空间计算产业链拆解 空间计算设备主要包括AR、VR、MR等终端设备。VR设备通常包括头戴式显示器&#xff08;VR头盔&#xff09;、手柄或追踪器等组件&#xff0c;用以完全封闭用户视野&#xff0c;营造虚拟环境体验。这些设备配备高分辨率显示屏、内置传感器和跟踪器。 …

电气自动化入门02:三相交流电及其主要应用参数

视频链接&#xff1a;1.2 电工知识&#xff1a;三相交流电及其主要应用参数_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1PJ41117PW?p3&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.什么是交流电 交流电就是&#xff1a;大小和方向都随时间不断变化的电流 2…

OPPO大模型升级为AndesGPT-2.0 支持多模态等能力

OPPO公司宣布其人工智能大模型AndesGPT-2.0在权威第三方评测机构SuperCLUE发布的《中文大模型基准评测2024年8月报告》中表现卓越&#xff0c;荣获多项第一&#xff0c;标志着OPPO在AI领域的技术实力进一步得到行业认可。 AndesGPT-2.0在总榜上位列国内大模型第二&#xff0c;同…

简单题27 - 移除元素(Java)20240917

问题描述&#xff1a; 代码&#xff1a; class Solution {public int removeElement(int[] nums, int val) {int k 0; // k指针用于记录不等于val的元素放置位置for (int i 0; i < nums.length; i) {if (nums[i] ! val) {nums[k] nums[i]; // 如果元素不等于val&#…

7.4 溪降技术:滑行

目录 7.4 滑行概览观看视频课程电子书: 滑行一级基本原则滑道评估接近滑行着陆 V7提示&#xff1a;着陆技巧总结 7.4 滑行 概览 滑入深潭 滑行是顺着瀑布下降的一种独特且有趣的方式。河流冲刷岩石在峡谷中形成了水道、滑梯或滑道。滑梯可以有不同的长度、高度、轨迹和水量。随…

【踩坑】Gradle依赖下载问题解决:stributions/gradle-8.10-bin.zip failed: timeout (10000ms)

在构建项目时&#xff0c;Gradle 默认会从 https://services.gradle.org/distributions/ 下载对应版本的 Gradle 包。由于网络问题&#xff0c;这个过程可能非常缓慢&#xff0c;甚至会出现下载失败的情况。为了解决这一问题&#xff0c;我将 gradle-wrapper.properties 文件中…

电路设计学习(一)

FUSB302BUCX 可编程 USB Type-C 控制器&#xff0c;带 PD&#xff08;默认 SNK&#xff09; FUSB302BUCX 是一款由 ON Semiconductor 生产的 USB Type-C 控制器&#xff0c;用于实现 USB Type-C 和 USB Power Delivery (PD) 协议。它主要负责 USB Type-C 端口的检测、CC 引脚…

SprinBoot+Vue爱老助老服务平台的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…