From 06a541bbbb321535a4f0b3d251235374278b0416 Mon Sep 17 00:00:00 2001 From: "lxbpxylps@126.com" Date: Sat, 30 Oct 2021 16:37:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=9B=B2=E7=BA=BF=E7=BB=98?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aboutwindow.py | 25 ++++++++ data.json | 24 +------ drawwindow.py | 143 +++++++++++++++++++++++++++++++++++++++++ gml.png | Bin 0 -> 5623 bytes index.html | 71 +++++++++++++++++++++ main.py | 163 ++--------------------------------------------- mainwindow.py | 161 ++++++++++++++++++++++++++++++++++++++++++++++ serialhandler.py | 80 +++++++++++++++++++++++ server.bat | 1 + 9 files changed, 489 insertions(+), 179 deletions(-) create mode 100644 aboutwindow.py create mode 100644 drawwindow.py create mode 100644 gml.png create mode 100644 index.html create mode 100644 mainwindow.py create mode 100644 serialhandler.py create mode 100644 server.bat diff --git a/aboutwindow.py b/aboutwindow.py new file mode 100644 index 0000000..47c7505 --- /dev/null +++ b/aboutwindow.py @@ -0,0 +1,25 @@ +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtGui import * + + +class AboutWindow(QWidget): + def __init__(self, parent=None): + super(AboutWindow, self).__init__(parent) + + self.setWindowTitle('关于') + self.setWindowIcon(QIcon('gml.png')) + self.setStyleSheet("QTextBrowser{font-size: 12pt;}") + + self.textbrowser = QTextBrowser() + self.textbrowser.setHtml( + '
NodeHost

' + + '
' + + '本软件为分布式环境监测系统的上位机软件
' + + '由 GML-Group 制作' + ) + + layout = QHBoxLayout() + layout.addWidget(self.textbrowser) + self.setLayout(layout) + self.setWindowModality(Qt.ApplicationModal) diff --git a/data.json b/data.json index 858ebf6..9c5601a 100644 --- a/data.json +++ b/data.json @@ -1,23 +1 @@ -{ - "time": "2021-10-24 00:43:56", - "node2": { - "humi": 44.9, - "temp": 25.2, - "light": 360.8 - }, - "node3": { - "humi": 43.2, - "temp": 26.8, - "light": 25.8 - }, - "node4": { - "humi": 43.4, - "temp": 26.6, - "light": 25.8 - }, - "node5": { - "humi": 44.5, - "temp": 25.6, - "light": 30.0 - } -} \ No newline at end of file +{"time": "2021-10-30 16:25:15", "node2": {"humi": 40.9, "temp": 25.6, "light": 389.2}, "node3": {"humi": 44.5, "temp": 26.7, "light": 14.2}, "node4": {"humi": 46.3, "temp": 26.0, "light": 12.5}, "node5": {"humi": 46.5, "temp": 25.3, "light": 13.3}} \ No newline at end of file diff --git a/drawwindow.py b/drawwindow.py new file mode 100644 index 0000000..98868f3 --- /dev/null +++ b/drawwindow.py @@ -0,0 +1,143 @@ +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtChart import QChart, QChartView, QLineSeries, QDateTimeAxis, QValueAxis + + +class DrawWindow(QWidget): + def __init__(self): + super().__init__() + + self.setGeometry(200, 200, 600, 400) + self.setWindowIcon(QIcon("gml.png")) + + self.humi_series = QLineSeries() + self.temp_series = QLineSeries() + self.light_series = QLineSeries() + + ctime = QDateTime.currentDateTime() + + humi_chart = QChart() + humi_chart.setTheme(QChart.ChartThemeLight) + humi_chart.addSeries(self.humi_series) + humi_chart.setTitle("时间-湿度变化曲线") + + temp_chart = QChart() + temp_chart.setTheme(QChart.ChartThemeLight) + temp_chart.addSeries(self.temp_series) + temp_chart.setTitle("时间-温度变化曲线") + + light_chart = QChart() + light_chart.setTheme(QChart.ChartThemeLight) + light_chart.addSeries(self.light_series) + light_chart.setTitle("时间-光照度变化曲线") + + self.humi_dtaxisX = QDateTimeAxis() + self.temp_dtaxisX = QDateTimeAxis() + self.light_dtaxisX = QDateTimeAxis() + self.humi_vlaxisY = QValueAxis() + self.temp_vlaxisY = QValueAxis() + self.light_vlaxisY = QValueAxis() + self.humi_dtaxisX.setMin(ctime.addSecs(0)) + self.humi_dtaxisX.setMax(ctime.addSecs(-60)) + self.temp_dtaxisX.setMin(ctime.addSecs(0)) + self.temp_dtaxisX.setMax(ctime.addSecs(-60)) + self.light_dtaxisX.setMin(ctime.addSecs(0)) + self.light_dtaxisX.setMax(ctime.addSecs(-60)) + self.humi_vlaxisY.setMin(40) + self.humi_vlaxisY.setMax(50) + self.temp_vlaxisY.setMin(20) + self.temp_vlaxisY.setMax(30) + self.light_vlaxisY.setMin(0) + self.light_vlaxisY.setMax(100) + + self.humi_dtaxisX.setFormat("hh:mm:ss") + self.humi_dtaxisX.setTickCount(10) + self.temp_dtaxisX.setFormat("hh:mm:ss") + self.temp_dtaxisX.setTickCount(10) + self.light_dtaxisX.setFormat("hh:mm:ss") + self.light_dtaxisX.setTickCount(10) + self.humi_vlaxisY.setTickCount(10) + self.temp_vlaxisY.setTickCount(10) + self.light_vlaxisY.setTickCount(1) + + self.humi_dtaxisX.setTitleText("时间") + self.temp_dtaxisX.setTitleText("时间") + self.light_dtaxisX.setTitleText("时间") + self.humi_vlaxisY.setTitleText("湿度") + self.temp_vlaxisY.setTitleText("温度") + self.light_vlaxisY.setTitleText("光照度") + + self.humi_vlaxisY.setGridLineVisible(True) + self.humi_vlaxisY.setGridLineColor(Qt.gray) + self.temp_vlaxisY.setGridLineVisible(True) + self.temp_vlaxisY.setGridLineColor(Qt.gray) + self.light_vlaxisY.setGridLineVisible(True) + self.light_vlaxisY.setGridLineColor(Qt.gray) + self.humi_dtaxisX.setGridLineVisible(True) + self.humi_dtaxisX.setGridLineColor(Qt.gray) + self.temp_dtaxisX.setGridLineVisible(True) + self.temp_dtaxisX.setGridLineColor(Qt.gray) + self.light_dtaxisX.setGridLineVisible(True) + self.light_dtaxisX.setGridLineColor(Qt.gray) + + humi_chart.addAxis(self.humi_dtaxisX, Qt.AlignBottom) + humi_chart.addAxis(self.humi_vlaxisY, Qt.AlignLeft) + temp_chart.addAxis(self.temp_dtaxisX, Qt.AlignBottom) + temp_chart.addAxis(self.temp_vlaxisY, Qt.AlignLeft) + light_chart.addAxis(self.light_dtaxisX, Qt.AlignBottom) + light_chart.addAxis(self.light_vlaxisY, Qt.AlignLeft) + + self.humi_series.attachAxis(self.humi_dtaxisX) + self.humi_series.attachAxis(self.humi_vlaxisY) + self.temp_series.attachAxis(self.temp_dtaxisX) + self.temp_series.attachAxis(self.temp_vlaxisY) + self.light_series.attachAxis(self.light_dtaxisX) + self.light_series.attachAxis(self.light_vlaxisY) + + humi_chartview = QChartView(humi_chart) + temp_chartview = QChartView(temp_chart) + light_chartview = QChartView(light_chart) + + layout = QGridLayout() + layout.addWidget(humi_chartview, 1, 1) + layout.addWidget(temp_chartview, 1, 2) + layout.addWidget(light_chartview, 2, 1) + self.setLayout(layout) + + self.node_seq = 2 + + def start(self, node_seq: int): + self.setWindowTitle(f"节点{node_seq}数据监测图") + self.node_seq = node_seq + self.show() + + def addPoint(self, node_seq: int, node_data: dict): + + if self.node_seq == node_seq: + ctime = QDateTime.currentDateTime() + self.humi_dtaxisX.setMin(ctime.addSecs(-60)) + self.humi_dtaxisX.setMax(ctime.addSecs(0)) + self.temp_dtaxisX.setMin(ctime.addSecs(-60)) + self.temp_dtaxisX.setMax(ctime.addSecs(0)) + self.light_dtaxisX.setMin(ctime.addSecs(-60)) + self.light_dtaxisX.setMax(ctime.addSecs(0)) + + if self.humi_vlaxisY.max() - 2 < node_data['humi'] or self.humi_vlaxisY.min() + 2 > node_data['humi']: + self.humi_vlaxisY.setMin(node_data['humi'] * 0.8) + self.humi_vlaxisY.setMax(node_data['humi'] * 1.2) + + if self.temp_vlaxisY.max() - 2 < node_data['temp'] or self.temp_vlaxisY.min() + 2 > node_data['temp']: + self.temp_vlaxisY.setMin(node_data['temp'] * 0.8) + self.temp_vlaxisY.setMax(node_data['temp'] * 1.2) + + if self.light_vlaxisY.max() - 20 < node_data['light'] or self.light_vlaxisY.min() + 20 > node_data['light']: + self.light_vlaxisY.setMin(node_data['light'] * 0.8) + self.light_vlaxisY.setMax(node_data['light'] * 1.2) + + self.humi_series.append( + ctime.toMSecsSinceEpoch(), node_data['humi']) + self.temp_series.append( + ctime.toMSecsSinceEpoch(), node_data['temp']) + self.light_series.append( + ctime.toMSecsSinceEpoch(), node_data['light']) diff --git a/gml.png b/gml.png new file mode 100644 index 0000000000000000000000000000000000000000..433a34c034082033f23aed5355ef06817f3717a1 GIT binary patch literal 5623 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 + + + + + 分布式环境监测 + + + + + +
+

分布式环境监测数据页面

+

数据由网关向节点收集,上传至远程服务器

+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • + + +
+
+ + + \ No newline at end of file diff --git a/main.py b/main.py index 73e0bf1..7d34ad7 100644 --- a/main.py +++ b/main.py @@ -1,165 +1,16 @@ import sys -import time -import json +import ctypes -import serial -import serial.tools.list_ports from PyQt5.QtWidgets import * -from PyQt5.QtCore import * -from PyQt5.QtGui import * - -class MainWidget(QWidget): - def __init__(self, parent=None): - super(MainWidget, self).__init__(parent) - - self.setWindowTitle('Demo') - - self.label_node2 = QLabel( - '节点:2 湿度: % 温度: °C 光照度: lx ') - self.label_node3 = QLabel( - '节点:3 湿度: % 温度: °C 光照度: lx ', self) - self.label_node4 = QLabel( - '节点:4 湿度: % 温度: °C 光照度: lx ', self) - self.label_node5 = QLabel( - '节点:5 湿度: % 温度: °C 光照度: lx ', self) - - self.combobox = QComboBox() - - self.port_list = serial.tools.list_ports.comports() - for port in self.port_list: - self.combobox.addItem(port.description) - - self.btnStart = QPushButton('开始') - self.btnExit = QPushButton('退出') - self.btnScanPort = QPushButton('刷新串口') - - # 实例化多线程对象 - self.thread = Worker(self.port_list[0].name) - - # 把控件放置在栅格布局中 - layout = QGridLayout(self) - layout.addWidget(self.label_node2, 1, 1, 1, 4) - layout.addWidget(self.label_node3, 2, 1, 1, 4) - layout.addWidget(self.label_node4, 3, 1, 1, 4) - layout.addWidget(self.label_node5, 4, 1, 1, 4) - layout.addWidget(self.btnScanPort, 6, 1, 1, 1) - layout.addWidget(self.combobox, 6, 2, 1, 1) - layout.addWidget(self.btnStart, 6, 3, 1, 1) - layout.addWidget(self.btnExit, 6, 4, 1, 1) - - # 信号与槽函数的连接 - self.thread.sign_node.connect(self.slotUpdateNode) - self.combobox.currentIndexChanged.connect(self.slotSelectPort) - self.btnScanPort.clicked.connect(self.slotScanPort) - self.btnStart.clicked.connect(self.slotStart) - self.btnExit.clicked.connect(self.slotStop) - - def slotScanPort(self): - self.port_list = serial.tools.list_ports.comports() - self.combobox.clear() - for port in self.port_list: - self.combobox.addItem(port.description) - self.thread.port_name = self.port_list[0].name - - def slotSelectPort(self, index): - self.thread.port_name = self.port_list[index].name - - def slotUpdateNode(self, node_seq, node_str): - # 更新 - if (node_seq == 2): - self.label_node2.setText(node_str) - if (node_seq == 3): - self.label_node3.setText(node_str) - if (node_seq == 4): - self.label_node4.setText(node_str) - if (node_seq == 5): - self.label_node5.setText(node_str) - - def slotStart(self): - self.combobox.setEnabled(False) - self.btnScanPort.setEnabled(False) - self.btnStart.setEnabled(False) - self.btnExit.setEnabled(True) - self.thread.start() - - def slotStop(self): - self.btnExit.setEnabled(False) - del self.thread - sys.exit() - - -class Worker(QThread): - sign_node = pyqtSignal(int, str) - port_name: str - - def __init__(self, port_name, parent=None): - super(Worker, self).__init__(parent) - # 设置工作状态与初始num数值 - self.port_name = port_name - self.working = True - - def __del__(self): - # 线程状态改变与线程终止 - self.working = False - self.wait() - - def run(self): - last_str = '' - - self.com = serial.Serial(self.port_name, 115200) - - while self.working == True: - - serial_str = self.com.readline().decode('utf-8') - - if (serial_str.find('ata') != -1): - continue - - str_list = serial_str.split('&') # 分割字符串 - - if len(str_list) != 5: # 数据缺失 - print('数据缺失!') - continue - - if (serial_str != last_str): # 检测数据是否重复 - last_str = serial_str - - try: # 转换数据 - seq: int = int(str_list[0]) - humi: float = round(float(str_list[1]), 1) - temp: float = round(float(str_list[2]), 1) - light: float = round(float(str_list[3]), 1) - - except Exception: - print('数据错误!') - continue - - print(f"节点:{seq} 湿度:{humi}% 温度:{temp}°C 光照度:{light}lx") - - # 写入 json 文件 - data = json.loads( - open('./data.json', 'r', encoding='utf-8').read()) - - data['node' + str(seq)]['humi'] = humi - data['node' + str(seq)]['temp'] = temp - data['node' + str(seq)]['light'] = light - data['time'] = time.strftime( - '%Y-%m-%d %H:%M:%S', time.localtime(time.time())) - - with open('./data.json', 'w') as fp: - fp.write(json.dumps(data)) - fp.close() - - # 获取文本 - node_str = f"节点:{seq} 湿度:{humi}% 温度:{temp}°C 光照度:{light}lx" - # 发射信号 - self.sign_node.emit(seq, node_str) +from mainwindow import MainWindow if __name__ == '__main__': + # 防止系统显示为 Python 图标 + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("NoidHost") + app = QApplication(sys.argv) - app.setStyleSheet("QLabel{font-size: 18pt;}") - demo = MainWidget() - demo.show() + main_widget = MainWindow() + main_widget.show() sys.exit(app.exec_()) diff --git a/mainwindow.py b/mainwindow.py new file mode 100644 index 0000000..8adb1a6 --- /dev/null +++ b/mainwindow.py @@ -0,0 +1,161 @@ +import sys + +import serial +import serial.tools.list_ports +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtGui import * + +from drawwindow import DrawWindow +from aboutwindow import AboutWindow +from serialhandler import SerialHandler + + +class MainWindow(QMainWindow): + def __init__(self, parent=None): + super(MainWindow, self).__init__(parent) + + self.setWindowTitle('NoidHost') + self.setWindowIcon(QIcon('gml.png')) + + self.statusbar = self.statusBar() + self.statusbar.showMessage('等待连接') + + menubar = self.menuBar() + + # 菜单栏-文件 + exit_action = QAction('&退出', self) + exit_action.setShortcut('Ctrl+Q') + exit_action.setStatusTip('退出应用') + exit_action.triggered.connect(lambda: sys.exit()) + + menubar_file = menubar.addMenu('文件') + menubar_file.addAction(exit_action) + + # 菜单栏-绘图 + draw_node2_action = QAction('&节点2', self) + draw_node3_action = QAction('&节点3', self) + draw_node4_action = QAction('&节点4', self) + draw_node5_action = QAction('&节点5', self) + + self.node2_drawwindow = DrawWindow() + self.node3_drawwindow = DrawWindow() + self.node4_drawwindow = DrawWindow() + self.node5_drawwindow = DrawWindow() + + menubar_draw = menubar.addMenu('绘图') + menubar_draw.addAction(draw_node2_action) + menubar_draw.addAction(draw_node3_action) + menubar_draw.addAction(draw_node4_action) + menubar_draw.addAction(draw_node5_action) + + draw_node2_action.triggered.connect( + lambda: self.drawStart(2)) + draw_node3_action.triggered.connect( + lambda: self.drawStart(3)) + draw_node4_action.triggered.connect( + lambda: self.drawStart(4)) + draw_node5_action.triggered.connect( + lambda: self.drawStart(5)) + + # 菜单栏-关于 + self.aboutwindow = AboutWindow() + about_action = QAction('&关于', self) + about_action.setShortcut('Ctrl+A') + about_action.setStatusTip('关于') + about_action.triggered.connect(lambda: self.aboutwindow.show()) + + menubar_about = menubar.addMenu('关于') + menubar_about.addAction(about_action) + + self.label_node2 = QLabel( + '节点:2 湿度: % 温度: °C 光照度: lx ') + self.label_node3 = QLabel( + '节点:3 湿度: % 温度: °C 光照度: lx ', self) + self.label_node4 = QLabel( + '节点:4 湿度: % 温度: °C 光照度: lx ', self) + self.label_node5 = QLabel( + '节点:5 湿度: % 温度: °C 光照度: lx ', self) + + self.combobox = QComboBox() + + self.port_list = serial.tools.list_ports.comports() + for port in self.port_list: + self.combobox.addItem(port.description) + + self.btnStart = QPushButton('开始') + self.btnExit = QPushButton('退出') + self.btnScanPort = QPushButton('刷新串口') + + # 实例化多线程对象 + self.handler = SerialHandler(self.port_list[0].name) + + # 把控件放置在栅格布局中 + layout = QGridLayout() + layout.addWidget(self.label_node2, 1, 1, 1, 4) + layout.addWidget(self.label_node3, 2, 1, 1, 4) + layout.addWidget(self.label_node4, 3, 1, 1, 4) + layout.addWidget(self.label_node5, 4, 1, 1, 4) + layout.addWidget(self.btnScanPort, 5, 1, 1, 1) + layout.addWidget(self.combobox, 5, 2, 1, 1) + layout.addWidget(self.btnStart, 5, 3, 1, 1) + layout.addWidget(self.btnExit, 5, 4, 1, 1) + + self.widget = QWidget() + self.widget.setLayout(layout) + self.widget.setStyleSheet("QLabel{font-size: 18pt;}") + self.setCentralWidget(self.widget) + + # 信号与槽函数的连接 + self.handler.sign_node.connect(self.slotUpdateNode) + self.combobox.currentIndexChanged.connect(self.slotSelectPort) + self.btnScanPort.clicked.connect(self.slotScanPort) + self.btnStart.clicked.connect(self.slotStart) + self.btnExit.clicked.connect(lambda: sys.exit()) + + def slotScanPort(self): + self.port_list = serial.tools.list_ports.comports() + self.combobox.clear() + for port in self.port_list: + self.combobox.addItem(port.description) + self.handler.port_name = self.port_list[0].name + + def slotSelectPort(self, index): + self.handler.port_name = self.port_list[index].name + + def slotUpdateNode(self, node_seq: int, node_data: dict): + # 更新 + if (node_seq == 2): + self.label_node2.setText( + f"节点:2 湿度:{node_data['humi']}% 温度:{node_data['temp']}°C 光照度:{node_data['light']}lx") + if (node_seq == 3): + self.label_node3.setText( + f"节点:3 湿度:{node_data['humi']}% 温度:{node_data['temp']}°C 光照度:{node_data['light']}lx") + if (node_seq == 4): + self.label_node4.setText( + f"节点:4 湿度:{node_data['humi']}% 温度:{node_data['temp']}°C 光照度:{node_data['light']}lx") + if (node_seq == 5): + self.label_node5.setText( + f"节点:5 湿度:{node_data['humi']}% 温度:{node_data['temp']}°C 光照度:{node_data['light']}lx") + + def slotStart(self): + self.combobox.setEnabled(False) + self.btnScanPort.setEnabled(False) + self.btnStart.setEnabled(False) + self.btnExit.setEnabled(True) + self.statusbar.showMessage('已连接') + self.handler.start() + + def drawStart(self, node_seq: int): + if node_seq == 2: + self.handler.sign_node.connect(self.node2_drawwindow.addPoint) + self.node2_drawwindow.start(node_seq) + elif node_seq == 3: + self.handler.sign_node.connect(self.node3_drawwindow.addPoint) + self.node3_drawwindow.start(node_seq) + elif node_seq == 4: + self.handler.sign_node.connect(self.node4_drawwindow.addPoint) + self.node4_drawwindow.start(node_seq) + elif node_seq == 5: + self.handler.sign_node.connect(self.node5_drawwindow.addPoint) + self.node5_drawwindow.start(node_seq) diff --git a/serialhandler.py b/serialhandler.py new file mode 100644 index 0000000..d761717 --- /dev/null +++ b/serialhandler.py @@ -0,0 +1,80 @@ +import time +import json + +import serial +import serial.tools.list_ports +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtGui import * + + +class SerialHandler(QThread): + sign_node = pyqtSignal(int, dict) + port_name: str + + def __init__(self, port_name, parent=None): + super(SerialHandler, self).__init__(parent) + self.port_name = port_name + self.working = True + + def __del__(self): + # 线程状态改变与线程终止 + self.working = False + self.wait() + + def run(self): + last_str = '' # 记录上一次收到的字符串 + self.com = serial.Serial(self.port_name, 115200) + + while self.working == True: + + serial_str = self.com.readline().decode('utf-8') + str_list = serial_str.split('&') # 分割字符串 + + if len(str_list) != 5: # 数据缺失 + print('数据缺失!') + continue + + if (serial_str != last_str): # 检测数据是否重复 + last_str = serial_str + + try: # 转换数据 + seq: int = int(str_list[0]) + humi: float = round(float(str_list[1]), 1) + temp: float = round(float(str_list[2]), 1) + light: float = round(float(str_list[3]), 1) + + except Exception: + print('数据错误!') + continue + + # 检测数据合理性 + if humi > 100 or temp > 50 or light > 100000: + print('数据不合理!') + continue + + print(f"节点:{seq} 湿度:{humi}% 温度:{temp}°C 光照度:{light}lx") + + # 写入 json 文件 + data = json.loads( + open('./data.json', 'r', encoding='utf-8').read()) + + data['node' + str(seq)]['humi'] = humi + data['node' + str(seq)]['temp'] = temp + data['node' + str(seq)]['light'] = light + data['time'] = time.strftime( + '%Y-%m-%d %H:%M:%S', time.localtime(time.time())) + + with open('./data.json', 'w') as fp: + fp.write(json.dumps(data)) + fp.close() + + # 获取文本 + node_data = { + "seq": seq, + "humi": humi, + "temp": temp, + "light": light + } + # 发射信号 + self.sign_node.emit(seq, node_data) diff --git a/server.bat b/server.bat new file mode 100644 index 0000000..5080519 --- /dev/null +++ b/server.bat @@ -0,0 +1 @@ +python -m http.server 8520 \ No newline at end of file