Skip to content

Commit

Permalink
Development (#17)
Browse files Browse the repository at this point in the history
* implemented env var based config and separate io microservice support

* bugfix

* replace custom URI logic with Yarl

* bugfix

* bugfix

* add missing keys

* bugfix to avoid key overriding

* fixed config loading pipeline

* bugfix

* changed config name
  • Loading branch information
arseniiarsenii authored Apr 14, 2022
1 parent 5ba26f6 commit 784ff65
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 213 deletions.
25 changes: 25 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
version: "3"
services:
feecc_workbench_daemon:
environment:
# Use these environment variables to configure your deployment
DB_MONGO_CONNECTION_URI: "" # Your MongoDB connection URI
ROBONOMICS_ENABLE_DATALOG: false # Whether to enable datalog posting or not
ROBONOMICS_ACCOUNT_SEED: "" # Your Robonomics network account seed phrase
ROBONOMICS_SUBSTRATE_NODE_URI: null # Robonomics network node URI
YOURLS_SERVER: "" # Your Yourls server URL
YOURLS_USERNAME: "" # Your Yourls server username
YOURLS_PASSWORD: "" # Your Yourls server password
IPFS_GATEWAY_ENABLE: false # Whether to enable IPFS posting or not
IPFS_GATEWAY_IPFS_SERVER_URI: http://127.0.0.1:8082 # Your IPFS gateway deployment URI
PRINTER_ENABLE: false # Whether to enable printing or not
PRINTER_PRINT_SERVER_URI: http://127.0.0.1:8083 # Your Print-server deployment URI
PRINTER_PRINT_BARCODE: true # Whether to print barcodes or not
PRINTER_PRINT_QR: true # Whether to print QR codes or not
PRINTER_PRINT_QR_ONLY_FOR_COMPOSITE: false # Whether to enable QR code printing for non-composite units or note or not
PRINTER_QR_ADD_LOGOS: false # Whether to add logos to the QR code or not
PRINTER_PRINT_SECURITY_TAG: false # Whether to enable printing security tags or not
PRINTER_SECURITY_TAG_ADD_TIMESTAMP: true # Whether to enable timestamps on security tags or not
CAMERA_ENABLE: "" # Whether to enable Cameraman or not
CAMERA_CAMERAMAN_URI: http://127.0.0.1:8081 # Your Cameraman deployment URI
CAMERA_CAMERA_NO: 1 # Camera number
WORKBENCH_NUMBER: 1 # Workbench number
HID_DEVICES_RFID_READER: IC Reader IC Reader # RFID reader device name
HID_DEVICES_BARCODE_READER: Newtologic NT1640S # Barcode reader device name
build:
context: ./
dockerfile: Dockerfile
Expand Down
31 changes: 29 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,35 @@
### Развертывание Feecc Workbench Daemon

Клонируйте данный репозиторий с GitHub с помощью команды `git clone`. Перейдите в папку репозитория и поменяйте файл
конфигурации `src/config/config.yaml` в соответствии со своими нуждами.
Клонируйте данный репозиторий с GitHub с помощью команды `git clone`.

Перейдите в папку репозитория и поменяйте файл `docker-compose.yml` в соответствии со своими нуждами. Конфигурация
определяется рядом переменных окружения:

- **DB_MONGO_CONNECTION_URL** (Required): Your MongoDB connection URI
- **ROBONOMICS_ENABLE_DATALOG** (Optional): Whether to enable datalog posting or not
- **ROBONOMICS_ACCOUNT_SEED** (Optional): Your Robonomics network account seed phrase
- **ROBONOMICS_SUBSTRATE_NODE_URL** (Optional): Robonomics network node URI
- **YOURLS_SERVER** (Required): Your Yourls server URL
- **YOURLS_USERNAME** (Required): Your Yourls server username
- **YOURLS_PASSWORD** (Required): Your Yourls server password
- **IPFS_GATEWAY_ENABLE** (Optional): Whether to enable IPFS posting or not
- **IPFS_GATEWAY_IPFS_SERVER_URI** (Optional): Your IPFS gateway deployment URI
- **PRINTER_ENABLE** (Optional): Whether to enable printing or not
- **PRINTER_PRINT_SERVER_URI** (Optional): Your Print-server deployment URI
- **PRINTER_PRINT_BARCODE** (Optional): Whether to print barcodes or not
- **PRINTER_PRINT_QR** (Optional): Whether to print QR codes or not
- **PRINTER_PRINT_QR_ONLY_FOR_COMPOSITE** (Optional): Whether to enable QR code printing for non-composite units or note
or not
- **PRINTER_QR_ADD_LOGOS** (Optional): Whether to add logos to the QR code or not
- **PRINTER_PRINT_SECURITY_TAG** (Optional): Whether to enable printing security tags or not
- **PRINTER_SECURITY_TAG_ADD_TIMESTAMP** (Optional): Whether to enable timestamps on security tags or not
- **CAMERA_ENABLE** (Optional): Whether to enable Cameraman or not
- **CAMERA_CAMERAMAN_URI** (Optional): Your Cameraman deployment URI
- **CAMERA_CAMERA_NO** (Optional): Camera number
- **WORKBENCH_NUMBER** (Required): Workbench number
- **HID_DEVICES_RFID_READER** (Optional): RFID reader device name
- **HID_DEVICES_BARCODE_READER** (Optional): Barcode reader device name

Разверните Feecc Workbench Daemon при помощи Docker-compose: находясь в корне репозитория введите команду
`sudo docker-compose up -d --build`.
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "Feecc-Workbench-Daemon"
version = "1.1.1"
version = "1.1.2"
description = "Workbench software for the Feecc QA system"
authors = ["arseniiarsenii <arseniivelichko2@gmail.com>"]
license = "Apache 2.0"
Expand All @@ -19,6 +19,8 @@ loguru = "^0.5.3"
motor = "^2.5.1"
httpx = "^0.19.0"
robonomics-interface = "^0.5.2"
environ-config = "^22.1.0"
yarl = "^1.7.2"

[tool.poetry.dev-dependencies]
mypy = "^0.902"
Expand Down
43 changes: 0 additions & 43 deletions src/config/config.yaml

This file was deleted.

9 changes: 6 additions & 3 deletions src/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from feecc_workbench.Employee import Employee
from feecc_workbench.Unit import Unit
from feecc_workbench.unit_utils import UnitStatus
from feecc_workbench.config import config
from feecc_workbench.config import CONFIG
from feecc_workbench.database import MongoDbWrapper
from feecc_workbench.exceptions import EmployeeNotFoundError, UnitNotFoundError
from feecc_workbench.utils import is_a_ean13_barcode
Expand Down Expand Up @@ -44,11 +44,14 @@ async def get_revision_pending_units() -> tp.List[Unit]:
return await MongoDbWrapper().get_all_units_by_status(UnitStatus.revision) # type: ignore


def identify_sender(event: models.HidEvent) -> models.HidEvent:
def identifrfid_readery_sender(event: models.HidEvent) -> models.HidEvent:
"""identify, which device the input is coming from and if it is known return it's role"""
logger.debug(f"Received event dict: {event.dict(include={'string', 'name'})}")

known_hid_devices: tp.Dict[str, str] = config.hid_devices_names.dict()
known_hid_devices: tp.Dict[str, str] = {
"rfid_reader": CONFIG.hid_devices.rfid_reader,
"barcode_reader": CONFIG.hid_devices.barcode_reader,
}

for sender_name, device_name in known_hid_devices.items():
if device_name == event.name:
Expand Down
10 changes: 5 additions & 5 deletions src/feecc_workbench/Camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import httpx
from loguru import logger

from .config import config
from .config import CONFIG
from .utils import get_headers

IO_GATEWAY_ADDRESS: str = config.feecc_io_gateway.gateway_address
CAMERAMAN_ADDRESS: str = CONFIG.camera.cameraman_uri


@dataclass
Expand All @@ -34,7 +34,7 @@ def __init__(self, number: int) -> None:
def _check_presence(self) -> None:
"""check if self is registered on the backend"""
try:
response = httpx.get(f"{IO_GATEWAY_ADDRESS}/video/cameras")
response = httpx.get(f"{CAMERAMAN_ADDRESS}/cameras")
except httpx.ConnectError:
logger.critical("GW connection has been refused. Is it up?")
return
Expand All @@ -52,7 +52,7 @@ async def start(self, rfid_card_id: str) -> None:
"""start the provided record"""
async with httpx.AsyncClient() as client:
response: httpx.Response = await client.post(
url=f"{IO_GATEWAY_ADDRESS}/video/camera/{self.number}/start", headers=get_headers(rfid_card_id)
url=f"{CAMERAMAN_ADDRESS}/camera/{self.number}/start", headers=get_headers(rfid_card_id)
)

if response.is_error:
Expand All @@ -70,7 +70,7 @@ async def end(self, rfid_card_id: str) -> None:

async with httpx.AsyncClient() as client:
response: httpx.Response = await client.post(
url=f"{IO_GATEWAY_ADDRESS}/video/record/{self.record.rec_id}/stop",
url=f"{CAMERAMAN_ADDRESS}/record/{self.record.rec_id}/stop",
headers=get_headers(rfid_card_id),
)

Expand Down
55 changes: 11 additions & 44 deletions src/feecc_workbench/IO_gateway.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,25 @@
from __future__ import annotations

import os
import socket
import typing as tp

import httpx
from loguru import logger
from robonomicsinterface import RobonomicsInterface

from ._image_generation import create_qr
from .config import config
from .config import CONFIG
from .database import MongoDbWrapper
from .utils import get_headers, time_execution

IO_GATEWAY_ADDRESS: str = config.feecc_io_gateway.gateway_address
PRINT_SERVER_ADDRESS: str = CONFIG.printer.print_server_uri
IPFS_GATEWAY_ADDRESS: str = CONFIG.ipfs_gateway.ipfs_server_uri
ROBONOMICS_CLIENT = RobonomicsInterface(
seed=config.robonomics_network.account_seed,
remote_ws=config.robonomics_network.substrate_node_url,
seed=CONFIG.robonomics.account_seed or None,
remote_ws=CONFIG.robonomics.substrate_node_uri or None,
)


def control_flag(func: tp.Any) -> tp.Any:
"""This ensures autonomous mode is handled properly"""

def wrap_func(*args: tp.Any, **kwargs: tp.Any) -> tp.Any:
if not config.feecc_io_gateway.autonomous_mode:
return func(*args, **kwargs)

return wrap_func


@control_flag
def gateway_is_up() -> None:
"""check if camera is reachable on the specified port and ip"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.25)

try:
gw_socket_no_proto = IO_GATEWAY_ADDRESS.split("//")[1]
ip, port = gw_socket_no_proto.split(":")
s.connect((ip, int(port)))
logger.debug(f"{IO_GATEWAY_ADDRESS} is up")
s.close()

except socket.error:
raise BrokenPipeError(f"{IO_GATEWAY_ADDRESS} is unreachable")

except Exception as e:
logger.error(e)


@time_execution
def generate_qr_code(target_link: str) -> str:
"""generate a QR code"""
Expand All @@ -66,19 +36,19 @@ async def post_to_datalog(content: str, unit_internal_id: str) -> None:
logger.info(f"Adding {txn_hash=} to unit {unit_internal_id} data")
unit = await MongoDbWrapper().get_unit_by_internal_id(unit_internal_id)
unit.txn_hash = txn_hash
await MongoDbWrapper().update_unit(unit)
await MongoDbWrapper().update_unit(unit, include_keys=["txn_hash"])
logger.info(f"{unit_internal_id} data has been updated")


@control_flag
@time_execution
async def publish_file(rfid_card_id: str, file_path: os.PathLike[tp.AnyStr]) -> tp.Tuple[str, str]:
"""publish a provided file to IPFS using the Feecc gateway and return it's CID and URL"""
gateway_is_up()
if not CONFIG.ipfs_gateway.enable:
raise ValueError("IPFS Gateway disabled in config")

is_local_path: bool = os.path.exists(file_path)
headers: tp.Dict[str, str] = get_headers(rfid_card_id)
base_url = f"{IO_GATEWAY_ADDRESS}/io-gateway/publish-to-ipfs"
base_url = f"{IPFS_GATEWAY_ADDRESS}/publish-to-ipfs"

async with httpx.AsyncClient(base_url=base_url, timeout=None) as client:
if is_local_path:
Expand All @@ -101,18 +71,15 @@ async def publish_file(rfid_card_id: str, file_path: os.PathLike[tp.AnyStr]) ->
return cid, link


@control_flag
@time_execution
async def print_image(file_path: str, rfid_card_id: str, annotation: tp.Optional[str] = None) -> None:
"""print the provided image file"""
if not config.printer.enable:
if not CONFIG.printer.enable:
logger.warning("Printer disabled, task dropped")
return

gateway_is_up()

async with httpx.AsyncClient() as client:
url = f"{IO_GATEWAY_ADDRESS}/printing/print_image"
url = f"{PRINT_SERVER_ADDRESS}/print_image"
headers: tp.Dict[str, str] = get_headers(rfid_card_id)
data = {"annotation": annotation}
files = {"image_file": open(file_path, "rb")}
Expand Down
22 changes: 10 additions & 12 deletions src/feecc_workbench/WorkBench.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from .utils import timestamp
from ._image_generation import create_seal_tag
from ._short_url_generator import generate_short_url
from .config import config
from .config import CONFIG
from .database import MongoDbWrapper
from .exceptions import StateForbiddenError
from .models import ProductionSchema
Expand All @@ -33,11 +33,9 @@ class WorkBench(metaclass=SingletonMeta):
@logger.catch
def __init__(self) -> None:
self._database: MongoDbWrapper = MongoDbWrapper()
self.number: int = config.workbench_config.number
camera_number: tp.Optional[int] = config.hardware.camera_no
self.camera: tp.Optional[Camera] = (
Camera(camera_number) if camera_number and not config.feecc_io_gateway.autonomous_mode else None
)
self.number: int = CONFIG.workbench.number
camera_number: tp.Optional[int] = CONFIG.camera.camera_no
self.camera: tp.Optional[Camera] = Camera(camera_number) if camera_number and CONFIG.camera.enable else None
self.employee: tp.Optional[Employee] = None
self.unit: tp.Optional[Unit] = None
self.state: State = State.AWAIT_LOGIN_STATE
Expand All @@ -52,7 +50,7 @@ async def create_new_unit(self, schema: ProductionSchema) -> Unit:
unit = Unit(schema)
await self._database.upload_unit(unit)

if config.printer.print_barcode and not config.feecc_io_gateway.autonomous_mode:
if CONFIG.printer.print_barcode and CONFIG.printer.enable:
if unit.schema.parent_schema_id is None:
annotation = unit.schema.unit_name
else:
Expand Down Expand Up @@ -190,16 +188,16 @@ async def upload_unit_passport(self) -> None:

passport_file_path = await construct_unit_passport(self.unit)

if not config.feecc_io_gateway.autonomous_mode:
if CONFIG.ipfs_gateway.enable:
res = await publish_file(file_path=Path(passport_file_path), rfid_card_id=self.employee.rfid_card_id)
cid, link = res or ("", "")
short_url: str = generate_short_url(link)

self.unit.passport_ipfs_cid = cid
self.unit.passport_short_url = short_url

if config.printer.print_qr and (
not config.printer.print_qr_only_for_composite
if CONFIG.printer.print_qr and (
not CONFIG.printer.print_qr_only_for_composite
or self.unit.schema.is_composite
or not self.unit.schema.is_a_component
):
Expand All @@ -210,11 +208,11 @@ async def upload_unit_passport(self) -> None:
annotation=f"{self.unit.model_name} (ID: {self.unit.internal_id}). {short_url}",
)

if config.printer.print_security_tag:
if CONFIG.printer.print_security_tag:
seal_tag_img: str = create_seal_tag()
await print_image(seal_tag_img, self.employee.rfid_card_id)

if config.robonomics_network.enable_datalog and res is not None:
if CONFIG.robonomics.enable_datalog and res is not None:
# for now Robonomics interface library doesn't support async io.
# This operation requires waiting for the block to be written in the blockchain,
# which takes 15 seconds on average, so it's done in another thread
Expand Down
Loading

0 comments on commit 784ff65

Please sign in to comment.