Python 实现上位机(三)

目的

记录并分享一个用 python 实现上位机功能的思路与模板。将通过三个方面分享: 

  1. Python 自定义简易通信规约,并实现 Server 端
  2. Python 实现 Client 端
  3. PyQt5 绘制上位机界面,通过上位机作为 Client 访问 Server 端

本章将用 PyQt5 实现上位机 GUI。

PyQt5Designer 绘制 UI

上位机通过 IP 地址以及 TCP 端口号连接到 Server 端。

IP 地址和 TCP 端口号分别用 QLineEdit 输入,连接按钮为一个 QPushButton。

连接成功之后,通过 GUI 发送 读/写 命令到指定的地址。 

读/写命令通过 QComboBox 选择。地址,数据通过 QLineEdit 输入。读的结果显示在一个 read-only 的 QLineEdit 中。发送按钮为一个 QPushButton。

pyuic5 工具将 ui 文件转换为 python code

ui 文件保存为 host.ui。通过 pyuic5 工具将 host.ui 转换为 ui_host.py。打开一个 Terminal 运行:

pyuic5 -o ui_host.py host.ui

ui 文件中的 main-window 对应生成为一个名为 Ui_MainWindow 的类。main-window 下的每一个 widget 都对应 Ui_MainWindow Class 下的一个成员。

python 完善 GUI 后端功能

from PyQt5 import QtWidgets
from ui_host import Ui_MainWindow

#region hostUI
class hostUI(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.setupUi(self)
        self.host = None
        # init
        self.setBaseSize(1000,500)
        self.cmd_change()
        self.resultLine.setEnabled(False)
        # slots
        self.connectButton.clicked.connect(self.connect_to_server)
        self.sendButton.clicked.connect(self.send_to_server)
        self.commandList.currentIndexChanged.connect(self.cmd_change)

    def connect_to_server(self):
        # connect to dummy server
        try:
            if self.connectButton.text().upper()=='CONNECT':
                ip = self.serverIp.text()
                port = int(self.serverPort.text())
                self.host = hostAPI(ip, port)
                self.connectButton.setText("disconnect")
            else:
                self.host.channel.close()
                self.connectButton.setText("connect")
        except Exception as err:
            logger.debug(f'[error   :] {err}')
            logger.debug(f'\n[ERR INFO:]\n{format_exc()}\n[END OF ERROR]\n')
    
    def send_to_server(self):
        # send message to dummy server
        try:
            cmd = self.commandList.currentText()
            # check read or write
            fc = 0x03 if cmd.upper()=='READ' else 0x06
            addr = int(self.address.text())
            # read message, data line is disabled
            data = 0 if fc==0x03 else int(self.dataLine.text())
            sMsg = self.host.generate_message(fc, addr, data)
            self.host.send_message(sMsg)
            rMsg = self.host.recv_message()
            if rMsg is None:
                raise Exception("Received None from host")
            _, _, res = self.host.parse(rMsg)
            self.resultLine.setText(str(res))
        except Exception as err:
            logger.debug(f'[error   :] {err}')
            logger.debug(f'\n[ERR INFO:]\n{format_exc()}\n[END OF ERROR]\n')

    def cmd_change(self):
        self.dataLine.setText('')
        self.resultLine.setText('')
        if self.commandList.currentText().upper()=='READ':
            self.dataLine.setEnabled(False)
        elif self.commandList.currentText().upper()=='WRITE':
            self.dataLine.setEnabled(True)
        else:
            logger.debug('unknown command text')

#endregion

主入口 __name__ == '__main__':

if __name__ == '__main__':
    # launch host UI
    app = QtWidgets.QApplication(sys.argv)
    win = hostUI()
    win.show()
    sys.exit(app.exec_())

运行与调试

首先启动 server 端:

cd ./dummyCom
python ./dummyCom.py

运行 Client 端 GUI:

python host.py

运行结果: 

连接到 127.0.0.1:2333

 

读取地址 120 数据 

写地址 120 数据为 240

再次读取 120 地址数据。数据变为 240,写入成功。

 

 附:host.py 完整源码:

'''
@auther:    echen.hu
@email:     echen.hu@163.com

This script defines a template for a host machine, which
can communicate with client machines through some kind of
protocol over ethernet, serial etc.
'''
import logging
import datetime
import socket
import sys
import os
from time import sleep
from traceback import format_exc
from dummyCom.dummyCom import dummyComStack, bprint
from PyQt5 import QtWidgets
from ui_host import Ui_MainWindow

#region LOGGING
LOGPATH = 'C:\\ProgramData\\pyHost'

if not os.path.exists(LOGPATH):
    os.makedirs(LOGPATH)

timeStamp = datetime.datetime.strftime(datetime.datetime.now(), '%Y_%m_%d_%H_%M_%S') 

logger = logging.getLogger(__name__)  
logger.setLevel(logging.DEBUG)
fileHandler = logging.FileHandler(f"{LOGPATH}\\log_{timeStamp}.log")
consoleHandler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(asctime)s : %(name)s  : %(funcName)s : %(levelname)s : %(message)s')
fileHandler.setFormatter(formatter)
consoleHandler.setFormatter(formatter)
logger.addHandler(fileHandler)
logger.addHandler(consoleHandler)
#endregion

#region HOSTAPI
class hostAPI(object):
    def __init__(self, *comParams):
        # define a communication channel
        # TCP communication is taken in the template
        self.channel = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.channel.settimeout(3)
        self.stack = dummyComStack()
        try:
            self.channel.connect(comParams)
            logger.debug(f'HOST connected: {comParams}')
        except Exception as err:
            logger.debug(f'HOST cannot connect: {comParams}')
            logger.debug(f'Exeption: {err}')
            logger.debug(f'{format_exc}')
            self.channel = None

    def parse(self, msg: bytes):
        '''
        @params message
        @return function_code, address, data
        '''
        return self.stack.parse_msg(msg)

    def generate_message(self, fc: int, addr: int, data: int):
        '''
        @params function_code, address, data
        @return message
        '''
        return self.stack.generate_msg(fc, addr, data)
    
    def send_message(self, msg: bytes):
        '''
        @params message
        @return send_res
        '''
        try:
            self.channel.sendall(msg) # type:ignore
            logger.debug(f'[send --->] {bprint(msg)}')
            return True
        except Exception as err:
            logger.debug(f'[error   :] {err}')
            logger.debug(f'\n[ERR INFO:]\n{format_exc()}\n[END OF ERROR]\n')
            return None

    def recv_message(self):
        '''
        @return message
        '''
        try:
            rMsg = self.channel.recv(1024)
            logger.debug(f'[recv <---] {bprint(rMsg)}')
            return rMsg # type:ignore
        except Exception as err:
            logger.debug(f'[error   :] {err}')
            logger.debug(f'\n[ERR INFO:]\n{format_exc()}\n[END OF ERROR]\n')
            return None
#endregion

#region hostUI
class hostUI(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.setupUi(self)
        self.host = None
        # init
        self.setBaseSize(1000,500)
        self.cmd_change()
        self.resultLine.setEnabled(False)
        # slots
        self.connectButton.clicked.connect(self.connect_to_server)
        self.sendButton.clicked.connect(self.send_to_server)
        self.commandList.currentIndexChanged.connect(self.cmd_change)

    def connect_to_server(self):
        # connect to dummy server
        try:
            if self.connectButton.text().upper()=='CONNECT':
                ip = self.serverIp.text()
                port = int(self.serverPort.text())
                self.host = hostAPI(ip, port)
                self.connectButton.setText("disconnect")
            else:
                self.host.channel.close()
                self.connectButton.setText("connect")
        except Exception as err:
            logger.debug(f'[error   :] {err}')
            logger.debug(f'\n[ERR INFO:]\n{format_exc()}\n[END OF ERROR]\n')
    
    def send_to_server(self):
        # send message to dummy server
        try:
            cmd = self.commandList.currentText()
            # check read or write
            fc = 0x03 if cmd.upper()=='READ' else 0x06
            addr = int(self.address.text())
            # read message, data line is disabled
            data = 0 if fc==0x03 else int(self.dataLine.text())
            sMsg = self.host.generate_message(fc, addr, data)
            self.host.send_message(sMsg)
            rMsg = self.host.recv_message()
            if rMsg is None:
                raise Exception("Received None from host")
            _, _, res = self.host.parse(rMsg)
            self.resultLine.setText(str(res))
        except Exception as err:
            logger.debug(f'[error   :] {err}')
            logger.debug(f'\n[ERR INFO:]\n{format_exc()}\n[END OF ERROR]\n')

    def cmd_change(self):
        self.dataLine.setText('')
        self.resultLine.setText('')
        if self.commandList.currentText().upper()=='READ':
            self.dataLine.setEnabled(False)
        elif self.commandList.currentText().upper()=='WRITE':
            self.dataLine.setEnabled(True)
        else:
            logger.debug('unknown command text')

#endregion

if __name__ == '__main__':
    # launch host UI
    app = QtWidgets.QApplication(sys.argv)
    win = hostUI()
    win.show()
    sys.exit(app.exec_())

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于python的心电图信号设计模型结构完成5分类任务(源码+模型+项目说明).zip ## 1. 介绍 心血管疾病是指影响心脏和血管健康的各种疾病,包括冠心病、心肌病、心律失常等,是全球范围内的头号杀手。随着现代生活压力的不断增大,人们越来越关注心血管健康问题。传统的医生问诊方式存在误诊率高、诊断不及时等弊端,而心血管疾病发病急、宜早治疗的特点更加强调了诊断的准确性和及时性。许多心血管疾病的病发初期常伴有心律失常的出现,因此心律失常的检测对于心血管疾病的预防具有重要意义。目前,广泛应用的心律失常检测方法是心电(ElectroCardioGram,ECG) 信号诊断。 传统处理心电图信号分类问题的机器学习方法为自回归模型、支持向量机等。近年来,神经网络的兴起为心电图的识别提供了多种解决方案。其中,一种效果良好的方法为对信号进行滤波处理后使用循环神经网络(RNN)进行特征分析;此外,深度学习的兴起为心电图的识别提供了一种端到端的解决方案。研究指出,卷积神经网络(CNN)中每个卷积核相当于一个滤波器,能够从原始信号中提取出有用的信息,因此可以达到信号滤波同样的作用,与此同时同步进行特征提取,从而可以直接将原始心电数据输入到模型中进行分类。本实验受到以上两种广泛使用的网络结构的启发,提出了一种利用CNN作为滤波器和特征提取器、利用RNN处理前者提取特征的网络结构,兼具两者优点,并在计算时间、收敛速度、准确率上达到了极好的tradeoff。 ## 2. 实验目的 本实验旨在探索高效的深度学习算法,对人体心电信号进行分类,以判断被测试者心跳是否正常,或者患有何种心脏疾病。最终的目标是实现心电数据的5分类。通过这种方法,可以减少人工处理心电数据的工作量,提高诊断的准确性和效率。同时,该方法还可以为未来的心血管疾病预测和早期预防提供新的思路和方法。 ## 3. 实验原理和方法 ### 3.1 心电图和心率失常 心电图的原理基于心脏的电活动,即心脏肌肉在收缩和舒张过程中产生的微弱电信号。这些电信号可以通过在身体表面放置多个电极,从而形成导联,进而测量和记录心电图。心电图记录了心脏电活动在时间和振幅上的变化,提供了关于心脏节律、传导系统、心室和心房肥厚以及心肌缺血等方面的信息。 心电图波形主要包括P波、QRS波群和T波。P波代表心房的除极,QRS波群反映心室的除极,而T波表示心室的复极。这些波形的形态、幅度和间隔可以提供诸如心律、传导异常和心肌缺血等方面的信息。当发生心律失常时,心脏出现不规律跳动,心脏节律发生改变,心电图中监测到的电信号也会随之出现明显变化。 ### 3.2 小波变换 受采集环境的影响,ECG信号中通常存在许多干扰信号和基线漂移。为了获取精准的心脏活动信号,避免噪声干扰产生误判,我们通常需要对ECG信号进行滤波,常用手段为小波变换(wavelet transform)。 小波变换是一种经常用于分析非平稳时间序列数据并提取有意义和正确特征的方法。广泛使用的方法为离散小波变换(DWT)。DWT是一种通过串行数字滤波器传递信号来降低连续小波计算成本并得出系数的方法。这种分解方法也被称为多分辨率分析。这些滤波器组包括高通(HP)和低通(LP)滤波器,如方程(1)和(2)所示, $$ H=\sum_{k=-\infty}^{\infty}S[k]\varphi_{h}[2n-k] $$ $$ L=\sum_{k=-\infty}^{\infty}S[k]\varphi_{g}[2n-k] $$ 其中S为输入信号,H和L分别为高通和低通滤波器的输出,$φ_h$和$φ_g$分别为低通和高通滤波器。高通滤波器输出包括输入信号的细节系数(D),低通滤波器输出包含近似系数(A)。通过滤波器的信号进行下采样,以增加频率分辨率。下图中展示了这些分解过程逐级进行的过程。 首先,输入信号S经过高通和低通滤波器,以获得第一级的细节和近似系数。然后,获得的近似系数再经过滤波器以获得第二级的分量。这个过程一直重复,直到达到指定的级别。 <img src="./README.assets/Screenshot 2023-05-29 at 20.11.28.png" alt="Screenshot 2023-05-29 at 20.11.28" style="zoom:40%;" /> <img src="./README.assets/Screenshot 2023-05-2
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值