Skip to content

Commit 6ad34ec

Browse files
committed
Merge branch 'feat-device-reader-constructors' of https://github.com/harp-tech/harp-python into feat-device-reader-constructors
2 parents 70cea11 + cc258c1 commit 6ad34ec

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed

harp/reader.py

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pathlib import Path
99
from typing import Callable, Iterable, Mapping, Optional, Protocol, Union
1010

11+
import requests
1112
from numpy import dtype
1213
from pandas import DataFrame, Series
1314
from pandas._typing import Axes # pyright: ignore[reportPrivateImportUsage]
@@ -312,6 +313,240 @@ def from_dataset(
312313
"The dataset must be a directory containing a device.yml file."
313314
)
314315

316+
@classmethod
317+
def from_file(
318+
cls,
319+
filepath: PathLike,
320+
base_path: Optional[PathLike] = None,
321+
include_common_registers: bool = True,
322+
epoch: Optional[datetime] = None,
323+
keep_type: bool = False,
324+
) -> "DeviceReader":
325+
"""Creates a device reader object from the specified schema yml file.
326+
327+
Parameters
328+
----------
329+
filepath
330+
A path to the device yml schema describing the device.
331+
base_path
332+
The path to attempt to resolve the location of data files.
333+
include_common_registers
334+
Specifies whether to include the set of Harp common registers in the
335+
parsed device schema object. If a parsed device schema object is provided,
336+
this parameter is ignored.
337+
epoch
338+
The default reference datetime at which time zero begins. If specified,
339+
the data frames returned by each register reader will have a datetime index.
340+
keep_type
341+
Specifies whether to include a column with the message type by default.
342+
343+
Returns
344+
-------
345+
A device reader object which can be used to read binary data for each
346+
register or to access metadata about each register. Individual registers
347+
can be accessed using dot notation using the name of the register as the
348+
key.
349+
"""
350+
351+
device = read_schema(filepath, include_common_registers)
352+
if base_path is None:
353+
path = Path(filepath).absolute().resolve()
354+
base_path = path.parent / device.device
355+
else:
356+
base_path = Path(base_path).absolute().resolve() / device.device
357+
358+
reg_readers = {
359+
name: _create_register_handler(device, name, _ReaderParams(base_path, epoch, keep_type))
360+
for name in device.registers.keys()
361+
}
362+
return cls(device, reg_readers)
363+
364+
@classmethod
365+
def from_url(
366+
cls,
367+
url: str,
368+
base_path: Optional[PathLike] = None,
369+
include_common_registers: bool = True,
370+
epoch: Optional[datetime] = None,
371+
keep_type: bool = False,
372+
timeout: int = 5,
373+
) -> "DeviceReader":
374+
"""Creates a device reader object from a url pointing to a device.yml file.
375+
376+
Parameters
377+
----------
378+
url
379+
The url pointing to the device.yml schema describing the device.
380+
base_path
381+
The path to attempt to resolve the location of data files.
382+
include_common_registers
383+
Specifies whether to include the set of Harp common registers in the
384+
parsed device schema object. If a parsed device schema object is provided,
385+
this parameter is ignored.
386+
epoch
387+
The default reference datetime at which time zero begins. If specified,
388+
the data frames returned by each register reader will have a datetime index.
389+
keep_type
390+
Specifies whether to include a column with the message type by default.
391+
timeout
392+
The number of seconds to wait for the server to send data before giving up.
393+
Returns
394+
-------
395+
A device reader object which can be used to read binary data for each
396+
register or to access metadata about each register. Individual registers
397+
can be accessed using dot notation using the name of the register as the
398+
key.
399+
"""
400+
401+
response = requests.get(url, timeout=timeout)
402+
text = response.text
403+
404+
device = read_schema(text, include_common_registers)
405+
if base_path is None:
406+
base_path = Path(device.device).absolute().resolve()
407+
else:
408+
base_path = Path(base_path).absolute().resolve()
409+
410+
reg_readers = {
411+
name: _create_register_handler(device, name, _ReaderParams(base_path, epoch, keep_type))
412+
for name in device.registers.keys()
413+
}
414+
return cls(device, reg_readers)
415+
416+
@classmethod
417+
def from_str(
418+
cls,
419+
schema: str,
420+
base_path: Optional[PathLike] = None,
421+
include_common_registers: bool = True,
422+
epoch: Optional[datetime] = None,
423+
keep_type: bool = False,
424+
) -> "DeviceReader":
425+
"""Creates a device reader object from a string containing a device.yml schema.
426+
427+
Parameters
428+
----------
429+
schema
430+
The string containing the device.yml schema describing the device.
431+
base_path
432+
The path to attempt to resolve the location of data files.
433+
include_common_registers
434+
Specifies whether to include the set of Harp common registers in the
435+
parsed device schema object. If a parsed device schema object is provided,
436+
this parameter is ignored.
437+
epoch
438+
The default reference datetime at which time zero begins. If specified,
439+
the data frames returned by each register reader will have a datetime index.
440+
keep_type
441+
Specifies whether to include a column with the message type by default.
442+
443+
Returns
444+
-------
445+
A device reader object which can be used to read binary data for each
446+
register or to access metadata about each register. Individual registers
447+
can be accessed using dot notation using the name of the register as the
448+
key.
449+
"""
450+
451+
device = read_schema(schema, include_common_registers)
452+
if base_path is None:
453+
base_path = Path(device.device).absolute().resolve()
454+
else:
455+
base_path = Path(base_path).absolute().resolve()
456+
457+
reg_readers = {
458+
name: _create_register_handler(device, name, _ReaderParams(base_path, epoch, keep_type))
459+
for name in device.registers.keys()
460+
}
461+
return cls(device, reg_readers)
462+
463+
@classmethod
464+
def from_model(
465+
cls,
466+
model: Model,
467+
base_path: Optional[PathLike] = None,
468+
epoch: Optional[datetime] = None,
469+
keep_type: bool = False,
470+
) -> "DeviceReader":
471+
"""Creates a device reader object from a parsed device schema object.
472+
473+
Parameters
474+
----------
475+
model
476+
The parsed device schema object describing the device.
477+
base_path
478+
The path to attempt to resolve the location of data files.
479+
epoch
480+
The default reference datetime at which time zero begins. If specified,
481+
the data frames returned by each register reader will have a datetime index.
482+
keep_type
483+
Specifies whether to include a column with the message type by default.
484+
485+
Returns
486+
-------
487+
A device reader object which can be used to read binary data for each
488+
register or to access metadata about each register. Individual registers
489+
can be accessed using dot notation using the name of the register as the
490+
key.
491+
"""
492+
493+
if base_path is None:
494+
base_path = Path(model.device).absolute().resolve()
495+
else:
496+
base_path = Path(base_path).absolute().resolve()
497+
498+
reg_readers = {
499+
name: _create_register_handler(model, name, _ReaderParams(base_path, epoch, keep_type))
500+
for name in model.registers.keys()
501+
}
502+
return cls(model, reg_readers)
503+
504+
@classmethod
505+
def from_dataset(
506+
cls,
507+
dataset: PathLike,
508+
include_common_registers: bool = True,
509+
epoch: Optional[datetime] = None,
510+
keep_type: bool = False,
511+
) -> "DeviceReader":
512+
"""Creates a device reader object from the specified dataset folder.
513+
514+
Parameters
515+
----------
516+
dataset
517+
A path to the dataset folder containing a device.yml schema describing the device.
518+
include_common_registers
519+
Specifies whether to include the set of Harp common registers in the
520+
parsed device schema object. If a parsed device schema object is provided,
521+
this parameter is ignored.
522+
epoch
523+
The default reference datetime at which time zero begins. If specified,
524+
the data frames returned by each register reader will have a datetime index.
525+
keep_type
526+
Specifies whether to include a column with the message type by default.
527+
528+
Returns
529+
-------
530+
A device reader object which can be used to read binary data for each
531+
register or to access metadata about each register. Individual registers
532+
can be accessed using dot notation using the name of the register as the
533+
key.
534+
"""
535+
536+
path = Path(dataset).absolute().resolve()
537+
is_dir = os.path.isdir(path)
538+
if is_dir:
539+
filepath = path / "device.yml"
540+
return cls.from_file(
541+
filepath=filepath,
542+
base_path=path,
543+
include_common_registers=include_common_registers,
544+
epoch=epoch,
545+
keep_type=keep_type,
546+
)
547+
else:
548+
raise ValueError("The dataset must be a directory containing a device.yml file.")
549+
315550

316551
def _compose_parser(
317552
f: Callable[[DataFrame], DataFrame],

0 commit comments

Comments
 (0)