Skip to content
Snippets Groups Projects
Commit 5d1e3d1a authored by Adam Abed Abud's avatar Adam Abed Abud
Browse files

Merge branch 'master' of https://github.com/hev-sw/hev-sw

parents 47c580db 5b1c2fb4
Branches
No related merge requests found
......@@ -14,20 +14,28 @@ class payloadType(Enum):
class BaseFormat():
def __init__(self):
self.RPI_VERSION = 0xA0
self.version = 0
self.byteArray = None
self._RPI_VERSION = 0xA0
self._byteArray = None
self._type = payloadType.payloadUnset
self._version = 0
@property
def byteArray(self):
return self._byteArray
@byteArray.setter
def byteArray(self):
print("Use fromByteArray to change this")
# check for mismatch between pi and microcontroller version
def checkVersion(self):
if self.RPI_VERSION == self.version :
self.version_error = True
if self._RPI_VERSION == self._version :
self._version_error = True
else :
self.version_error = False
self._version_error = False
def getSize(self):
return len(self.byteArray)
return len(self._byteArray)
def getType(self):
return self._type
......@@ -48,72 +56,72 @@ class dataFormat(BaseFormat):
# < = little endian
# > = big endian
# ! = network format (big endian)
self.dataStruct = Struct("!BBHHHHHHHHHBBBBBB")
self.byteArray = None
self._dataStruct = Struct("!BBHHHHHHHHHBBBBBB")
self._byteArray = None
self._type = payloadType.payloadData
# make all zero to start with
self.version = 0
self.fsm_state = 0
self.pressure_air_supply = 0
self.pressure_air_regulated = 0
self.pressure_o2_supply = 0
self.pressure_o2_regulated = 0
self.pressure_buffer = 0
self.pressure_inhale = 0
self.pressure_patient = 0
self.temperature_buffer = 0
self.pressure_diff_patient = 0
self.readback_valve_air_in = 0
self.readback_valve_o2_in = 0
self.readback_valve_inhale = 0
self.readback_valve_exhale = 0
self.readback_valve_purge = 0
self.readback_mode = 0
self._version = 0
self._fsm_state = 0
self._pressure_air_supply = 0
self._pressure_air_regulated = 0
self._pressure_o2_supply = 0
self._pressure_o2_regulated = 0
self._pressure_buffer = 0
self._pressure_inhale = 0
self._pressure_patient = 0
self._temperature_buffer = 0
self._pressure_diff_patient = 0
self._readback_valve_air_in = 0
self._readback_valve_o2_in = 0
self._readback_valve_inhale = 0
self._readback_valve_exhale = 0
self._readback_valve_purge = 0
self._readback_mode = 0
def __repr__(self):
return f"""{{
"version" : {self.version},
"fsm_state" : {self.fsm_state},
"pressure_air_supply" : {self.pressure_air_supply},
"pressure_air_regulated" : {self.pressure_air_regulated},
"pressure_o2_supply" : {self.pressure_o2_supply},
"pressure_o2_regulated" : {self.pressure_o2_regulated},
"pressure_buffer" : {self.pressure_buffer},
"pressure_inhale" : {self.pressure_inhale},
"pressure_patient" : {self.pressure_patient},
"temperature_buffer" : {self.temperature_buffer},
"pressure_diff_patient" : {self.pressure_diff_patient},
"readback_valve_air_in" : {self.readback_valve_air_in},
"readback_valve_o2_in" : {self.readback_valve_o2_in},
"readback_valve_inhale" : {self.readback_valve_inhale},
"readback_valve_exhale" : {self.readback_valve_exhale},
"readback_valve_purge" : {self.readback_valve_purge},
"readback_mode" : {self.readback_mode}
"version" : {self._version},
"fsm_state" : {self._fsm_state},
"pressure_air_supply" : {self._pressure_air_supply},
"pressure_air_regulated" : {self._pressure_air_regulated},
"pressure_o2_supply" : {self._pressure_o2_supply},
"pressure_o2_regulated" : {self._pressure_o2_regulated},
"pressure_buffer" : {self._pressure_buffer},
"pressure_inhale" : {self._pressure_inhale},
"pressure_patient" : {self._pressure_patient},
"temperature_buffer" : {self._temperature_buffer},
"pressure_diff_patient" : {self._pressure_diff_patient},
"readback_valve_air_in" : {self._readback_valve_air_in},
"readback_valve_o2_in" : {self._readback_valve_o2_in},
"readback_valve_inhale" : {self._readback_valve_inhale},
"readback_valve_exhale" : {self._readback_valve_exhale},
"readback_valve_purge" : {self._readback_valve_purge},
"readback_mode" : {self._readback_mode}
}}"""
# for receiving dataFormat from microcontroller
# fill the struct from a byteArray,
def fromByteArray(self, byteArray):
self.byteArray = byteArray
(self.version,
self.fsm_state,
self.pressure_air_supply,
self.pressure_air_regulated,
self.pressure_o2_supply,
self.pressure_o2_regulated,
self.pressure_buffer,
self.pressure_inhale,
self.pressure_patient,
self.temperature_buffer,
self.pressure_diff_patient,
self.readback_valve_air_in,
self.readback_valve_o2_in,
self.readback_valve_inhale,
self.readback_valve_exhale,
self.readback_valve_purge,
self.readback_mode) = self.dataStruct.unpack(self.byteArray)
self._byteArray = byteArray
(self._version,
self._fsm_state,
self._pressure_air_supply,
self._pressure_air_regulated,
self._pressure_o2_supply,
self._pressure_o2_regulated,
self._pressure_buffer,
self._pressure_inhale,
self._pressure_patient,
self._temperature_buffer,
self._pressure_diff_patient,
self._readback_valve_air_in,
self._readback_valve_o2_in,
self._readback_valve_inhale,
self._readback_valve_exhale,
self._readback_valve_purge,
self._readback_mode) = self._dataStruct.unpack(self._byteArray)
# for sending dataFormat to microcontroller
......@@ -121,47 +129,47 @@ class dataFormat(BaseFormat):
# to the microcontroller
def toByteArray(self):
# since pi is sender
self.version = self.RPI_VERSION
self.byteArray = self.dataStruct.pack(
self.RPI_VERSION,
self.fsm_state,
self.pressure_air_supply,
self.pressure_air_regulated,
self.pressure_o2_supply,
self.pressure_o2_regulated,
self.pressure_buffer,
self.pressure_inhale,
self.pressure_patient,
self.temperature_buffer,
self.pressure_diff_patient,
self.readback_valve_air_in,
self.readback_valve_o2_in,
self.readback_valve_inhale,
self.readback_valve_exhale,
self.readback_valve_purge,
self.readback_mode
self._version = self._RPI_VERSION
self._byteArray = self._dataStruct.pack(
self._RPI_VERSION,
self._fsm_state,
self._pressure_air_supply,
self._pressure_air_regulated,
self._pressure_o2_supply,
self._pressure_o2_regulated,
self._pressure_buffer,
self._pressure_inhale,
self._pressure_patient,
self._temperature_buffer,
self._pressure_diff_patient,
self._readback_valve_air_in,
self._readback_valve_o2_in,
self._readback_valve_inhale,
self._readback_valve_exhale,
self._readback_valve_purge,
self._readback_mode
)
def getDict(self):
data = {
"version" : self.version,
"fsm_state" : self.fsm_state,
"pressure_air_supply" : self.pressure_air_supply,
"pressure_air_regulated" : self.pressure_air_regulated,
"pressure_o2_supply" : self.pressure_o2_supply,
"pressure_o2_regulated" : self.pressure_o2_regulated,
"pressure_buffer" : self.pressure_buffer,
"pressure_inhale" : self.pressure_inhale,
"pressure_patient" : self.pressure_patient,
"temperature_buffer" : self.temperature_buffer,
"pressure_diff_patient" : self.pressure_diff_patient,
"readback_valve_air_in" : self.readback_valve_air_in,
"readback_valve_o2_in" : self.readback_valve_o2_in,
"readback_valve_inhale" : self.readback_valve_inhale,
"readback_valve_exhale" : self.readback_valve_exhale,
"readback_valve_purge" : self.readback_valve_purge,
"readback_mode" : self.readback_mode
"version" : self._version,
"fsm_state" : self._fsm_state,
"pressure_air_supply" : self._pressure_air_supply,
"pressure_air_regulated" : self._pressure_air_regulated,
"pressure_o2_supply" : self._pressure_o2_supply,
"pressure_o2_regulated" : self._pressure_o2_regulated,
"pressure_buffer" : self._pressure_buffer,
"pressure_inhale" : self._pressure_inhale,
"pressure_patient" : self._pressure_patient,
"temperature_buffer" : self._temperature_buffer,
"pressure_diff_patient" : self._pressure_diff_patient,
"readback_valve_air_in" : self._readback_valve_air_in,
"readback_valve_o2_in" : self._readback_valve_o2_in,
"readback_valve_inhale" : self._readback_valve_inhale,
"readback_valve_exhale" : self._readback_valve_exhale,
"readback_valve_purge" : self._readback_valve_purge,
"readback_mode" : self._readback_mode
}
return data
......@@ -171,26 +179,26 @@ class dataFormat(BaseFormat):
class commandFormat(BaseFormat):
def __init__(self):
super().__init__()
self.dataStruct = Struct("!BBI")
self.byteArray = None
self._dataStruct = Struct("!BBI")
self._byteArray = None
self._type = payloadType.payloadCmd
self.version = 0
self.cmdCode = 0
self.param = 0
self._version = 0
self._cmdCode = 0
self._param = 0
def fromByteArray(self, byteArray):
self.byteArray = byteArray
(self.version,
self.cmdCode,
self.param) = self.dataStruct.unpack(self.byteArray)
self._byteArray = byteArray
(self._version,
self._cmdCode,
self._param) = self._dataStruct.unpack(self._byteArray)
def toByteArray(self):
# since pi is sender
self.byteArray = self.dataStruct.pack(
self.RPI_VERSION,
self.cmdCode,
self.param
self._byteArray = self._dataStruct.pack(
self._RPI_VERSION,
self._cmdCode,
self._param
)
class command_codes(Enum):
......@@ -203,25 +211,25 @@ class command_codes(Enum):
class alarmFormat(BaseFormat):
def __init__(self):
super().__init__()
self.dataStruct = Struct("!BBI")
self.byteArray = None
self._dataStruct = Struct("!BBI")
self._byteArray = None
self._type = payloadType.payloadAlarm
self.version = 0
self.alarmCode = 0
self.param = 0
self._version = 0
self._alarmCode = 0
self._param = 0
def fromByteArray(self, byteArray):
self.byteArray = byteArray
(self.version,
self.alarmCode,
self.param) = self.dataStruct.unpack(self.byteArray)
self._byteArray = byteArray
(self._version,
self._alarmCode,
self._param) = self._dataStruct.unpack(self._byteArray)
def toByteArray(self):
self.byteArray = self.dataStruct.pack(
self.RPI_VERSION,
self.alarmCode,
self.param
self._byteArray = self._dataStruct.pack(
self._RPI_VERSION,
self._alarmCode,
self._param
)
class alarm_codes(Enum):
......
......@@ -22,34 +22,34 @@ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(
class commsControl():
def __init__(self, port, baudrate = 115200, queueSizeReceive = 16, queueSizeSend = 16):
self.serial_ = None
self._serial = None
self.openSerial(port, baudrate)
# send queues are FIFO ring-buffers of the defined size
self.alarms_ = deque(maxlen = queueSizeSend)
self.commands_ = deque(maxlen = queueSizeSend)
self.data_ = deque(maxlen = queueSizeSend)
self._alarms = deque(maxlen = queueSizeSend)
self._commands = deque(maxlen = queueSizeSend)
self._data = deque(maxlen = queueSizeSend)
# received queue and observers to be notified on update
self.payloadrecv_ = deque(maxlen = queueSizeReceive)
self.observers_ = []
self._payloadrecv = deque(maxlen = queueSizeReceive)
self._observers = []
# needed to find packet frames
self.received_ = []
self.foundStart_ = False
self.timeLastTransmission_ = int(round(time.time() * 1000))
self._received = []
self._foundStart = False
self._timeLastTransmission = int(round(time.time() * 1000))
# packet counter checker
self.sequenceSend_ = 0
self.sequenceReceive_ = 0
self._sequenceSend = 0
self._sequenceReceive = 0
# initialize of the multithreading
self.lock_ = threading.Lock()
self.receiving_ = True
self._lock = threading.Lock()
self._receiving = True
receivingWorker = threading.Thread(target=self.receiver, daemon=True)
receivingWorker.start()
self.sending_ = True
self._sending = True
self._datavalid = threading.Event() # callback for send process
self._dvlock = threading.Lock() # make callback threadsafe
sendingWorker = threading.Thread(target=self.sender, daemon=True)
......@@ -58,51 +58,51 @@ class commsControl():
# open serial port
def openSerial(self, port, baudrate = 115200, timeout = 2):
if port is not None:
self.serial_ = serial.Serial(port = port, baudrate=baudrate, timeout = timeout)
self._serial = serial.Serial(port = port, baudrate=baudrate, timeout = timeout)
else:
try:
self.serial_.close()
self._serial.close()
except:
logging.warning("Serial device not open")
self.serial_ = None
self._serial = None
def sender(self):
while self.sending_:
while self._sending:
self._datavalid.wait()
if self.serial_ is not None:
if not self.serial_.in_waiting > 0:
self.sendQueue(self.alarms_ , 10)
self.sendQueue(self.commands_, 50)
self.sendQueue(self.data_ , 200)
if self._serial is not None:
if not self._serial.in_waiting > 0:
self.sendQueue(self._alarms , 10)
self.sendQueue(self._commands, 50)
self.sendQueue(self._data , 200)
with self._dvlock:
self._datavalid.clear()
def receiver(self):
while self.receiving_:
if self.serial_ is not None:
if self.serial_.in_waiting > 0:
with self.lock_:
while self._receiving:
if self._serial is not None:
if self._serial.in_waiting > 0:
with self._lock:
logging.debug("Receiving data...")
data = self.serial_.read(self.serial_.in_waiting)
data = self._serial.read(self._serial.in_waiting)
self.processPacket(data)
def sendQueue(self, queue, timeout):
if len(queue) > 0:
logging.debug(f'Queue length: {len(queue)}')
currentTime = int(round(time.time() * 1000))
if currentTime > (self.timeLastTransmission_ + timeout):
with self.lock_:
self.timeLastTransmission_ = currentTime
queue[0].setSequenceSend(self.sequenceSend_)
if currentTime > (self._timeLastTransmission + timeout):
with self._lock:
self._timeLastTransmission = currentTime
queue[0].setSequenceSend(self._sequenceSend)
self.sendPacket(queue[0])
def getQueue(self, payloadType):
if payloadType == commsConstants.payloadType.payloadAlarm:
return self.alarms_
return self._alarms
elif payloadType == commsConstants.payloadType.payloadCmd:
return self.commands_
return self._commands
elif payloadType == commsConstants.payloadType.payloadData:
return self.data_
return self._data
else:
return None
......@@ -122,21 +122,21 @@ class commsControl():
byte = bytes([byte])
# TODO: this could be written in more pythonic way
# force read byte by byte
self.received_.append(byte)
self._received.append(byte)
# logging.debug(byte)
# find starting flag of the packet
if not self.foundStart_ and byte == bytes([0x7E]):
self.foundStart_ = True
self.receivedStart_ = len(self.received_)
if not self._foundStart and byte == bytes([0x7E]):
self._foundStart = True
self._receivedStart = len(self._received)
# find ending flag of the packet
elif byte == bytes([0x7E]) :
decoded = self.decoder(self.received_, self.receivedStart_)
decoded = self.decoder(self._received, self._receivedStart)
if decoded is not None:
logging.debug(binascii.hexlify(decoded))
tmpComms = commsFormat.commsFromBytes(decoded)
if tmpComms.compareCrc():
control = tmpComms.getData()[tmpComms.getControl()+1]
self.sequenceReceive_ = (tmpComms.getData()[tmpComms.getControl()] >> 1) & 0x7F
self._sequenceReceive = (tmpComms.getData()[tmpComms.getControl()] >> 1) & 0x7F
# get type of payload and corresponding queue
payloadType = self.getInfoType(tmpComms.getData()[tmpComms.getAddress()])
......@@ -166,10 +166,10 @@ class commsControl():
commsResponse.setSequenceReceive(sequenceReceive)
self.sendPacket(commsResponse)
self.received_.clear()
self._received.clear()
self.foundStart_ = False
self.receivedStart_ = -1
self._foundStart = False
self._receivedStart = -1
def writePayload(self, payload):
payloadType = payload.getType()
......@@ -193,14 +193,14 @@ class commsControl():
def sendPacket(self, comms):
logging.debug("Sending data...")
logging.debug(binascii.hexlify(self.encoder(comms.getData())))
self.serial_.write(self.encoder(comms.getData()))
self._serial.write(self.encoder(comms.getData()))
def finishPacket(self, queue):
try:
if len(queue) > 0:
# 0x7F to deal with possible overflows (0 should follow after 127)
if ((queue[0].getSequenceSend() + 1) & 0x7F) == self.sequenceReceive_:
self.sequenceSend_ = (self.sequenceSend_ + 1) % 128
if ((queue[0].getSequenceSend() + 1) & 0x7F) == self._sequenceReceive:
self._sequenceSend = (self._sequenceSend + 1) % 128
queue.popleft()
except:
logging.debug("Queue is probably empty")
......@@ -252,27 +252,27 @@ class commsControl():
# callback to dependants to read the received payload
@property
def payloadrecv(self):
return self.payloadrecv_
return self._payloadrecv
@payloadrecv.setter
def payloadrecv(self, payload):
self.payloadrecv_.append(payload)
self._payloadrecv.append(payload)
logging.debug(f"Pushed {payload} to FIFO")
for callback in self.observers_:
for callback in self._observers:
# peek at the leftmost item, don't pop until receipt confirmed
callback(self.payloadrecv_[0])
callback(self._payloadrecv[0])
def bind_to(self, callback):
self.observers_.append(callback)
self._observers.append(callback)
def pop_payloadrecv(self):
# from callback. confirmed receipt, pop value
poppedval = self.payloadrecv_.popleft()
poppedval = self._payloadrecv.popleft()
logging.debug(f"Popped {poppedval} from FIFO")
if len(self.payloadrecv_) > 0:
if len(self._payloadrecv) > 0:
# purge full queue if Dependant goes down when it comes back up
for callback in self.observers_:
callback(self.payloadrecv_[0])
for callback in self._observers:
callback(self._payloadrecv[0])
if __name__ == "__main__" :
# example dependant
......
......@@ -30,9 +30,9 @@ def generateData(payload):
# basic format based on HDLC
class commsFormat:
def __init__(self, infoSize = 0, address = 0x00, control = [0x00, 0x00]):
self.data_ = bytearray(7 + infoSize)
self.infoSize_ = infoSize
self.crc_ = None
self._data = bytearray(7 + infoSize)
self._infoSize = infoSize
self._crc = None
self.assignBytes(self.getStart() , bytes([0x7E]) , calcCrc = False)
self.assignBytes(self.getAddress(), bytes([address]), calcCrc = False)
......@@ -50,11 +50,11 @@ class commsFormat:
def getInformation(self):
return 4
def getFcs(self):
return 4 + self.infoSize_
return 4 + self._infoSize
def getStop(self):
return 4 + self.infoSize_ + 2
return 4 + self._infoSize + 2
def setAddress(self, adddress):
def setAddress(self, address):
self.assignBytes(self.getAddress(), bytes([address]), 1)
def setControl(self, control):
......@@ -66,7 +66,7 @@ class commsFormat:
def setSequenceSend(self, value):
# sequence sent valid only for info frames (not supervisory ACK/NACK)
if (self.data_[self.getControl() + 1] & 0x01) == 0:
if (self._data[self.getControl() + 1] & 0x01) == 0:
value = (value << 1) & 0xFE
self.assignBytes(self.getControl() + 1, value.to_bytes(1, byteorder='little'), 1)
......@@ -76,41 +76,41 @@ class commsFormat:
def getSequenceSend(self):
# sequence sent valid only for info frames (not supervisory ACK/NACK)
if (self.data_[self.getControl() + 1] & 0x01) == 0:
return (self.data_[self.getControl() + 1] >> 1) & 0x7F
if (self._data[self.getControl() + 1] & 0x01) == 0:
return (self._data[self.getControl() + 1] >> 1) & 0x7F
else:
return 0xFF
def getSequenceReceive(self):
return (self.data_[self.getControl()] >> 1) & 0x7F
return (self._data[self.getControl()] >> 1) & 0x7F
def assignBytes(self, start, values, calcCrc = True):
for idx in range(len(values)):
self.data_[start + idx] = values[idx]
self._data[start + idx] = values[idx]
if calcCrc:
self.generateCrc()
# generate checksum
def generateCrc(self, assign = True):
self.crc_ = libscrc.x25(bytes(self.data_[self.getAddress():self.getFcs()])).to_bytes(2, byteorder='little')
self._crc = libscrc.x25(bytes(self._data[self.getAddress():self.getFcs()])).to_bytes(2, byteorder='little')
if assign:
self.assignBytes(self.getFcs(), self.crc_, calcCrc = False)
self.assignBytes(self.getFcs(), self._crc, calcCrc = False)
def compareCrc(self):
self.generateCrc(False)
fcs = self.getData()[self.getFcs():self.getFcs()+2]
return self.crc_ in fcs
return self._crc in fcs
def getData(self):
return self.data_
return self._data
def copyData(self, dataArray):
self.copyBytes(dataArray.to_bytes(self.infoSize_, byteorder='little'))
self.copyBytes(dataArray.to_bytes(self._infoSize, byteorder='little'))
def copyBytes(self, bytesArray):
self.infoSize_ = len(bytesArray) - 7
self.data_ = bytesArray
self._infoSize = len(bytesArray) - 7
self._data = bytesArray
# ACK specific formating
class commsACK(commsFormat):
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment