Browse Source

backend: Add DI framework and logging from serial_communication.py

ble
Andreas Berthoud 5 years ago
parent
commit
8f324ff550
  1. 5
      backend/__init__.py
  2. 132
      backend/command.py
  3. 58
      backend/container.py
  4. 3
      backend/defaults/config.yml
  5. 1
      requirements.txt

5
backend/__init__.py

@ -4,6 +4,7 @@ from logging.config import dictConfig
from flask_api import FlaskAPI
from . import command
from . import container
def create_app() -> FlaskAPI:
@ -25,10 +26,12 @@ def create_app() -> FlaskAPI:
"formatter": "default",
},
},
"root": {"level": "INFO", "handlers": ["wsgi"]},
"root": {"level": "DEBUG", "handlers": ["wsgi"]},
},
)
container.init_app(app)
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
# prevent from be called twice in debug mode
command.start_backgroup_process()

132
backend/command.py

@ -2,6 +2,8 @@ import atexit
import logging
import os
import time
from dataclasses import dataclass
from enum import Enum
from multiprocessing import Process
from multiprocessing import Queue
from typing import Optional
@ -10,6 +12,10 @@ from flask import Response
from flask import blueprints
from flask import request
from flask_api import status
from serial import Serial
from .container import get_container
from .container import initialize_container
bp = blueprints.Blueprint("command", __name__)
@ -35,28 +41,130 @@ def _end_running_process():
_process.kill()
def poll_from_usb(queue: Queue):
def worker_process(
queue: Queue,
config,
# container: Container
):
logging.basicConfig(
level=logging.DEBUG,
format="[%(asctime)s] [%(levelname)-8s] --- %(message)s",
format="[%(asctime)s] [%(name)-20s] [%(levelname)-8s] --- %(message)s",
)
logger = logging.getLogger("poll_from_usb")
logger = logging.getLogger("worker_process")
initialize_container(config)
container = get_container()
counter = 1
while True:
logger.info(f"Ping {counter} {os.getpid()}")
counter += 1
time.sleep(1)
with container.serial() as serial:
while True:
logger.info(f"Ping {counter} {os.getpid()}")
counter += 1
time.sleep(0.5)
receive_and_log(
serial=serial,
header_size=container.config.header_size(),
)
if queue is not None:
while not queue.empty():
command_id = queue.get()
logger.debug(f"device_id {container.config.device_id()}")
logger.info(f"would execute command: {command_id}")
class CommandId(Enum):
command_none = 0
command_log = 0xFF
@dataclass
class CommandMeta:
command_id: CommandId
data_length: int
@dataclass
class LogCommand:
"""Command ID: command_log"""
level: int
message: str
HEADER_SIZE = 1 # log level
def __init__(
self,
data: bytes,
) -> None:
self._logger = logging.getLogger(self.__class__.__name__)
self.received_logger = logging.getLogger("stm32wb55")
self.received_logger.setLevel(logging.DEBUG)
self._logger.setLevel(logging.INFO)
level = int(data[0])
self._logger.debug(f"level: {level}")
if queue is not None:
while not queue.empty():
command_id = queue.get()
logger.info(f"would execute command: {command_id}")
message = data[self.HEADER_SIZE :]
self._logger.debug("Message: " + str(message))
self.level = level
self.message = message.decode()
def execute(self):
self.received_logger.log(level=self.level, msg=self.message)
def receive_and_log(
serial: Serial,
header_size: int,
):
logger = logging.getLogger("receive_and_log")
logger.setLevel(logging.INFO)
bytes_read = serial.read(serial.in_waiting)
while bytes_read:
logger.debug(f"bytes: {bytes_read.hex()}")
command_id = int(bytes_read[0])
logger.debug(f"command_id: {command_id}")
data_length = (int(bytes_read[1]) << 4) + int(bytes_read[2])
logger.debug(f"data_length: {data_length}")
meta = CommandMeta(
command_id=CommandId(command_id),
data_length=data_length,
)
if meta.command_id == CommandId.command_log:
command = LogCommand(
data=bytes_read[header_size : header_size + meta.data_length],
)
command.execute()
else:
return
stop_byte = bytes_read[header_size + meta.data_length]
logger.debug(f"stop_byte: {stop_byte}")
assert stop_byte == 0xFF
bytes_read = bytes_read[header_size + meta.data_length + 1 :]
def start_backgroup_process():
_logger.warning("start_backgroup_process called")
global _process
_process = Process(target=poll_from_usb, args=(_queue,))
_process = Process(
target=worker_process,
args=(
_queue,
get_container().config(),
),
)
_process.start()
atexit.register(_end_running_process)

58
backend/container.py

@ -0,0 +1,58 @@
from pathlib import Path
from typing import Dict
from typing import Optional
from dependency_injector import containers
from dependency_injector import providers
from flask_api import FlaskAPI
from serial import Serial
DEFAULTS_DIR = Path(__file__).parent / "defaults"
CONFIG_FILE = DEFAULTS_DIR / "config.yml"
class Container(containers.DeclarativeContainer):
config = providers.Configuration("config")
serial = providers.Factory(
Serial,
port=config.device_id.required(),
baudrate=config.baudrate.required(),
)
__container: Optional[Container] = None
def initialize_container(
config: Optional[Dict] = None,
config_file: Optional[Path] = None,
) -> Container:
global __container
if __container is not None:
raise RuntimeError("Container already initialized")
__container = Container()
__container.config.from_yaml(CONFIG_FILE)
if config is not None:
__container.config.from_dict(config)
if config_file is not None:
__container.config.from_yaml(config_file)
return __container
def init_app(app: FlaskAPI):
initialize_container(
config=app.config.get("DI_CONFIG"),
config_file=app.config.get("DI_CONFIG_FILE"),
)
def get_container() -> Container:
global __container
if __container is None:
raise RuntimeError("Container not initialized")
return __container

3
backend/defaults/config.yml

@ -0,0 +1,3 @@
device_id: /dev/tty.usbmodem2067368F32521
baudrate: 115200
header_size: 4

1
requirements.txt

@ -1,3 +1,4 @@
dependency-injector[yaml]>=4.34.0,<5
flask-api>=3.0.post1,<4
pre-commit
pyserial>=3.5,<4

Loading…
Cancel
Save