Skip to content

Commit 4db09e9

Browse files
authored
Merge pull request #4 from harp-tech/gl-dev
Add dynamic schema-based register reader classes
2 parents 184e2d2 + 0b9a132 commit 4db09e9

3 files changed

Lines changed: 118 additions & 6 deletions

File tree

harp/io.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from os import PathLike
2-
from typing import Any, Optional, Union
2+
from typing import Any, BinaryIO, Optional, Union
33
from pandas._typing import Axes
44
import numpy as np
55
import pandas as pd
66

7-
_SECONDS_PER_TICK = 32e6
7+
_SECONDS_PER_TICK = 32e-6
88
payloadtypes = {
99
1: np.dtype(np.uint8),
1010
2: np.dtype(np.uint16),
@@ -19,7 +19,7 @@
1919

2020

2121
def read(
22-
file: Union[str, bytes, PathLike[Any], np._IOProtocol],
22+
file: Union[str, bytes, PathLike[Any], BinaryIO],
2323
columns: Optional[Axes] = None,
2424
):
2525
"""

harp/reader.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import re
2+
from functools import partial
3+
from pandas import DataFrame, Series
4+
from typing import Iterable, Callable, Union
5+
from harp.model import BitMask, GroupMask, MaskValueItem, Model, Register
6+
from harp.io import read
7+
8+
_camel_to_snake_regex = re.compile(r"(?<!^)(?=[A-Z])")
9+
10+
11+
class RegisterReader:
12+
register: Register
13+
read: Callable[[str], DataFrame]
14+
15+
def __init__(self, register: Register, read: Callable[[str], DataFrame]) -> None:
16+
self.register = register
17+
self.read = read
18+
19+
20+
class DeviceReader:
21+
device: Model
22+
registers: dict[str, RegisterReader]
23+
24+
def __init__(self, device: Model, registers: dict[str, RegisterReader]) -> None:
25+
self.device = device
26+
self.registers = registers
27+
28+
def __dir__(self) -> Iterable[str]:
29+
return self.registers.keys()
30+
31+
def __getattr__(self, __name: str) -> RegisterReader:
32+
return self.registers[__name]
33+
34+
35+
def _compose(f, g):
36+
return lambda *a, **kw: f(g(*a, **kw))
37+
38+
39+
def _id_camel_to_snake(id: str):
40+
return _camel_to_snake_regex.sub("_", id).lower()
41+
42+
43+
def _keys_camel_to_snake(keys: Iterable[str]):
44+
return [_id_camel_to_snake(k) for k in keys]
45+
46+
47+
def _create_bit_parser(mask: Union[int, MaskValueItem]):
48+
def parser(xs: Series) -> Series:
49+
return (xs & mask) != 0
50+
51+
return parser
52+
53+
54+
def _create_bitmask_parser(bitMask: BitMask):
55+
lookup = [
56+
(_id_camel_to_snake(k), _create_bit_parser(v.root))
57+
for k, v in bitMask.bits.items()
58+
]
59+
60+
def parser(df: DataFrame):
61+
return DataFrame({n: f(df[0]) for n, f in lookup}, index=df.index)
62+
63+
return parser
64+
65+
66+
def _create_groupmask_parser(name: str, groupMask: GroupMask):
67+
name = _id_camel_to_snake(name)
68+
lookup = {v.root: n for n, v in groupMask.values.items()}
69+
70+
def parser(df: DataFrame):
71+
return DataFrame({name: df.map(lambda x: lookup[x])})
72+
73+
return parser
74+
75+
76+
def _create_register_reader(device: Model, name: str):
77+
register = device.registers[name]
78+
reader = read
79+
80+
if register.maskType is not None:
81+
key = register.maskType.root
82+
bitMask = device.bitMasks.get(key)
83+
if bitMask is not None:
84+
parser = _create_bitmask_parser(bitMask)
85+
reader = _compose(parser, reader)
86+
return RegisterReader(register, reader)
87+
88+
groupMask = device.groupMasks.get(key)
89+
if groupMask is not None:
90+
parser = _create_groupmask_parser(name, groupMask)
91+
reader = _compose(parser, reader)
92+
return RegisterReader(register, reader)
93+
94+
if register.payloadSpec is not None:
95+
columns = register.payloadSpec.keys()
96+
columns = _keys_camel_to_snake(columns)
97+
reader = partial(reader, columns=columns)
98+
return RegisterReader(register, reader)
99+
100+
columns = [_id_camel_to_snake(name)]
101+
reader = partial(reader, columns=columns)
102+
return RegisterReader(register, reader)
103+
104+
105+
def create_reader(device: Model):
106+
reg_readers = {
107+
name: _create_register_reader(device, name) for name in device.registers.keys()
108+
}
109+
return DeviceReader(device, reg_readers)

pyproject.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,17 @@ classifiers = [
3131
[project.optional-dependencies]
3232
dev = [
3333
"datamodel-code-generator",
34-
"setuptools_scm",
3534
"black"
3635
]
36+
jupyter = [
37+
"ipykernel"
38+
]
3739

3840
[build-system]
3941
requires = [
40-
"setuptools>=45",
4142
"wheel",
42-
"setuptools_scm[toml]>=6.2",
43+
"setuptools",
44+
"setuptools_scm[toml]",
4345
]
4446
build-backend = "setuptools.build_meta"
4547

@@ -62,5 +64,6 @@ extend-exclude = '''
6264
^/LICENSE
6365
^/README.md
6466
| harp/model.py
67+
| reflex-generator
6568
)
6669
'''

0 commit comments

Comments
 (0)