commit
28a2b34b28
@ -0,0 +1 @@
|
|||||||
|
./__pycache__
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
def modbus_crc16(data: bytearray) -> bytearray:
|
||||||
|
crc = 0xFFFF
|
||||||
|
for byte in data:
|
||||||
|
crc ^= byte
|
||||||
|
for _ in range(8):
|
||||||
|
if crc & 0x0001:
|
||||||
|
crc = (crc >> 1) ^ 0xA001
|
||||||
|
else:
|
||||||
|
crc >>= 1
|
||||||
|
return bytearray([crc >> 8,crc & 0xFF])
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import sys
|
||||||
|
from time import sleep, time
|
||||||
|
|
||||||
|
from source.IdiBus4DC import Module4DC
|
||||||
|
from source.IdiBusSerialLine import IdiBusSerialLine
|
||||||
|
|
||||||
|
|
||||||
|
class Executioner():
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
self.port = ""
|
||||||
|
self.address = 0
|
||||||
|
self.speed = 0
|
||||||
|
def execute(self, command, args):
|
||||||
|
pass
|
||||||
|
# Must return string with all data in csv format. without \n or \r
|
||||||
|
def get_log_string(self):
|
||||||
|
pass
|
||||||
|
class Exec4DC(Executioner):
|
||||||
|
def __init__(self, ):
|
||||||
|
super().__init__("Module4DC")
|
||||||
|
def execute(self, command, args):
|
||||||
|
module = Module4DC(addr=self.address, line=IdiBusSerialLine(port=self.port, baudrate=self.speed))
|
||||||
|
if command == "set-data":
|
||||||
|
module.set_channel_data(args[0], args[1], args[2])
|
||||||
|
elif command == "set-state":
|
||||||
|
module.set_channel_state(args[0], args[1])
|
||||||
|
else:
|
||||||
|
print("Unknown command: " + command)
|
||||||
|
def get_log_string(self):
|
||||||
|
module = Module4DC(addr=self.address, line=IdiBusSerialLine(port=self.port, baudrate=self.speed))
|
||||||
|
str = ""
|
||||||
|
for i in range(4):
|
||||||
|
data = module.set_channel_data(i)
|
||||||
|
if data == None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
str += f"{data["voltage"]},{data["current"]},"
|
||||||
|
class ExecSystem(Executioner):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Module4DC")
|
||||||
|
def execute(self, command, args):
|
||||||
|
if command == "sleep":
|
||||||
|
sleep(args[0])
|
||||||
|
elif command == "exit":
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print("Unknown command: " + command)
|
||||||
|
def get_log_string(self):
|
||||||
|
return "System is ok. :P"
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
from enum import Enum
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
class comFuncList(Enum):
|
||||||
|
IDIMMES_COM_C_Init = 220
|
||||||
|
IDIMMES_COM_C_ShtDown = 221
|
||||||
|
IDIMMES_COM_C_Freeze = 222
|
||||||
|
IDIMMES_COM_C_Resume = 223
|
||||||
|
IDIMMES_COM_C_Dummy = 224
|
||||||
|
IDIMMES_COM_C_AssignGroup = 225
|
||||||
|
IDIMMES_COM_C_SetAlarmL12 = 226
|
||||||
|
IDIMMES_COM_C_SetAlarmL = 227
|
||||||
|
IDIMMES_COM_C_Virtual = 228
|
||||||
|
IDIMMES_COM_C_SyncReadChnl = 229
|
||||||
|
IDIMMES_COM_C_SyncRead = 230
|
||||||
|
IDIMMES_COM_C_SyncDoChnl = 231
|
||||||
|
IDIMMES_COM_C_SyncDo = 232
|
||||||
|
IDIMMES_COM_C_SyncClear = 233
|
||||||
|
IDIMMES_COM_C_BurstReadCnt = 234
|
||||||
|
IDIMMES_COM_C_BurstReadTime = 235
|
||||||
|
IDIMMES_COM_C_SendTimeDate = 236
|
||||||
|
IDIMMES_COM_C_MkTimedMaster = 237
|
||||||
|
IDIMMES_COM_C_EnterBootloader = 238
|
||||||
|
#Skipped for historical reasons
|
||||||
|
IDIMMES_COM_C_ReadDevFullSN_MS = 241
|
||||||
|
IDIMMES_COM_C_WriteSnIPv4IPv6 = 242
|
||||||
|
IDIMMES_COM_C_WriteSnVerifyDates = 243
|
||||||
|
IDIMMES_COM_C_WriteSnAES256 = 244
|
||||||
|
IDIMMES_COM_C_SendLongMessage = 245
|
||||||
|
IDIMMES_COM_C_GetLondMessage = 246
|
||||||
|
IDIMMES_COM_C_DummyModule = 247
|
||||||
|
IDIMMES_COM_C_CheckModuleLongOp = 248
|
||||||
|
IDIMMES_COM_C_CheckChannelLongOp = 249
|
||||||
|
|
||||||
|
IDIMMES_COM_C_FmwBootloaderInfo = 250 # Ask for bootloader version
|
||||||
|
IDIMMES_COM_C_FmwBootloaderStart = 251 # Start upload and recieve fwinfo
|
||||||
|
IDIMMES_COM_C_FmwBootloaderWrite = 252 # Write chunk
|
||||||
|
IDIMMES_COM_C_FmwBootloaderEnd = 253 # upload end. Finish operations and reboot
|
||||||
|
IDIMMES_COM_C_GotoApp = 254 #Goto APP
|
||||||
|
@classmethod
|
||||||
|
def values(cls) -> List[int]:
|
||||||
|
"""Список всех значений"""
|
||||||
|
return [status.value for status in cls]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def names(cls) -> List[str]:
|
||||||
|
"""Список всех имен"""
|
||||||
|
return [status.name for status in cls]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def dict(cls) -> Dict[str, int]:
|
||||||
|
"""Словарь имя -> значение"""
|
||||||
|
return {status.name: status.value for status in cls}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_value(cls, value: int):
|
||||||
|
"""Получить статус по значению"""
|
||||||
|
for status in cls:
|
||||||
|
if status.value == value:
|
||||||
|
return status
|
||||||
|
return None
|
||||||
|
|
||||||
|
'''
|
||||||
|
moduleTypeList = {
|
||||||
|
"TYPE_TST" : "ATmega328PB",
|
||||||
|
"TYPE_1W1" : "ATmega328PB",
|
||||||
|
"TYPE_TTT" : "ATmega2560",
|
||||||
|
"TYPE_2ST" : "ATmega2560",
|
||||||
|
"TYPE_4DC" : "ATmega2560",
|
||||||
|
"TYPE_RM8" : "ATmega2560",
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
class IdiBusSpeedCodes(Enum):
|
||||||
|
CODE_19200B = 19200,
|
||||||
|
CODE_500K = 576000,
|
||||||
|
CODE_2400B = 2400,
|
||||||
|
CODE_9600B = 9600,
|
||||||
|
CODE_115200B = 115200,
|
||||||
|
CODE_250K = 256000,
|
||||||
|
CODE_1M = 921600,
|
||||||
|
CODE_10M = 10000000
|
||||||
|
@classmethod
|
||||||
|
def values(cls) -> List[int]:
|
||||||
|
"""Список всех значений"""
|
||||||
|
return [status.value for status in cls]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def names(cls) -> List[int]:
|
||||||
|
"""Список всех имен"""
|
||||||
|
return [status.name for status in cls]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def dict(cls) -> Dict[str, int]:
|
||||||
|
"""Словарь имя -> значение"""
|
||||||
|
return {status.name: status.value for status in cls}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_value(cls, value: str):
|
||||||
|
"""Получить статус по значению"""
|
||||||
|
for status in cls:
|
||||||
|
if status.value == value:
|
||||||
|
return status
|
||||||
|
return None
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
from .IdiBusDefs import comFuncList
|
||||||
|
from .IdiBusParse import IdiBusSMESParser
|
||||||
|
from .IdiBusSerialLine import IdiBusSerialLine
|
||||||
|
from .IdiBusMessage import *
|
||||||
|
|
||||||
|
|
||||||
|
class IdiBusModule():
|
||||||
|
def __init__(self, addr, line:IdiBusSerialLine):
|
||||||
|
self.addr = addr
|
||||||
|
self.last_mmes_err = 0
|
||||||
|
if not isinstance(line, IdiBusSerialLine):
|
||||||
|
raise TypeError("Line is not an IdiBusSerialLine")
|
||||||
|
self.line = line
|
||||||
|
def startLongOp(self):
|
||||||
|
self.line.timeout = 3
|
||||||
|
def endLongOp(self):
|
||||||
|
self.line.timeout = self.line.defaultTimeout
|
||||||
|
def c_Init(self):
|
||||||
|
return self._sendMMESG(comFuncList.IDIMMES_COM_C_Init.value, await_bytes=5)
|
||||||
|
def c_Shutdown(self):
|
||||||
|
return self._sendMMESG(comFuncList.IDIMMES_COM_C_ShtDown.value, await_bytes=5)
|
||||||
|
def c_Dummy(self):
|
||||||
|
return self._sendMMESG(comFuncList.IDIMMES_COM_C_Dummy.value, await_bytes=5)
|
||||||
|
def c_GotoApp(self):
|
||||||
|
return self._sendMMESG(comFuncList.IDIMMES_COM_C_GotoApp.value, await_bytes=5)
|
||||||
|
def c_FmwBootloaderInfo(self):
|
||||||
|
return self._sendMMESG(comFuncList.IDIMMES_COM_C_FmwBootloaderInfo.value, )
|
||||||
|
def c_EnterBootloader(self, moduleType, hwRev, SN):
|
||||||
|
moduleType = bytearray(moduleType, 'ascii')
|
||||||
|
hwRev = bytearray(hwRev, 'ascii')
|
||||||
|
#hwRev[0]-=48
|
||||||
|
#hwRev[1]-=48
|
||||||
|
SN = bytearray(SN, 'ascii')
|
||||||
|
devdata =bytearray()
|
||||||
|
devdata.extend(moduleType)
|
||||||
|
devdata.extend(hwRev)
|
||||||
|
devdata.extend(SN)
|
||||||
|
return self._sendMMESG(comFuncList.IDIMMES_COM_C_EnterBootloader.value, data=devdata, await_bytes=5)
|
||||||
|
def c_ReadDevFullSN_MS(self):
|
||||||
|
return self._sendMMESG(comFuncList.IDIMMES_COM_C_ReadDevFullSN_MS.value)
|
||||||
|
def c_BootloaderStart(self, data):
|
||||||
|
self.startLongOp()
|
||||||
|
rcv = self._sendMMESG(comFuncList.IDIMMES_COM_C_FmwBootloaderStart.value, data=data, await_bytes=5)
|
||||||
|
self.endLongOp()
|
||||||
|
return rcv
|
||||||
|
def c_BootloaderWrite(self, data):
|
||||||
|
self.startLongOp()
|
||||||
|
rcv = self._sendMMESG(comFuncList.IDIMMES_COM_C_FmwBootloaderWrite.value, data=data, await_bytes=5)
|
||||||
|
self.endLongOp()
|
||||||
|
return rcv
|
||||||
|
def c_BootloaderEnd(self):
|
||||||
|
self.startLongOp()
|
||||||
|
rcv = self._sendMMESG(comFuncList.IDIMMES_COM_C_FmwBootloaderEnd.value, await_bytes=5)
|
||||||
|
self.endLongOp()
|
||||||
|
return rcv
|
||||||
|
def _sendMMESG(self, func, data=None, await_bytes = 0):
|
||||||
|
if not func in comFuncList.values():
|
||||||
|
self.last_mmes_err = "BadFunc"
|
||||||
|
return 1
|
||||||
|
init_com = IdiBusMMESG(
|
||||||
|
self.addr,
|
||||||
|
IdiBusMMPS(IdiBusMMPS.typeMMESG).getValue(),
|
||||||
|
func,
|
||||||
|
)
|
||||||
|
#Append data if present
|
||||||
|
if not data is None:
|
||||||
|
init_com.addData(data)
|
||||||
|
|
||||||
|
self.line.writeData(init_com.getMsg())
|
||||||
|
|
||||||
|
#Set max packet size if not specified
|
||||||
|
if await_bytes == 0:
|
||||||
|
await_bytes = self.line.maxPacketSize
|
||||||
|
#read all data
|
||||||
|
SMES = self.line.readData(await_bytes)
|
||||||
|
#No response from slave
|
||||||
|
if len(SMES) == 0:
|
||||||
|
self.last_mmes_err = "NoResponse"
|
||||||
|
return 1
|
||||||
|
if len(SMES) < 5 or len(SMES) > 270:
|
||||||
|
self.last_mmes_err = "InvalidPacketSize"
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
self.parser = IdiBusSMESParser(SMES)
|
||||||
|
if self.parser.validate() == -1:
|
||||||
|
self.last_mmes_err = "Corrupted"
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
#Packet good
|
||||||
|
return 0
|
||||||
|
def _sendMMES(self,msg:IdiBusMMES, await_bytes=0 ):
|
||||||
|
#send it
|
||||||
|
#print(f"raw message : {msg.getMsg().hex()}")
|
||||||
|
self.line.writeData(msg.getMsg())
|
||||||
|
# read all data
|
||||||
|
SMES = self.line.readData(await_bytes)
|
||||||
|
#print(f"raw answer : {SMES.hex()}")
|
||||||
|
# No response from slave
|
||||||
|
if len(SMES) == 0:
|
||||||
|
self.last_mmes_err = "NoResponse"
|
||||||
|
return 1
|
||||||
|
if len(SMES) < 5 or len(SMES) > 270:
|
||||||
|
self.last_mmes_err = "InvalidPacketSize"
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
self.parser = IdiBusSMESParser(SMES)
|
||||||
|
if self.parser.validate() == -1:
|
||||||
|
self.last_mmes_err = "Corrupted"
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
# Packet good
|
||||||
|
self.last_mmes_err = self.parser.getErrcode()
|
||||||
|
return 0
|
||||||
|
class IdiBusDevice():
|
||||||
|
def __init__(self, num):
|
||||||
|
self.num = num
|
||||||
|
self.errors = {}
|
||||||
|
self.commands = {}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
from .CRC16 import modbus_crc16
|
||||||
|
|
||||||
|
#Fast Master message type -I hate this-
|
||||||
|
|
||||||
|
#Usage
|
||||||
|
#1. Construct message
|
||||||
|
#2. Append data if needed
|
||||||
|
#3. get message body (CRC will be added automatically)
|
||||||
|
class IdiBusMMESG:
|
||||||
|
def __init__(self, address, mmps, cmd):
|
||||||
|
self.MMESG = bytearray()
|
||||||
|
self.MMESG.extend(int(address).to_bytes(1, byteorder="little"))
|
||||||
|
self.MMESG.extend(int(mmps).to_bytes(1, byteorder="little"))
|
||||||
|
self.MMESG.extend(int(cmd).to_bytes(1, byteorder="little"))
|
||||||
|
|
||||||
|
def addData(self, data):
|
||||||
|
self.MMESG.extend(data)
|
||||||
|
#Appends CRC16 and returns whole packet
|
||||||
|
def getMsg(self):
|
||||||
|
crc = modbus_crc16(self.MMESG)
|
||||||
|
self.MMESG.extend(crc)
|
||||||
|
return self.MMESG
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"MMESG({self.MMESG})"
|
||||||
|
|
||||||
|
#Usage
|
||||||
|
#1. Construct message
|
||||||
|
#2. Append data if needed
|
||||||
|
#3. get message body (CRC will be added automatically)
|
||||||
|
#Main Master message type
|
||||||
|
class IdiBusMMES:
|
||||||
|
def __init__(self, address, mmps, dev, ch,cmd):
|
||||||
|
self.MMES = bytearray()
|
||||||
|
if (cmd <=15):
|
||||||
|
mmps |= (cmd << 2)
|
||||||
|
self.MMES.extend(address.to_bytes(1, byteorder="little"))
|
||||||
|
self.MMES.extend(mmps.to_bytes(1, byteorder="little"))
|
||||||
|
self.MMES.extend(dev.to_bytes(1, byteorder="little"))
|
||||||
|
self.MMES.extend(ch.to_bytes(1, byteorder="little"))
|
||||||
|
if (cmd > 15):
|
||||||
|
self.MMES.extend(cmd.to_bytes(1, byteorder="little"))
|
||||||
|
def addData(self, data):
|
||||||
|
self.MMES.extend(bytearray(data))
|
||||||
|
|
||||||
|
def getMsg(self):
|
||||||
|
crc = modbus_crc16(self.MMES)
|
||||||
|
self.MMES.extend(crc)
|
||||||
|
return self.MMES
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Addr:{self.getAddr()}"
|
||||||
|
|
||||||
|
|
||||||
|
#Slave message constructor
|
||||||
|
class IdiBusSMES:
|
||||||
|
def __init__(self, address, spdu, errcode):
|
||||||
|
self.SMES = bytearray()
|
||||||
|
self.SMES.extend(address.to_bytes(1, byteorder="little"))
|
||||||
|
self.SMES.extend(spdu.to_bytes(1, byteorder="little"))
|
||||||
|
self.SMES.extend(errcode.to_bytes(1, byteorder="little"))
|
||||||
|
def addData(self, data):
|
||||||
|
self.SMES.extend(bytearray(data))
|
||||||
|
|
||||||
|
def getMsg(self):
|
||||||
|
crc = modbus_crc16(self.SMES)
|
||||||
|
self.SMES.extend(crc)
|
||||||
|
return self.SMES
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"SMES: {self.SMES}"
|
||||||
|
|
||||||
|
|
||||||
|
class IdiBusMMPS:
|
||||||
|
typeMMESG = 1
|
||||||
|
typeMMES = 0
|
||||||
|
def __init__(self, MesType = typeMMESG,):
|
||||||
|
self.LongMessage = 0
|
||||||
|
self.MesType = MesType
|
||||||
|
self.CommandType = 0
|
||||||
|
self.AlarmFrame = 0
|
||||||
|
self.EncriptedAES = 0
|
||||||
|
def getValue(self):
|
||||||
|
val = 0
|
||||||
|
val |= (self.LongMessage << 0) | (self.MesType << 1) | (self.CommandType << 2) | (self.AlarmFrame << 6) | (self.EncriptedAES << 7)
|
||||||
|
return val
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
import struct
|
||||||
|
from .CRC16 import modbus_crc16
|
||||||
|
|
||||||
|
|
||||||
|
def get_bit(byte, position):
|
||||||
|
return (byte >> position) & 1
|
||||||
|
|
||||||
|
|
||||||
|
class IdiBusMES:
|
||||||
|
def __init__(self, data: bytearray):
|
||||||
|
if len(data) == 0:
|
||||||
|
raise TypeError("Data is an empty bytearray")
|
||||||
|
self.data = data
|
||||||
|
def validate(self):
|
||||||
|
if self.getCRC() != modbus_crc16(self.data[0:-2]):
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
def getAddress(self):
|
||||||
|
return int(self.data[0])
|
||||||
|
def getCRC(self):
|
||||||
|
return self.data[-2::]
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Addr:{self.getAddress()}, Body:{self.data[1::]}"
|
||||||
|
class IdiBusMMESParser(IdiBusMES):
|
||||||
|
def __init__(self, data: bytearray):
|
||||||
|
super().__init__(data)
|
||||||
|
def getMMPS(self):
|
||||||
|
return self.data[1]
|
||||||
|
def getLongMes(self):
|
||||||
|
return get_bit(int(self.data[1]), 0)
|
||||||
|
def getMesType(self):
|
||||||
|
return get_bit(int(self.data[1]), 1)
|
||||||
|
def getAlarm(self):
|
||||||
|
return get_bit(int(self.data[1]), 6)
|
||||||
|
def getEncStatus(self):
|
||||||
|
return get_bit(int(self.data[1]), 7)
|
||||||
|
def getDEV(self):
|
||||||
|
return int(self.data[2])
|
||||||
|
def getCHAN(self):
|
||||||
|
return int(self.data[3])
|
||||||
|
def getCMD(self):
|
||||||
|
return int(self.data[4])
|
||||||
|
def getData(self):
|
||||||
|
return self.data[4:-2]
|
||||||
|
|
||||||
|
class IdiBusMMESGParser(IdiBusMES):
|
||||||
|
def __init__(self, data: bytearray):
|
||||||
|
super().__init__(data)
|
||||||
|
def getMMPS(self):
|
||||||
|
return int(self.data[1])
|
||||||
|
def getCMD(self):
|
||||||
|
return int(self.data[2])
|
||||||
|
def getData(self):
|
||||||
|
return self.data[3:-2]
|
||||||
|
class IdiBusSMESParser(IdiBusMES):
|
||||||
|
def __init__(self, data: bytearray):
|
||||||
|
super().__init__(data)
|
||||||
|
def getSPDU(self):
|
||||||
|
return int(self.data[1])
|
||||||
|
def getErrorBit(self):
|
||||||
|
return get_bit(self.data[1], 0)
|
||||||
|
def getLongMessage(self):
|
||||||
|
return get_bit(self.data[1], 1)
|
||||||
|
def getErrcode(self):
|
||||||
|
return int(self.data[2])
|
||||||
|
def getData(self):
|
||||||
|
return self.data[3:-2]
|
||||||
|
def getDataStruct(self, format_string):
|
||||||
|
try:
|
||||||
|
unpacked = struct.unpack(format_string, self.getData())
|
||||||
|
return unpacked
|
||||||
|
except struct.error as e:
|
||||||
|
print(f"Parse error: {e}")
|
||||||
|
return None
|
||||||
|
except UnicodeDecodeError as e:
|
||||||
|
print(f"Parse error: {e}")
|
||||||
|
return None
|
||||||
|
class IdiBusDeviceInfoParser():
|
||||||
|
# Формат: 'B H I f 10s'
|
||||||
|
# B - unsigned char (1 байт)
|
||||||
|
# H - unsigned short (2 байта)
|
||||||
|
# I - unsigned int (4 байта)
|
||||||
|
# f - float (4 байта)
|
||||||
|
# 10s - строка из 10 байт
|
||||||
|
format_string = '<B 3s 6s 3s 2B 7s 6s 2B I'
|
||||||
|
def __init__(self, data: bytearray):
|
||||||
|
if len(data) == 0:
|
||||||
|
raise TypeError("Data is an empty bytearray")
|
||||||
|
self.data = data
|
||||||
|
#print(data[-4::])
|
||||||
|
def getStruct(self):
|
||||||
|
try:
|
||||||
|
unpacked = struct.unpack(self.format_string, self.data)
|
||||||
|
packed = {
|
||||||
|
'Padding': unpacked[0],
|
||||||
|
'GS1_country': unpacked[1].decode('utf-8'),
|
||||||
|
'GS1_company': unpacked[2].decode('utf-8'),
|
||||||
|
'ModuleType': unpacked[3].decode('utf-8'),
|
||||||
|
'HW_revison': str(unpacked[4]) +"." + str(unpacked[5]),
|
||||||
|
'SN': unpacked[6].decode('utf-8'),
|
||||||
|
'MAC': unpacked[7].decode('utf-8'),
|
||||||
|
'SW': str(unpacked[8]) + "." + str(unpacked[9]),
|
||||||
|
'AppCRC': hex(unpacked[10]),
|
||||||
|
}
|
||||||
|
return packed
|
||||||
|
except struct.error as e:
|
||||||
|
print(f"Parse error: {e}")
|
||||||
|
return None
|
||||||
|
except UnicodeDecodeError as e:
|
||||||
|
print(f"Parse error: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import serial
|
||||||
|
from serial import STOPBITS_TWO
|
||||||
|
|
||||||
|
|
||||||
|
class IdiBusSerialLine(serial.Serial):
|
||||||
|
|
||||||
|
def __init__(self, port="", baudrate=0, **kwargs):
|
||||||
|
if not port or not baudrate:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.maxPacketSize = 256 + 2 + 5 # From defines in Slave
|
||||||
|
self.defaultTimeout = 0.025
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
port=port,
|
||||||
|
baudrate=baudrate,
|
||||||
|
timeout=kwargs.get('timeout', self.defaultTimeout),
|
||||||
|
parity=kwargs.get('parity', serial.PARITY_NONE),
|
||||||
|
stopbits=kwargs.get('stopbits', STOPBITS_TWO),
|
||||||
|
**{k: v for k, v in kwargs.items()
|
||||||
|
if k not in ['timeout', 'parity', 'stopbits']}
|
||||||
|
)
|
||||||
|
|
||||||
|
#print(f"Serial port opened at {self.port}")
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if hasattr(self, 'is_open') and self.is_open:
|
||||||
|
self.close()
|
||||||
|
#print(f"Serial port closed at {self.port}")
|
||||||
|
|
||||||
|
def writeData(self, msg):
|
||||||
|
"""Отправка данных"""
|
||||||
|
return self.write(msg) # Возвращаем количество байт
|
||||||
|
|
||||||
|
def readData(self, await_bytes=0):
|
||||||
|
"""Чтение данных"""
|
||||||
|
if await_bytes == 0:
|
||||||
|
await_bytes = self.maxPacketSize
|
||||||
|
return self.read(await_bytes)
|
||||||
|
|
||||||
|
# Дополнительные полезные методы
|
||||||
|
def write_read(self, msg, response_bytes=0):
|
||||||
|
"""Отправить и прочитать ответ"""
|
||||||
|
self.writeData(msg)
|
||||||
|
return self.readData(response_bytes)
|
||||||
|
|
||||||
|
def flush_buffers(self):
|
||||||
|
"""Очистить буферы"""
|
||||||
|
self.reset_input_buffer()
|
||||||
|
self.reset_output_buffer()
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
from .IdiBusSerialLine import *
|
||||||
|
from .IdiBusParse import IdiBusMMESParser, IdiBusMMESGParser
|
||||||
|
|
||||||
|
SelfAddress = 254
|
||||||
|
|
||||||
|
#Emulates slave module to communicate with master
|
||||||
|
class IdiBusSlave():
|
||||||
|
def __init__(self,address,line:IdiBusSerialLine):
|
||||||
|
self.address = address
|
||||||
|
self.line = line
|
||||||
|
|
||||||
|
def await_message(self, retries):
|
||||||
|
addr = 0
|
||||||
|
for i in range(0,retries):
|
||||||
|
msg = self.line.readData(self.line.maxPacketSize)
|
||||||
|
# No response
|
||||||
|
if len(msg) < 3:
|
||||||
|
continue
|
||||||
|
parser = IdiBusMMESParser(msg)
|
||||||
|
# Corrupted
|
||||||
|
if parser.validate() == -1:
|
||||||
|
continue
|
||||||
|
# This one is not for this Slave
|
||||||
|
if parser.getAddress() != self.address:
|
||||||
|
continue
|
||||||
|
# Got a correct message
|
||||||
|
if parser.getMesType() == 1:
|
||||||
|
parser = IdiBusMMESGParser(msg)
|
||||||
|
return parser
|
||||||
|
return None
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
from .IdiBusMaster import IdiBusSerialLine, IdiBusModule, IdiBusSMES
|
||||||
|
from .IdiBusSlave import IdiBusSlave
|
||||||
|
from .IdiBusDefs import comFuncList
|
||||||
|
|
||||||
|
class IdiBusUtil():
|
||||||
|
def __init__(self, line):
|
||||||
|
if not isinstance(line, IdiBusSerialLine):
|
||||||
|
raise TypeError("Line is not an IdiBusSerialLine")
|
||||||
|
self.line = line
|
||||||
|
def __del__(self):
|
||||||
|
self.line.close()
|
||||||
|
#Returns list with adresses of all active devices
|
||||||
|
def releaseLine(self):
|
||||||
|
self.line.close()
|
||||||
|
def scanBus(self,addr):
|
||||||
|
Dev = IdiBusModule(addr= addr, line=self.line)
|
||||||
|
if Dev.c_Init() == "OK":
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
def scanBus(self,start, end):
|
||||||
|
addresses = []
|
||||||
|
for i in range(start,end):
|
||||||
|
Dev = IdiBusModule(addr= i, line=self.line)
|
||||||
|
if Dev.c_Init() == "OK":
|
||||||
|
addresses.append(i)
|
||||||
|
return addresses
|
||||||
|
def shutdownMaster(self):
|
||||||
|
#print(f"Trying to shutdown Master")
|
||||||
|
SelfAddress = 254
|
||||||
|
self.line.timeout = 0.01
|
||||||
|
SpareMaster = IdiBusSlave(SelfAddress, self.line)
|
||||||
|
self.line.flush_buffers()
|
||||||
|
msg = SpareMaster.await_message(10)
|
||||||
|
#print(msg)
|
||||||
|
if msg == None:
|
||||||
|
return -1
|
||||||
|
if comFuncList.IDIMMES_COM_C_Init.value == int(msg.getCMD()):
|
||||||
|
#print("Got c_Init")
|
||||||
|
resp = IdiBusSMES(SelfAddress, 2, 0)
|
||||||
|
SpareMaster.line.writeData(resp.getMsg())
|
||||||
|
msg = SpareMaster.await_message(1)
|
||||||
|
try:
|
||||||
|
if len(msg.data) == 0:
|
||||||
|
return -1
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
#print(f'{msg.data}')
|
||||||
|
if msg.getMMPS() != 4:
|
||||||
|
#print(f"Got wrong mmps {msg.getMMPS()} != 4")
|
||||||
|
return -1
|
||||||
|
#print("Got order request")
|
||||||
|
resp = IdiBusSMES(SelfAddress, 2, 0)
|
||||||
|
a = bytearray()
|
||||||
|
a.append(5)
|
||||||
|
resp.addData(a)
|
||||||
|
msg = resp.getMsg()
|
||||||
|
SpareMaster.line.writeData(msg)
|
||||||
|
msg = SpareMaster.await_message(1)
|
||||||
|
try:
|
||||||
|
if len(msg.data) == 0:
|
||||||
|
return -1
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return -1
|
||||||
|
#print(f'{msg.data}')
|
||||||
|
|
||||||
|
if msg.getMMPS() != 8:
|
||||||
|
return -1
|
||||||
|
#print("Got order ans")
|
||||||
|
return 0
|
||||||
|
def recoverMaster(self):
|
||||||
|
print("Recover Master")
|
||||||
Loading…
Reference in new issue