题目:实现一个简易的传感器数据采集系统,完成以下功能:
- 数据采集客户端。能够随机产生等模拟数据(如:温度、湿度),并发送到服务器端。
- 数据采集服务器,接收多个客户端的数据并进行展示。
- 通讯协议设计,包括:心跳协议、数据上报、客户端上线、下线等。
要求:
- 必须有图形界面
- 通信协议必须完整实现
- GUI界面可以采用(PyQt、Electron、Java Swing等)
一. 采集端代码:
import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QVBoxLayout, QLineEdit, QPushButton, QWidget from PyQt5.QtCore import QTimer, QThread, pyqtSignal import socket import random import time # 定义一个客户端线程类,用于发送数据到服务器 class ClientThread(QThread): update_signal = pyqtSignal(str) # 定义一个信号,用于更新UI def __init__(self, username, host, port): super().__init__() self.username = username # 用户名 self.host = host # 服务器地址 self.port = port # 服务器端口 self.running = True # 线程运行状态 def run(self): # 创建socket连接 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((self.host, self.port)) # 连接服务器 while self.running: temperature = random.uniform(20, 30) # 生成随机温度 humidity = random.uniform(40, 60) # 生成随机湿度 data = f"{self.username}: Temperature={temperature}, Humidity={humidity}" s.sendall(data.encode()) # 发送数据 self.update_signal.emit(data) # 发送信号更新UI time.sleep(2) # 休眠2秒 def stop(self): self.running = False # 停止线程 # 定义客户端主窗口类 class ClientWindow(QMainWindow): def __init__(self): super().__init__() self.initUI() self.client_threads = [] # 存储客户端线程列表 def initUI(self): # 设置窗口标题和大小 self.setWindowTitle('Sensor Data Client') self.setGeometry(100, 100, 400, 300) # 创建列表控件、用户名输入框、连接按钮和下线按钮 self.list_widget = QListWidget() self.username_line = QLineEdit(self) self.username_line.setPlaceholderText('Enter your username') self.connect_button = QPushButton('Connect', self) self.connect_button.clicked.connect(self.connect_to_server) # 绑定按钮点击事件 self.disconnect_button = QPushButton('Disconnect All', self) self.disconnect_button.clicked.connect(self.disconnect_all) # 绑定按钮点击事件 # 布局管理 layout = QVBoxLayout() layout.addWidget(self.username_line) layout.addWidget(self.connect_button) layout.addWidget(self.disconnect_button) # 添加下线按钮 layout.addWidget(self.list_widget) # 设置中心窗口 central_widget = QWidget() central_widget.setLayout(layout) self.setCentralWidget(central_widget) def connect_to_server(self): # 获取用户名并创建客户端线程 username = self.username_line.text() if username: client_thread = ClientThread(username, 'localhost', 12345) client_thread.update_signal.connect(self.update_list) # 绑定信号更新列表 client_thread.start() self.client_threads.append(client_thread) self.username_line.clear() # 清空输入框 def disconnect_all(self): # 停止所有客户端线程 for client_thread in self.client_threads: client_thread.stop() self.client_threads = [] # 清空线程列表 self.list_widget.clear() # 清空列表控件 def update_list(self, data): # 更新列表控件 self.list_widget.addItem(data) def closeEvent(self, event): # 关闭窗口时停止所有客户端线程 for client_thread in self.client_threads: client_thread.stop() super().closeEvent(event) # 程序入口 if __name__ == '__main__': app = QApplication(sys.argv) client = ClientWindow() client.show() sys.exit(app.exec_())
二.服务器端代码:
import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QVBoxLayout, QLineEdit, QPushButton, QWidget from PyQt5.QtCore import QTimer, QThread, pyqtSignal import socket import random import time # 定义一个客户端线程类,用于发送数据到服务器 class ClientThread(QThread): update_signal = pyqtSignal(str) # 定义一个信号,用于更新UI def __init__(self, username, host, port): super().__init__() self.username = username # 用户名 self.host = host # 服务器地址 self.port = port # 服务器端口 self.running = True # 线程运行状态 def run(self): # 创建socket连接 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((self.host, self.port)) # 连接服务器 while self.running: temperature = random.uniform(20, 30) # 生成随机温度 humidity = random.uniform(40, 60) # 生成随机湿度 data = f"{self.username}: Temperature={temperature}, Humidity={humidity}" s.sendall(data.encode()) # 发送数据 self.update_signal.emit(data) # 发送信号更新UI time.sleep(2) # 休眠2秒 def stop(self): self.running = False # 停止线程 # 定义客户端主窗口类 class ClientWindow(QMainWindow): def __init__(self): super().__init__() self.initUI() self.client_threads = [] # 存储客户端线程列表 def initUI(self): # 设置窗口标题和大小 self.setWindowTitle('Sensor Data Client') self.setGeometry(100, 100, 400, 300) # 创建列表控件、用户名输入框、连接按钮和下线按钮 self.list_widget = QListWidget() self.username_line = QLineEdit(self) self.username_line.setPlaceholderText('Enter your username') self.connect_button = QPushButton('Connect', self) self.connect_button.clicked.connect(self.connect_to_server) # 绑定按钮点击事件 self.disconnect_button = QPushButton('Disconnect All', self) self.disconnect_button.clicked.connect(self.disconnect_all) # 绑定按钮点击事件 # 布局管理 layout = QVBoxLayout() layout.addWidget(self.username_line) layout.addWidget(self.connect_button) layout.addWidget(self.disconnect_button) # 添加下线按钮 layout.addWidget(self.list_widget) # 设置中心窗口 central_widget = QWidget() central_widget.setLayout(layout) self.setCentralWidget(central_widget) def connect_to_server(self): # 获取用户名并创建客户端线程 username = self.username_line.text() if username: client_thread = ClientThread(username, 'localhost', 12345) client_thread.update_signal.connect(self.update_list) # 绑定信号更新列表 client_thread.start() self.client_threads.append(client_thread) self.username_line.clear() # 清空输入框 def disconnect_all(self): # 停止所有客户端线程 for client_thread in self.client_threads: client_thread.stop() self.client_threads = [] # 清空线程列表 self.list_widget.clear() # 清空列表控件 def update_list(self, data): # 更新列表控件 self.list_widget.addItem(data) def closeEvent(self, event): # 关闭窗口时停止所有客户端线程 for client_thread in self.client_threads: client_thread.stop() super().closeEvent(event) # 程序入口 if __name__ == '__main__': app = QApplication(sys.argv) client = ClientWindow() client.show() sys.exit(app.exec_())
三. 运行效果: