题目:现场快递柜状态采集与控制系统目标:设计实现一个对现场快递柜状态数据采集、显示、参数设置、抽屉打开、保鲜控 制等功能软件系统。
编译环境: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(存放接收到的数据结构): 文章来源:https://www.toymoban.com/news/detail-537956.html
# 控制表状态,用字典储存各个信息,每读取一次更新一次
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
文章来源地址https://www.toymoban.com/news/detail-537956.html
到了这里,关于现场快递柜状态采集与控制系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!