diff --git a/src/instrumentation/.thermocouple.py.swp b/src/instrumentation/.thermocouple.py.swp new file mode 100644 index 0000000..8cd281a Binary files /dev/null and b/src/instrumentation/.thermocouple.py.swp differ diff --git a/src/instrumentation/read_labjack.py b/src/instrumentation/read_labjack.py index 8d06faa..57cf36d 100644 --- a/src/instrumentation/read_labjack.py +++ b/src/instrumentation/read_labjack.py @@ -1,6 +1,9 @@ import u6 import json from thermocouple import * +import os +import posix +import datetime # Gains X1 = 0b00000000 @@ -131,10 +134,10 @@ ######### BEGIN USER ADJUSTABLE ######### # Stream settings -scan_frequency = 25 -resolution_index = 2 -settling_factor = 2 -samples_per_packet = 50 +scan_frequency = 500 +resolution_index = 4 +settling_factor = 4 +samples_per_packet = 12 channel_settings = [(86, DIFF | X1000), # L_RUN_TANK (SEEMS GOOD. CHECK CAL) (87, DIFF | X1000), # L_THRUST (VERY NOISY) @@ -195,38 +198,50 @@ SettlingFactor = settling_factor, SamplesPerPacket = samples_per_packet) -# Get cold junction voltage using LJ internal temp sensor -V_ref = get_ref_voltage(d.getTemperature()) -print("T_ref in K: ", d.getTemperature()) -print("V_ref in V: ", V_ref) - -# Avoid having to power cycle the LJ on restart try: d.streamStop() + print("Stream was still running. Stopping...") except: pass +# Get cold junction voltage using LJ internal temp sensor +V_ref = get_ref_voltage(d.getTemperature()) + +# Avoid having to power cycle the LJ on restart + if samples_per_packet < len(channel_settings): raise ValueError \ ("samples_per_packet: (" + str(samples_per_packet) + \ ") must be at least the number of channels: (" + \ str(len(channel_settings)) + ")!") +pipe_path = "/home/uvr/Documents/GitHub/PDP-Monitoring-System/src/instrumentation/data" + +try: + posix.mkfifo(pipe_path) + print("Named pipe created successfully!") +except FileExistsError: + print("Named pipe already exists!") +except OSError as e: + print(f"Named pipe creation failed: {e}") + # Stream data from the LJ if d is None: print("No LabJack device connected. Exiting...") exit() else: + print("Starting stream...") d.streamStart() try: - with open('instrumentation_data.txt', 'w') as file: + #with open('data_log.txt', 'w') as data_log: + with open(pipe_path, 'w') as file: # Contains sensor values in SI units converted = {} for reading in d.streamData(convert=False): - + # Reading is a dict of many things, one of which is the # 'result' which can be passed to processStreamData() to # give voltages. @@ -253,6 +268,7 @@ SHUNT = values[CHAN_SHUNT] # Convert voltage to sensor value in SI units and store in dict + converted['P_INJECTOR'] = \ (sum(P_INJECTOR)/len(P_INJECTOR))*GAIN_P_INJECTOR @@ -268,6 +284,12 @@ converted['P_RUN_TANK'] = \ (sum(P_RUN_TANK)/len(P_RUN_TANK))*GAIN_P_RUN_TANK + converted['L_RUN_TANK'] = \ + (sum(L_RUN_TANK)/len(L_RUN_TANK))*GAIN_L_RUN_TANK+OFFSET_L_RUN_TANK + + converted['L_THRUST'] = \ + (sum(L_THRUST)/len(L_THRUST))*GAIN_L_THRUST+OFFSET_L_THRUST + # Thermocouples converted['T_RUN_TANK'] = \ V_to_K((sum(T_RUN_TANK)/len(T_RUN_TANK)), V_ref) @@ -281,15 +303,21 @@ converted['T_POST_COMB'] = \ V_to_K((sum(T_POST_COMB)/len(T_POST_COMB)), V_ref) + converted['timestamp'] = str(datetime.datetime.now().time()) + # Write to file so websocket can send to ground support file.write(f'{json.dumps(converted)}\n') - with open('tmp.txt', 'w') as tmp: - tmp.write(f'{json.dumps(converted)}') - tmp.write('\n!') + file.flush() + + # Log to PDP for later analysis + #data_log.write(f'{json.dumps(converted)}\n') + except: print("Interrupt signal received!") finally: d.streamStop() print("Stream stopped.\n") d.close() + os.remove(pipe_path) + diff --git a/src/instrumentation/thermocouple.py b/src/instrumentation/thermocouple.py index 119d1b8..9b70bc8 100644 --- a/src/instrumentation/thermocouple.py +++ b/src/instrumentation/thermocouple.py @@ -8,14 +8,14 @@ def get_ref_voltage(T_cold_junction_K): # For 0C to 1372C at reduced accuracy c0 = -1.7600413686 * 10**-2 - c1 = 3.8921204975 * 10**-2 - c2 = 1.8558770032 * 10**-5 + c1 = 3.8921204975 * 10**-2 + c2 = 1.8558770032 * 10**-5 c3 = -9.9457592874 * 10**-8 - c4 = 3.1840945719 * 10**-10 + c4 = 3.1840945719 * 10**-10 c5 = -5.6072844889 * 10**-13 - c6 = 5.6075059059 * 10**-16 + c6 = 5.6075059059 * 10**-16 c7 = -3.2020720003 * 10**-19 - c8 = 9.7151147152 * 10**-23 + c8 = 9.7151147152 * 10**-23 c9 = -1.2104721275 * 10**-26 mV = c0 + \ diff --git a/src/main.py b/src/main.py index b603bb7..d83fdc9 100644 --- a/src/main.py +++ b/src/main.py @@ -22,7 +22,7 @@ def main() -> None: exit(1) try: - wss = WebSocketServer("serial") + wss = WebSocketServer("SERIAL_WS") except Exception as e: print(f"Failed to initialize websocket server: {e}") exit(1) diff --git a/src/server/instrumentationMock.py b/src/server/instrumentationMock.py new file mode 100644 index 0000000..204ad4d --- /dev/null +++ b/src/server/instrumentationMock.py @@ -0,0 +1,23 @@ +import math +import time + +TEMPERATURE_SENSOR_RANGE = range(273, 300) +PRESSURE_SENSOR_RANGE = range(1, 100) +LOAD_SENSOR_RANGE = range(0, 15) + +def labjack_mock(): + time.sleep(0.001) + return { + 'P_INJECTOR': math.randint(PRESSURE_SENSOR_RANGE), + 'P_COMB_CHMBR': math.randint(PRESSURE_SENSOR_RANGE), + 'P_N2O_FLOW': math.randint(PRESSURE_SENSOR_RANGE), + 'P_N2_FLOW': math.randint(PRESSURE_SENSOR_RANGE), + 'P_RUN_TANK': math.randint(PRESSURE_SENSOR_RANGE), + 'L_RUN_TANK': math.randint(LOAD_SENSOR_RANGE), + 'L_THRUST': math.randint(LOAD_SENSOR_RANGE), + 'T_RUN_RANK': math.randint(TEMPERATURE_SENSOR_RANGE), + 'T_INJECTOR': math.randint(TEMPERATURE_SENSOR_RANGE), + 'T_COMB_CHMBR': math.randint(TEMPERATURE_SENSOR_RANGE), + 'T_POST_COMB': math.randint(TEMPERATURE_SENSOR_RANGE), + 'SHUNT': math.randint(TEMPERATURE_SENSOR_RANGE), + } \ No newline at end of file diff --git a/src/server/serailMock.py b/src/server/serailMock.py new file mode 100644 index 0000000..33ff79f --- /dev/null +++ b/src/server/serailMock.py @@ -0,0 +1,21 @@ +import time + +def serial_feedback_mock( + valve: str, + action: str +) -> dict: + ''' + Name: + serial_feedback(command: string, valve: string, action: string) -> dict + Args: + valve: the valve that the command is for + action: the action to take on the valve + Desc: + Creates a dictionary to send over the websocket + ''' + action = action if action == "OPEN" or action == "CLOSE" else "TRANSIT" + return { + 'identifier': 'FEEDBACK', + 'valve': valve, + 'action': action + } \ No newline at end of file diff --git a/src/server/testModeUtils/___init__.py b/src/server/testModeUtils/___init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/server/testModeUtils/mock_instrumentation.py b/src/server/testModeUtils/mock_instrumentation.py new file mode 100644 index 0000000..177adab --- /dev/null +++ b/src/server/testModeUtils/mock_instrumentation.py @@ -0,0 +1,57 @@ +import json +import random +import time +import tornado.ioloop +import tornado.web +import tornado.websocket + +clients = set() # Keep track of connected clients + +# Function to generate a dictionary with fixed keys and random values +def generate_data(): + return { + "P_INJECTOR": random.randint(0, 1000000), + "P_COMB_CHMBR": random.randint(0, 500000), + "P_N2O_FLOW": random.randint(0, 1000000), + "P_N2_FLOW": random.randint(0, 1000000), + "P_RUN_TANK": random.randint(0, 1000000), + + "T_RUN_TANK": random.randint(200, 300), + "T_INJECTOR": random.randint(200, 300), + "T_COMB_CHMBR": random.randint(300, 1500), + "T_POST_COMB": random.randint(300, 1500), + + "L_RUN_TANK": random.randint(0, 20), + "L_THRUST": random.randint(0, 1200) + } + +class WebSocketHandler(tornado.websocket.WebSocketHandler): + def open(self): + clients.add(self) + print("New client connected.") + + def on_close(self): + clients.remove(self) + print("Client disconnected.") + +# Function to send data to clients +def send_data(): + data = generate_data() + json_data = json.dumps({'data':data}) + for client in clients: + client.write_message(json_data) # Send to each connected client + #print("Sent data:", json_data) + tornado.ioloop.IOLoop.current().call_later(0.001, send_data) # Call again in 1 second + +def make_app(): + return tornado.web.Application([ + (r'/websocket', WebSocketHandler), + ]) + +if __name__ == "__main__": + app = make_app() + app.listen(8888) # Choose an appropriate port + print("Starting WebSocket server on port 8888...") + send_data() # Start sending data + tornado.ioloop.IOLoop.current().start() # Start the Tornado I/O loop + diff --git a/src/server/testPipes.py b/src/server/testPipes.py new file mode 100644 index 0000000..dedd31c --- /dev/null +++ b/src/server/testPipes.py @@ -0,0 +1,5 @@ + +while True: + with open("../instrumentation/data") as pipe: + line = pipe.readline() + print(line) diff --git a/src/server/wsEntrypoint.py b/src/server/wsInstrumentationEntrypoint.py similarity index 76% rename from src/server/wsEntrypoint.py rename to src/server/wsInstrumentationEntrypoint.py index 6d66550..f70b339 100644 --- a/src/server/wsEntrypoint.py +++ b/src/server/wsInstrumentationEntrypoint.py @@ -5,5 +5,6 @@ if __name__ == "__main__" or __name__ == "LJWebsocket": print("Entered Instrumentation") - ws = WebSocketServer("instrumentation") + ws = WebSocketServer("INSTRUMENTATION_WS") asyncio.run(ws.start_instrumentation()) + print("yes") diff --git a/src/server/wsTestServerEntryPoint.py b/src/server/wsTestServerEntryPoint.py new file mode 100644 index 0000000..b04b393 --- /dev/null +++ b/src/server/wsTestServerEntryPoint.py @@ -0,0 +1,33 @@ +import asyncio +from wss import WebSocketServer + +__name__ = "TestServer" + +def main() -> None: + serial_wss = None + instrumentation_wss = None + + try: + serial_wss = WebSocketServer(ws_type="SERIAL_WS", test_mode=True) + instrumentation_wss = WebSocketServer(ws_type="INSTRUMENTATION_WS", test_mode=True) + except Exception as e: + print(f"Failed to initialize websocket server: {e}") + exit(1) + + event_loop = asyncio.get_event_loop() + try: + event_loop.create_task(serial_wss.start_serial()) + event_loop.create_task(instrumentation_wss.start_instrumentation()) + except Exception as e: + print(f"Failed to start websocket server: {e}") + exit(1) + + try: + event_loop.run_forever() + except KeyboardInterrupt: + event_loop.close() + print("Test Server terminated by user") + exit(0) + +if __name__ == "TestServer" or __name__ == "__main__": + main() diff --git a/src/server/wss.py b/src/server/wss.py index 5095dbb..6f37ae1 100644 --- a/src/server/wss.py +++ b/src/server/wss.py @@ -1,18 +1,29 @@ +import os import asyncio +import time import websockets import json import logging import platform +import posix +# from .instrumentationMock import labjack_mock as lj_mock +# from .serailMock import serial_feedback_mock as serial_mock __name__ = "WebSocketServer" OS = platform.system() -HOST = "192.168.0.1" if OS == "Linux" else "localhost" +HOST_PRODUCTION = "192.168.0.1" +HOST_TEST = "localhost" + PORT_SERIAL = 8080 PORT_INSTRUMENTATION = 8888 -INSTRUMENTATION_FILE_DATA_PATH = 'tmp.txt' +INSTRUMENTATION_FILE_DATA_PATH = "/home/uvr/Documents/GitHub/PDP-Monitoring-System/src/instrumentation/data" + + +INSTRUMENTATION_WS_TYPE = "INSTRUMENTATION_WS" +SERIAL_WS_TYPE = "SERIAL_WS" class Command: def __init__(self, command, valve, action): @@ -27,16 +38,21 @@ def __init__(self, valve, action): self.action = action class WebSocketServer: - def __init__(self, ws_type: str): + def __init__(self, ws_type: str, test_mode: bool = False): self.__ws_type = ws_type - if self.__ws_type == "instrumentation": + self.__test_mode = test_mode + self.__host = HOST_PRODUCTION + + if test_mode: + self.__test_mode = True + self.__host = HOST_TEST + + if self.__ws_type == INSTRUMENTATION_WS_TYPE: self.__port = PORT_INSTRUMENTATION else: self.__port = PORT_SERIAL - print(f'type:{self.__ws_type}, port:{self.__port}') - - self.__host = HOST + print(f'type:{self.__ws_type}, port:{self.__port}, host:{self.__host}') self.__wss_instance = None @@ -45,6 +61,7 @@ def __init__(self, ws_type: str): self.__incoming_queue = asyncio.LifoQueue() self.__configure_log() + self.__instrumentation_data = open(INSTRUMENTATION_FILE_DATA_PATH, 'r') def __configure_log(self): @@ -93,15 +110,42 @@ async def __instrumentation_handler(self, websocket): Handles the websocket requests and serial feedback to send over the websocket ''' print("instrumentation handler") + last_line = "" + while True: + lines = self.__instrumentation_data.readline() + + if lines != last_line: + await websocket.send(json.dumps({ + "identifier": "INSTRUMENTATION", + "data": json.loads(lines) + })) + last_line = lines + await asyncio.sleep(0.0005) + + + async def __test_instrumentation__handler(self, websocket): + print("Test Instrumentation Handler") while True: - with open('../instrumentation/tmp.txt', 'r') as file: - lines = file.readlines() - if len(lines) > 1: - await websocket.send(json.dumps({ - "identifier": "INSTRUMENTATION", - "data": json.loads(lines[0]) - })) - await asyncio.sleep(0.1) + packet = lj_mock() + await websocket.send(json.dumps({ + "identifier": "INSTRUMENTATION", + "data": packet + })) + await asyncio.sleep(0) + + + async def __test_serial_handler(self, websocket): + print("Test Serial Handler") + while True: + async for message in websocket: + packet = json.loads(message) + await websocket.send(json.dumps(serial_mock(valve=packet['valve'], action='TRANSIT'))) + print(f"Sent: {packet['valve']} TRANSIT") + time.sleep(3) + feedback = serial_mock(valve=packet['valve'], action=packet['action']) + print(f"Sent: {feedback}") + await websocket.send(json.dumps(feedback)) + await asyncio.sleep(0) async def wss_reception_handler(self, queue): @@ -160,7 +204,8 @@ async def start_serial(self): Desc: Starts the websocket server ''' - async with websockets.serve(self.__serial_handler, self.__host, self.__port): + handler = self.__serial_handler if not self.__test_mode else self.__test_serial_handler + async with websockets.serve(handler, self.__host, self.__port): await asyncio.Future() async def start_instrumentation(self): @@ -170,6 +215,6 @@ async def start_instrumentation(self): Desc: Starts the websocket server ''' - async with websockets.serve(self.__instrumentation_handler, self.__host, self.__port): + handler = self.__instrumentation_handler if not self.__test_mode else self.__test_instrumentation__handler + async with websockets.serve(handler, self.__host, self.__port): await asyncio.Future() - \ No newline at end of file