Skip to content

Commit

Permalink
Pushing up code for poking at and debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
fall-byrd committed Oct 13, 2024
1 parent a993cb0 commit 3f599b2
Show file tree
Hide file tree
Showing 8 changed files with 410 additions and 94 deletions.
5 changes: 3 additions & 2 deletions backend/api/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import logging

import api.routes
from config import microlabConfig as config
from api.server import APIServer
# from config import microlabConfig as config
from api.app import app
from microlab.interface import MicrolabInterface

Expand All @@ -18,4 +19,4 @@ def runFlask(in_queue, out_queue):
werkzeugLogger.setLevel(logging.WARNING)

api.routes.microlabInterface = MicrolabInterface(in_queue, out_queue)
app.run(host="0.0.0.0", port=config.apiPort)
APIServer(app).run()
14 changes: 14 additions & 0 deletions backend/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

from api.app import app
from api.server import APIServer
from flask import jsonify, request, send_file
from werkzeug.utils import secure_filename
from os.path import join
Expand Down Expand Up @@ -388,3 +389,16 @@ def fetchLogs():
mostRecent = logFiles[-1]
data = data + Path(mostRecent).read_text()
return (jsonify({'logs': data}), 200)


@app.route('/shutdown', methods=['PUT'])
def shutdown_server():
"""Stops server """
# At first read this seems excessive to kill the server however after
# digging deeper into it this may be the cleanest way to deal with this.
print('Hit shutdown endpoint')
try:
return (jsonify({'response': 'ok'}), 200)
finally:
print('Calling server shutdown now')
APIServer.shutdown()
35 changes: 35 additions & 0 deletions backend/api/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import logging

from waitress.server import create_server
from flask import Flask

from config import microlabConfig as config


class APIServer:

_server = None

def __init__(self, app: Flask):
# self._server = make_server('0.0.0.0', config.apiPort, app)
self._app = app
# self.get_server(app)
# ctx = app.app_context()
# ctx.push()

def run(self):
logging.info('Starting backend server')
# self._server.serve_forever()
self.get_server(self._app).run()

@classmethod
def get_server(cls, app: Flask):
if cls._server is None:
cls._server = create_server(app, host='0.0.0.0', port=config.apiPort)
return cls._server

@classmethod
def shutdown(cls):
logging.info('Shutting down backend server')
cls._server.close()
logging.info('Shutting down backend server complete')
2 changes: 1 addition & 1 deletion backend/hardware/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def get_microlab_hardware_controller(cls):

return cls._microlabHardware

def loadHardware(self, deviceDefinition: list[dict]):
def loadHardware(self, deviceDefinition: list[dict]) -> tuple[bool, str]:
"""
Loads and initializes the hardware devices
Expand Down
124 changes: 103 additions & 21 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
Look in api.routes for the actual api code
"""

from multiprocessing import Process, Queue
from microlab.core import startMicrolabProcess
import signal

import requests

from multiprocessing import Process, Queue, Value
from microlab.core import MicrolabHardwareManager
from api.core import runFlask
import config
import multiprocessing_logging
Expand All @@ -16,6 +20,8 @@
from util.logFormatter import MultiLineFormatter
import sys

from hardware.core import MicroLabHardware


def setupLogging():
logHandlers = []
Expand All @@ -37,25 +43,101 @@ def setupLogging():
multiprocessing_logging.install_mp_handler()


if __name__ == "__main__":
config.initialSetup()
setupLogging()
class BackendManager:

logging.info("### STARTING MAIN MICROLAB SERVICE ###")
def __init__(self):
self._microlab_manager_should_run = Value('i', 1)
self._q1 = Queue()
self._q2 = Queue()

q1 = Queue()
q2 = Queue()
def _shutdown_flask(self):
shutdown_url = f'http://localhost:{config.microlabConfig.apiPort}/shutdown'
print(f'_shutdown_flask: shutdown_url: {shutdown_url}')
response = requests.put(shutdown_url, timeout=1)
print(f'_shutdown_flask: response code: {response.status_code}')
# try:
# self._flaskProcess.terminate()
# except ValueError as e:
# print(e)
# # When you terminate the process this way it will kill it however it will
# # raise a secondary exception when killing the process, claiming there is no process to kill
# # even though there certainly is. I suspect this may be Flasks behavior in how it handles signals
# pass

microlabProcess = Process(
target=startMicrolabProcess, args=(q1, q2), name="microlab"
)
microlabProcess.start()
flaskProcess = Process(target=runFlask, args=(q2, q1), name="flask")
flaskProcess.start()

microlabProcess.join()
flaskProcess.join()
q1.close()
q2.close()
q1.join_thread()
q2.join_thread()
# except AttributeError as e:
# print(e)
# pass

def _handle_exit_signals(self, signum, frame):
print('in _handle_exit_signals')
self._microlab_manager_should_run.value = 0
self._shutdown_flask()
# print('in _handle_exit_signals closing queue 1')
# self._q1.close()
# print('in _handle_exit_signals closing queue 2')
# self._q2.close()
# print('in _handle_exit_signals queues closed')

def run(self):
config.initialSetup()
setupLogging()

logging.info("### STARTING MAIN MICROLAB SERVICE ###")

print('MAIN before')
# We're setting up a shared memory value here so that if the main process recieves a signal to terminate
# we can update the value to indicate to the process that it needs to terminate

self._microlab_hardware = MicroLabHardware.get_microlab_hardware_controller()
# self._microlab_manager = MicrolabHardwareManager(
# self._microlab_hardware, q1, q2, self._microlab_manager_should_run
# )
self._microlab_manager_process = MicrolabHardwareManager(
self._microlab_hardware, self._q1, self._q2, self._microlab_manager_should_run
)
print('MAIN before lab start')
# self._microlab_manager_process = Process(target=self._microlab_manager.run, name="microlab")
self._microlab_manager_process.start()
print(f'MAIN self._microlab_manager_process pid: {self._microlab_manager_process.pid}')

self._flaskProcess = Process(target=runFlask, args=(self._q2, self._q1), name="flask", daemon=True)
print('MAIN before flask start')
self._flaskProcess.start()

print(f'MAIN self._flaskProcess pid: {self._flaskProcess.pid}')
signal.signal(signal.SIGINT, self._handle_exit_signals)
signal.signal(signal.SIGTERM, self._handle_exit_signals)

print('MAIN before flask join')
self._flaskProcess.join()

print('MAIN before lab join')
try:
self._microlab_manager_process.join()
except Exception as e:
# We re-raise any exceptions in execution and if we see them we need to shut down flask
print(f'Hit lab exception: {e}')
self._shutdown_flask()
# raise
import sys
sys.exit(1)

print('MAIN before q1 close')
self._q1.close()
print('MAIN before q1 join')
self._q1.join_thread()
print('MAIN before q2 close')
self._q2.close()
print('MAIN before q2 join')
self._q2.join_thread()
print('MAIN run done')
# sys.exit(0)


def main():
backend_manager = BackendManager()
backend_manager.run()


if __name__ == "__main__":
main()
Loading

0 comments on commit 3f599b2

Please sign in to comment.