Skip to content
Snippets Groups Projects
Commit ecdb7603 authored by Stephen Farry's avatar Stephen Farry
Browse files

Merge branch 'master' of ssh://gitlab.cern.ch:7999/hev-sw/hev-sw

parents ef579072 5dac9e1b
Branches
No related merge requests found
......@@ -259,7 +259,7 @@ $(document).ready(function() {
data: {
//labels: initial_xaxis,
datasets: [{
data: {},
data: [],
label: "Var1",
//borderColor: "#3e95cd",
borderColor: "#000000",
......@@ -335,7 +335,7 @@ $(document).ready(function() {
data: {
//labels: initial_xaxis,
datasets: [{
data: {},
data: [],
label: "Var1",
borderColor: "#ba0202",
borderWidth: 4,
......
#!/usr/bin/env python3
import asyncio
import logging
logging.basicConfig(level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s')
import sys
import time
import argparse
import json
import threading
from PyQt5 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot, mkColor
import pyqtgraph as pg
import sys
import os
from hevclient import HEVClient
class ClientPlots(QtWidgets.QMainWindow):
def __init__(self, light=False, port=54322, *args, **kwargs):
super(ClientPlots, self).__init__(*args, **kwargs)
self.history_length = 300
self.xrange = 300
self.port = port
self.setWindowTitle("HEV socket debug")
self.graphWidget = pg.GraphicsLayoutWidget()
self.setCentralWidget(self.graphWidget)
self.statusBar().showMessage("Waiting for data")
self.pressurePlot = self.graphWidget.addPlot(title="Pressure")
self.graphWidget.nextRow()
self.flowPlot = self.graphWidget.addPlot(title="Flow")
self.graphWidget.nextRow()
self.volumePlot = self.graphWidget.addPlot(title="Volume")
self.graphWidget.nextRow()
self.timestamp = list(el*(-1) for el in range(self.history_length))[::-1]
self.PID_P = list(0 for _ in range(self.history_length))
self.PID_I = list(0 for _ in range(self.history_length))
self.PID_D = list(0 for _ in range(self.history_length))
if light:
# light theme
self.graphWidget.setBackground('w')
else:
# dark theme
self.graphWidget.setBackground(mkColor(30,30,30))
#Add grid
self.flowPlot.showGrid(x=True, y=True)
self.volumePlot.showGrid(x=True, y=True)
self.pressurePlot.showGrid(x=True, y=True)
#Set Range
self.flowPlot.setXRange(self.xrange * (-1), 0, padding=0)
self.volumePlot.setXRange(self.xrange * (-1), 0, padding=0)
self.pressurePlot.setXRange(self.xrange * (-1), 0, padding=0)
self.flowPlot.enableAutoRange('y', True)
self.volumePlot.enableAutoRange('y', True)
self.pressurePlot.enableAutoRange('y', True)
self.line1 = self.plot(self.flowPlot, self.timestamp, self.PID_D, "Flow", "00F")
self.line2 = self.plot(self.volumePlot, self.timestamp, self.PID_I, "Volume", "707")
self.line3 = self.plot(self.pressurePlot, self.timestamp, self.PID_P, "Airway Pressure", "077")
# get data in another thread
self.worker = threading.Thread(target=self.polling, daemon=True)
self.worker.start()
def polling(self):
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# create tasks
plot = self.redraw()
debug = self.cmdDebug()
tasks = [debug, plot]
# run tasks
asyncio.gather(*tasks, return_exceptions=True)
loop.run_forever()
except asyncio.CancelledError:
pass
finally:
loop.close()
def plot(self, canvas, x, y, plotname, color):
pen = pg.mkPen(color=color, width=3)
return canvas.plot(x, y, name=plotname, pen=pen)
async def redraw(self):
reader, writer = await asyncio.open_connection("127.0.0.1", self.port)
while True:
try:
data = await reader.readuntil(separator=b'\0')
data = data[:-1] # snip off nullbyte
packet = json.loads(data.decode("utf-8"))
brtype = packet["type"]
if brtype == "keepalive":
continue
payload = packet[packet["type"]]
logging.debug(f"Received {brtype} packet: {payload}")
if brtype == "DATA":
self.statusBar().showMessage(f"Got data for timestamp {payload['timestamp']}")
logging.info("data acquired")
self.PID_D.append(payload["flow"])
self.PID_I.append(payload["volume"])
self.PID_P.append(payload["airway_pressure"])
if len(self.PID_D) > self.history_length:
self.PID_D = self.PID_D[1:]
self.PID_I = self.PID_I[1:]
self.PID_P = self.PID_P[1:]
self.line1.setData(self.timestamp, self.PID_D)
self.line2.setData(self.timestamp, self.PID_I)
self.line3.setData(self.timestamp, self.PID_P)
elif brtype == "READBACK":
pass
elif brtype == "CYCLE":
pass
elif brtype == "THRESHOLDS":
pass
elif brtype == "ALARM":
logging.error(f"received ALARM {payload}")
elif brtype == "IVT":
pass
elif brtype == "DEBUG":
pass
else:
raise KeyError
self._alarms = packet["alarms"]
except json.decoder.JSONDecodeError:
logging.warning(f"Could not decode packet: {data}")
except KeyError as e:
logging.warning(f"Invalid payload: {data}")
logging.error(e)
except Exception as e:
logging.error(e)
raise
# close connection
writer.close()
await writer.wait_closed()
async def cmdDebug(self):
"""Put debug code here"""
try:
client = HEVClient(polling=False) # just use hevclient for requests
await asyncio.sleep(2)
# trigger an alarm
await client.send_request("CMD", cmdtype="SET_THRESHOLD_MIN", cmd="APNEA",param=0)
while True:
await asyncio.sleep(60)
except Exception as e:
# otherwise never propagated
print(e)
if __name__ == "__main__":
# parse args and setup logging
parser = argparse.ArgumentParser(description='Plotting script for the HEV lab setup')
parser.add_argument('-d', '--debug', action='count', default=0, help='Show debug output')
parser.add_argument('--native', action='store_true', help="Use a port that doesn't clash with NativeUI")
parser.add_argument('--light', action='store_true', help='Use light mode')
args = parser.parse_args()
if args.debug == 0:
logging.getLogger().setLevel(logging.WARNING)
elif args.debug == 1:
logging.getLogger().setLevel(logging.INFO)
else:
logging.getLogger().setLevel(logging.DEBUG)
# setup pyqtplot widget
app = QtWidgets.QApplication(sys.argv)
port = 54320 if args.native else 54322
dep = ClientPlots(light=args.light, port=port)
dep.show()
sys.exit(app.exec_())
\ No newline at end of file
......@@ -13,7 +13,7 @@ import svpi
import hevfromtxt
from hevtestdata import HEVTestData
from CommsLLI import CommsLLI
from CommsCommon import PAYLOAD_TYPE, CMD_TYPE, CMD_GENERAL, CMD_SET_TIMEOUT, VENTILATION_MODE, ALARM_CODES, CMD_MAP, CommandFormat, AlarmFormat
from CommsCommon import PAYLOAD_TYPE, CMD_TYPE, CMD_GENERAL, CMD_SET_TIMEOUT, VENTILATION_MODE, ALARM_TYPE, ALARM_CODES, CMD_MAP, CommandFormat, AlarmFormat
from collections import deque
from serial.tools import list_ports
from typing import List
......@@ -41,14 +41,7 @@ class HEVServer(object):
logging.debug(f"Payload received: {payload}")
# check if it is data or alarm
payload_type = payload.getType()
if payload_type == PAYLOAD_TYPE.ALARM:
# Alarm is latched until acknowledged in GUI
with self._dblock:
if payload not in self._alarms:
self._alarms.append(payload)
# let broadcast thread know there is data to send
self._datavalid.set()
elif payload_type in [1,2,3,4,7,8] :
if payload_type in [1,2,3,4,6,7,8] :
# pass data to db
with self._dblock:
self._values = payload
......@@ -111,17 +104,18 @@ class HEVServer(object):
elif reqtype == "ALARM":
# acknowledgement of alarm from gui
reqalarm_type = request["alarm_type"]
reqalarm_code = ALARM_CODES[request["alarm_code"]].value
reqalarm_code = ALARM_CODES[request["alarm_code"]]
reqparam = request["param"] if request["param"] is not None else 0
alarm_to_ack = AlarmFormat(alarm_type=reqalarm_type,
alarm_to_ack = AlarmFormat(alarm_type=ALARM_TYPE[reqalarm_type],
alarm_code=reqalarm_code,
param=reqparam)
try:
# delete alarm if it exists
with self._dblock:
self._alarms.remove(alarm_to_ack)
for alarm in self._alarms:
if alarm == alarm_to_ack:
self._alarms.remove(alarm)
payload = {"type": "ack"}
except NameError as e:
raise HEVPacketError(f"Alarm could not be removed. May have been removed already. {e}")
......@@ -155,6 +149,15 @@ class HEVServer(object):
if self._values is None:
continue # should never get here
values: List[float] = self._values
# Alarm is latched until acknowledged in GUI
if self._values.getType() == PAYLOAD_TYPE.ALARM:
if self._values not in self._alarms:
self._alarms.append(self._values)
else:
# update param and timestamp
idx = self._alarms.index(self._values)
self._alarms[idx] = self._values
alarms = self._alarms if len(self._alarms) > 0 else None
data_type = values.getType().name
......
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