1.设计目的
实现两台 PC 机之间通过阿里云平台进行双向传输和通信。
2.功能要求和关键问题
2.1 系统功能描述
1、注册阿里云账户,然后在阿里云的物联网平台
(iot.console.aliyun.com/lk/summary/new)中创建:产品、设备和规则引擎;
2、在 PC 机上,按一定的规则(比如随机数、正态分布、泊松分布等)产生一批数据,或者绘制一个图片文件。
3、将上述数据传输到阿里云。
4、从阿里云将数据回传到 PC 上,由应用程序接收数据并进行图形化显示。
5、设计一个图形用户界面,来实现文本数据或二进制数据(比如图像文件或者音频文件等)的双向传输。
6、实现方法不限,其它功能可以自行扩展。
2.2 关键问题分析
1.云平台产品设备的设置:
首先要在云平台上创建一个产品,两个设备和两个规则引擎中的云产品流
转,用以数据流的传输。具体介绍在第四部分模块设计中介绍,此处不详细说。
2.Python 程序的编写:
显而易见的,我们可以得知本次设计的核心内容在于和云平台的交互以及数据的传输,云平台最常见的数据流的传输方式是二进制数据流,那么我们势必要将图片和音频文件转化成二进制数据流的形式进行传输,而且要考虑速度的问题,可以将数据流切片,以此来加快传输速度。那么在解决完传输这个问题的时候,图形化界面的工程量就较小,只要用qt designer 制作一个 UI 界面即可。
3.系统整体框架设计
阿里云平台的设计将在第四个部分详细介绍,Python 程序在进行设计前,需要下载一个库用以阿里云的连接,只要在运行提示符(win+R)中输入 pip install aliyun-python-sdk-iot 即可。
那么在使运行环境无碍后,可以开始写模块,本次设计我将模块分为:1. 连接阿里云 2.发送数据 3.接收数据 4.图形化界面。本次设计我未将其分开放
在类中,由于连接阿里云的基础框架我是从官网上下载,所以以不同的函数为主,只有图形化界面是单独放了一个类。
4.放代码
curve_plotting.py 用来制作图
#Python绘制分布曲线
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
#正态分布的概率密度函数
def normpdf(x,mu,sigma):
pdf=np.exp(-(x-mu)**2/(2*sigma**2))/(sigma * np.sqrt(2 * np.pi))
return pdf
def draw_curve(mu,sigma):
# mu=int(input("请输入期望"))#mu:期望;sigma:标准差
# sigma=int(input("请输入标准差"))
# mu=3
# sigma=5
x= np.arange(mu-3*sigma,mu+3*sigma,0.01) #生成数据,步长越小,曲线越平滑
y=normpdf(x,mu,sigma)
#概率分布曲线
plt.plot(x,y,'g--',linewidth=2)
plt.title('Normal Distribution: mu = {:.2f}, sigma={:.2f}'.format(mu,sigma))
plt.vlines(mu, 0, normpdf(mu,mu,sigma), colors = "c", linestyles = "dotted")
plt.vlines(mu+sigma, 0, normpdf(mu+sigma,mu,sigma), colors = "y", linestyles = "dotted")
plt.vlines(mu-sigma, 0, normpdf(mu-sigma,mu,sigma), colors = "y", linestyles = "dotted")
plt.xticks ([mu-sigma,mu,mu+sigma],['μ-σ','μ','μ+σ'])
#输出
plt.grid()
plt.savefig('D:/python/cloud_access/chen/curve.jpg')
plt.show()
#泊松分布
def poisson_draw():
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
X = range(0, 51)
Y = []
for k in X:
p = stats.poisson.pmf(k, 20)
Y.append(p)
plt.bar(X, Y, color="skyblue")
plt.xlabel("X")
plt.ylabel("f(X)")
plt.title("泊松分布")
# plt.grid()
plt.savefig('D:/python/cloud_access/chen/poisson.jpg')
plt.show()
# poisson_draw()
receive_data.py用来收数据
import paho.mqtt.client as mqtt
import time
import hashlib
import hmac
import base64
import curve_plotting
from PySide2.QtUiTools import QUiLoader
from threading import Thread
from PySide2.QtWidgets import QApplication,QMessageBox
from PySide2.QtUiTools import QUiLoader #qt designer 好用的库,图形化界面必备
from PySide2.QtCore import QFile
from PySide2.QtCore import Signal, QObject
from PySide2.QtWidgets import QFileDialog
import re
from playsound import playsound
import os
options = {
'productKey': 'a1yCMSUd9Xz',
'deviceName': 'winform_1',
'deviceSecret': '8d33ac30d3c5bbb95f2330a6e288d3dd',
'regionId': 'cn-shanghai'
}
HOST = options['productKey'] + '.iot-as-mqtt.' + options['regionId'] + '.aliyuncs.com'
PORT = 1883
PUB_TOPIC = "/sys/" + options['productKey'] + "/" + options['deviceName'] + "/thing/event/property/post"
NEW_TOPIC = options['productKey'] + "/" + options['deviceName'] + "/user/update"
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc): # 连接设备
print("Connected with result code " + str(rc))
# client.subscribe("the/topic")
def getAliyunIoTClient():
timestamp = str(int(time.time()))
CLIENT_ID = "paho.py|securemode=3,signmethod=hmacsha1,timestamp=" + timestamp + "|"
CONTENT_STR_FORMAT = "clientIdpaho.pydeviceName" + options['deviceName'] + "productKey" + options[
'productKey'] + "timestamp" + timestamp
# set username/password.
USER_NAME = options['deviceName'] + "&" + options['productKey']
PWD = hmacsha1(options['deviceSecret'], CONTENT_STR_FORMAT)
client = mqtt.Client(client_id=CLIENT_ID, clean_session=False)
client.username_pw_set(USER_NAME, PWD)
return client
def hmacsha1(key, msg):
return hmac.new(key.encode(), msg.encode(), hashlib.sha1).hexdigest()
def on_message(client,data,msg): # 接收数据
print(msg.topic + " " + str(msg.payload))
img = msg.payload
if (len(img) >= 100):
if img[0:5] == b'music':
img = img[5:].decode()
data = base64.b64decode(str(img))
with open("music.mp3", "wb") as fp:
fp.write(data)
else:
img = img.decode()
data = base64.b64decode(str(img))
with open("receive1.jpeg", "wb") as file:
file.write(data)
status.ms.receive_print.emit()
else:
print(img)
img=img.decode()
status.ms.text_print.emit(img)
class MySignals(QObject):
# 定义一种信号,两个参数 类型分别是: QTextBrowser 和 字符串
# 调用 emit方法 发信号时,传入参数 必须是这里指定的 参数类型
receive_print =Signal()
text_print = Signal(str)
class Stats:
def __init__(self):
qfile_Server = QFile("receive.ui")
qfile_Server.open(QFile.ReadOnly)
qfile_Server.close()
# 从 UI 定义中动态 创建一个相应的窗口对象
self.ui = QUiLoader().load(qfile_Server)
self.ui.linkaliyun_button.clicked.connect(self.link) # 链接按钮
self.ui.normal_button.clicked.connect(self.normal)
self.ui.poisson_button.clicked.connect(self.poission)
self.ui.close_button.clicked.connect(self.close)
self.ui.txt_button.clicked.connect(self.txt)
self.ui.chose_button.clicked.connect(self.chose)
self.ui.music_button.clicked.connect(self.music)
self.ui.play_button.clicked.connect(self.play)
self.ms = MySignals()
self.ms.receive_print.connect(self.rece_signal)
self.ms.text_print.connect(self.text_signal)
def link(self):
client = getAliyunIoTClient()
client.on_connect = on_connect
client.on_message = on_message
client.connect(HOST, 1883, 300)
client.subscribe("/a1yCMSUd9Xz/winform_1/user/update")
client.subscribe("/a1yCMSUd9Xz/winform_1/user/get")
t_js = Thread(target=client.loop_forever)
t_js.setDaemon(True)
t_js.start()
return client
def play(self):
os.system('music.mp3')
def music(self):
client = self.link()
filePath, _ = QFileDialog.getOpenFileName(
self.ui, # 父窗口对象
"选择你要发送的音乐", # 标题
r"D:\\python\\cloud_access\\chen\\music", # 起始目录
"音乐类型 (*.mp3)" # 选择类型过滤项,过滤内容在括号中
)
# self.ui.photo.setPixmap(filePath)
# self.ui.photo.setScaledContents(True)
with open(filePath, "rb") as f:
img = base64.b64encode(f.read())
img = 'music'+img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1)
print("已发送")
def chose(self):
client=self.link()
filePath, _ = QFileDialog.getOpenFileName(
self.ui, # 父窗口对象
"选择你要发送的图片", # 标题
r"D:\\python\\cloud_access\\chen\\picture", # 起始目录
"图片类型 (*.png *.jpg *.bmp *.jpeg)" # 选择类型过滤项,过滤内容在括号中
)
# self.ui.photo.setPixmap(filePath)
# self.ui.photo.setScaledContents(True)
with open(filePath, "rb") as f:
img = base64.b64encode(f.read())
img=img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1)
print("已发送")
def normal(self):
client=self.link()
mu=self.ui.mu_lineEdit.text()
sigma=self.ui.sigma_lineEdit.text()
mu=int(re.sub("\D","",mu))
sigma=int(re.sub("\D","",sigma))
curve_plotting.draw_curve(mu,sigma)
with open("curve.jpg", "rb") as f:
img = base64.b64encode(f.read())
print(img)
img = img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1)
def poission(self):
client = self.link()
curve_plotting.poisson_draw()
with open("poisson.jpg", "rb") as f:
img = base64.b64encode(f.read())
print(img)
img = img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1)
def txt(self):
client = self.link()
data = self.ui.txt_lineEdit.text()
client.publish(NEW_TOPIC, payload=data, qos=1)
def rece_signal(self):
self.ui.photo.setPixmap("receive1.jpeg")
self.ui.photo.setScaledContents(True)
def text_signal(self, text):
self.ui.textbox.setPlainText(text)
def close(self):
exit()
if __name__ == '__main__':
app=QApplication([])
status=Stats()
status.ui.show()
app.exec_()
send_data.py 用来收数据
import paho.mqtt.client as mqtt #MQTT协议之订阅与发布
import time
import hashlib #接收一系列数据
import hmac #加密
import base64 #解码
import curve_plotting
from PySide2.QtCore import Signal, QObject
from PySide2.QtUiTools import QUiLoader
from threading import Thread
from PySide2.QtWidgets import QApplication,QMessageBox
from PySide2.QtUiTools import QUiLoader #qt designer 好用的库,图形化界面必备
from PySide2.QtCore import QFile
from PySide2.QtWidgets import QFileDialog
import re
from playsound import playsound
import os
options = {
'productKey': 'a1yCMSUd9Xz',
'deviceName': 'DHT_11',
'deviceSecret': '88d988f8d6d223c919e94b35ae09862f',
'regionId': 'cn-shanghai'
}
HOST = options['productKey'] + '.iot-as-mqtt.' + options['regionId'] + '.aliyuncs.com'
PORT = 1883
PUB_TOPIC = "/sys/" + options['productKey'] + "/" + options['deviceName'] + "/thing/event/property/post"
NEW_TOPIC =options['productKey'] + "/" + options['deviceName'] + "/user/update"
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc): # 连接设备
print("Connected with result code " + str(rc))
# client.subscribe("the/topic")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg): # 接收数据
print(msg.topic + " " + str(msg.payload))
img = msg.payload
print(img[0:5])
if (len(img) >= 100): #进行文件类型的判断
if img[0:5] == b'music':
img = img[5:].decode()
data = base64.b64decode(str(img))
with open("music.mp3", "wb") as fp: #将数据保存至文件中
fp.write(data)
print("ok")
else:
img = img.decode()
data = base64.b64decode(str(img)) #反解码
with open("receive1.jpeg", "wb") as file:
file.write(data)
status.ms.receive_print.emit() #收到数据后发送信号
else:
print(img)
img = img.decode()
status.ms.text_print.emit(img)
def hmacsha1(key, msg):
return hmac.new(key.encode(), msg.encode(), hashlib.sha1).hexdigest()
def getAliyunIoTClient():
timestamp = str(int(time.time()))
CLIENT_ID = "paho.py|securemode=3,signmethod=hmacsha1,timestamp=" + timestamp + "|"
CONTENT_STR_FORMAT = "clientIdpaho.pydeviceName" + options['deviceName'] + "productKey" + options[
'productKey'] + "timestamp" + timestamp
# set username/password.
USER_NAME = options['deviceName'] + "&" + options['productKey']
PWD = hmacsha1(options['deviceSecret'], CONTENT_STR_FORMAT)
client = mqtt.Client(client_id=CLIENT_ID, clean_session=False)
client.username_pw_set(USER_NAME, PWD)
return client
class MySignals(QObject):
# 定义一种信号,两个参数 类型分别是: QTextBrowser 和 字符串
# 调用 emit方法 发信号时,传入参数 必须是这里指定的 参数类型
receive_print =Signal()
text_print = Signal(str)
class Stats:
def __init__(self):
qfile_Server = QFile("send.ui")
qfile_Server.open(QFile.ReadOnly)
qfile_Server.close()
# 从 UI 定义中动态 创建一个相应的窗口对象
self.ui = QUiLoader().load(qfile_Server)
self.ui.linkaliyun_button.clicked.connect(self.link) # 链接按钮
self.ui.normal_button.clicked.connect(self.normal)
self.ui.poisson_button.clicked.connect(self.poission)
self.ui.close_button.clicked.connect(self.close)
self.ui.txt_button.clicked.connect(self.txt)
self.ui.chose_button.clicked.connect(self.chose)
self.ui.music_button.clicked.connect(self.music)
self.ui.play_button.clicked.connect(self.play)
self.ms = MySignals()
self.ms.receive_print.connect(self.rece_signal) #接收信号后使其执行函数
self.ms.text_print.connect(self.text_signal)
def link(self):
client = getAliyunIoTClient()
client.on_connect = on_connect #连接阿里云
client.on_message = on_message #接收数据
client.connect(HOST, 1883, 300)
client.subscribe("/a1yCMSUd9Xz/DHT_11/user/update")
client.subscribe("/a1yCMSUd9Xz/DHT_11/user/get")
t_js = Thread(target=client.loop_forever) #多线程,使子线程的操作不影响到主线程
t_js.setDaemon(True)
t_js.start() #开启线程
return client
def play(self):
os.system('music.mp3')
def music(self):
client = self.link()
filePath, _ = QFileDialog.getOpenFileName(
self.ui, # 父窗口对象
"选择你要发送的音乐", # 标题
r"D:\\python\\cloud_access\\chen\\music", # 起始目录
"音乐类型 (*.mp3)" # 选择类型过滤项,过滤内容在括号中
)
# self.ui.photo.setPixmap(filePath)
# self.ui.photo.setScaledContents(True)
with open(filePath, "rb") as f:
img = base64.b64encode(f.read()) #解码
img = 'music'+img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1) #发送数据到云
print("已发送")
def chose(self):
client=self.link()
filePath, _ = QFileDialog.getOpenFileName(
self.ui, # 父窗口对象
"选择你要发送的图片", # 标题
r"D:\\python\\cloud_access\\chen\\picture", # 起始目录
"图片类型 (*.png *.jpg *.bmp *.jpeg)" # 选择类型过滤项,过滤内容在括号中
)
# self.ui.photo.setPixmap(filePath)
# self.ui.photo.setScaledContents(True)
with open(filePath, "rb") as f:
img = base64.b64encode(f.read())
img=img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1)
print("已发送")
def normal(self):
client=self.link()
mu=self.ui.mu_lineEdit.text()
sigma=self.ui.sigma_lineEdit.text()
mu=int(re.sub("\D","",mu)) #正则表达式过滤非数字字符和空格
sigma=int(re.sub("\D","",sigma))
curve_plotting.draw_curve(mu,sigma)
with open("curve.jpg", "rb") as f:
img = base64.b64encode(f.read())
print(img)
img = img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1)
def poission(self):
client = self.link()
curve_plotting.poisson_draw()
with open("poisson.jpg", "rb") as f:
img = base64.b64encode(f.read())
print(img)
img = img.decode()
client.publish(NEW_TOPIC, payload=img, qos=1)
def txt(self):
client = self.link()
data = self.ui.txt_lineEdit.text() #发送文本不需要解码
client.publish(NEW_TOPIC, payload=data, qos=1)
def rece_signal(self):
self.ui.photo.setPixmap("receive1.jpeg") #将图片在名为photo的一个Qlabel中显示出来
self.ui.photo.setScaledContents(True) #使图片适配屏幕
def text_signal(self,text):
self.ui.textbox.setPlainText(text)
def close(self):
exit()
if __name__ == '__main__':
app = QApplication([]) #开启
status = Stats()
status.ui.show()
app.exec_()
结果演示:因为界面用qt designer 不放在这边,
界面如下: