Skip to content
Snippets Groups Projects
Commit 8edb09d1 authored by Dónal Murray's avatar Dónal Murray
Browse files

Add command sending capabilities. Streamline commandFormat

parent 1b06392a
Branches
No related merge requests found
from struct import Struct
from enum import Enum, auto
from enum import Enum, auto, unique
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
......@@ -107,8 +107,6 @@ class dataFormat(BaseFormat):
# for receiving dataFormat from microcontroller
# fill the struct from a byteArray,
def fromByteArray(self, byteArray):
logging.debug(f"Byte array of size {len(byteArray)}")
logging.debug(f"Byte array of size {byteArray}")
self._byteArray = byteArray
(self._version,
self._fsm_state,
......@@ -182,16 +180,37 @@ class dataFormat(BaseFormat):
# cmd type payload
# =======================================
class commandFormat(BaseFormat):
def __init__(self):
def __init__(self, cmdCode=0, param=0):
super().__init__()
self._dataStruct = Struct("<BBI")
self._byteArray = None
self._type = payloadType.payloadCmd
self._version = 0
self._cmdCode = 0
self._param = 0
self._cmdCode = cmdCode
self._param = param
self.toByteArray()
# manage direct reading and writing of member variables
@property
def cmdCode(self):
return self._cmdCode
@cmdCode.setter
def cmdCode(self, cmdCodeIn):
self._cmdCode = cmdCodeIn
self.toByteArray()
@property
def param(self):
return self._param
@param.setter
def param(self, paramIn):
self._param = paramIn
self.toByteArray()
# print nicely
def __repr__(self):
return f"""{{
"version" : {self._version},
......@@ -221,9 +240,10 @@ class commandFormat(BaseFormat):
}
return data
@unique
class command_codes(Enum):
CMD_START = 1
CMD_STOP = 2
CMD_START = 0x1
CMD_STOP = 0x2
# =======================================
# alarm type payload
......@@ -267,6 +287,7 @@ class alarmFormat(BaseFormat):
}
return data
@unique
class alarm_codes(Enum):
ALARM_START = 1
ALARM_STOP = 2
\ No newline at end of file
......@@ -25,16 +25,14 @@ dep = Dependant(comms)
start = 0x1
stop = 0x2
cmd = commandFormat()
cmd.cmdCode = start
cmd.toByteArray()
# initialise as start command, automatically executes toByteArray()
cmd = commandFormat(cmdCode=start)
comms.writePayload(cmd)
#comms.sender()
while True:
time.sleep(30)
cmd.cmdCode = stop
cmd.toByteArray()
cmd.cmdCode = stop # automatically executes toByteArray()
comms.writePayload(cmd)
#comms.registerData(stop)
pass
......
......@@ -10,7 +10,7 @@ import threading
from typing import List, Dict
import logging
logging.basicConfig(level=logging.INFO,
format='hevclient %(asctime)s - %(levelname)s - %(message)s')
format='%(asctime)s - %(levelname)s - %(message)s')
polling = True
setflag = False
......@@ -38,11 +38,10 @@ class HEVClient(object):
# grab data from the socket as soon as it is available and dump it in the db
while self._polling:
data = await reader.read(500)
data = data.decode("utf-8")
data = json.loads(data)
payload = json.loads(data.decode("utf-8"))
with self._lock:
self._values = data["sensors"]
self._alarms = data["alarms"]
self._values = payload["sensors"]
self._alarms = payload["alarms"]
# close connection
writer.close()
......@@ -51,15 +50,19 @@ class HEVClient(object):
def start_client(self) -> None:
asyncio.run(self.polling())
async def send_request(self, type: str, mode: str = None, thresholds: List[float] = None) -> bool:
async def send_request(self, cmd: str, param: str=None) -> bool:
# open connection and send packet
reader, writer = await asyncio.open_connection("127.0.0.1", 54321)
payloads = {
"setmode": f"""{{"type": "setmode", "mode": \"{mode}\" }}""",
"setthresholds": f"""{{"type": "setthresholds", "thresholds": \"{thresholds}\"}}""",
"setup": f"""{{"type": "setup", "mode": \"{mode}\", "thresholds": \"{thresholds}\"}}"""
payload = {
"type": "cmd",
"cmd": cmd,
"param": param
}
writer.write(payloads[type].encode())
packet = json.dumps(payload).encode()
writer.write(packet)
await writer.drain()
# wait for acknowledge
......@@ -71,31 +74,16 @@ class HEVClient(object):
await writer.wait_closed()
# check that acknowledge is meaningful
if type == "setmode" and data["type"] == "ackmode":
logging.info(f"Mode {mode} set successfully")
return True
if type == "setthresholds" and data["type"] == "ackthresholds":
logging.info(f"Thresholds {thresholds} set successfully")
return True
if type == "setup" and data["type"] == "ack":
logging.info(
f"Mode {mode} and thresholds {thresholds} set successfully")
if data["type"] == "ack":
logging.info(f"Command {cmd} sent successfully")
return True
else:
logging.warning(f'Setting {type} failed')
logging.warning(f"Sending command {cmd} failed")
return False
def set_mode(self, mode: str) -> bool:
# set a mode and wait for acknowledge
return asyncio.run(self.send_request("setmode", mode=mode))
def set_thresholds(self, thresholds: List[float]) -> bool:
# set a threshold and wait for acknowledge
return asyncio.run(self.send_request("setthresholds", thresholds=thresholds))
def setup(self, mode: str, thresholds: List[float]) -> bool:
# set a mode and thresholds
return asyncio.run(self.send_request("setup", mode=mode, thresholds=thresholds))
def send_cmd(self, cmd: str, param: str=None) -> bool:
# send a cmd and wait to see if it's valid
return asyncio.run(self.send_request(cmd))
def get_values(self) -> Dict:
# get sensor values from db
......@@ -111,6 +99,7 @@ if __name__ == "__main__":
# just import hevclient and do something like the following
hevclient = HEVClient()
# Play with sensor values and alarms
for i in range(30):
values = hevclient.get_values() # returns a dict or None
......@@ -122,12 +111,11 @@ if __name__ == "__main__":
print(f"Alarms: {alarms}")
time.sleep(1)
# set modes and thresholds (this will change)
print(hevclient.set_mode("CPAP"))
print(hevclient.set_thresholds([12.3, 45.6, 78.9]))
print(hevclient.setup("CPAP", [12.3, 45.6, 78.9]))
print("The next one should fail as it is not a valid mode:")
print(hevclient.set_mode("foo"))
# send commands:
print(hevclient.send_cmd("CMD_START"))
time.sleep(1)
print("This one will fail since foo is not in the command_codes enum:")
print(hevclient.send_cmd("foo"))
# print some more values
for i in range(10):
......
......@@ -10,14 +10,16 @@ import threading
import argparse
import svpi
import commsControl
from commsConstants import payloadType
from commsConstants import payloadType, command_codes, alarm_codes, commandFormat
from collections import deque
from serial.tools import list_ports
from typing import List
import logging
logging.basicConfig(level=logging.DEBUG,
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
class HEVPacketError(Exception):
pass
class HEVServer(object):
def __init__(self, lli):
......@@ -80,47 +82,43 @@ class HEVServer(object):
# listen for queries on the request socket
data = await reader.read(300)
request = json.loads(data.decode("utf-8"))
# logging
addr = writer.get_extra_info("peername")
logging.info(f"Answering request from {addr}")
payload = ""
# three possible queries: set mode, set thresholds or both
if request["type"] == "setmode":
mode = request["mode"]
logging.debug(f"{addr!r} requested to change to mode {mode!r}")
# send via protocol and prepare reply
if self._lli.setMode(mode):
packet = f"""{{"type": "ackmode", "mode": \"{mode}\"}}""".encode()
else:
packet = f"""{{"type": "nack"}}""".encode()
elif request["type"] == "setthresholds":
thresholds = request["thresholds"]
logging.debug(
f"{addr!r} requested to set thresholds to {thresholds!r}")
# send via protocol
payload = self._lli.setThresholds(thresholds)
# prepare reply
packet = f"""{{"type": "ackthresholds", "thresholds": \"{payload}\"}}""".encode()
elif request["type"] == "setup":
mode = request["mode"]
thresholds = request["thresholds"]
logging.debug(f"{addr!r} requested to change to mode {mode!r}")
logging.debug(
f"{addr!r} requested to set thresholds to {thresholds!r}")
# send via protocol and prepare reply
if self._lli.setMode(mode):
self._lli.setThresholds(thresholds)
packet = f"""{{"type": "ack", "mode": \"{mode}\", "thresholds": \"{thresholds}\"}}""".encode()
else:
packet = f"""{{"type": "nack"}}""".encode()
# send reply and close connection
writer.write(packet)
await writer.drain()
writer.close()
try:
reqtype = request["type"]
if reqtype == "cmd":
reqcmd = request["cmd"]
reqparam = request["param"] if request["param"] is not None else 0
if reqcmd in command_codes.__members__:
# valid request
command = commandFormat(cmdCode=command_codes[reqcmd].value, param=reqparam)
self._lli.writePayload(command)
# processed and sent to controller, send ack to GUI since it's in enum
# TODO should we wait for ack from controller or is that going to block the port for too long?
payload = {"type": "ack"}
else:
raise HEVPacketError("Invalid command packet")
packet = json.dumps(payload).encode()
# send reply and close connection
writer.write(packet)
await writer.drain()
writer.close()
except (NameError, HEVPacketError) as e:
# invalid request: reject immediately
logging.warning(f"Invalid command packet. Type {reqtype} does not exist")
payload = {"type": "nack"}
packet = json.dumps(payload).encode()
writer.write(packet)
await writer.drain()
writer.close()
async def handle_broadcast(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
# log address
......@@ -142,7 +140,7 @@ class HEVServer(object):
broadcast_packet["sensors"] = values
broadcast_packet["alarms"] = alarms # add alarms key/value pair
logging.info(f"Send: {json.dumps(broadcast_packet,indent=4)}")
logging.debug(f"Send: {json.dumps(broadcast_packet,indent=4)}")
try:
writer.write(json.dumps(broadcast_packet).encode())
......
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