Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
amitschang committed Mar 21, 2024
1 parent 71c18d8 commit 785a2a8
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 145 deletions.
21 changes: 0 additions & 21 deletions evolver/adapter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +0,0 @@
from abc import ABC, abstractmethod
from pydantic import BaseModel


class Adapter(ABC):
class Config(BaseModel):
pass

def __init__(self, evolver, config: Config = None):
self.config = config or self.Config()

@abstractmethod
def react(self):
pass


class NoOpAdapter(Adapter):
ncalls = 0

def react(self):
self.ncalls += 1
8 changes: 8 additions & 0 deletions evolver/adapter/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from evolver.adapter.interface import Adapter


class NoOpAdapter(Adapter):
ncalls = 0

def react(self):
self.ncalls += 1
14 changes: 14 additions & 0 deletions evolver/adapter/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pydantic
from abc import ABC, abstractmethod


class Adapter(ABC):
class Config(pydantic.BaseModel):
pass

def __init__(self, evolver, config: Config = None):
self.config = config or self.Config()

@abstractmethod
def react(self):
pass
4 changes: 2 additions & 2 deletions evolver/app/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ def test_evolver_app_default_config_dump_endpoint(self, app_client):
assert sorted(response.json().keys()) == ['config', 'last_read', 'state']

def test_evolver_update_config_endpoint(self, app_client):
data = {'hardware': {'test': {'driver': 'evolver.hardware.NoOpSensorDriver'}}}
data = {'hardware': {'test': {'driver': 'evolver.hardware.demo.NoOpSensorDriver'}}}
response = app_client.post('/', json=data)
assert response.status_code == 200
newconfig = app_client.get('/').json()['config']
assert newconfig['hardware']['test']['driver'] == 'evolver.hardware.NoOpSensorDriver'
assert newconfig['hardware']['test']['driver'] == 'evolver.hardware.demo.NoOpSensorDriver'

def test_evolver_app_react_loop_setup(self, app_client):
# The context manager ensures that startup event loop is called
Expand Down
8 changes: 4 additions & 4 deletions evolver/device.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import time
from pydantic import BaseModel
import pydantic
from evolver.util import load_class_fqcn, driver_from_descriptor
from evolver.hardware import SensorDriver, EffectorDriver
from evolver.hardware.interface import SensorDriver, EffectorDriver
from evolver.serial import EvolverSerialUART
from evolver.history import HistoryServer

Expand All @@ -10,7 +10,7 @@
DEFAULT_HISTORY = HistoryServer


class AdapterDescriptor(BaseModel):
class AdapterDescriptor(pydantic.BaseModel):
driver: str
config: dict = {}

Expand All @@ -19,7 +19,7 @@ class HardwareDriverDescriptor(AdapterDescriptor):
calibrator: AdapterDescriptor = None


class EvolverConfig(BaseModel):
class EvolverConfig(pydantic.BaseModel):
vials: list = list(range(16))
hardware: dict[str, HardwareDriverDescriptor] = {}
adapters: list[AdapterDescriptor] = []
Expand Down
107 changes: 0 additions & 107 deletions evolver/hardware/__init__.py
Original file line number Diff line number Diff line change
@@ -1,107 +0,0 @@
from abc import ABC, abstractmethod
from copy import copy
from pydantic import BaseModel


class VialConfigBaseModel(BaseModel):
vials: list[int] | None = None


class VialBaseModel(BaseModel):
vial: int


class BaseCalibrator(ABC):
class Config(BaseModel):
calibfile: str = None

def __init__(self, evovler = None, config: Config = Config()):
self.config = config

@property
@abstractmethod
def status(self):
pass


class NoOpCalibrator(BaseCalibrator):
@property
def status(self):
return True


class HardwareDriver(ABC):
class Config(BaseModel):
pass
calibrator = NoOpCalibrator()

def __init__(self, evolver, config = None, calibrator = None):
self.evolver = evolver
self.reconfigure(config or self.Config())
if calibrator:
self.calibrator = calibrator

def reconfigure(self, config):
self.config = config


class VialHardwareDriver(HardwareDriver):
def reconfigure(self, config):
super().reconfigure(config)
if config.vials is None:
config.vials = self.evolver.config.vials
else:
if not set(config.vials).issubset(self.evolver.all_vials):
raise ValueError('invalid vials found in config')


class SensorDriver(VialHardwareDriver):
class Config(VialConfigBaseModel):
pass
class Output(VialBaseModel):
raw: int
value: float
outputs: dict[int, Output] = {}

def get(self) -> list[Output]:
return self.outputs

@abstractmethod
def read(self):
pass


class EffectorDriver(VialHardwareDriver):
class Config(VialConfigBaseModel):
pass
class Input(VialBaseModel):
value: float
proposal: dict[int, Input] = {}
committed: dict[int, Input] = {}

def set(self, input: Input):
self.proposal[input.vial] = input

@abstractmethod
def commit(self):
pass


class NoOpSensorDriver(SensorDriver):
class Config(VialConfigBaseModel):
echo_raw: int = 1
echo_val: int = 2

def read(self):
self.outputs = {
i: self.Output(vial=i, raw=self.config.echo_raw, value=self.config.echo_val)
for i in self.config.vials
}

def get(self):
return self.outputs


class NoOpEffectorDriver(EffectorDriver):
def commit(self):
self.comitted = copy(self.proposal)
28 changes: 28 additions & 0 deletions evolver/hardware/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from copy import copy
from evolver.hardware.interface import SensorDriver, EffectorDriver, VialConfigBaseModel, BaseCalibrator


class NoOpSensorDriver(SensorDriver):
class Config(VialConfigBaseModel):
echo_raw: int = 1
echo_val: int = 2

def read(self):
self.outputs = {
i: self.Output(vial=i, raw=self.config.echo_raw, value=self.config.echo_val)
for i in self.config.vials
}

def get(self):
return self.outputs


class NoOpEffectorDriver(EffectorDriver):
def commit(self):
self.comitted = copy(self.proposal)


class NoOpCalibrator(BaseCalibrator):
@property
def status(self):
return True
80 changes: 80 additions & 0 deletions evolver/hardware/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import pydantic
from abc import ABC, abstractmethod


class VialConfigBaseModel(pydantic.BaseModel):
vials: list[int] | None = None


class VialBaseModel(pydantic.BaseModel):
vial: int


class BaseCalibrator(ABC):
class Config(pydantic.BaseModel):
calibfile: str = None

def __init__(self, evovler = None, config: Config = Config()):
self.config = config

@property
@abstractmethod
def status(self):
pass


class HardwareDriver(ABC):
class Config(pydantic.BaseModel):
pass
calibrator = None

def __init__(self, evolver, config = None, calibrator = None):
self.evolver = evolver
self.reconfigure(config or self.Config())
if calibrator:
self.calibrator = calibrator

def reconfigure(self, config):
self.config = config


class VialHardwareDriver(HardwareDriver):
def reconfigure(self, config):
super().reconfigure(config)
if config.vials is None:
config.vials = self.evolver.config.vials
else:
if not set(config.vials).issubset(self.evolver.all_vials):
raise ValueError('invalid vials found in config')


class SensorDriver(VialHardwareDriver):
class Config(VialConfigBaseModel):
pass
class Output(VialBaseModel):
raw: int
value: float
outputs: dict[int, Output] = {}

def get(self) -> list[Output]:
return self.outputs

@abstractmethod
def read(self):
pass


class EffectorDriver(VialHardwareDriver):
class Config(VialConfigBaseModel):
pass
class Input(VialBaseModel):
value: float
proposal: dict[int, Input] = {}
committed: dict[int, Input] = {}

def set(self, input: Input):
self.proposal[input.vial] = input

@abstractmethod
def commit(self):
pass
4 changes: 2 additions & 2 deletions evolver/history.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import time
import pydantic
from abc import ABC, abstractmethod
from collections import defaultdict
from pydantic import BaseModel


class History(ABC):
class Config(BaseModel):
class Config(pydantic.BaseModel):
pass

@abstractmethod
Expand Down
8 changes: 4 additions & 4 deletions evolver/serial.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import serial
from abc import ABC, abstractmethod
from pydantic import BaseModel
import pydantic
from threading import Lock


class SerialData(BaseModel):
class SerialData(pydantic.BaseModel):
addr: str
data: list[bytes]
kind: str = 'r'


class Serial(ABC):
class Config(BaseModel):
class Config(pydantic.BaseModel):
pass

def __init__(self, evolver = None, config: Config = None):
Expand All @@ -24,7 +24,7 @@ def communicate(cmd: SerialData) -> SerialData:


class EvolverSerialUART(Serial):
class Config(BaseModel):
class Config(pydantic.BaseModel):
port: str = '/dev/ttyAMA0'
baudrate: int = 9600
timeout: float = 1
Expand Down
10 changes: 5 additions & 5 deletions evolver/tests/test_device.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from evolver.device import Evolver, EvolverConfig
from evolver.hardware import NoOpSensorDriver
from evolver.hardware.demo import NoOpSensorDriver


@pytest.fixture
Expand All @@ -10,16 +10,16 @@ def conf_with_driver():
'hardware': {
'testsensor': {
'driver':
'evolver.hardware.NoOpSensorDriver',
'evolver.hardware.demo.NoOpSensorDriver',
'config': {},
'calibrator': {
'driver': 'evolver.hardware.NoOpCalibrator'
'driver': 'evolver.hardware.demo.NoOpCalibrator'
}
},
'testeffector': {'driver': 'evolver.hardware.NoOpEffectorDriver', 'config': {}},
'testeffector': {'driver': 'evolver.hardware.demo.NoOpEffectorDriver', 'config': {}},
},
'adapters': [
{'driver': 'evolver.adapter.NoOpAdapter'},
{'driver': 'evolver.adapter.demo.NoOpAdapter'},
],
'serial': { 'driver': 'evolver.serial.EchoSerial' },
}
Expand Down

0 comments on commit 785a2a8

Please sign in to comment.