Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "reflex-generator"]
path = reflex-generator
url = https://github.com/harp-tech/reflex-generator.git
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# harp-python

A low-level interface to data collected with the [Harp](https://harp-tech.org/) binary protocol.

## Data model

To regenerate Pydantic data models from device schema definitions, activate a virtual environment with `dev` dependencies, and run:

```
datamodel-codegen --input ./reflex-generator/schema/device.json --output harp/model.py --output-model-type pydantic_v2.BaseModel
```

> [!IMPORTANT]
> 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.
180 changes: 180 additions & 0 deletions harp/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# generated by datamodel-codegen:
# filename: device.json
# timestamp: 2023-10-30T11:46:57+00:00

from __future__ import annotations

from enum import Enum
from typing import Any, Dict, List, Optional, Union

from pydantic import BaseModel, ConfigDict, Field, RootModel, conint


class Type(Enum):
U8 = 'U8'
S8 = 'S8'
U16 = 'U16'
S16 = 'S16'
U32 = 'U32'
S32 = 'S32'
U64 = 'U64'
S64 = 'S64'
Float = 'Float'


class Access(Enum):
Read = 'Read'
Write = 'Write'
Event = 'Event'


class MaskValueItem(BaseModel):
model_config = ConfigDict(
extra='forbid',
)
description: Optional[str] = Field(
None, description='Specifies a summary description of the mask value function.'
)


class MaskValue(RootModel[Union[int, MaskValueItem]]):
root: Union[int, MaskValueItem]


class BitMask(BaseModel):
description: Optional[str] = Field(
None, description='Specifies a summary description of the bit mask function.'
)
bits: Dict[str, MaskValue]


class GroupMask(BaseModel):
description: Optional[str] = Field(
None, description='Specifies a summary description of the group mask function.'
)
values: Dict[str, MaskValue]


class MaskType(RootModel[str]):
root: str = Field(
...,
description='Specifies the name of the bit mask or group mask used to represent the payload value.',
)


class InterfaceType(RootModel[str]):
root: str = Field(
...,
description='Specifies the name of the type used to represent the payload value in the high-level interface.',
)


class Converter(Enum):
None_ = 'None'
Payload = 'Payload'
RawPayload = 'RawPayload'


class MinValue(RootModel[float]):
root: float = Field(
...,
description='Specifies the minimum allowable value for the payload or payload member.',
)


class MaxValue(RootModel[float]):
root: float = Field(
...,
description='Specifies the maximum allowable value for the payload or payload member.',
)


class DefaultValue(RootModel[float]):
root: float = Field(
...,
description='Specifies the default value for the payload or payload member.',
)


class PayloadMember(BaseModel):
mask: Optional[int] = Field(
None,
description='Specifies the mask used to read and write this payload member.',
)
offset: Optional[int] = Field(
None,
description='Specifies the payload array offset where this payload member is stored.',
)
description: Optional[str] = Field(
None, description='Specifies a summary description of the payload member.'
)
minValue: Optional[MinValue] = None
maxValue: Optional[MaxValue] = None
defaultValue: Optional[DefaultValue] = None
maskType: Optional[MaskType] = None
interfaceType: Optional[InterfaceType] = None
converter: Optional[Converter] = None


class Visibility(Enum):
public = 'public'
private = 'private'


class Register(BaseModel):
address: conint(le=255) = Field(
..., description='Specifies the unique 8-bit address of the register.'
)
type: Type
length: Optional[conint(ge=1)] = Field(
None, description='Specifies the length of the register payload.'
)
access: Union[Access, List[Access]] = Field(
..., description='Specifies the expected use of the register.'
)
description: Optional[str] = Field(
None, description='Specifies a summary description of the register function.'
)
minValue: Optional[MinValue] = None
maxValue: Optional[MaxValue] = None
defaultValue: Optional[DefaultValue] = None
maskType: Optional[MaskType] = None
visibility: Optional[Visibility] = Field(
None,
description='Specifies whether the register function is exposed in the high-level interface.',
)
volatile: Optional[bool] = Field(
None,
description='Specifies whether register values can be saved in non-volatile memory.',
)
payloadSpec: Optional[Dict[str, PayloadMember]] = None
interfaceType: Optional[InterfaceType] = None
converter: Optional[Converter] = None


class Registers(BaseModel):
registers: Dict[str, Register] = Field(
...,
description='Specifies the collection of registers implementing the device function.',
)
bitMasks: Optional[Dict[str, BitMask]] = Field(
None,
description='Specifies the collection of masks available to be used with the different registers.',
)
groupMasks: Optional[Dict[str, GroupMask]] = Field(
None,
description='Specifies the collection of group masks available to be used with the different registers.',
)


class Model(Registers):
device: str = Field(..., description='Specifies the name of the device.')
whoAmI: int = Field(
..., description='Specifies the unique identifier for this device type.'
)
firmwareVersion: str = Field(
..., description='Specifies the semantic version of the device firmware.'
)
hardwareTargets: str = Field(
..., description='Specifies the semantic version of the device hardware.'
)
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@ extend-exclude = '''
(
^/LICENSE
^/README.md
| harp/model.py
)
'''
1 change: 1 addition & 0 deletions reflex-generator
Submodule reflex-generator added at 44f204