dts-py-app/SDT_Device/Protocol.py

267 lines
7.6 KiB
Python
Raw Permalink Normal View History

2024-04-24 02:24:33 +00:00
import struct
import socket
import threading
from enum import Enum
from queue import Queue
class State(Enum):
STX = 0
LEN = 1
DEVICE_ID = 2
DEVICE_SERIAL = 3
CLASS = 4
COMMAND = 5
DATA = 6
ETX = 7
class Packet:
def __init__(self):
self.stx = 0
self.len = 0
self.deviceId = 0
self.deviceSerial = 0
self.classType = 0
self.command = 0
self.data = []
self.etx = 0
class Protocol:
DEVICE_ID = {"CCU": 0x1, "TTMU": 0x2, "PG": 0x3, "QC": 0x4, "DTS": 0x5}
CLASS = {
"DATA": 0x44,
"GET": 0x47,
"SET": 0x53,
"EVENT": 0x45,
"HEART_BIT": 0x48,
}
HEADER_LEN = 20
STX = 0x2
ETX = 0x3
def __init__(self, ip, port):
self.state = State.STX
self.dataIndex = 0
self.deviceId = 0
self.deviceSerial = 0
self.packet = Packet()
self.maxLen = 0
self.recvCount = 0
self.classGetCallback = None
self.classHeartBitCallback = None
self.classDataCallback = None
self.dataQueue = Queue()
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((ip, port))
recv_thread = threading.Thread(target=self._recvThread)
recv_thread.daemon = True
recv_thread.start()
parsing_thread = threading.Thread(target=self._parsingThread)
parsing_thread.daemon = True
parsing_thread.start()
def setMaxLen(self, len):
self.maxLen = len
def setDeviceId(self, deviceId):
self.deviceId = deviceId
def getDeviceId(self):
return self.deviceId
def setDeviceSerial(self, deviceSerial):
self.deviceSerial = deviceSerial
def getDeviceSerial(self):
return self.deviceSerial
def send(self, arg):
print("Build: ", self._buildPacket(*arg))
# print("Build: ", int.from_bytes(self._buildPacket(*arg), byteorder='little'))
self.sock.sendall(self._buildPacket(*arg))
def _buildPacket(self, classType, cmd, data=None):
length = self.HEADER_LEN
print(f"Build Packet: {classType} / {cmd}")
payload = []
if data is not None:
payload = data
length = self.HEADER_LEN + 4 * len(payload)
string_format = f"<{7+len(payload)}I"
return struct.pack(
string_format,
self.STX,
length,
self.deviceId,
self.deviceSerial,
classType,
cmd,
*payload,
self.ETX,
)
def parsing(self, word):
ret = False
# print("[TEST] Parsing")
switch_dict = {
State.STX: self.case_stx,
State.LEN: self.case_len,
State.DEVICE_ID: self.case_producId,
State.DEVICE_SERIAL: self.case_productSerial,
State.CLASS: self.case_class,
State.COMMAND: self.case_command,
State.DATA: self.case_data,
State.ETX: self.case_etx,
}
print(f"Parsing {self.state} / {word}")
if self.state in switch_dict:
ret = switch_dict[self.state](word)
else:
self.case_default()
return ret
def case_stx(self, word):
if word == self.STX:
self.dataIndex = 0
self.packet = Packet()
self.packet.stx = word
self.state = State.LEN
return False
def case_len(self, word):
self.packet.len = word
if 20 <= self.packet.len <= self.maxLen:
self.state = State.DEVICE_ID
else:
self.state = State.STX
return False
def case_producId(self, word):
self.packet.deviceId = word
if self.packet.deviceId == self.deviceId:
self.state = State.DEVICE_SERIAL
else:
self.state = State.STX
return False
def case_productSerial(self, word):
self.packet.deviceSerial = word
if self.packet.deviceSerial == self.deviceSerial:
self.state = State.CLASS
else:
self.state = State.STX
return False
def case_class(self, word):
self.packet.classType = word
if self.packet.classType in self.CLASS.values():
self.state = State.COMMAND
else:
self.state = State.STX
return False
def case_command(self, word):
self.packet.command = word
if (self.packet.len - self.HEADER_LEN) == 0:
self.state = State.ETX
elif (self.packet.len - self.HEADER_LEN) > self.maxLen:
self.state = State.STX
else:
self.state = State.DATA
return False
def case_data(self, word):
self.packet.data.append(word)
if (len(self.packet.data) * 4) >= (self.packet.len - self.HEADER_LEN):
self.state = State.ETX
return False
def case_etx(self, word):
ret = False
self.packet.etx = word
self.state = State.STX
if self.packet.etx == self.ETX:
ret = True
return ret
def case_default(self):
self.state = State.STX
def _recvThread(self):
while True:
recvData = self.sock.recv(16384)
print(f"[REcived] {recvData}")
self.recvCount += len(recvData)
self.dataQueue.put(recvData)
def _parsingThread(self):
self.func1()
def func1(self):
oldArray = bytearray()
while True:
newArray = self.dataQueue.get()
oldArray = oldArray + newArray
endIdx = len(oldArray) - len(oldArray) % 4
dataArray = oldArray[:endIdx]
oldArray = oldArray[endIdx:]
for i in range(0, endIdx, 4):
int_value = int.from_bytes(dataArray[i : i + 4], byteorder="little")
print(f"[IntValue] {int_value}")
if self.parsing(int_value) == True:
self.commandProc()
def func2(self):
oldArray = bytearray()
while True:
newArray = self.dataQueue.get()
if len(newArray) % 4 != 0:
oldArray = oldArray + newArray
return 0
else:
oldArray = newArray
string_format = f"<{int(len(oldArray)/4)}I"
intArray = struct.unpack(string_format, oldArray)
for i in range(len(intArray)):
if self.parsing(intArray[i]) == True:
self.commandProc()
def commandProc(self):
classType = self.packet.classType
command = self.packet.command
payload = self.packet.data
if classType == self.CLASS["GET"]:
print("GET")
if self.classGetCallback != None:
self.classGetCallback(command, payload)
elif classType == self.CLASS["HEART_BIT"]:
print("HearBeat")
self._setHeartBit()
if self.classHeartBitCallback != None:
self.classHeartBitCallback()
elif classType == self.CLASS["DATA"]:
print("Data")
if self.classDataCallback != None:
self.classDataCallback(command, payload)
def _setHeartBit(self):
args = (self.CLASS["HEART_BIT"], self.CMD["HEART_BIT"])
print(self.CLASS["HEART_BIT"], "/" , self.CMD["HEART_BIT"])
self.send(args)