Skip to content

Commit

Permalink
Updates and fixes to run on Debian 12 aarch64
Browse files Browse the repository at this point in the history
This update allows to use QuestDB on Raspi (64bit!) and to have type
annotations (Python >=3.10)
  • Loading branch information
schwabix-1311 committed Dec 3, 2024
1 parent 1661ece commit 71d6042
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 94 deletions.
2 changes: 2 additions & 0 deletions ToDo
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ aquaPi ToDo list
================

- Known defects:
# possibly switch to PyPI's QuestDB to replace psycopg
# logging: resolve the abuse of logging.WARNING as loglevel.BRIEF; log.verbose() could be a functools.partialmethod(log.log, loglevel.INFO-1,...)
# REAL_CONFIG: GPIO13 must be permanently on (Filter!) - might need a new ConstInput(100)
# cron specs with comma !??
# startup behaviour of bus isn't good - let everybody post its data as response to HELLO?
# on Raspi driver TC420 finds a device although there's none

Expand Down
6 changes: 5 additions & 1 deletion aquaPi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@ def create_app() -> Flask:
return app

from .machineroom import MachineRoom
app.extensions['machineroom'] = MachineRoom(app.config['CONFIG'])
try:
app.extensions['machineroom'] = MachineRoom(app.config['CONFIG'])
except:
log.fatal("Fatal error in App.__init__. Subsequent errors are a side effect.")
return None

#FIXME bus is used by Python code in jinja template 'settings'
@app.context_processor
Expand Down
2 changes: 2 additions & 0 deletions aquaPi/driver/DriverADC.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ def find_ports() -> dict[str, IoPort]:
log.brief('I²C device at 0x%02X seems not to be an '
'ADS1x15, probably a different device, '
'or already in use.', adr)
except ValueError as ex:
log.info(ex)
except Exception as ex:
# pass # whatever it is, ignore this device
log.debug('%r', ex)
Expand Down
22 changes: 11 additions & 11 deletions aquaPi/driver/DriverOneWire.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,7 @@ class DriverDS1820(AInDriver):
@staticmethod
def find_ports() -> dict[str, IoPort]:
io_ports = {}
if not is_raspi():
# name: IoPort('function', 'driver', 'cfg', 'dependants')
io_ports = {
'DS1820 xA2E9C': IoPort(PortFunc.Ain, DriverDS1820,
{'adr': '28-0119383a2e9c', 'fake': True}, []),
'DS1820 x7A71E': IoPort(PortFunc.Ain, DriverDS1820,
{'adr': '28-01193867a71e', 'fake': True}, [])
}
else:
if is_raspi():
# TODO: GPIO 4 is the Raspi default, allow alternatives!
deps = ['GPIO 4 in', 'GPIO 4 out']

Expand All @@ -36,6 +28,14 @@ def find_ports() -> dict[str, IoPort]:
DriverDS1820,
{'adr': sensor},
deps)
else:
# name: IoPort('function', 'driver', 'cfg', 'dependants')
io_ports = {
'DS1820 xA2E9C': IoPort(PortFunc.Ain, DriverDS1820,
{'adr': '28-0119383a2e9c', 'fake': True}, []),
'DS1820 x7A71E': IoPort(PortFunc.Ain, DriverDS1820,
{'adr': '28-01193867a71e', 'fake': True}, [])
}
return io_ports

def __init__(self, cfg: dict[str, str], func: PortFunc):
Expand Down Expand Up @@ -64,7 +64,7 @@ def __init__(self, cfg: dict[str, str], func: PortFunc):
# ../conv_time(750)
self._sysfs_adr: str = cfg['adr']
if not path.exists(self._sysfs_adr):
raise DriverInvalidAddrError(adr=self._sysfs_adr)
raise DriverInvalidAddrError(self._sysfs_adr)
self._temp: str = path.join(self._sysfs_adr, 'temperature')
if not path.exists(self._temp):
self._temp = path.join(self._sysfs_adr, 'w1_slave')
Expand All @@ -89,7 +89,7 @@ def read(self) -> float:
elif self._err_cnt <= self._err_retry:
self._err_cnt += 1
else:
raise DriverReadError(self.name)
raise DriverReadError()

log.info('%s = %s', self.name, self._val)
return float(self._val)
35 changes: 17 additions & 18 deletions aquaPi/driver/DriverPWM.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,7 @@ class DriverPWM(DriverPWMbase):
@staticmethod
def find_ports() -> dict[str, IoPort]:
io_ports = {}
if not is_raspi():
# name: IoPort('function', 'driver', 'cfg', 'dependants')
io_ports = {
'PWM 0': IoPort(PortFunc.Aout, DriverPWM,
{'pin': 18, 'channel': 0, 'fake': True},
['GPIO 18 in', 'GPIO 18 out']),
'PWM 1': IoPort(PortFunc.Aout, DriverPWM,
{'pin': 19, 'channel': 1, 'fake': True},
['GPIO 19 in', 'GPIO 19 out'])
}
else:
if is_raspi():
cnt = 0
for pin in range(28):
try:
Expand All @@ -61,10 +51,20 @@ def find_ports() -> dict[str, IoPort]:
{'pin': pin, 'channel': cnt},
deps)
cnt += 1
else:
log.debug('pin %d is in use as %s', pin, func.name)
#else:
# log.debug('pin %d is configured as %s', pin, func.name)
except KeyError:
log.debug('Unknown function on pin %d = %d', pin, gpio_function(pin))
# name: IoPort('function', 'driver', 'cfg', 'dependants')
else:
io_ports = {
'PWM 0': IoPort(PortFunc.Aout, DriverPWM,
{'pin': 18, 'channel': 0, 'fake': True},
['GPIO 18 in', 'GPIO 18 out']),
'PWM 1': IoPort(PortFunc.Aout, DriverPWM,
{'pin': 19, 'channel': 1, 'fake': True},
['GPIO 19 in', 'GPIO 19 out'])
}
return io_ports

def __init__(self, cfg: dict[str, str], func: PortFunc):
Expand All @@ -76,18 +76,17 @@ def __init__(self, cfg: dict[str, str], func: PortFunc):
self._pwmchip: str = '/sys/class/pwm/pwmchip0'
self._pwmchannel: str = path.join(self._pwmchip, 'pwm%d' % self._channel)

try:
if not path.exists(self._pwmchannel):
log.debug('Creating sysfs PWM channel %d ...', self._channel)
with open(path.join(self._pwmchip, 'export'), 'wt', encoding='ascii') as p:
p.write('%d' % self._channel)
sleep(.1) # sombody (kernel?) needs a bit of time to finish it!
log.debug('Created sysfs PWM channel %d', self._channel)
except OSError:
pass
with open(path.join(self._pwmchannel, 'enable'), 'wt', encoding='ascii') as p:
p.write('0')

with open(path.join(self._pwmchannel, 'period'), 'wt', encoding='ascii') as p:
p.write('3333333')
with open(path.join(self._pwmchannel, 'enable'), 'wt', encoding='ascii') as p:
p.write('0')
else:
self.name = '!' + self.name

Expand Down
24 changes: 11 additions & 13 deletions aquaPi/driver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import glob

from .base import (IoPort, PortFunc, Driver,
DriverPortInuseError, DriverParamError)
DriverPortInuseError, DriverInvalidPortError, DriverParamError)

log = logging.getLogger('driver')
log.brief = log.warning # alias, warning is used as brief info, level info is verbose
Expand Down Expand Up @@ -79,11 +79,9 @@ def __init__(self):
for name in drv_mod_names:
drv_module = sys.modules[name]
log.debug('# module %s', drv_module)
dict_classes = [cl for cl in drv_module.__dict__.values() if type(cl) is type]
for mod_cl in dict_classes:
if issubclass(mod_cl, Driver):
log.debug('found drv class %r', mod_cl)
drv_classes.add(mod_cl)
mod_drivers = {cl for cl in drv_module.__dict__.values()
if type(cl) is type and issubclass(cl, Driver)}
drv_classes |= mod_drivers

for drv in drv_classes:
if hasattr(drv, 'find_ports'):
Expand Down Expand Up @@ -112,11 +110,11 @@ def driver_factory(self, port: str, drv_options: dict | None = None
"""
log.debug('create a driver for %r', port)
if port not in IoRegistry._map:
raise DriverParamError('There is no port named %s' % port)
raise DriverInvalidPortError(port)

io_port = IoRegistry._map[port]
if io_port.used:
raise DriverPortInuseError(port=port)
raise DriverPortInuseError(port)

try:
if drv_options:
Expand All @@ -132,13 +130,13 @@ def driver_factory(self, port: str, drv_options: dict | None = None

return driver
except Exception:
log.exception('Failed to create driver: %s', port)
return None
log.exception('Failed to create port driver: %s' % port)
raise

def driver_destruct(self, port: str, driver: Driver) -> None:
log.debug('destruct driver for %r', port)
if port not in IoRegistry._map:
raise DriverParamError('There is no driver for port %s' % port)
raise DriverInvalidPortError(port)

io_port = IoRegistry._map[port]
driver.close()
Expand Down Expand Up @@ -168,7 +166,7 @@ def driver_destruct(self, port: str, driver: Driver) -> None:

for drv_path in __path__:
for drv_file in glob.glob(path.join(drv_path, DRIVER_FILE_PREFIX + '*.py')):
log.debug('Found driver file %s', drv_file)
log.info('Found driver file %s', drv_file)

drv_name = path.basename(drv_file)
if drv_name.startswith(DRIVER_FILE_PREFIX):
Expand All @@ -177,7 +175,7 @@ def driver_destruct(self, port: str, driver: Driver) -> None:
drv_name = drv_name[:-3]
drv_name = __name__ + '.' + drv_name.lower()
drv_spec = importlib.util.spec_from_file_location(drv_name, drv_file)
log.debug('Driver spec %s', drv_spec)
#log.debug('Driver spec %s', drv_spec)

if drv_spec:
drv_mod = importlib.util.module_from_spec(drv_spec)
Expand Down
46 changes: 27 additions & 19 deletions aquaPi/driver/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3

import logging
from typing import Any
from os import path
from enum import Enum
from collections import namedtuple
Expand All @@ -26,44 +27,51 @@ def is_raspi() -> bool:
# ========== Exceptions ==========


class DriverNYI(Exception):
def __init__(self, msg: str = 'Not yet implemented.'):
class DriverError(Exception):
def __init__(self, msg):
super().__init__()
self.msg: str = msg


class DriverParamError(Exception):
class DriverNYI(DriverError):
def __init__(self, msg: str = 'Not yet implemented.'):
super().__init__(msg)


class DriverParamError(DriverError):
def __init__(self, msg: str = 'Invalid parameter value.'):
super().__init__()
self.msg: str = msg
super().__init__(msg)


class DriverInvalidAddrError(Exception):
def __init__(self, msg: str = '', adr=None):
class DriverInvalidAddrError(DriverError):
def __init__(self, adr: Any, msg: str = ''):
if not msg:
msg = 'Pin, channel or address %r does not exist.' % adr
super().__init__()
self.msg = msg
super().__init__(msg)


class DriverPortInuseError(Exception):
def __init__(self, msg: str = '', port=None):
class DriverInvalidPortError(DriverError):
def __init__(self, port: Any, msg: str = ''):
if not msg:
msg = 'There is no port named "%s"' % port
super().__init__(msg)


class DriverPortInuseError(DriverError):
def __init__(self, port: Any, msg: str = ''):
if not msg:
msg = 'Pin or channel %r is already assigned.' % port
super().__init__()
self.msg = msg
super().__init__(msg)


class DriverReadError(Exception):
class DriverReadError(DriverError):
def __init__(self, msg: str = 'Failed to read a valid value.'):
super().__init__()
self.msg: str = msg
super().__init__(msg)


class DriverWriteError(Exception):
class DriverWriteError(DriverError):
def __init__(self, msg: str = 'Failed to write value to the output.'):
super().__init__()
self.msg: str = msg
super().__init__(msg)


# ========== common types ==========
Expand Down
26 changes: 16 additions & 10 deletions aquaPi/machineroom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .aux_nodes import ScaleAux, MinAux, MaxAux, AvgAux
from .hist_nodes import History
from .alert_nodes import Alert, AlertAbove, AlertBelow
from ..driver import DriverError


log = logging.getLogger('machineroom')
Expand All @@ -33,18 +34,23 @@ def __init__(self, bus_storage: str) -> None:
"""
self.bus_storage: str = bus_storage

if not path.exists(self.bus_storage):
self.bus: MsgBus = MsgBus() # threaded=True)
try:
if not path.exists(self.bus_storage):
self.bus: MsgBus = MsgBus() # threaded=True)

log.brief("=== There are no controllers defined, creating default")
self.create_default_nodes()
self.save_nodes(self.bus)
log.brief("=== Successfully created Bus and default Nodes")
log.brief(" ... and saved to %s", self.bus_storage)
log.brief("=== There are no controllers defined, creating default")
self.create_default_nodes()
self.save_nodes(self.bus)
log.brief("=== Successfully created Bus and default Nodes")
log.brief(" ... and saved to %s", self.bus_storage)

else:
log.brief("=== Loading Bus & Nodes from %s", self.bus_storage)
self.bus = self.restore_nodes()

else:
log.brief("=== Loading Bus & Nodes from %s", self.bus_storage)
self.bus = self.restore_nodes()
except DriverError as ex:
log.fatal("Creation of a controller failed: %s", ex.msg)
raise

# Our __del__ would not be called after Ctrl-C.
atexit.register(self.shutdown)
Expand Down
Loading

0 comments on commit 71d6042

Please sign in to comment.