Commit ee166aa1 authored by 方开's avatar 方开 🍔
Browse files

Merge branch 'redev' into 'master'

Redev

See merge request fangkai/cloud-machine!1
parents 518ae2b6 1c76fb1e
*.pyc
\ No newline at end of file
*.pyc
*.log
*.DS_Store
water/
\ No newline at end of file
# linux主机程序
# linux主机程序整体架构图
## 串口通信
## CRC校验
![python](https://img.shields.io/badge/pyton-3.6%20-brightgreen.svg)
![platform](https://img.shields.io/badge/platform-win%20%7C%20osx%20%7C%20linux-lightgrey.svg)
![license](https://img.shields.io/badge/license-MIT-blue.svg)
![platform](https://i.loli.net/2019/03/06/5c7f78d86a845.png)
## 设备接入
设备接入分为两部分,和云平台连接以及和硬件连接。涉及到**硬件-云主机**协议、**云主机-云平台**协议。
1. 和云平台连接,采用TCP心跳包方式维持和云平台的连接通信,用到的库(gevent)
2. 和硬件设备连接,硬件设备全部采用TCP Server通信,支持同时连接多个仓库的硬件。
## 设备管理
通过本地的`house.json`配置文件来管理多个仓房的不同设备,实现对测试指令的判别。具体请查看本地的配置文件
## 数据分析
数据分析包含对`云平台的测试指令`的解析、根据`house.json`配置文件生成待发送给硬件的指令、硬件返回的各种类型数据
* 云平台的测试指令,参照[弘恩云接口协议](http://47.96.175.196:5566/fangkai/protocoltest/blob/master/README.md)
* 配置文件生成待发送指令,参照**控制柜转换指令**
* 硬件返回数据解析,参照各种类型解析成实际数据的文档
## 控制设备
主要是控制通风,参考上述的协议
## 程序介绍
* 命令生成: handle.py,根据配置文件生成命令文件cmd.json
* 处理水分数据: handle_water.py,处理水分的数据,得到实际值
* 通信部分: measure.py,和云平台、硬件连接通信,完成大部分类型的硬件数据类型解析
* crc16校验: 对应文件`crc16.py` ,支持直接将`原始硬件数据`进行校验,或者将`转换后的字符串`进行校验
## 部署运行
待补充
\ No newline at end of file
import json
from common.filePath.filePath import checkWaterPath,logging
# 读取水分数据对照表中的数据
with open(checkWaterPath, "r", encoding="utf-8") as f:
checkData = json.load(f)
# 根据谷物种类和温湿值来查找对应的水分值
def checkSpecies(species, tempHumi):
try:
species = checkData.get(species)
# 如果存在谷物类型拼写正确,返回对应的水分值数据
if species:
waterValue = species.get(tempHumi)
return waterValue
else:
return None
except Exception as e:
logging.info(f"查找谷物种类出错:{e}")
return None
def checkWater(species, waterData):
# 通道数据列表存放每一路的水分值
channelDataList = []
# 循环找到各点数据,没找到默认为None值,实时样例数据字符串每一路为62长度,返回所有路的数据
# 1F 00 00 00 44 01 AC 05 FB 12 A1 05 D1 13 4D 05 D1 13 43 FF FF FF FF FF FF FF FF FF FF FF FF
# 分割出每一路的水分数据进行处理
for i in range(0, len(waterData), 62):
try:
channelData = waterData[i:i + 62].split("ac")[1]
channelDataList.append(channelData)
except Exception as e:
# 填充6个ffff ffff 对应着六个点的温湿度
logging.info(f"{e}")
channelDataList.append("ffffffff" * 6)
logging.info(f"接收到的水份通道数据是:{channelDataList}")
# 对每一路数据进行解析处理
# 水分数据存放列表
waterDataList = []
try:
for data in channelDataList:
# 分割出每个点的温湿度,每个点长度为8
dataList = [data[i:i + 8] for i in range(0, len(data), 8)]
water1 = []
for pointData in dataList:
# 温度高低字节数据
tempData = pointData[:4]
# 湿度高低字节数据
humiData = pointData[4:8]
# 分别处理温度的高低字节
hiTemp = int(tempData[:2], 16)
loTemp = int(tempData[-2:], 16)
# 分别处理湿度字节
hiHumi = int(humiData[:2], 16)
loHumi = int(humiData[-2:], 16)
# 判断温度高字节,等于256 说明该点未配置返回水分值为None
if hiTemp == 255:
tempHumi = "null/null"
waterValue = None
else:
temp = (hiTemp * 256 + loTemp) / 100
# 湿度直接换算
humi = (hiHumi * 256 + loHumi) / 100
# 对温湿度取整,合并成 " 温度/湿度 " 的形式,进行下一步校验
tempHumi = str(int(temp)) + "/" + str(int(humi))
waterValue = checkSpecies(species, tempHumi)
water1.append(waterValue)
logging.info(f"{water1}")
waterDataList.append(water1)
return waterDataList
except Exception as e:
logging.info(f"出错信息为{e}")
{
"001": {
"ip": "192.168.2.222",
"port": "7002",
"pointNum": "40",
"cmds": [
"030101000000010111",
"030101000000010112",
"030101000000010113",
"030101000000010114",
"030101000000010115",
"030101000000010116",
"030101000000010117",
"030101000000010118",
"030101000000010119",
"03010100000001011a",
"03010100000001011b",
"03010100000001011c",
"03010100000001011d",
"03010100000001011e",
"03010100000001011f",
"030101000000010120"
]
},
"002": {
"ip": "192.168.2.222",
"port": "7002",
"pointNum": "40",
"cmds": [
"030101000000010121",
"030101000000010122",
"030101000000010123",
"030101000000010124"
]
},
"003": {
"ip": "192.168.2.222",
"port": "7002",
"cmds": [
"0301020000000101F001",
"0301020000000101F002",
"0301020000000101F003",
"0301020000000101F004",
"0301020000000101F005",
"0301020000000101F006",
"0301020000000101F007",
"0301020000000101F008",
"0301020000000101F009",
"0301020000000101F00a",
"0301020000000101F00b",
"0301020000000101F00c",
"0301020000000101F00d",
"0301020000000101F00e",
"0301020000000101F00f",
"0301020000000101F010",
"0301020000000101F011",
"0301020000000101F012",
"0301020000000101F013",
"0301020000000101F014",
"0301020000000101F015",
"0301020000000101F016",
"0301020000000101F017",
"0301020000000101F018",
"0301020000000101F019",
"0301020000000101F01a",
"0301020000000101F01b",
"0301020000000101F01c",
"0301020000000101F01d",
"0301020000000101F01e",
"0301020000000101F01f",
"0301020000000101F020",
"0301020000000101F021",
"0301020000000101F022",
"0301020000000101F023",
"0301020000000101F024",
"0301020000000101F025",
"0301020000000101F026",
"0301020000000101F027",
"0301020000000101F028",
"0301020000000101F029",
"0301020000000101F02a",
"0301020000000101F02b",
"0301020000000101F02c",
"0301020000000101F02d",
"0301020000000101F02e",
"0301020000000101F02f",
"0301020000000101F030",
"0301020000000101F031",
"0301020000000101F032",
"0301020000000101F033",
"0301020000000101F034",
"0301020000000101F035",
"0301020000000101F036",
"0301020000000101F037",
"0301020000000101F038",
"0301020000000101F039",
"0301020000000101F03a",
"0301020000000101F03b",
"0301020000000101F03c",
"0301020000000101F03d",
"0301020000000101F03e",
"0301020000000101F03f"
]
},
"004": {
"ip": "192.168.2.222",
"port": "7002",
"cmds": [
"030102000000010190",
"030102000000010191",
"030102000000010192",
"030102000000010193",
"030102000000010194",
"030102000000010195",
"030102000000010196",
"030102000000010197",
"030102000000010198",
"030102000000010199",
"03010200000001019a",
"03010200000001019b",
"03010200000001019c",
"03010200000001019d",
"03010200000001019e",
"03010200000001019f"
]
},
"005": {
"ip": "192.168.2.222",
"port": "7002",
"cmds": [
"0301020000000101a0",
"0301020000000101a1",
"0301020000000101a2",
"0301020000000101a3",
"0301020000000101a4",
"0301020000000101a5",
"0301020000000101a6",
"0301020000000101a7",
"0301020000000101a8",
"0301020000000101a9",
"0301020000000101aa",
"0301020000000101ab",
"0301020000000101ac",
"0301020000000101ad",
"0301020000000101ae",
"0301020000000101af"
]
},
"006": {
"ip": "192.168.2.222",
"port": "7002",
"cmds": [
"0301020000000101b0",
"0301020000000101b1",
"0301020000000101b2",
"0301020000000101b3",
"0301020000000101b4",
"0301020000000101b5",
"0301020000000101b6",
"0301020000000101b7",
"0301020000000101b8",
"0301020000000101b9",
"0301020000000101ba",
"0301020000000101bb",
"0301020000000101bc",
"0301020000000101bd",
"0301020000000101be",
"0301020000000101bf"
]
},
"007": {
"ip": "192.168.2.222",
"port": "7002",
"cmds": [
"0301020000000101F101",
"0301020000000101F102",
"0301020000000101F103",
"0301020000000101F104",
"0301020000000101F105",
"0301020000000101F106",
"0301020000000101F107",
"0301020000000101F108",
"0301020000000101F109",
"0301020000000101F10a",
"0301020000000101F10b",
"0301020000000101F10c",
"0301020000000101F10d",
"0301020000000101F10e",
"0301020000000101F10f",
"0301020000000101F110",
"0301020000000101F111",
"0301020000000101F112",
"0301020000000101F113",
"0301020000000101F114",
"0301020000000101F115",
"0301020000000101F116",
"0301020000000101F117",
"0301020000000101F118",
"0301020000000101F119",
"0301020000000101F11a",
"0301020000000101F11b",
"0301020000000101F11c",
"0301020000000101F11d",
"0301020000000101F11e",
"0301020000000101F11f",
"0301020000000101F120",
"0301020000000101F121",
"0301020000000101F122",
"0301020000000101F123",
"0301020000000101F124",
"0301020000000101F125",
"0301020000000101F126",
"0301020000000101F127",
"0301020000000101F128",
"0301020000000101F129",
"0301020000000101F12a",
"0301020000000101F12b",
"0301020000000101F12c",
"0301020000000101F12d",
"0301020000000101F12e",
"0301020000000101F12f",
"0301020000000101F130",
"0301020000000101F131",
"0301020000000101F132",
"0301020000000101F133",
"0301020000000101F134",
"0301020000000101F135",
"0301020000000101F136",
"0301020000000101F137",
"0301020000000101F138",
"0301020000000101F139",
"0301020000000101F13a",
"0301020000000101F13b",
"0301020000000101F13c",
"0301020000000101F13d",
"0301020000000101F13e",
"0301020000000101F13f"
]
}
}
\ No newline at end of file
import json
from common.crc16.crc16 import crc16s
from common.filePath.filePath import configPath, cmdPath, specialPath
with open(configPath, "r", encoding="utf-8") as f:
configData = json.load(f)
# 解析出所有的通道号
def splitChanID(channelId: str):
# 10-20 代表 16、17、18...32通道共16个通道号
# a - b,a,b均为16进制
if "-" in channelId:
# 分割出通道返回之后计算生成从低到高的通道号
channelRange = channelId.replace(" ", "").split("-")
channelLow = int(channelRange[0], 16)
channelHigh = int(channelRange[1], 16)
channelIds = [
format(houseData, "02x")
for houseData in range(channelLow, channelHigh + 1, 1)
]
elif "," in channelId:
channelIds = channelId.replace(" ", "").split(",")
elif len(channelId) == 2:
channelIds = [channelId]
else:
channelIds = None
return channelIds
# 生成不同类型分机的命令
def genSubMachineCmd(subMachine: dict, suffix: str):
channelIds = subMachine.get("channelId")
subId = subMachine.get("subId")
subAddr = subMachine.get("subAddr")
splitChannel = splitChanID(channelIds)
# 如果通道号列表不为空
if splitChannel != None:
prex = "0301" + subAddr + "00000001" + suffix
cmds = [prex + k for k in splitChannel]
return subId, cmds
# 获取配置信息
def getConfig(configData: dict):
d = {}
# 如果config文件填写正确的话
if configData.get("data"):
for houseData in configData["data"]:
# 获取此仓库的ip地址、端口号、仓库Id、仓库类型
ip = houseData.get("ip")
port = houseData.get("port")
houseId = houseData.get("houseId")
houseType = houseData.get("type")
# 根据类型判断是控制柜还是弘恩分机
if houseData.get("type") == "control" or houseData.get(
"type") == "multi":
# 统计所有分机的指令
if houseData.get("subMachine"):
d1 = {}
# 存放各种类型的临时数据
l1, tempList, humiList, bugList, airList, waterList,\
windList, weatherList, allList = [], [], [], [], [], [], [], [], []
for subMachineData in houseData.get("subMachine"):
d2 = {}
# 分机类型、分机id、分机硬件地址
subType = subMachineData.get("type")
subId = subMachineData.get("subId")
subAddr = subMachineData.get("subAddr")
# 根据分机类型来生成对应的指令
if subType == "temp":
subId, cmds = genSubMachineCmd(
subMachineData, "01")
pointNum = subMachineData.get("pointNum")
d2["pointNum"] = pointNum
tempList.append(subId)
allList.append(subId)
elif subType == "temphumi":
subId, cmds = genSubMachineCmd(
subMachineData, "01")
pointNum = subMachineData.get("pointNum")
# 空气温湿度通道号为23 代表外温外湿,需要设置全局指令
for cmd in cmds:
if cmd[-2:] == "23":
# 将外温外湿指令从该仓删除作为全仓指令
cmds.remove(cmd)
d3 = {"extertemphumi":{"ip": ip, "port": port, "cmd": cmd}}
# 对于外温外湿需要设置成全局指令,为了区别单独放在一个文件里
with open(specialPath, "w", encoding="utf-8") as f:
json.dump(d3, f, ensure_ascii=False, indent=4)
d2["pointNum"] = pointNum
humiList.append(subId)
allList.append(subId)
elif subType == "bug":
cmds = [
"0301" + subAddr + "0000000101f0" +
subMachineData.get("channelId")
]
bugList.append(subId)
allList.append(subId)
elif subType == "air":
cmds = [
"0301" + subAddr + "0000000101" +
subMachineData.get("channelId")
]
airList.append(subId)
allList.append(subId)
elif subType == "water":
cmds = [
"0301" + subAddr + "0000000101f1" +
subMachineData.get("channelId")
]
cerealsSpecies = subMachineData.get(
"cerealsSpecies")
d2["cerealsSpecies"] = cerealsSpecies
waterList.append(subId)
allList.append(subId)
elif subType == "wind":
addrs = format(int(subAddr, 16) + 200, "02x")
cmds = [{
"status": [f"0301{addrs}000000010121"],
"on": [
f"0301{addrs}00000001013f",
f"0301{addrs}000000010132"
],
"off": [
f"0301{addrs}000000010162",
f"0301{addrs}00000001016f"
]
}]
windList.append(subId)
elif subType == "weather":
cmds = [f"0301{subAddr}0000000101ff01"]
weatherList.append(subId)
allList.append(subId)
else:
pass
# 分机id对应的所有的发送指令
d2[subId] = cmds
l1.append(d2)
d1 = {
"ip": ip,
"port": port,
"housetype": houseType,
"all": allList,
"temp": tempList,
"temphumi": humiList,
"bug": bugList,
"air": airList,
"wind": windList,
"water": waterList,
"weather": weatherList,
"cmds": l1
}
d[houseId] = d1
# 如果接的是普通的分机,只有弘恩的测温测湿分机
elif houseData.get("type") == "single":
if houseData.get("subMachine"):
d1 = {}
l1, tempList, humiList, allList = [], [], [], []
for subMachineData in houseData.get("subMachine"):
d2 = {}
subType = subMachineData.get("type")
subAddr = subMachineData.get("subAddr")
subId = subMachineData.get("subId")
channelIds = subMachineData.get("channelId")
# 根据分机类型来生成对应的指令
if subType == "temp":
splitChannel = splitChanID(channelIds)
tempList.append(subId)
pointNum = subMachineData.get("pointNum")
d2["pointNum"] = pointNum
# 如果通道号列表正确
if splitChannel:
cmds = []
for houseData in splitChannel:
# crc校验和生成硬件指令
prex = subAddr + "04" + format(
(int(houseData, 16) - 1) * 62,
"04x") + "003E"
crcvalue = crc16s(prex)
cmd = prex + crcvalue
cmds.append(cmd)
elif subType == "temphumi":
humiList.append(subId)
pointNum = subMachineData.get("pointNum")
d2["pointNum"] = pointNum
# 如果通道号列表正确
if splitChannel:
cmds = []
for houseData in splitChannel:
prex = subAddr + "04" + format(
(int(houseData, 16) - 1) * 62,
"04x") + "003E"
crcvalue = crc16s(prex)
cmd = prex + crcvalue
cmds.append(cmd)
else:
pass
d2[subId] = cmds
l1.append(d2)
allList.append(subId)
d1 = {
"ip": ip,
"port": port,
"housetype": houseType,
"all": allList,
"temp": tempList,
"temphumi": humiList,
"cmds": l1
}
d[houseId] = d1