Skip to content

Commit 184e2d2

Browse files
authored
Merge pull request #3 from harp-tech/gl-dev
Add auto-generated device schema classes
2 parents 9f55f8b + eae7701 commit 184e2d2

5 files changed

Lines changed: 196 additions & 0 deletions

File tree

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "reflex-generator"]
2+
path = reflex-generator
3+
url = https://github.com/harp-tech/reflex-generator.git

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
# harp-python
22

33
A low-level interface to data collected with the [Harp](https://harp-tech.org/) binary protocol.
4+
5+
## Data model
6+
7+
To regenerate Pydantic data models from device schema definitions, activate a virtual environment with `dev` dependencies, and run:
8+
9+
```
10+
datamodel-codegen --input ./reflex-generator/schema/device.json --output harp/model.py --output-model-type pydantic_v2.BaseModel
11+
```
12+
13+
> [!IMPORTANT]
14+
> Currently code generation adds an unwanted field at the very end of the data model definition `registers: Optional[Any] = None`. This declaration needs to be removed for serialization to work properly.

harp/model.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# generated by datamodel-codegen:
2+
# filename: device.json
3+
# timestamp: 2023-10-30T11:46:57+00:00
4+
5+
from __future__ import annotations
6+
7+
from enum import Enum
8+
from typing import Any, Dict, List, Optional, Union
9+
10+
from pydantic import BaseModel, ConfigDict, Field, RootModel, conint
11+
12+
13+
class Type(Enum):
14+
U8 = 'U8'
15+
S8 = 'S8'
16+
U16 = 'U16'
17+
S16 = 'S16'
18+
U32 = 'U32'
19+
S32 = 'S32'
20+
U64 = 'U64'
21+
S64 = 'S64'
22+
Float = 'Float'
23+
24+
25+
class Access(Enum):
26+
Read = 'Read'
27+
Write = 'Write'
28+
Event = 'Event'
29+
30+
31+
class MaskValueItem(BaseModel):
32+
model_config = ConfigDict(
33+
extra='forbid',
34+
)
35+
description: Optional[str] = Field(
36+
None, description='Specifies a summary description of the mask value function.'
37+
)
38+
39+
40+
class MaskValue(RootModel[Union[int, MaskValueItem]]):
41+
root: Union[int, MaskValueItem]
42+
43+
44+
class BitMask(BaseModel):
45+
description: Optional[str] = Field(
46+
None, description='Specifies a summary description of the bit mask function.'
47+
)
48+
bits: Dict[str, MaskValue]
49+
50+
51+
class GroupMask(BaseModel):
52+
description: Optional[str] = Field(
53+
None, description='Specifies a summary description of the group mask function.'
54+
)
55+
values: Dict[str, MaskValue]
56+
57+
58+
class MaskType(RootModel[str]):
59+
root: str = Field(
60+
...,
61+
description='Specifies the name of the bit mask or group mask used to represent the payload value.',
62+
)
63+
64+
65+
class InterfaceType(RootModel[str]):
66+
root: str = Field(
67+
...,
68+
description='Specifies the name of the type used to represent the payload value in the high-level interface.',
69+
)
70+
71+
72+
class Converter(Enum):
73+
None_ = 'None'
74+
Payload = 'Payload'
75+
RawPayload = 'RawPayload'
76+
77+
78+
class MinValue(RootModel[float]):
79+
root: float = Field(
80+
...,
81+
description='Specifies the minimum allowable value for the payload or payload member.',
82+
)
83+
84+
85+
class MaxValue(RootModel[float]):
86+
root: float = Field(
87+
...,
88+
description='Specifies the maximum allowable value for the payload or payload member.',
89+
)
90+
91+
92+
class DefaultValue(RootModel[float]):
93+
root: float = Field(
94+
...,
95+
description='Specifies the default value for the payload or payload member.',
96+
)
97+
98+
99+
class PayloadMember(BaseModel):
100+
mask: Optional[int] = Field(
101+
None,
102+
description='Specifies the mask used to read and write this payload member.',
103+
)
104+
offset: Optional[int] = Field(
105+
None,
106+
description='Specifies the payload array offset where this payload member is stored.',
107+
)
108+
description: Optional[str] = Field(
109+
None, description='Specifies a summary description of the payload member.'
110+
)
111+
minValue: Optional[MinValue] = None
112+
maxValue: Optional[MaxValue] = None
113+
defaultValue: Optional[DefaultValue] = None
114+
maskType: Optional[MaskType] = None
115+
interfaceType: Optional[InterfaceType] = None
116+
converter: Optional[Converter] = None
117+
118+
119+
class Visibility(Enum):
120+
public = 'public'
121+
private = 'private'
122+
123+
124+
class Register(BaseModel):
125+
address: conint(le=255) = Field(
126+
..., description='Specifies the unique 8-bit address of the register.'
127+
)
128+
type: Type
129+
length: Optional[conint(ge=1)] = Field(
130+
None, description='Specifies the length of the register payload.'
131+
)
132+
access: Union[Access, List[Access]] = Field(
133+
..., description='Specifies the expected use of the register.'
134+
)
135+
description: Optional[str] = Field(
136+
None, description='Specifies a summary description of the register function.'
137+
)
138+
minValue: Optional[MinValue] = None
139+
maxValue: Optional[MaxValue] = None
140+
defaultValue: Optional[DefaultValue] = None
141+
maskType: Optional[MaskType] = None
142+
visibility: Optional[Visibility] = Field(
143+
None,
144+
description='Specifies whether the register function is exposed in the high-level interface.',
145+
)
146+
volatile: Optional[bool] = Field(
147+
None,
148+
description='Specifies whether register values can be saved in non-volatile memory.',
149+
)
150+
payloadSpec: Optional[Dict[str, PayloadMember]] = None
151+
interfaceType: Optional[InterfaceType] = None
152+
converter: Optional[Converter] = None
153+
154+
155+
class Registers(BaseModel):
156+
registers: Dict[str, Register] = Field(
157+
...,
158+
description='Specifies the collection of registers implementing the device function.',
159+
)
160+
bitMasks: Optional[Dict[str, BitMask]] = Field(
161+
None,
162+
description='Specifies the collection of masks available to be used with the different registers.',
163+
)
164+
groupMasks: Optional[Dict[str, GroupMask]] = Field(
165+
None,
166+
description='Specifies the collection of group masks available to be used with the different registers.',
167+
)
168+
169+
170+
class Model(Registers):
171+
device: str = Field(..., description='Specifies the name of the device.')
172+
whoAmI: int = Field(
173+
..., description='Specifies the unique identifier for this device type.'
174+
)
175+
firmwareVersion: str = Field(
176+
..., description='Specifies the semantic version of the device firmware.'
177+
)
178+
hardwareTargets: str = Field(
179+
..., description='Specifies the semantic version of the device hardware.'
180+
)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,6 @@ extend-exclude = '''
6161
(
6262
^/LICENSE
6363
^/README.md
64+
| harp/model.py
6465
)
6566
'''

reflex-generator

Submodule reflex-generator added at 44f2049

0 commit comments

Comments
 (0)