267 lines
7.6 KiB
Python
267 lines
7.6 KiB
Python
|
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)
|