现场快递柜状态采集与控制系统

题目:现场快递柜状态采集与控制系统
目标:设计实现一个对现场快递柜状态数据采集、显示、参数设置、抽屉打开、保鲜控 制等功能软件系统。

编译环境:Vscode

程序语言:python


UI界面截图:

运行状态截图:

温度曲线截图:

 Server.py(主程序代码):

from PySide2.QtCore import QFile    # 导入文件类
from PySide2.QtWidgets import QApplication  # 导入QtWidgets模块
from PySide2.QtUiTools import QUiLoader  # 加载UI文件
import threading  # 线程模块
import serial  # 导入串口模块
from ControlTable import *  # 导入控制表类(用户)
from send import *  # 导入发送模块(用户)
from receive import *  # 导入接收模块(用户)

# 配置串口
portx = "COM2"
bps = 38400
# 超时设置,None:永远等待操作,0为立即返回请求结果,其他值为等待超时时间(单位为秒)
timex = None
ser = serial.Serial(portx, bps, timeout=timex)  # 开启串口通信


class Software:

    def __init__(self):
        # 从文件中加载UI定义
        qfile_Server = QFile("UI/software.ui")
        qfile_Server.open(QFile.ReadOnly)
        qfile_Server.close()
        # 从 UI 定义中动态 创建一个相应的窗口对象
        self.ui = QUiLoader().load(qfile_Server)
        self.ui.Shezhiwendu.setPlaceholderText('请输入温度')
        self.ui.openButton.clicked.connect(self.open)
        self.ui.closeButton.clicked.connect(self.close)
        self.ui.yaStateSet.clicked.connect(self.CompressorSet)
        self.ui.drawer1.clicked.connect(lambda: self.drawer(1))
        self.ui.drawer2.clicked.connect(lambda: self.drawer(2))
        self.ui.drawer3.clicked.connect(lambda: self.drawer(3))
        self.ui.drawer4.clicked.connect(lambda: self.drawer(4))
        self.ui.drawer5.clicked.connect(lambda: self.drawer(5))
        self.ui.drawer6.clicked.connect(lambda: self.drawer(6))
        self.ui.drawer7.clicked.connect(lambda: self.drawer(7))
        self.ui.drawer8.clicked.connect(lambda: self.drawer(8))
        self.ui.drawer9.clicked.connect(lambda: self.drawer(9))
        self.ui.drawer10.clicked.connect(lambda: self.drawer(10))
        self.ui.Shezhiwendu.returnPressed.connect(self.TemptureSet)
        self.ui.setAttritutes.clicked.connect(self.SendAttributes)
        #  定义初始状态参数
        self.yastate = False
        self.drawerStates = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    def open(self):  # 开机
        self.ui.systemstate.setText('     ' + "运行")
        self.ui.openButton.setEnabled(False)
        self.ui.closeButton.setEnabled(True)
        self.ui.Shezhiwendu.setEnabled(True)
        self.ui.yastate.setEnabled(True)
        self.ui.drawer1.setEnabled(True)
        self.ui.drawer2.setEnabled(True)
        self.ui.drawer3.setEnabled(True)
        self.ui.drawer4.setEnabled(True)
        self.ui.drawer5.setEnabled(True)
        self.ui.drawer6.setEnabled(True)
        self.ui.drawer7.setEnabled(True)
        self.ui.drawer8.setEnabled(True)
        self.ui.drawer9.setEnabled(True)
        self.ui.drawer10.setEnabled(True)

        self.ui.DeviceID.setEnabled(True)
        self.ui.DeviceAddress.setEnabled(True)
        self.ui.readInterpret.setEnabled(True)
        self.ui.ComDelay.setEnabled(True)
        self.ui.temptureSet.setEnabled(True)
        self.ui.temptureError.setEnabled(True)
        self.ui.setAttritutes.setEnabled(True)
        self.ui.yaStateSet.setEnabled(True)

    def close(self):  # 关机
        self.ui.Shezhiwendu.clear()
        self.ui.Shezhiwendu.setEnabled(False)
        self.ui.yastate.setEnabled(False)
        self.ui.Currentwendu.setText("")  # 将文本框置空
        self.ui.yastate.setText("")  # 将文本框置空
        self.ui.systemstate.setText("")  # 将文本框置空
        self.ui.DeviceID.setText("")  # 将文本框置空
        self.ui.temptureSet.setText("")  # 将文本框置空
        self.ui.openButton.setEnabled(True)
        self.ui.closeButton.setEnabled(False)
        # 将文本框置空
        self.ui.drawer1.setText("")
        self.ui.drawer2.setText("")
        self.ui.drawer3.setText("")
        self.ui.drawer4.setText("")
        self.ui.drawer5.setText("")
        self.ui.drawer6.setText("")
        self.ui.drawer7.setText("")
        self.ui.drawer8.setText("")
        self.ui.drawer9.setText("")
        self.ui.drawer10.setText("")
        self.ui.drawer1.setEnabled(False)
        self.ui.drawer2.setEnabled(False)
        self.ui.drawer3.setEnabled(False)
        self.ui.drawer4.setEnabled(False)
        self.ui.drawer5.setEnabled(False)
        self.ui.drawer6.setEnabled(False)
        self.ui.drawer7.setEnabled(False)
        self.ui.drawer8.setEnabled(False)
        self.ui.drawer9.setEnabled(False)
        self.ui.drawer10.setEnabled(False)
        self.ui.DeviceID.setEnabled(False)
        self.ui.DeviceAddress.setEnabled(False)
        self.ui.readInterpret.setEnabled(False)
        self.ui.ComDelay.setEnabled(False)
        self.ui.temptureSet.setEnabled(False)
        self.ui.temptureError.setEnabled(False)
        self.ui.setAttritutes.setEnabled(False)
        self.ui.yaStateSet.setEnabled(False)

    def drawer(self, i):   # 开关抽屉控制
        drawerStates_Send = self.drawerStates
        if i == 1:
            if self.drawerStates[0] == 0:
                drawerStates_Send[0] = 1
            else:
                drawerStates_Send[0] = 0
        elif i == 2:
            if self.drawerStates[1] == 0:
                drawerStates_Send[1] = 1
            else:
                drawerStates_Send[1] = 0
        elif i == 3:
            if self.drawerStates[2] == 0:
                drawerStates_Send[2] = 1
            else:
                drawerStates_Send[2] = 0
        elif i == 4:
            if self.drawerStates[3] == 0:
                drawerStates_Send[3] = 1
            else:
                drawerStates_Send[3] = 0
        elif i == 5:
            if self.drawerStates[4] == 0:
                drawerStates_Send[4] = 1
            else:
                drawerStates_Send[4] = 0
        elif i == 6:
            if self.drawerStates[5] == 0:
                drawerStates_Send[5] = 1
            else:
                drawerStates_Send[5] = 0
        elif i == 7:
            if self.drawerStates[6] == 0:
                drawerStates_Send[6] = 1
            else:
                drawerStates_Send[6] = 0
        elif i == 8:
            if self.drawerStates[7] == 0:
                drawerStates_Send[7] = 1
            else:
                drawerStates_Send[7] = 0
        elif i == 9:
            if self.drawerStates[8] == 0:
                drawerStates_Send[8] = 1
            else:
                drawerStates_Send[8] = 0
        elif i == 10:
            if self.drawerStates[9] == 0:
                drawerStates_Send[9] = 1
            else:
                drawerStates_Send[9] = 0
        drawControlFrame_Send(ser, 0x3f, drawerStates_Send)

    def TemptureSet(self):  # 设置温度
        tempture = self.ui.Shezhiwendu.text()
        if tempture != "":
            address = ControlTableAttributes['Address']
            setTempFrame_Send(ser, address, int(tempture))

    def CompressorSet(self):  # 压缩机控制按钮
        if self.yastate == False:
            self.yastate = True
            self.ui.signal.setText(
                "The start compressor command has been send")
        else:
            self.yastate = False
            self.ui.signal.setText(
                "The stop compressor command has been send")

    def SendAttributes(self):  # 发送设置的属性
        if self.ui.DeviceID.text() != "" and self.ui.temptureSet.text() != "":
            Attributes = {
                'DeviceID': int(self.ui.DeviceID.text(), 16).to_bytes(5, 'big'),
                'Address': int(self.ui.DeviceAddress.currentText()),
                'StateUpload': int(self.ui.readInterpret.currentText()),
                'CompressorStartDelay': int(self.ui.ComDelay.currentText()),
                'tempture_set': tempture_send(int(self.ui.temptureSet.text())),
                'temptureError': int(self.ui.temptureError.currentText()),
            }
            print(Attributes)
            setParaFrame_Send(ser, Attributes)
            self.ui.signal.setText("")
        else:   # 如果未填写好
            self.ui.signal.setText("Please enter DeviceID and tempture of set")


# 实例化窗口程序
app = QApplication([])
software = Software()
software.ui.show()
# 设置接受数据守护线程
receivethread = threading.Thread(
    target=lambda: Receive(software, ser))
receivethread.setDaemon(True)
receivethread.start()
# 循环执行
app.exec_()

Receive.py(接收模块) :

from ControlTable import *  # 导入控制表数据结构(用户)
import datetime  # 计算时间
import chart    # 导入图像模块(用户)
from send import compressorControlFrame_Send  # 字节流处理(用户)


TemptureList = []  # 存储温度列表
DateTimeList = []  # 时间轴更新


def Receive(software, ser):  # 接收线程
    while(True):
        if software.ui.closeButton.isEnabled():
            Frame = ser.read(80)    # 接收80B数据
            resultFrame = ReceiveFrame(Frame)  # 将接收到的数据转换为常量字节流
            for result in resultFrame:
                if result[2] == 0x2C:   # 接收到的帧为控制表状态帧
                    # 保存接收到的数据
                    ControlTableAttributes_set(
                        result[6:11], result[11], result[13], result[14], result[17], result[18])
                    ControlTableStatus_set(
                        result[24:29], result[29], result[31], result[32], result[33], result[36:38])

                    tempture = temptureReceive()  # 计算接收到的温度并绘制图像

                    if software.yastate == True:    # 如果启动了压缩机
                        # 设置温度计算
                        temptureSet = '{:0>8}'.format(
                            str(bin(ControlTableStatus['tempture_set']))[2:])
                        if(temptureSet[0] == '0'):
                            temptureSet = str(
                                int(temptureSet[1:7], 2)+1/2*int(temptureSet[7], 2))
                        else:
                            temptureSet = '-' + str(
                                int(temptureSet[1:7], 2)+1/2*int(temptureSet[7], 2))
                        # 判断当前温度,调控压缩机开关
                        if float(tempture) <= float(temptureSet) - ControlTableAttributes['tempture_controlerror'] + 1:
                            compressorControlFrame_Send(ser, 0x3f, False)
                        if float(tempture) >= float(temptureSet) + ControlTableAttributes['tempture_controlerror'] - 1:
                            compressorControlFrame_Send(ser, 0x3f, True)
                    else:   # 如果没有启动压缩机
                        compressorControlFrame_Send(ser, 0x3f, False)

                    Lock = LockReceive()  # 计算Lock状态
                    UISoftwareUpdate(software, Lock, tempture)  # UI更新


def temptureReceive():  # 计算接收到的温度并绘制图像
    tempture = '{:0>8}'.format(
        str(bin(ControlTableStatus['tempture_current']))[2:])
    if(tempture[0] == '0'):
        tempture = str(
            int(tempture[1:7], 2)+1/2*int(tempture[7], 2))
    else:
        tempture = '-' + str(
            int(tempture[1:7], 2)+1/2*int(tempture[7], 2))

    DateTimeList.append(
        datetime.datetime.now().strftime("%H:%M:%S"))
    TemptureList.append(float(tempture))
    chart.Scatter(DateTimeList, TemptureList, '#abddff')

    return tempture


def LockReceive():  # 计算Lock状态
    return '{:0>8}'.format(
        str(bin(ControlTableStatus['LockState'][0]))[2:])[::-1] + '{:0>8}'.format(
            str(bin(ControlTableStatus['LockState'][1]))[2:4])[::-1] + '{:0>8}'.format(
        str(bin(ControlTableStatus['LockState'][1]))[4:])


def UISoftwareUpdate(software, Lock, tempture):  # 更新UI界面
    if software.ui.closeButton.isEnabled():
        drawerStateReceived(software, Lock)
        software.ui.yastate.setText('     ' + CompressorState_show(
            ControlTableStatus['CompressorState']))
        software.ui.Currentwendu.setText(tempture + "℃")


def drawerStateReceived(software, Lock):  # 根据Lock字段显示各个抽屉的状态
    if Lock[0] == '1':
        software.drawerStates[0] = 1
        software.ui.drawer1.setText("开")
    else:
        software.drawerStates[0] = 0
        software.ui.drawer1.setText("关")
    if Lock[1] == '1':
        software.drawerStates[1] = 1
        software.ui.drawer2.setText("开")
    else:
        software.drawerStates[1] = 0
        software.ui.drawer2.setText("关")
    if Lock[2] == '1':
        software.drawerStates[2] = 1
        software.ui.drawer3.setText("开")
    else:
        software.drawerStates[2] = 0
        software.ui.drawer3.setText("关")
    if Lock[3] == '1':
        software.drawerStates[3] = 1
        software.ui.drawer4.setText("开")
    else:
        software.drawerStates[3] = 0
        software.ui.drawer4.setText("关")
    if Lock[4] == '1':
        software.drawerStates[4] = 1
        software.ui.drawer5.setText("开")
    else:
        software.drawerStates[4] = 0
        software.ui.drawer5.setText("关")
    if Lock[5] == '1':
        software.drawerStates[5] = 1
        software.ui.drawer6.setText("开")
    else:
        software.drawerStates[5] = 0
        software.ui.drawer6.setText("关")
    if Lock[6] == '1':
        software.drawerStates[6] = 1
        software.ui.drawer7.setText("开")
    else:
        software.drawerStates[6] = 0
        software.ui.drawer7.setText("关")
    if Lock[7] == '1':
        software.drawerStates[7] = 1
        software.ui.drawer8.setText("开")
    else:
        software.drawerStates[7] = 0
        software.ui.drawer8.setText("关")
    if Lock[8] == '1':
        software.drawerStates[8] = 1
        software.ui.drawer9.setText("开")
    else:
        software.drawerStates[8] = 0
        software.ui.drawer9.setText("关")
    if Lock[9] == '1':
        software.drawerStates[9] = 1
        software.ui.drawer10.setText("开")
    else:
        software.drawerStates[9] = 0
        software.ui.drawer10.setText("关")


def ReceiveFrame(hex_datas):  # 匹配帧头和帧尾,获取匹配成功的帧
    resultFrame = []
    for i in range(0, len(hex_datas)-1):
        if hex_datas[i] == 0xFF and hex_datas[i+1] == 0xFF:
            for j in range(i, len(hex_datas)-1):
                if hex_datas[j] == 0xFF and hex_datas[j+1] == 0xF7:
                    result = hex_datas[i:j+2]
                    if len(result) == 14 or len(result) == 44:
                        resultFrame.append(result)
    return resultFrame  # 返回匹配成功的帧(列表)

chart.py(绘图模块):

import pyecharts.options as opts  # 绘图
from pyecharts.charts import Line   # 导入Line模块


def Scatter(time, tempture, colour):  # 绘制折线图html文件
    time = time[-10:]   # 取time末尾10位
    tempture = tempture[-10:]  # 取温度末尾10位
    c = (
        Line(init_opts=opts.InitOpts(
            width="900px",
            height="600px",
        )).add_xaxis(time).add_yaxis('温度/°C', tempture).set_colors(colour).set_global_opts(
            title_opts=opts.TitleOpts(title="温度变化"),
            yaxis_opts=opts.AxisOpts(name="温度°C"),
            xaxis_opts=opts.AxisOpts(name="time"))
    ).render("html\chart.html")  # 生成折线图html文件

send.py(发送模块):

import packet  # 封装成帧格式

# 启停压缩机控制帧发送


def compressorControlFrame_Send(ser, address, comOn):
    result = ser.write(packet.compressorControlFrame(address, comOn))

# 开锁帧发送


def drawControlFrame_Send(ser, address, draw):
    result = ser.write(packet.drawControlFrame(address, draw))

# 查询帧发送


def searchFrame_Send(ser, address):
    result = ser.write(packet.searchFrame(address))

# 设置温度帧发送


def setTempFrame_Send(ser, address, temp):
    temp = tempture_send(temp)
    result = ser.write(packet.setTempFrame(address, temp))

# 设置参数帧发送


def setParaFrame_Send(ser, Attributes):
    result = ser.write(packet.setParaFrame(Attributes))

# 设置温度转换成发送格式


def tempture_send(tempture):
    tempture = float(tempture)  # 将温度转化为浮点型
    s = str(tempture).split('.')  # 再转化为字符串型,以.分割列表
    tempture = str(int(int(s[0]) < 0)) + \
        '{:0>6}'.format(
            str(bin(int(s[0]))[2:])) + str(int(int(s[1]) != 0))  # 填充左边数字补零,宽度为6 将温度转换为十六进制数
    tempture = int(tempture, 2)
    return tempture

packet.py (封装成帧 ):

import struct  # 导入封装成帧模块
frameNum = 0  # 帧号

# 封装成帧


def DataFrame(address, funNum, data):
    length = len(data) + 10
    crc_frame = calc_crc(struct.pack(
        '4B', length, frameNum, address, funNum) + data)
    return struct.pack('6B', eval('0xFF'), eval('0xFF'), length, frameNum,
                       address, funNum) + data + struct.pack('H', crc_frame) + struct.pack('2B', eval('0xFF'), eval('0xF7'))

# 解封装


def DataFrame_unpack(frame):
    return struct.unpack('6B', frame[0:6]) + struct.unpack(str(len(frame[6:-2])) + 'B', frame[6:-2]) + struct.unpack('2B', frame[-2:])

# (1)查询帧(10Byte)


def searchFrame(address):  # 设备地址(16进制)
    global frameNum
    frameNum = (frameNum + 1) % 255
    Data = DataFrame(address, 1, b'')
    return Data

# (2)启停压缩机控制帧(11Byte)


def compressorControlFrame(address, comOn):  # 设备地址(16进制),是否启动压缩机(bool)
    global frameNum
    frameNum = (frameNum + 1) % 255
    if comOn:
        Data = DataFrame(address, 2, b'\x01')  # 数据1,启动
    else:
        Data = DataFrame(address, 2, b'\x00')  # 数据0,停止
    return Data

# (3)开锁帧(12Byte)


def drawControlFrame(address, draw):  # 设备地址(16进制),抽屉状态(元组)
    global frameNum
    b1 = 0
    b2 = 0
    frameNum = (frameNum + 1) % 255
    # 处理第一个字节
    for i in range(8):
        b1 += draw[i]*2**i
    # 处理第二个字节
    for i in [8, 9]:
        b2 += draw[i]*2**(i-8)

    Data = DataFrame(address, 3, b1.to_bytes(
        1, 'big')+b2.to_bytes(1, 'big'))  # 7
    return Data

# (4)设置温度帧(11Byte)


def setTempFrame(address, temp):  # 设备地址(16进制),设置温度(10进制)
    global frameNum
    frameNum = (frameNum + 1) % 255
    Data = DataFrame(address, 4, temp.to_bytes(1, 'big'))
    return Data

# (5)设置参数帧(28Byte)


def setParaFrame(para):
    global frameNum
    frameNum = (frameNum + 1) % 255
    data = para['DeviceID']+para['Address'].to_bytes(1, 'big')+b'\x00'+para['StateUpload'].to_bytes(1, 'big')+para['CompressorStartDelay'].to_bytes(
        1, 'big')+b'\x00\x00'+para['tempture_set'].to_bytes(1, 'big')+para['temptureError'].to_bytes(1, 'big')+b'\xff\xff\xff\xff\x00'
    Data = DataFrame(0x7f, 5, data)
    return Data

# (6)设置温度控制偏差帧(11Byte)


def setCtrlDeviationFrame(address, deviation):  # address是16进制,deviation是10进制
    global frameNum
    frameNum = (frameNum + 1) % 255
    Data = DataFrame(address, 6, deviation.to_bytes(1, 'big'))
    return Data

# (7)设置设备地址帧(16Byte)


def setDeviceAdressFrame(eqCodeing, newAddress):  # 形参均为16进制整型
    global frameNum
    frameNum = (frameNum + 1) % 255
    Data = DataFrame(0x7f, 9, eqCodeing.to_bytes(
        5, 'big')+newAddress.to_bytes(1, 'big'))
    return Data

# 循环冗余校验


def calc_crc(string):
    crc = 0xFFFF
    for pos in string:
        crc ^= pos
        for i in range(len(string)):
            if ((crc & 1) != 0):
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return eval(hex(((crc & 0xff) << 8) + (crc >> 8)))  # 返回CRC校验码(16进制字节流)

ControlTable.py(存放接收到的数据结构): 

# 控制表状态,用字典储存各个信息,每读取一次更新一次
ControlTableStatus = {
    'DeviceID': b'\xFF\xFF\xFF\xFF\xFF',  # 设备ID比特流
    'SystemState': 0x00,  # 二位十六进制数表示一个比特
    'CompressorState': 0x00,
    'tempture_set': 0x00,
    'tempture_current': 0x00,
    'LockState': b'\x00\x00',
}

# 控制表状态设置


def ControlTableStatus_set(DeviceID, SystemState, CompressorState, tempture_set, tempture_current, LockState):
    ControlTableStatus['DeviceID'] = DeviceID
    ControlTableStatus['SystemState'] = SystemState
    ControlTableStatus['CompressorState'] = CompressorState
    ControlTableStatus['tempture_set'] = tempture_set
    ControlTableStatus['tempture_current'] = tempture_current
    ControlTableStatus['LockState'] = LockState
    return ControlTableStatus

# 展示系统状态,用数字0,1,2分别表示三种状态


def SystemState_show(SystemState):
    if SystemState == 0:
        return "停止"
    elif SystemState == 1:
        return "预启动"
    elif SystemState == 2:
        return "运行"

# 展示压缩机状态,表示四种状态


def CompressorState_show(CompressorState):
    if CompressorState == 0:
        return "停止"
    elif CompressorState == 1:
        return "预启动"
    elif CompressorState == 2:
        return "运行"
    elif CompressorState == 3:
        return "故障"


# 控制表属性,同样用字典储存
ControlTableAttributes = {
    'DeviceID': b'\xFF\xFF\xFF\xFF\xFF',
    'Address': 0x01,
    'StateUpload': 0x01,
    'CompressorStartDelay': 0x1E,
    'tempture_set': 0x00,
    'tempture_controlerror': 0x02,
}

# 控制表属性设置


def ControlTableAttributes_set(DeviceID, Address, StateUpload, CompressorStartDelay, tempture_set, tempture_controlerror):
    ControlTableAttributes['DeviceID'] = DeviceID
    ControlTableAttributes['Address'] = Address
    ControlTableAttributes['StateUpload'] = StateUpload
    ControlTableAttributes['CompressorStartDelay'] = CompressorStartDelay
    ControlTableAttributes['tempture_set'] = tempture_set
    ControlTableAttributes['tempture_controlerror'] = tempture_controlerror
    return ControlTableAttributes

  

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I_love_hanser_QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值