diff --git a/Makefile b/Makefile index 387319b..3ead5e0 100644 --- a/Makefile +++ b/Makefile @@ -25,26 +25,6 @@ server: env --lss-address-cache-file=$(LSS_ADDRESS_CACHE_FILE) \ $(args) -# packaging environment ####################################################### -$(PYTHON_PACKAGING_VENV)/.created: REQUIREMENTS.packaging.txt - rm -rf $(PYTHON_PACKAGING_VENV) && \ - $(PYTHON) -m venv $(PYTHON_PACKAGING_VENV) && \ - . $(PYTHON_PACKAGING_VENV)/bin/activate && \ - pip install --upgrade pip && \ - pip install -r REQUIREMENTS.packaging.txt - date > $(PYTHON_PACKAGING_VENV)/.created - -packaging-env: $(PYTHON_PACKAGING_VENV)/.created - -sdist: packaging-env - . $(PYTHON_PACKAGING_VENV)/bin/activate && \ - rm -rf dist *.egg-info && \ - ./setup.py sdist - -_release: sdist - . $(PYTHON_PACKAGING_VENV)/bin/activate && \ - twine upload dist/* - # testing ##################################################################### $(PYTHON_TESTING_ENV)/.created: rm -rf $(PYTHON_TESTING_ENV) && \ diff --git a/REQUIREMENTS.packaging.txt b/REQUIREMENTS.packaging.txt deleted file mode 100644 index 69c408a..0000000 --- a/REQUIREMENTS.packaging.txt +++ /dev/null @@ -1,2 +0,0 @@ -setuptools>=36.5.0 -twine diff --git a/bin/lxa-iobus-can-setup b/bin/lxa-iobus-can-setup deleted file mode 100755 index 4ab935a..0000000 --- a/bin/lxa-iobus-can-setup +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -interface=can0 -bitrate=100000 - -usage="$(basename "$0") [-i interface] [-b bitrate]" - -while getopts :hi:b: flag -do - case "${flag}" in - h) - echo $usage - exit - ;; - - i) - interface=${OPTARG} - ;; - - b) - bitrate=${OPTARG} - ;; - - \?) - exit - ;; - esac -done - -set -e -set -x - -sudo ip link set $interface type can bitrate $bitrate -sudo ip link set up dev $interface diff --git a/bin/lxa-iobus-server b/bin/lxa-iobus-server deleted file mode 100755 index 47aeddb..0000000 --- a/bin/lxa-iobus-server +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import asyncio -import errno -import logging -import os -import signal -import sys -import threading -import traceback -from argparse import ArgumentParser - -from aiohttp.web import Application, run_app - -from lxa_iobus.network import LxaNetwork -from lxa_iobus.server.server import LXAIOBusServer - - -def trace_handler(num, frame): - print("\nDumping all stacks:\n") - - for th in threading.enumerate(): - print(th) - traceback.print_stack(sys._current_frames()[th.ident]) - print() - - for task in asyncio.all_tasks(): - if not task.done(): - task.print_stack() - print() - - -signal.signal(signal.SIGQUIT, trace_handler) - - -def exit_handler(num, frame): - print("\nExiting...\n") - - os.kill(os.getpid(), signal.SIGKILL) - - -signal.signal(signal.SIGINT, exit_handler) - -# parse command line arguments -parser = ArgumentParser() - -parser.add_argument("interface") -parser.add_argument( - "--port", - type=int, - default=8080, - help="Port to serve on. Defaults to 8080", -) -parser.add_argument( - "--host", - type=str, - default="localhost", - help="Host to bind to. Defaults to 'localhost'", -) -parser.add_argument( - "--shell", - action="store_true", - help="Embeds an ipython shell for easy debugging", -) -parser.add_argument( - "--firmware-directory", - type=str, - default="firmware", - help="Directory where uploaded firmware is stored. " "Only used when --allow-custom-firmware is set.", -) -parser.add_argument( - "--allow-custom-firmware", - action="store_true", - help="Allows to upload and flash own (arbitrary) firmware " "into the program section of the nodes.", -) -parser.add_argument( - "--lss-address-cache-file", - type=str, - default="", - help="LSS addresses cache as json. Reduces startup time for known nodes.", -) - -parser.add_argument( - "-l", - "--log-level", - choices=["DEBUG", "INFO", "WARN", "ERROR", "FATAL"], - default="WARN", -) - -args = parser.parse_args() - -# setup logging -log_level = { - "DEBUG": logging.DEBUG, - "INFO": logging.INFO, - "WARN": logging.WARN, - "ERROR": logging.ERROR, - "FATAL": logging.FATAL, -}[args.log_level] - -logging.basicConfig(level=log_level) - -# setup server -loop = asyncio.new_event_loop() -asyncio.set_event_loop(loop) -app = Application() - -# setup lxa network -network = LxaNetwork( - loop=loop, - interface=args.interface, - lss_address_cache_file=args.lss_address_cache_file, -) - -app["network"] = network - - -async def shutdown_network(app): - await app["network"].shutdown() - - -app.on_shutdown.append(shutdown_network) -loop.create_task(network.run()) - -# start server -try: - server = LXAIOBusServer( - app, - loop, - network, - args.firmware_directory, - args.allow_custom_firmware, - ) - -except OSError as e: - if e.errno == errno.ENODEV: # can interface not available - exit("interface {} not available".format(args.interface)) - -loop.create_task(server.flush_state_periodically()) - -if args.shell: - - def _start_shell(): - import IPython - from traitlets.config import Config - - config = Config() - - IPython.embed(config=config) - - # shutdown server - os.kill(os.getpid(), signal.SIGTERM) - - loop.create_task(server.rpc.worker_pool.run(_start_shell)) - -print("starting server on http://{}:{}/".format(args.host, args.port)) - -try: - kwargs = {} - if args.log_level != "DEBUG": - kwargs["access_log"] = None - run_app( - app=app, - host=args.host, - port=args.port, - handle_signals=False, - print=lambda *args, **kwargs: None, - loop=loop, - **kwargs, - ) - -except KeyboardInterrupt: - server.shutdown() - os.kill(os.getpid(), signal.SIGTERM) - -except OSError: - server.shutdown() - exit("ERROR: can not bind to port {}".format(args.port)) - -print("\rshutting down server") - -server.shutdown() -os.kill(os.getpid(), signal.SIGTERM) diff --git a/lxa_iobus/__init__.py b/lxa_iobus/__init__.py index a53c75a..e69de29 100644 --- a/lxa_iobus/__init__.py +++ b/lxa_iobus/__init__.py @@ -1,2 +0,0 @@ -VERSION = (0, 4, 2) -VERSION_STRING = "{}".format(".".join([str(i) for i in VERSION])) diff --git a/lxa_iobus/cli/__init__.py b/lxa_iobus/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bin/lxa-iobus-lpc11xxcanisp-invoke b/lxa_iobus/cli/lpc11xxcanisp_invoke.py old mode 100755 new mode 100644 similarity index 95% rename from bin/lxa-iobus-lpc11xxcanisp-invoke rename to lxa_iobus/cli/lpc11xxcanisp_invoke.py index 897f8cb..8af3fc3 --- a/bin/lxa-iobus-lpc11xxcanisp-invoke +++ b/lxa_iobus/cli/lpc11xxcanisp_invoke.py @@ -29,7 +29,7 @@ def invoke_isp(self): self.node.sdo.download(0x2B07, 0, struct.pack("I", 0x12345678)) -def invoke_rom_loader(): +def main(): can = CanOpen() can.setup() @@ -38,4 +38,4 @@ def invoke_rom_loader(): if __name__ == "__main__": - invoke_rom_loader() + main() diff --git a/bin/lxa-iobus-lpc11xxcanisp-program b/lxa_iobus/cli/lpc11xxcanisp_program.py old mode 100755 new mode 100644 similarity index 100% rename from bin/lxa-iobus-lpc11xxcanisp-program rename to lxa_iobus/cli/lpc11xxcanisp_program.py diff --git a/lxa_iobus/cli/server.py b/lxa_iobus/cli/server.py new file mode 100644 index 0000000..2a10e16 --- /dev/null +++ b/lxa_iobus/cli/server.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import asyncio +import errno +import logging +import os +import signal +import sys +import threading +import traceback +from argparse import ArgumentParser + +from aiohttp.web import Application, run_app + +from lxa_iobus.network import LxaNetwork +from lxa_iobus.server.server import LXAIOBusServer + + +def trace_handler(num, frame): + print("\nDumping all stacks:\n") + + for th in threading.enumerate(): + print(th) + traceback.print_stack(sys._current_frames()[th.ident]) + print() + + for task in asyncio.all_tasks(): + if not task.done(): + task.print_stack() + print() + + +signal.signal(signal.SIGQUIT, trace_handler) + + +def exit_handler(num, frame): + print("\nExiting...\n") + + os.kill(os.getpid(), signal.SIGKILL) + + +signal.signal(signal.SIGINT, exit_handler) + + +def main(): + # parse command line arguments + parser = ArgumentParser() + + parser.add_argument("interface") + parser.add_argument( + "--port", + type=int, + default=8080, + help="Port to serve on. Defaults to 8080", + ) + parser.add_argument( + "--host", + type=str, + default="localhost", + help="Host to bind to. Defaults to 'localhost'", + ) + parser.add_argument( + "--shell", + action="store_true", + help="Embeds an ipython shell for easy debugging", + ) + parser.add_argument( + "--firmware-directory", + type=str, + default="firmware", + help="Directory where uploaded firmware is stored. " "Only used when --allow-custom-firmware is set.", + ) + parser.add_argument( + "--allow-custom-firmware", + action="store_true", + help="Allows to upload and flash own (arbitrary) firmware " "into the program section of the nodes.", + ) + parser.add_argument( + "--lss-address-cache-file", + type=str, + default="", + help="LSS addresses cache as json. Reduces startup time for known nodes.", + ) + + parser.add_argument( + "-l", + "--log-level", + choices=["DEBUG", "INFO", "WARN", "ERROR", "FATAL"], + default="WARN", + ) + + args = parser.parse_args() + + # setup logging + log_level = { + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARN": logging.WARN, + "ERROR": logging.ERROR, + "FATAL": logging.FATAL, + }[args.log_level] + + logging.basicConfig(level=log_level) + + # setup server + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + app = Application() + + # setup lxa network + network = LxaNetwork( + loop=loop, + interface=args.interface, + lss_address_cache_file=args.lss_address_cache_file, + ) + + app["network"] = network + + async def shutdown_network(app): + await app["network"].shutdown() + + app.on_shutdown.append(shutdown_network) + loop.create_task(network.run()) + + # start server + try: + server = LXAIOBusServer( + app, + loop, + network, + args.firmware_directory, + args.allow_custom_firmware, + ) + + except OSError as e: + if e.errno == errno.ENODEV: # can interface not available + exit("interface {} not available".format(args.interface)) + + loop.create_task(server.flush_state_periodically()) + + if args.shell: + + def _start_shell(): + import IPython + from traitlets.config import Config + + config = Config() + + IPython.embed(config=config) + + # shutdown server + os.kill(os.getpid(), signal.SIGTERM) + + loop.create_task(server.rpc.worker_pool.run(_start_shell)) + + print("starting server on http://{}:{}/".format(args.host, args.port)) + + try: + kwargs = {} + if args.log_level != "DEBUG": + kwargs["access_log"] = None + run_app( + app=app, + host=args.host, + port=args.port, + handle_signals=False, + print=lambda *args, **kwargs: None, + loop=loop, + **kwargs, + ) + + except KeyboardInterrupt: + server.shutdown() + os.kill(os.getpid(), signal.SIGTERM) + + except OSError: + server.shutdown() + exit("ERROR: can not bind to port {}".format(args.port)) + + print("\rshutting down server") + + server.shutdown() + os.kill(os.getpid(), signal.SIGTERM) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index ed7e54e..dd0b914 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,40 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "lxa-iobus" +description = "Linux Automation IOBus Server" +version = "0.4.2" +authors = [ + { name = "Linux Automation GmbH", email = "info@linux-automation.com" }, +] +readme = "README.rst" +license = { file = "LICENSE.txt" } +dependencies = [ + "aiohttp~=3.8", + "aiohttp-json-rpc==0.13.3", + "canopen", + "python-can", + "janus", +] + +[project.scripts] +lxa-iobus-server = "lxa_iobus.cli.server:main" +lxa-iobus-lpc11xxcanisp-invoke = "lxa_iobus.cli.lpc11xxcanisp_invoke:main" +lxa-iobus-lpc11xxcanisp-program = "lxa_iobus.cli.lpc11xxcanisp_program:main" + +[tool.setuptools] +packages = [ + "lxa_iobus", + "lxa_iobus.cli", + "lxa_iobus.lpc11xxcanisp", + "lxa_iobus.lpc11xxcanisp.firmware", + "lxa_iobus.lpc11xxcanisp.loader", + "lxa_iobus.server", +] +include-package-data = true + [tool.ruff] line-length = 119 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 521ad5b..0000000 --- a/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[metadata] -description = Linux Automation iobus -long_description = file: README.rst -license_file = LICENSE.txt diff --git a/setup.py b/setup.py deleted file mode 100755 index 73b7d67..0000000 --- a/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from setuptools import find_packages, setup - -import lxa_iobus - -EXTRAS_REQUIRE = { - "server": [ - "aiohttp~=3.8", - "aiohttp-json-rpc==0.13.3", - ], - "shell": [ - "ipython<7", - ], -} - -EXTRAS_REQUIRE["full"] = sum([v for k, v in EXTRAS_REQUIRE.items()], []) - -setup( - include_package_data=True, - name="lxa-iobus", - version=lxa_iobus.VERSION_STRING, - author="Linux Automation GmbH", - url="https://github.com/linux-automation/lxa-iobus", - author_email="python@pengutronix.de", - license="Apache License 2.0", - packages=find_packages(), - install_requires=[ - "python-can", - "janus", - ], - extras_require=EXTRAS_REQUIRE, - scripts=[ - "bin/lxa-iobus-server", - "bin/lxa-iobus-can-setup", - "bin/lxa-iobus-lpc11xxcanisp-invoke", - "bin/lxa-iobus-lpc11xxcanisp-program", - ], -)