感知与控制 课程设计 智能保鲜柜终端(Python实现)

江苏大学 物联网专业课程设计

最终实现代码仍有很多BUG未解决,但能实现课设要求的基本功能,此文章仅作参考。

一.前期准备

老师一般会给布三个必备的工具/软件:分别为【RCPET】【sscom32】和【VSPD 6.9】

1.1 RCPET

该软件是老师已写好的“快递柜模拟程序”。之后所写的Python程序主要就是与【RCPET】进行数据的交互。解压【RCPET.rar】点击【RCP_ET.exe】即可运行。

 1.2 sscom32

串口调试助手,前期可用于测试【模拟串口】是否能正常通信,解压点击【sscom5.13.1.exe】即可使用

1.3 VPSD

虚拟串口工具,用于在PC上创建虚拟串口对,若下载未破解版的VPSD,注意其有试用期


二.关键代码设计

注;该部分只给出关键代码的功能解释,可参照源码进行理解。

2.1【crc16_xmodem+get_crc_value】 CRC校验

         CRC校验码的生成与校对 【crc16_xmodem+get_crc_value】 此次串口通信收发的数据帧均要求实现CRC校验,只有CRC校验合法的数据帧才能进行进一步的分析处理。CRC校验函数的依据CRC中的“xmodem”规则编写,每次将数据帧中去除“信息头”、“校验”和“结束标志”的部分传入进行校验计算,计算结果与数据帧携带的“校验”部分相比较,若二者一致则表示该数据包CRC校验合法。

2.2【build_frame(length,function,data)】数据帧的构建

         该函数用于实现构建数据帧的功能。三个参数分别为:“length”为需构建数据帧的长度,需以16进制传入、“function”为需构建数据帧的功能号、“data”为需构建数据包的真正数据部分。

2.3 【recv_data()】数据帧实时接收

        该函数用于实现控制端实时接收快递柜端发送的各类帧。函数先将串口接收的数据进行CRC的合法性校验,校验通过后根据“上次状态帧”和“Ack帧”两种不同的帧类型执行不同的操作。同时根据接收的合法数据帧,对界面中的“.温度参数”、“抽屉状态”和“压缩机状态监”进行状态更新。

2.4【openBox()】 抽屉锁控

        函数主要实现控制端通过串口通信模拟远程控制抽屉的开关。对抽屉的控制具体转化为构建12Byte的“开锁帧”。锁控的开关通过button点击事件模拟,按下置1表示开,否则置0表示关。通过规定的编码方式[抽屉8+抽屉7+抽屉6+抽屉5+抽屉4+抽屉3+抽屉2+”000000”+抽屉10+抽屉9]将抽屉状态抽象为16位2进制数,进而转换为2B的16进制数据部分。最后调用build_lock_frame()函数将构建好的“开锁帧”发出,实现开关锁的控制。

2.5【paramControl()】设置设备参数

        该函数主要实现对快递柜各项参数的修改。修改项包括:“设备编码”、“设备地址”、“上传间隔”、“压缩机启动延时”、“设定温度”和“温度控制偏差”。通过读取子窗口各entry中数据后调用build_data_frame()函数构建好“设置参数帧”并发出,实现设备参数的设置。

2.6 【tempControl()】设置设定温度

      该函数主要实现设置快递柜的设定温度。通过读取子界面entry中的数值后调用build_temp_frame()函数构建好“设置温度帧”并发出,实现设定温度的设置。

2.7【pressorControl()】压缩机自动控制

        该函数主要实现压缩机的开启和自动控制。点击“压缩机控制”,压缩机的总开关被打开,依据课设指导书中的要求,压缩机打开后将实现温度自动控制。压缩机会将抽屉中的“采集温度”控制在“设定温度”±“控制偏差”的范围内。

2.8【pressorControl2()】压缩机关闭

       该函数主要实现关闭压缩机的总开关。点击“压缩机关闭”,无论此时压缩机处于何种状态,都将其关闭。只有重新点击“压缩机控制”按钮压缩机才会正常工作。

2.9【draw_temp()】温度曲线绘制

        该函数主要实现将串口实时读取的快递柜采集温度绘制成温度曲线,以便直观地观察快递柜的温度变化。主要通过Python的matplotlib.pyplot包中的散点图scatter实现。通过调整draw_temp()中两个for循环的参数可以调节每次绘制散点图的最大数量。


三.运行效果

3.1 数据帧接收

3.2 设置设参数

①点击“设置设备参数”;

②在“设置”窗口中输入需设置的数值后再点击“发送”;

③可在快递柜端“系统参数”一栏看到设备参数已修改成功。

3.3 抽屉的开关

初始化时抽屉状态:

 进行开关抽屉操作:

①.点击“打开抽屉”按钮;

②.在子窗口“抽屉控制开关”中勾选想要打开的抽屉,并点击发送;

③.快递柜端抽屉状态随②中勾选的选项进行改变;

④.终端根据快递柜回送的Ack帧更新抽屉面板状态

3.4 设置设定温度

①.点击“设置温度”按钮;

②.在“温度控制”子窗口输入想要设置的温度,点击“发送”按钮;

③.可观察到终端【设定温度】一栏已被设置为②中输入的温度。

3.5 压缩机自动控制

3.6 绘制温度曲线

①.点击“压缩机控制”按钮,进行压缩机自动控制,以便观察温度曲线;

②.点击“绘制温度曲线”按钮,开始绘制实时温度曲线;

③.等待片刻,可观察到如图所示的温度曲线,温度在下降到“设定温度”的偏差范围内压缩机开始介入,通过自动控制,将温度控制在一个合理的范围区间内。


四.BUG说明及源代码 

4.1 BUG说明

①用于发送开关压缩机状态帧(源码535行-546行)写在了recv_data中,若在开关压缩机的同时再点击【打开抽屉】进行抽屉开关控制,可能会导致报错。

②绘制实时温度曲线的函数【draw_temp】中每次绘图的散点个数的固定的【由两组for循环中的数字决定】,若在【固定】散点个数未绘制结束之前关闭主窗口,会导致无限弹窗,目前尚未解决。

③ 由于【RCPET】返回帧的速度较快,有时点击开关抽屉会接收不到返回的Ack帧,Ack帧可能会与其他帧连在一起导致无法接收,也没有很好的解决方法。在点击开关抽屉时可适当慢一点

④ 源码中有很多地方为了方便使用了大量的重复if代码,可自行优化。

该源码仍有很多的BUG和待完善的地方,仅作为参考交流。

4.2 程序源码

编写不易,点个赞吧

from tkinter import ttk, messagebox
from tkinter import *
import tkinter as tk
import time
import binascii
import serial.tools.list_ports
from time import sleep
import threading
import matplotlib.pyplot as plt
from binascii import unhexlify
from crcmod import mkCrcFun  #用于CRC校验
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
frame_num=0 #全局帧号
ser=serial.Serial(
    port="COM2",
    baudrate=38400,
    bytesize=8,
    parity=serial.PARITY_NONE,
    stopbits=1,
    timeout=0.2)
box0=0
box1=0
box2=0
box3=0
box4=0
box5=0
box6=0
box7=0
box8=0
box9=0
"绘制曲线图的温度数据"
draw=0
box="0" #2B的锁控位 16进制
box_recv="0" #2B锁控返回位 16进制
press_recv="0" #压缩机工作状态
device_code="FFFFFFFFFF" #设备编码 默认全F
device_add="01" #设备地址 缺省为1
post_time ="01" #上传间隔 缺省为1
pressor_start_delay= "1e" #压缩机启动延时 缺省30-》1e
Temp="08" #当前温度的16进制字符串 缺省为4 转为8
Temp_pc = "02" #温控偏差 缺省为2
recv="0"
Temp_recv=0 #返回的设定温度
Temp_pc_recv=0 #返回的温度控制偏差
press_button=0 #压缩机总开关 0是关 1是开
def draw_temp():  # 绘制温度曲线图
    global draw  #全局温度变化量
    i = 0
    t = []
    list1 = []
    plt.figure(1)
    plt.xlabel("时间(s)")
    plt.ylabel("温度(℃)")
    plt.title("温度实时监控曲线")
    for m in range(10):   #两个for循环控制每次画图的总点数
        for x in range(5):
            list1.append(draw)
            i = i + 1
            t.append(i)
            plt.scatter(i, draw)  #绘图点坐标 温度用全局变量
            plt.pause(1.5)
            plt.plot(t, list1) #连线
    plt.plot(t, list1)   #总连线
    plt.grid()
    plt.show()
    time.sleep(2)
    plt.cla()  #清除原图

def xitong():
    global frame_num
    root = Tk()
    root.title('智能快递柜终端v1.0')
    root.geometry('800x550')
    root.maxsize(800, 800)
    root.minsize(800, 550)
    ##################工作函数区###################
    def left_zero_hex1(data):  # 1B 16进制左补0
        b = data
        if (len(data) == 1): b = "0" + data  # 小于两位 补0
        return b
    def left_zero_hex2(data):  # 2B 16进制左补0
        b = "0000"
        if (len(data) == 1): b = "000" + data
        if (len(data) == 2): b = "00" + data
        if (len(data) == 3): b = "0" + data
        if (len(data) == 4):  b = data
        return b
    def frame_num_re():  # 帧号合法性验证
        global frame_num
        if frame_num > 255:
            frame_num = 0
    def get_crc_value(s, crc16):  #crc工作函数
        data = s.replace(' ', '')
        crc_out = hex(crc16(unhexlify(data)))
        str_list = list(crc_out)
        if len(str_list) == 5:
            str_list.insert(2, '0')  # 位数不足补0
        crc_data = ''.join(str_list[2:])
        return crc_data[2:] + crc_data[:2]
    def crc16_xmodem(s):     #xmodem工作函数
        crc16 = mkCrcFun(0x11021, rev=False, initCrc=0x0000, xorOut=0x0000)
        return get_crc_value(s, crc16)
    def build_frame(length, function, data):  # 帧长,功能,数据
        global frame_num
        frame_num=frame_num+1
        frame_num_re()
        f = []
        f.append("ffff")
        f.append(length)  # 帧长
        f.append(left_zero_hex1(hex(frame_num)[2:]))  # 帧号10转16
        f.append(device_add)
        f.append(function)  # 功能号
        f.append(data)  # 数据载荷
        ############CRC#################
        f_crc = ''.join(f)
        f_crc=crc16_xmodem(f_crc[4:])  #计算生成crc
        f.append(f_crc)  # CRC
        ############CRC#################
        f.append("fff7")  # 帧结尾标识
        f_return = ''.join(f)
        return f_return
    ##################工作函数区###################
    ##################控制函数区###################
    def paramControl(): #设备参数控制

        def build_data_frame():  # 整合设备参数数据部分
                global frame_num
                devide_code = entry1.get()  # 长度5B
                print("设备编码", devide_code)
                devide_add = left_zero_hex1(hex(int(entry2.get()))[2:])  # 长度1B
                print("设备地址", devide_add)
                post_time = left_zero_hex1(hex(int(entry4.get()))[2:])  # 长度1B
                print("上传间隔", post_time)
                pressor_start_delay = left_zero_hex1(hex(int(entry5.get()))[2:])  # 长度1B
                print("延时", pressor_start_delay)
                Temp = int(entry7.get())  # 1B
                Temp_2 = '{:06b}'.format(Temp)  # 温度转二进制
                Temp_2 = '0' + Temp_2 + '0'  # 拼成8位二进制
                Temp = left_zero_hex1(hex(int(Temp_2, 2))[2:])  # 16进制温度最终数据
                print("温度", Temp)
                Temp_pc = left_zero_hex1(hex(int(entry8.get()))[2:])  # 1B 温度控制偏差
                print("偏差", Temp_pc)
                frame = devide_code + devide_add + "00" + post_time + pressor_start_delay + "0000" + Temp + Temp_pc + "ffff" + "ffff" + "00"
                frame = build_frame("1c", "05", frame)  # 构造参数帧
                print(frame)
                d = bytes.fromhex(frame)
                ser.write(d)
                print("帧号:",frame_num)
                frame_num = frame_num + 1  # 帧号自增1
                sleep(0.1)

        root1 = Toplevel(root)
        root1.title('设置')
        root1.geometry('400x400')
        root1.maxsize(400, 400)
        root1.minsize(400, 400)

        '''设备编码'''
        label1 = Label(root1,text="设备编码:", height=1)
        label1.place(x=120, y=5)
        entry1 = Entry(root1, width=20, textvariable='')
        entry1.place(x=180, y=5)

        '''设备地址'''
        label2 = Label(root1,text="设备地址:", height=1)
        label2.place(x=120, y=30)
        entry2 = Entry(root1, width=20, textvariable='')
        entry2.place(x=180, y=30)

        '''注册帧'''
        label3 = Label(root1,text="发送注册帧定时时间(s)", height=1)
        label3.place(x=50, y=60)
        entry3 = Entry(root1, width=20, textvariable='')
        entry3.place(x=180, y=60)

        '''采集时间'''
        label4 = Label(root1,text="采集时间间隔(s)", height=1)
        label4.place(x=85, y=90)
        entry4 = Entry(root1, width=20, textvariable='')
        entry4.place(x=180, y=90)

        '''压缩机启动延时'''
        label5 = Label(root1,text="压缩机启动延时(s)", height=1)
        label5.place(x=75, y=120)
        entry5 = Entry(root1, width=20, textvariable='')
        entry5.place(x=180, y=120)

        '''设定温度'''
        label7 = Label(root1,text="设定温度(℃)", height=1)
        label7.place(x=100, y=180)
        entry7 = Entry(root1, width=20, textvariable='')
        entry7.place(x=180, y=180)

        '''温度控制偏差'''
        label8 = Label(root1,text="温度控制偏差(℃)", height=1)
        label8.place(x=80, y=210)
        entry8 = Entry(root1, width=20, textvariable='')
        entry8.place(x=180, y=210)

        root1.Button1 = Button(root1, command=build_data_frame, height=3, width=10, bg="skyblue", text="发送")
        root1.Button1.place(x=100, y=280)

        root1.Button2 = Button(root1, command=root1.destroy, height=3, width=10, text="取消")
        root1.Button2.place(x=200, y=280)

        root1.mainloop()
    def openBox():  #抽屉锁控
        def build_lock_frame():  # 功能号03
            global box,frame_num
            # 将box转为16进制保存
            frame_num_re() #帧号合法性校验
            #print("xx",left_zero_hex2(hex(int(box, 2))[2:]))
            frame = build_frame("0c", "03", left_zero_hex2(hex(int(box, 2))[2:]))
            d = bytes.fromhex(frame)
            ser.write(d)
            frame_num = frame_num + 1  # 帧号自增1
            sleep(0.1)

        class GUI:
            def __init__(self):
                self.root2 = tk.Toplevel(root)
                self.root2.title('抽屉开锁控制')
                self.root2.geometry("300x100")
                self.interface()

            def interface(self):
                """"界面编写位置"""
                self.Button0 = tk.Button(self.root2, text="确定", command=self.event)
                self.Button0.grid(row=4, column=3)

                self.v1 = tk.IntVar()
                self.Checkbutton01 = tk.Checkbutton(self.root2, text="1号柜", command=self.Check_box, variable=self.v1)
                self.Checkbutton01.grid(row=0, column=1)

                self.v2 = tk.IntVar()
                self.Checkbutton02 = tk.Checkbutton(self.root2, text="2号柜", command=self.Check_box, variable=self.v2)
                self.Checkbutton02.grid(row=0, column=2)

                self.v3 = tk.IntVar()
                self.Checkbutton03 = tk.Checkbutton(self.root2, text="3号柜", command=self.Check_box, variable=self.v3)
                self.Checkbutton03.grid(row=0, column=3)

                self.v4 = tk.IntVar()
                self.Checkbutton04 = tk.Checkbutton(self.root2, text="4号柜", command=self.Check_box, variable=self.v4)
                self.Checkbutton04.grid(row=0, column=4)

                self.v5 = tk.IntVar()
                self.Checkbutton05 = tk.Checkbutton(self.root2, text="5号柜", command=self.Check_box, variable=self.v5)
                self.Checkbutton05.grid(row=0, column=5)

                self.v6 = tk.IntVar()
                self.Checkbutton06 = tk.Checkbutton(self.root2, text="6号柜", command=self.Check_box, variable=self.v6)
                self.Checkbutton06.grid(row=1, column=1)

                self.v7 = tk.IntVar()
                self.Checkbutton07 = tk.Checkbutton(self.root2, text="7号柜", command=self.Check_box, variable=self.v7)
                self.Checkbutton07.grid(row=1, column=2)

                self.v8 = tk.IntVar()
                self.Checkbutton08 = tk.Checkbutton(self.root2, text="8号柜", command=self.Check_box, variable=self.v8)
                self.Checkbutton08.grid(row=1, column=3)

                self.v9 = tk.IntVar()
                self.Checkbutton09 = tk.Checkbutton(self.root2, text="9号柜", command=self.Check_box, variable=self.v9)
                self.Checkbutton09.grid(row=1, column=4)

                self.v10 = tk.IntVar()
                self.Checkbutton10 = tk.Checkbutton(self.root2, text="10号柜", command=self.Check_box, variable=self.v10)
                self.Checkbutton10.grid(row=1, column=5)

                #self.w1 = tk.Text(self.root2, width=20, height=10)
                #self.w1.grid(row=5, column=0)

            def event(self):  # 绑定到开锁帧生成发送事件
                global box
                '''按钮事件,获取复选框的状态,1表示勾选,0表示未勾选'''
                box = str(box7) + str(box6) + str(box5) + str(box4) + str(box3) + str(box2) + str(box1) + str(
                    box0) + "000000" + str(box9) + str(box8)  # 保存当前锁控16位2进制
                #print(box) 检验box序列
                build_lock_frame()  # 构建开锁帧

            def Check_box(self):
                '''复选框事件'''
                global box0, box1, box2, box3, box4, box5, box6, box7, box8, box9
                if self.v1.get() == 1: box0 = 1
                if self.v1.get() == 0: box0 = 0
                if self.v2.get() == 1: box1 = 1
                if self.v2.get() == 0: box1 = 0
                if self.v3.get() == 1: box2 = 1
                if self.v3.get() == 0: box2 = 0
                if self.v4.get() == 1: box3 = 1
                if self.v4.get() == 0: box3 = 0
                if self.v5.get() == 1: box4 = 1
                if self.v5.get() == 0: box4 = 0
                if self.v6.get() == 1: box5 = 1
                if self.v6.get() == 0: box5 = 0
                if self.v7.get() == 1: box6 = 1
                if self.v7.get() == 0: box6 = 0
                if self.v8.get() == 1: box7 = 1
                if self.v8.get() == 0: box7 = 0
                if self.v9.get() == 1: box8 = 1
                if self.v9.get() == 0: box8 = 0
                if self.v10.get() == 1: box9 = 1
                if self.v10.get() == 0: box9 = 0
        a = GUI()  # a 是 GUI 类 的 一个 实例化 对象
        a.root2.mainloop()
    def tempControl():
        def build_temp_frame():  # 功能号04
                global frame_num
                Temp = int(entry4.get())
                Temp_2 = '{:06b}'.format(Temp)  # 温度转二进制
                Temp_2 = '0' + Temp_2 + '0'  # 拼成8位二进制
                Temp = hex(int(Temp_2, 2))[2:]
                frame = build_frame("0B", "04", Temp)
                d = bytes.fromhex(frame)
                ser.write(d)
                print("帧号:", frame_num)
                frame_num = frame_num + 1  # 帧号自增1
                sleep(0.1)

        root3 = tk.Toplevel(root)
        root3.title('温度控制')
        root3.geometry('350x300')
        root3.maxsize(350, 300)
        root3.minsize(350, 300)

        '''设备号'''
        label1 = Label(root3,text="设备号:", height=1)
        label1.place(x=80, y=30)
        combo = ttk.Combobox(root3,width=17, height=8, justify=CENTER)
        combo['values'] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
        combo.bind("<<ComboboxSelected>>", lambda event: 'combo_handler(var=varBitrate.get())')
        combo.current(0)
        combo.place(x=140, y=30)

        '''设定温度'''
        label4 = Label(root3,text="设定温度(℃):", height=1)
        label4.place(x=60, y=90)
        entry4 = Entry(root3, width=20, textvariable='')
        entry4.place(x=140, y=90)

        root3.Button1 = Button(root3, command=build_temp_frame, height=3, width=10, bg="skyblue", text="发送")
        root3.Button1.place(x=100, y=150)

        root3.Button2 = Button(root3, command=root3.destroy, height=3, width=10, text="取消")
        root3.Button2.place(x=200, y=150)

        root3.mainloop()
    def pressorControl():  # 压缩机开启控制   温度偏差36-37 温度设定64-65 采集温度66-67
        global draw, Temp_recv,Temp_pc_recv,press_button  # 采集温度 设定温度 温度控制偏差 均为int 10进制 直接操作
        press_button =1#压缩机总开关
        #采集温度±控制偏差 与设定温度不符合则开启压缩机,否则不用开启压缩机
        if ((press_button==1)and(draw > Temp_recv + Temp_pc_recv)):  # 开启压缩机
            frame_num_re()  # 帧号合法性校验
            frame = build_frame("0b", "02", "01")  # 构建发送帧  01表示开启
            d = bytes.fromhex(frame)
            ser.write(d)  # 发送帧
        if ((press_button==1)and((draw >= Temp_recv - Temp_pc_recv and draw <= Temp_recv + Temp_pc_recv)or(draw < Temp_recv - Temp_pc_recv))):  # 在温控范围内
            frame_num_re()  # 帧号合法性校验
            frame = build_frame("0b", "02", "00")  # 构建发送帧  00表示关闭
            d = bytes.fromhex(frame)
            ser.write(d)  # 发送
    def pressorControl2():  # 压缩机关闭控制
        global press_button  # 压缩机总开关
        press_button = 0
        frame = "FFFF0B7601020060C8FFF7"
        d = bytes.fromhex(frame)
        ser.write(d)
    ##################控制函数区###################
    #接收表
    columns = ('帧长', '帧号', '地址', '功能号', '数据', '校验码')
    tv = ttk.Treeview(root, show='headings', columns=columns,height=50)
    tv.column('帧长', width=80, anchor='center')
    tv.column('帧号', width=80, anchor='center')
    tv.column('地址', width=80, anchor='center')
    tv.column('功能号', width=80, anchor='center')
    tv.column('数据', width=200, anchor='center')
    tv.column('校验码', width=80, anchor='center')

    tv.heading('帧长', text='帧长')
    tv.heading('帧号', text='帧号')
    tv.heading('地址', text='地址')
    tv.heading('功能号', text='功能号')
    tv.heading('数据', text='数据')
    tv.heading('校验码', text='校验码')
    tv.place(x=2,y=5,height=250,width=650)
    #1号抽屉
    b1 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b1.place(x=20, y=320)
    b11= tk.Label(root,text="抽屉1")
    b11.place(x=30,y=295)
    #2号抽屉
    b2 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b2.place(x=110, y=320)
    b22 =tk.Label(root,text="抽屉2")
    b22.place(x=120,y=295)
    #3号抽屉
    b3 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b3.place(x=200, y=320)
    b33 =tk.Label(root,text="抽屉3")
    b33.place(x=210,y=295)
    #4号抽屉
    b4 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b4.place(x=290, y=320)
    b44 =tk.Label(root,text="抽屉4")
    b44.place(x=300,y=295)
    #5号抽屉
    b5 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b5.place(x=380, y=320)
    b55 =tk.Label(root,text="抽屉5")
    b55.place(x=390,y=295)
    #6号抽屉
    b6 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b6.place(x=20, y=450)
    b66=tk.Label(root,text="抽屉6")
    b66.place(x=30,y=425)
    #7号抽屉
    b7 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b7.place(x=110, y=450)
    b77 =tk.Label(root,text="抽屉7")
    b77.place(x=120,y=425)
    #8号抽屉
    b8 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b8.place(x=200, y=450)
    b88 =tk.Label(root,text="抽屉8")
    b88.place(x=210,y=425)
    #9号抽屉
    b9 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b9.place(x=290, y=450)
    b99 =tk.Label(root,text="抽屉9")
    b99.place(x=300,y=425)
    #10号抽屉
    b10 = tk.Label(root, height=3,width=7,bg="red",text="OFF")
    b10.place(x=380, y=450)
    b1010 =tk.Label(root,text="抽屉10")
    b1010.place(x=390,y=425)
    #压缩机状态
    b11 =tk.Label(root,height=9,width=7,bg="red",text="OFF")
    b11.place(x=480,y=350)
    b1111 =tk.Label(root,text="压缩机\n状态")
    b1111.place(x=485,y=310)
    #温度监控
    a1=tk.Label(root,text="【设定温度】")
    a1.place(x=560,y=360)
    global Temp_recv,draw,Temp_pc_recv
    a11=tk.Label(root,text=str(Temp_recv)+'℃')
    a11.place(x=585,y=380)
    a2=tk.Label(root,text="【实时温度】")
    a2.place(x=560,y=410)
    a22=tk.Label(root,text=str(draw)+'℃')
    a22.place(x=585,y=430)
    a3=tk.Label(root,text="【偏差控制】")
    a3.place(x=560,y=460)
    a33=tk.Label(root,text=str(Temp_pc_recv)+'℃')
    a33.place(x=585,y=480)
    #实时接收函数
    def recv_data():  #实时接收数据并更新各项信息
        global recv,draw,box_recv,press_recv,Temp_recv,draw,Temp_pc_recv,press_button,frame_num
        def box_check():  #抽屉总体定时情况更新
            state_10 = int(box_recv, 16)
            data = '{:016b}'.format(state_10)  # 补齐16位的进制转换
            #print(data)   #8+7+6+5+4+3+2+1+000000+10+9  #顺序
            #更新主界面抽屉锁状态
            if data[7] == '0': b1.config(bg="red",text="OFF")
            if data[7] == '1': b1.config(bg="green",text="ON")
            if data[6] == '0': b2.config(bg="red",text="OFF")
            if data[6] == '1': b2.config(bg="green",text="ON")
            if data[5] == '0': b3.config(bg="red",text="OFF")
            if data[5] == '1': b3.config(bg="green",text="ON")
            if data[4] == '0': b4.config(bg="red",text="OFF")
            if data[4] == '1': b4.config(bg="green",text="ON")
            if data[3] == '0': b5.config(bg="red",text="OFF")
            if data[3] == '1': b5.config(bg="green",text="ON")
            if data[2] == '0': b6.config(bg="red",text="OFF")
            if data[2] == '1': b6.config(bg="green",text="ON")
            if data[1] == '0': b7.config(bg="red",text="OFF")
            if data[1] == '1': b7.config(bg="green",text="ON")
            if data[0] == '0': b8.config(bg="red",text="OFF")
            if data[0] == '1': b8.config(bg="green",text="ON")
            if data[15] == '0': b9.config(bg="red",text="OFF")
            if data[15] == '1': b9.config(bg="green",text="ON")
            if data[14] == '0': b10.config(bg="red",text="OFF")
            if data[14] == '1': b10.config(bg="green",text="ON")
            if press_recv == '00':b11.config(bg="red",text="OFF")
            if press_recv == "01":b11.config(bg="blue",text="预启动")
            if press_recv == "02":b11.config(bg="green",text="ON")
            if press_recv == "03":b11.config(bg="yellow",text="故障")
        ##################实时检测启停压缩机################
        if ser.isOpen():  #持续监听函数
            print("open")
            while True:  #循环接收数据包
                n = ser.inWaiting()
                if n:
                    sleep(0.1)
                    n = ser.inWaiting()
                    payload = ser.read(n)
                    data = str(binascii.b2a_hex(payload))[2:-1]
                    if(crc16_xmodem(data[4:-8])==data[-8:-4]):  #接收的数据进行crc合法性校验
                        if (len(data) == 88): #上传状态帧
                            print("上传状态帧:", data)
                            #print("crc计算值",crc)
                            #print("crc切片值",data[-8:-4])
                            #######数据处理##########
                            box_recv=data[72:76]  #用于更新开锁状态
                            press_recv=data[62:64] #用于更新压缩机状态
                            #print("press",press_recv)
                            box_check() #更新快递柜状态
                            data_num=data[6:8]  #帧号16转10
                            data_num = str(int(data_num, 16))
                            data_add=data[8:10]  #设备地址16转10
                            data_add = str(int(data_add,16))
                            data_function= "Status" #功能号16转10
                            data_crc=data[80:84]  #crc校验码
                            data_payload=data[12:30]+"..."  #只显示部分数据帧
                            Temp_pc_recv=int(data[36:38],16)  #温度控制偏差返回值 16进制
                            #print("控制偏差",Temp_pc_recv)
                            #----------采集温度转换-------------
                            #print("采集温度",data[66:68])
                            drawx='{:08b}'.format(int(data[66:68],16))
                            if(drawx[0]=='0'): #符号为非负
                                draw=int(drawx[1:7],2) #可用于画图的采集温度
                            if(drawx[0]=='1'): #符号为负
                                draw = int(drawx[1:7], 2)  # 可用于画图的采集温度
                                draw= draw -2*draw   #负数取相反数
                            #print("采集温度",draw)
                            # ----------采集温度转换-------------
                            # ----------设定温度转换-------------
                            #print("设定温度",data[64:66])
                            Temp_recv='{:08b}'.format(int(data[64:66],16))
                            Temp_recv = int(Temp_recv[1:7],2) #可用于画图的采集温度
                            #print("设定温度",Temp_recv)
                            # ----------设定温度转换-------------
                            ##########压缩机控制###########
                            if ((press_button==1)and(draw > Temp_recv + Temp_pc_recv)):  # 开启压缩机
                                frame_num_re()  # 帧号合法性校验
                                frame = build_frame("0b", "02", "01")  # 构建发送帧  01表示开启
                                d = bytes.fromhex(frame)
                                ser.write(d)  # 发送帧
                            if ((press_button==1)and((draw >= Temp_recv - Temp_pc_recv and draw <= Temp_recv + Temp_pc_recv) or (
                                draw < Temp_recv - Temp_pc_recv))):  # 在温控范围内
                                frame_num_re()  # 帧号合法性校验
                                frame = build_frame("0b", "02", "00")  # 构建发送帧  00表示关闭
                                d = bytes.fromhex(frame)
                                ser.write(d)  # 发送
                            ##########压缩机控制###########
                            ########温度面板更新###########
                            a11.config(text=str(Temp_recv)+'℃')
                            a22.config(text=str(draw)+'℃')
                            a33.config(text=str(Temp_pc_recv)+'℃')
                            ########温度面板更新###########
                            #######数据处理##########
                            tv.insert("", 0, values=("44",data_num,data_add,data_function,data_payload,data_crc))
                        if (len(data) == 28):  #显示ACK帧
                            print("Ack帧:", data)
                            #######数据处理##########
                            data_num=data[6:8]  #帧号16转10
                            data_num = str(int(data_num, 16))
                            data_add=data[8:10]  #设备地址16转10
                            data_add = str(int(data_add,16))
                            data_function= "Ack" #功能号16转10
                            data_payload=data[12:]+"..."
                            data_crc2 = data[20:24]  # crc校验码
                            #######数据处理##########
                            tv.insert("", 0, values=("28",data_num,data_add,data_function,data_payload,data_crc2))
        else:
            print(" not open")
    t2=threading.Thread(target=recv_data)  #单开线程用于运行while true接收,防止主界面与接收冲突
    t2.start()
    tv.insert("", 0, values=("default", "default", "default", "default", "FFFF0B7601020160C8FFF7"))

    tv.Button2 = Button(root, text="压缩机控制", command=pressorControl,height=2,width=13)
    tv.Button2.place(x=670,y=5)

    tv.Button8 = Button(root, text="压缩机关闭", command=pressorControl2,height=2,width=13)
    tv.Button8.place(x=670,y=75)

    tv.Button5 = Button(root, text="打开抽屉  ", command=openBox,height=2,width=13)
    tv.Button5.place(x=670, y=145)

    tv.Button6=Button(root,text="设置设备参数",command=paramControl,height=2,width=13)
    tv.Button6.place(x=670, y=215)

    tv.Button7=Button(root,text="绘制温度曲线",command=lambda:draw_temp(),height=2,width=13)
    tv.Button7.place(x=670, y=285)

    tv.Button4 = Button(root, text="设置温度  ", command=tempControl,height=2,width=13)
    tv.Button4.place(x=670, y=355)
    root.mainloop()

if __name__ == "__main__":
    xitong()

猜你喜欢

转载自blog.csdn.net/SCY164759920/article/details/125619666