Skip to content

Commit ca0c665

Browse files
authored
Merge pull request #20 from harp-tech/gl-dev
Surface public API through top-level package
2 parents f9f03f6 + 515f669 commit ca0c665

5 files changed

Lines changed: 104 additions & 32 deletions

File tree

README.md

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,55 @@ A low-level interface to data collected with the [Harp](https://harp-tech.org/)
44

55
## Data model
66

7-
To regenerate Pydantic data models from device schema definitions, activate a virtual environment with `dev` dependencies, and run:
7+
The interface makes use of a Pydantic data model generated from Harp device schema definitions. The schema data classes are used to automatically generate binary readers for each device.
8+
9+
All binary data files from a single device need to be stored in the same folder alongside the device meta-schema, named `device.yml`. Each register file should have the following naming convention `<deviceName>_<registerAddress>.bin`.
10+
11+
For example, for a dataset collected with a `Behavior` device, you might have:
812

913
```
10-
datamodel-codegen --input ./reflex-generator/schema/device.json --output harp/model.py --output-model-type pydantic_v2.BaseModel
14+
📦device.harp
15+
┣ 📜Behavior_0.bin
16+
┣ 📜Behavior_1.bin
17+
...
18+
┗ 📜device.yml
1119
```
1220

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.
15-
1621
## How to use
1722

18-
### Read Harp device schema from YML file
19-
20-
```python
21-
from harp.schema import read_schema
22-
schema = read_schema('device.yml')
23-
```
24-
2523
### Create device reader object from schema
2624

2725
```python
28-
from harp.reader import create_reader
29-
reader = create_reader(schema)
26+
import harp
27+
reader = harp.create_reader("device.harp")
3028
```
3129

3230
### Read data from named register
3331

3432
```python
35-
reader.OperationControl.read("data/Behavior_10.bin")
33+
reader.OperationControl.read()
3634
```
3735

3836
### Access register metadata
3937

4038
```python
4139
reader.OperationControl.register.address
4240
```
41+
42+
### Create device reader object with UTC datetime format
43+
44+
```python
45+
reader = harp.create_reader("device.harp", epoch=harp.REFERENCE_EPOCH)
46+
```
47+
48+
### Read data with message type information
49+
50+
```python
51+
reader.OperationControl.read(keep_type=True)
52+
```
53+
54+
### Read data from a specific file
55+
56+
```python
57+
reader.OperationControl.read("data/Behavior_10.bin")
58+
```

harp/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from harp.io import REFERENCE_EPOCH, MessageType, read
2+
from harp.reader import create_reader
3+
from harp.schema import read_schema

harp/io.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
import numpy as np
77
import pandas as pd
88

9-
"""The reference epoch for UTC harp time."""
109
REFERENCE_EPOCH = datetime(1904, 1, 1)
10+
"""The reference epoch for UTC harp time."""
1111

1212

1313
class MessageType(IntEnum):
14+
"""Specifies the type of a Harp message."""
15+
1416
NA = 0
1517
READ = 1
1618
WRITE = 2
@@ -41,22 +43,33 @@ def read(
4143
epoch: Optional[datetime] = None,
4244
keep_type: bool = False,
4345
):
44-
"""
45-
Read single-register Harp data from the specified file.
46-
47-
:param file: Open file object or filename containing binary data from
48-
a single device register.
49-
:param address: Expected register address. If specified, the address of
50-
the first message in the file is used for validation.
51-
:param dtype: Expected data type of the register payload. If specified, the
52-
payload type of the first message in the file is used for validation.
53-
:param length: Expected number of elements in register payload. If specified,
54-
the payload length of the first message in the file is used for validation.
55-
:param columns: The optional column labels to use for the data values.
56-
:param epoch: Reference datetime at which time zero begins. If specified,
57-
the result data frame will have a datetime index.
58-
:param keep_type: Specifies whether to include a column with the message type.
59-
:return: A pandas data frame containing message data, sorted by time.
46+
"""Read single-register Harp data from the specified file.
47+
48+
Parameters
49+
----------
50+
file
51+
Open file object or filename containing binary data from
52+
a single device register.
53+
address
54+
Expected register address. If specified, the address of
55+
the first message in the file is used for validation.
56+
dtype
57+
Expected data type of the register payload. If specified, the
58+
payload type of the first message in the file is used for validation.
59+
length
60+
Expected number of elements in register payload. If specified, the
61+
payload length of the first message in the file is used for validation.
62+
columns
63+
The optional column labels to use for the data values.
64+
epoch
65+
Reference datetime at which time zero begins. If specified,
66+
the result data frame will have a datetime index.
67+
keep_type
68+
Specifies whether to include a column with the message type.
69+
70+
Returns
71+
-------
72+
A pandas data frame containing message data, sorted by time.
6073
"""
6174
data = np.fromfile(file, dtype=np.uint8)
6275
if len(data) == 0:

harp/reader.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,30 @@ def create_reader(
213213
epoch: Optional[datetime] = None,
214214
keep_type: bool = False,
215215
):
216+
"""Creates a device reader object from the specified dataset or schema.
217+
218+
Parameters
219+
----------
220+
device
221+
A path to the device schema, dataset folder, or parsed device schema object
222+
describing the device.
223+
include_common_registers
224+
Specifies whether to include the set of Harp common registers in the
225+
parsed device schema object. If a parsed device schema object is provided,
226+
this parameter is ignored.
227+
epoch
228+
The default reference datetime at which time zero begins. If specified,
229+
the data frames returned by each register reader will have a datetime index.
230+
keep_type
231+
Specifies whether to include a column with the message type by default.
232+
233+
Returns
234+
-------
235+
A device reader object which can be used to read binary data for each
236+
register or to access metadata about each register. Individual registers
237+
can be accessed using dot notation using the name of the register as the
238+
key.
239+
"""
216240
if isinstance(device, Model):
217241
base_path = Path(device.device)
218242
else:

harp/schema.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ def _read_common_registers() -> Registers:
1414
def read_schema(
1515
file: Union[str, PathLike, TextIO], include_common_registers: bool = True
1616
) -> Model:
17+
"""Read and parse a device schema from the specified file.
18+
19+
Parameters
20+
----------
21+
file
22+
Open file object or filename containing a YAML text stream describing
23+
a device schema.
24+
include_common_registers
25+
Specifies whether to include the set of Harp common registers in the
26+
returned device schema object.
27+
28+
Returns
29+
-------
30+
A Pydantic model object representing the Harp device schema.
31+
"""
32+
1733
if isinstance(file, (str, PathLike)):
1834
with open(file) as fileIO:
1935
return read_schema(fileIO)

0 commit comments

Comments
 (0)