一般一个典型的物联网系统包括感控层(传感器),网络层和应用层组成,而网络层主要用于实现感控对象与应用层的服务对象之间的通信。本次作业就以TCP/IP 协议栈中传输层协议的应用开发为目标,以UDP方式实现一种感控对象与服务对象之间的通信机制,其体系结构如图1所示。其中感控对象为一个虚拟路灯对象,在实现过程中用随机数模拟其温度、湿度和环境照度等感知数据,灯作为被控对象,可以通过服务器对其进行打开、关闭控制,且用不同颜色表示其开关状态。每个虚拟路灯都将有一个标识,以示区别。而服务对象可以同时与若干个虚拟路灯对象通信,每个虚拟路灯会定期向服务对象发送其当前状态,服务对象可以对任一个虚拟路灯进行开关控制。
PySide2安装:
pip install PySide2
UI:
需添加UIClient.ui和UIServer.ui两个ui组件
udp客户端:使用时需导入UIClient.ui,并更改代码的文件目录
import random
from socket import *
from PySide2.QtCore import QFile
from PySide2.QtWidgets import QApplication
from PySide2.QtUiTools import QUiLoader
import threading
import time
import datetime
HostIP = gethostbyname(gethostname())
sendAddress = (HostIP, 8081)
class Client:
def __init__(self):
# 从文件中加载UI定义
qfile_Server = QFile("venv/UIClient.ui")
qfile_Server.open(QFile.ReadOnly)
qfile_Server.close()
# 从 UI 定义中动态 创建一个相应的窗口对象
self.ui = QUiLoader().load(qfile_Server)
self.wendu = self.shidu = self.zhaodu = 0
self.ui.btlight.clicked.connect(self.light)
self.ui.btwork.clicked.connect(self.work)
self.ui.btstopwork.clicked.connect(self.stopwork)
self.ui.btsenddata.clicked.connect(self.senddata)
self.open = False
# 发送数据,格式:str:data
def senddata(self):
dt = datetime.datetime.now()
self.senddata = '路灯编号:1号\n数据发出时间:'+dt.strftime("%y-%m-%d %H:%M:%S")+'\nTemperature:' + str(self.wendu) + 'degree\nHumidity:' + str(self.shidu) + 'RH\nEnvironmental illumination:' + str(self.zhaodu) + 'lx'
udpclient.sendto(self.senddata.encode(), sendAddress)
# 客户端手动开灯
def light(self):
if self.open == False:
self.open = True
self.ui.btlight.setStyleSheet("background-color:#ffff00;")
else:
self.open = False
self.ui.btlight.setStyleSheet("background-color:#ffffff;")
# 客户端工作开始
def work(self):
self.wendu = round(random.uniform(1, 100), 2)
self.shidu = round(random.uniform(40, 60), 2)
self.zhaodu = round(random.uniform(250, 2000), 1)
self.ui.Textwendu.setText(str(self.wendu) + '摄氏度')
self.ui.Textshidu.setText(str(self.shidu) + 'RH')
self.ui.Textzhaodu.setText(str(self.zhaodu) + '勒克斯')
self.ui.btlight.setEnabled(True)
self.ui.btstopwork.setEnabled(True)
self.ui.btwork.setEnabled(False)
self.ui.Textwendu.setEnabled(True)
self.ui.Textshidu.setEnabled(True)
self.ui.Textzhaodu.setEnabled(True)
self.ui.TextAddress.setEnabled(True)
self.ui.btsenddata.setEnabled(True)
self.ui.TextAddress.setText("IP地址:"+HostIP+"端口号:8080")
# 客户端停止工作
def stopwork(self):
self.ui.Textwendu.setText('')
self.ui.Textshidu.setText('')
self.ui.Textzhaodu.setText('')
self.ui.TextAddress.setText('')
self.ui.btwork.setEnabled(True)
self.ui.btlight.setEnabled(False)
self.ui.Textwendu.setEnabled(False)
self.ui.Textshidu.setEnabled(False)
self.ui.Textzhaodu.setEnabled(False)
self.ui.TextAddress.setEnabled(False)
self.ui.btsenddata.setEnabled(False)
self.ui.btstopwork.setEnabled(False)
self.ui.btlight.setStyleSheet("background-color:#d3d3d3")
# 实例化窗口程序
app = QApplication([])
stats = Client()
stats.ui.show()
# 定义socket对象
udpclient = socket(AF_INET, SOCK_DGRAM)
udpclient.bind((HostIP, 8080))
print(f'客户端1启动成功...')
# 接受服务端数据包,并发送握手数据包,格式为str:ACK,data
def receive():
while (True):
receiveData = udpclient.recvfrom(10240)
if stats.ui.btlight.isEnabled():
if int(receiveData[0].decode()) == 1:
dt = datetime.datetime.now()
stats.ui.btlight.setStyleSheet("background-color:#ffff00;")
udpclient.sendto(("ACK,"+'路灯编号:1号\n数据发出时间:'+dt.strftime("%y-%m-%d %H:%M:%S")+'\nTemperature:' + str(stats.wendu) + 'degree\nHumidity:' + str(stats.shidu) + 'RH\nEnvironmental illumination:illumination:' + str(stats.zhaodu) + 'lx').encode(), sendAddress)
else:
dt = datetime.datetime.now()
stats.ui.btlight.setStyleSheet("background-color:#ffffff;")
udpclient.sendto(("ACK,"+'路灯编号:1号\n数据发出时间:'+dt.strftime("%y-%m-%d %H:%M:%S")+'\nTemperature:' + str(stats.wendu) + 'degree\nHumidity:' + str(stats.shidu) + 'RH\nEnvironmental illumination:illumination:' + str(stats.zhaodu) + 'lx').encode(), sendAddress)
time.sleep(0.1)
# 每隔1min发送1次数据(温度,湿度,照度),格式:str:data
def send():
while(True):
time.sleep(60)
dt = datetime.datetime.now()
udpclient.sendto(('路灯编号:1号\n数据发出时间:'+dt.strftime("%y-%m-%d %H:%M:%S")+'\nTemperature:' + str(stats.wendu) + 'degree\nHumidity:' + str(stats.shidu) + 'RH\nEnvironmental illumination:illumination:' + str(stats.zhaodu) + 'lx').encode(), sendAddress)
# 设置接受数据守护线程
receivethread = threading.Thread(target=receive)
receivethread.setDaemon(True)
receivethread.start()
# 设置发送数据守护线程
sendthread = threading.Thread(target=send)
sendthread.setDaemon(True)
sendthread.start()
# 循环执行
app.exec_()
udp服务端:使用时需导入UIServer.ui,并更改代码的文件目录
from socket import *
from PySide2.QtCore import QFile
from PySide2.QtWidgets import QApplication
from PySide2.QtUiTools import QUiLoader
import threading
import time
import datetime
HostIP=gethostbyname(gethostname())
sendAddress=[(HostIP, 8080),(HostIP, 8082),(HostIP, 8083)]
# 接收客户端的数据包
def receive():
while(True):
receiveData = udpserver.recvfrom(102400)
time.sleep(0.1)
if stats.ui.btnoserver.isEnabled():
if receiveData[0].decode().startswith('ACK'):
dt = datetime.datetime.now()
data=receiveData[0].decode().split(',')
stats.ui.Textshangchuan.append('接收时间:'+dt.strftime("%y-%m-%d %H:%M:%S")+'\t'+data[0]+"\nIP地址:"+str(receiveData[1][0])+"\t端口号:"+str(receiveData[1][1])+"\nSuccess!")
stats.ui.Textshangchuan.ensureCursorVisible()
stats.ui.Textzhongduan.append('接收时间:'+dt.strftime("%y-%m-%d %H:%M:%S")+'\t'+'IP地址:' + str(receiveData[1][0]))
stats.ui.Textzhongduan.insertPlainText('\t端口号:' + str(receiveData[1][1]))
stats.ui.Textzhongduan.append(data[1])
stats.ui.Textzhongduan.insertPlainText('\n')
stats.ui.Textzhongduan.ensureCursorVisible()
else:
dt = datetime.datetime.now()
stats.ui.Textzhongduan.append('接收时间:'+dt.strftime("%y-%m-%d %H:%M:%S")+'\t'+'IP地址:'+str(receiveData[1][0]))
stats.ui.Textzhongduan.insertPlainText('端口号:'+str(receiveData[1][1]))
stats.ui.Textzhongduan.append(str(receiveData[0].decode()))
stats.ui.Textzhongduan.insertPlainText('\n')
stats.ui.Textzhongduan.ensureCursorVisible()
class Server:
def __init__(self):
# 从文件中加载UI定义
qfile_Server = QFile("C:/Users/hp/Desktop/Python/venv/UIServer.ui")
qfile_Server.open(QFile.ReadOnly)
qfile_Server.close()
# 从 UI 定义中动态 创建一个相应的窗口对象
self.ui = QUiLoader().load(qfile_Server)
self.ui.btopen.clicked.connect(self.openlight)
self.ui.btclose.clicked.connect(self.closelight)
self.ui.btserver.clicked.connect(self.startserver)
self.ui.btnoserver.clicked.connect(self.stopserver)
self.ui.btclear.clicked.connect(self.clear)
# 清空列表
def clear(self):
self.ui.Textzhongduan.setText('')
self.ui.Textshangchuan.setText('')
self.ui.Textxiafa.setText('')
# 开灯,发送开灯数据包,格式为:str(1)
def openlight(self):
choose = self.ui.comboBox.currentText()
if choose == '全部':
for add in sendAddress:
udpserver.sendto('1'.encode(), add)
self.ui.Textxiafa.append('开灯指令已发出.'+'\nIP地址:'+add[0]+'端口号:'+str(add[1]))
self.ui.Textxiafa.ensureCursorVisible()
elif choose == '路灯1':
udpserver.sendto('1'.encode(), sendAddress[0])
self.ui.Textxiafa.append('开灯指令已发出.' + '\nIP地址:' + sendAddress[0][0] + '端口号:' + str(sendAddress[0][1]))
self.ui.Textxiafa.ensureCursorVisible()
elif choose == '路灯2':
udpserver.sendto('1'.encode(), sendAddress[1])
self.ui.Textxiafa.append('开灯指令已发出.' + '\nIP地址:' + sendAddress[1][0] + '端口号:' + str(sendAddress[1][1]))
self.ui.Textxiafa.ensureCursorVisible()
elif choose == '路灯3':
udpserver.sendto('1'.encode(), sendAddress[2])
self.ui.Textxiafa.append('开灯指令已发出.' + '\nIP地址:' + sendAddress[2][0] + '端口号:' + str(sendAddress[2][1]))
self.ui.Textxiafa.ensureCursorVisible()
# 关灯,发送关灯数据包,格式为:str(0)
def closelight(self):
choose = self.ui.comboBox.currentText()
if choose == '全部':
for add in sendAddress:
udpserver.sendto('0'.encode(), add)
self.ui.Textxiafa.append('关灯指令已发出.' + '\nIP地址:' + add[0] + '端口号:' + str(add[1]))
self.ui.Textxiafa.ensureCursorVisible()
elif choose == '路灯1':
udpserver.sendto('0'.encode(), sendAddress[0])
self.ui.Textxiafa.append('关灯指令已发出.' + '\nIP地址:' + sendAddress[0][0] + '端口号:' + str(sendAddress[0][1]))
self.ui.Textxiafa.ensureCursorVisible()
elif choose == '路灯2':
udpserver.sendto('0'.encode(), sendAddress[1])
self.ui.Textxiafa.append('关灯指令已发出.' + '\nIP地址:' + sendAddress[1][0] + '端口号:' + str(sendAddress[1][1]))
self.ui.Textxiafa.ensureCursorVisible()
elif choose == '路灯3':
udpserver.sendto('0'.encode(), sendAddress[2])
self.ui.Textxiafa.append('关灯指令已发出.' + '\nIP地址:' + sendAddress[2][0] + '端口号:' + str(sendAddress[2][1]))
self.ui.Textxiafa.ensureCursorVisible()
# 开始服务/开机
def startserver(self):
self.ui.btopen.setEnabled(True)
self.ui.btclose.setEnabled(True)
self.ui.btnoserver.setEnabled(True)
self.ui.Textzhongduan.setEnabled(True)
self.ui.Textshangchuan.setEnabled(True)
self.ui.Textxiafa.setEnabled(True)
self.ui.btserver.setEnabled(False)
self.ui.btclear.setEnabled(True)
self.ui.comboBox.setEnabled(True)
# 停止服务/关机
def stopserver(self):
self.ui.Textzhongduan.setText('')
self.ui.Textshangchuan.setText('')
self.ui.Textxiafa.setText('')
self.ui.btserver.setEnabled(True)
self.ui.btopen.setEnabled(False)
self.ui.btclose.setEnabled(False)
self.ui.btnoserver.setEnabled(False)
self.ui.Textzhongduan.setEnabled(False)
self.ui.Textshangchuan.setEnabled(False)
self.ui.Textxiafa.setEnabled(False)
self.ui.btclear.setEnabled(False)
self.ui.comboBox.setEnabled(False)
# 实例化窗口程序
app = QApplication([])
stats = Server()
stats.ui.show()
# 定义socket对象
udpserver = socket(AF_INET, SOCK_DGRAM)
udpserver.bind((HostIP,8081))
print(f'服务端启动成功...')
# 设置接受数据守护线程
receivethread = threading.Thread(target=receive)
receivethread.setDaemon(True)
receivethread.start()
# 循环执行
app.exec_()