diff --git a/armarx_core/time/date_time.py b/armarx_core/time/date_time.py index c4432d39dadbad6f528b103dd8411d3533cac354..aa8826792a8079307a578c243111ef1358a496d8 100644 --- a/armarx_core/time/date_time.py +++ b/armarx_core/time/date_time.py @@ -3,7 +3,7 @@ import time import numpy as np import dataclasses -from enum import Enum, auto +from enum import IntEnum import armarx_core from armarx_core.ice_conversion.ice_converter import IceConverter @@ -14,14 +14,16 @@ slice_loader.load_armarx_slice("ArmarXCore", "core/time.ice") from armarx.core.time.dto import DateTime as DateTimeIce from armarx.core.time.dto.ClockType import ClockTypeEnum +from armarx_core.time.duration import Duration, MIN_LONG_CPP -MIN_LONG_CPP = -9223372036854775808 INVALID_TIME_USEC = MIN_LONG_CPP + # auxiliary function def time_usec() -> int: return int(time.time() * 1e6) + # Converter between dto and a plain int value class DateTimeIceConverter(IceConverter): @classmethod @@ -41,19 +43,11 @@ class DateTimeIceConverter(IceConverter): # DateTime type corresponding to C++ type (preferred) @dataclasses.dataclass -class ClockType(Enum): - Realtime = auto() - Monotonic = auto() - Virtual = auto() - Unknown = auto() - - def to_aron(self): - return pythonic_to_aron_ice(self.value) - - -@dataclasses.dataclass -class Duration(AronDataclass): - microSeconds: np.int64 = MIN_LONG_CPP +class ClockType(IntEnum): + Realtime = 0 + Monotonic = 1 + Virtual = 2 + Unknown = 3 @dataclasses.dataclass @@ -70,7 +64,7 @@ class DateTime(AronDataclass): @staticmethod def from_seconds(time_in_sec: float): dto = DateTime() - dto.timeSinceEpoch.microSeconds = int(round(time_in_sec * 1e6)) + dto.timeSinceEpoch.microSeconds = np.int64(round(time_in_sec * 1e6)) return dto diff --git a/armarx_core/time/duration.py b/armarx_core/time/duration.py index 92012f38619982d7a5deb6695ea88b324c9bd201..0b3a5f7687d1873f9fbdce12b940410164fbab3f 100644 --- a/armarx_core/time/duration.py +++ b/armarx_core/time/duration.py @@ -1,26 +1,34 @@ +import dataclasses +import numpy as np + import armarx_core from armarx_core.ice_conversion.ice_converter import IceConverter from armarx_core import slice_loader - -from armarx_core.time.date_time import MIN_LONG_CPP +from armarx_memory.aron.aron_dataclass import AronDataclass slice_loader.load_armarx_slice("ArmarXCore", "core/time.ice") from armarx.core.time import dto +MIN_LONG_CPP = -9223372036854775808 + +@dataclasses.dataclass +class Duration(AronDataclass): + microSeconds: np.int64 = MIN_LONG_CPP, # std::numeric_limits<long>::min() -class Duration: - def __init__( - self, - microseconds = MIN_LONG_CPP, # std::numeric_limits<long>::min() - ): - self.microseconds = microseconds + @staticmethod + def from_seconds(seconds: float) -> "Duration": + return Duration(np.int64(round(seconds * 1e6))) + + def as_seconds(self) -> float: + print(self.microSeconds) + return self.microSeconds * 1e-6 def __repr__(self) -> str: - return f"<Duration {self.microseconds} µs>" + return f"<Duration {self.microSeconds} µs>" def __str__(self) -> str: unit = "µs" - time_count = self.microseconds + time_count = self.microSeconds unit_ratios = [ ("ms", 1000), @@ -46,12 +54,12 @@ class DurationIceConv(IceConverter): def _from_ice(self, dto: dto.Duration) -> Duration: return Duration( - microseconds=dto.microSeconds, + microSeconds=dto.microSeconds, ) def _to_ice(self, bo: Duration) -> dto.Duration: return dto.Duration( - microSeconds=bo.microseconds, + microSeconds=bo.microSeconds, ) @@ -64,3 +72,9 @@ if __name__ == '__main__': dt2 = conv.to_ice(bo) print(dt2) assert dt2 == dt + + # without converter, using standard aron conversions: + dto = Duration.from_seconds(1.2) + ice = dto.to_aron_ice() + dto2 = Duration.from_aron_ice(ice) + assert dto.as_seconds() == 1.2 diff --git a/armarx_memory/aron/common/framed/__init__.py b/armarx_memory/aron/common/framed/__init__.py index e28abd91be74c1f21e5f7ba591ba204783bf93b7..93d1c55da130912f31c0d0d80732914c32ed4006 100644 --- a/armarx_memory/aron/common/framed/__init__.py +++ b/armarx_memory/aron/common/framed/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import dataclasses as dc import numpy as np @@ -42,3 +44,14 @@ class FramedPose(AronDataclass): """ header: FrameID = dc.field(default_factory=FrameID) pose: np.ndarray = dc.field(default_factory=lambda: np.eye(4, dtype=np.float32)) + + @classmethod + def with_type_check(cls, header: FrameID, pose: np.ndarray) -> FramedPose: + """ + Ensures that the data type of the pose array is correct; otherwise, there might be problems when reading + the memory from C++ + """ + assert isinstance(header, FrameID) + assert isinstance(pose, np.ndarray) + assert pose.shape == (4, 4) + return cls(header, pose.astype(np.float32)) diff --git a/armarx_memory/aron/conversion/dataclass_from_to_pythonic.py b/armarx_memory/aron/conversion/dataclass_from_to_pythonic.py index 8ee03bdf900b50e7c86aed4ca0c18faf16a48bcf..28e0e3d5a549b221d0923baa6f43789d4ad2c3be 100644 --- a/armarx_memory/aron/conversion/dataclass_from_to_pythonic.py +++ b/armarx_memory/aron/conversion/dataclass_from_to_pythonic.py @@ -296,7 +296,7 @@ class DataclassFromToDict: union_types = type_.__args__ assert type(None) in union_types, (type_, origin, union_types) else: - assert type_ == type(None), (type_, origin) # Note, If this fails, comment out + assert type_ == type(None), (type_, origin) # Note, If this fails, comment out; beforehand, make sure you have properly annotated optionals as Union[TheType, None] return None @@ -308,6 +308,9 @@ class DataclassFromToDict: elif isinstance(value, dict): return {k: self.dataclass_to_dict(v, depth=depth + 1) for k, v in value.items()} + elif isinstance(value, enum.IntEnum) or isinstance(value, enum.Enum): + return value.value + else: if self.logger is not None: self.logger.debug(f"{pre}> Process other type: {type_}") diff --git a/armarx_memory/aron/conversion/pythonic_from_to_aron_ice.py b/armarx_memory/aron/conversion/pythonic_from_to_aron_ice.py index e1891bd004fa5b8676ec6ef838584e20f2f68110..6bdfe76e7dc96612f94522c5e697845f963cf521 100644 --- a/armarx_memory/aron/conversion/pythonic_from_to_aron_ice.py +++ b/armarx_memory/aron/conversion/pythonic_from_to_aron_ice.py @@ -29,6 +29,10 @@ def pythonic_to_aron_ice( elif isinstance(value, np.int64): return AronIceTypes.long(int(value)) elif isinstance(value, int) or isinstance(value, np.int32): + assert(isinstance(value, int)), value + assert 'invalid value - expected int' not in str(AronIceTypes.int(int(value))), \ + f'Casting {value} to int failed. Did you intend to use np.int64 instead, ' \ + 'but assigned a plain int value, or converted the value somewhere in the meantime?' return AronIceTypes.int(int(value)) elif isinstance(value, np.float64): return AronIceTypes.double(float(value)) @@ -37,7 +41,15 @@ def pythonic_to_aron_ice( elif isinstance(value, list): return AronIceTypes.list(list(map(pythonic_to_aron_ice, value))) elif isinstance(value, enum.IntEnum): + assert(isinstance(value.value, int)), f'Value of IntEnum is not an int: {value.value}' return pythonic_to_aron_ice(value.value) # int + elif isinstance(value, enum.Enum): + if not isinstance(value.value, int): + raise NotImplementedError( + f'enum.Enum is only supported as long as it uses int values, but got value {value.value}. ' + 'Consider directly using enum.IntEnum.' + ) + return pythonic_to_aron_ice(value.value) elif isinstance(value, dict): a = AronIceTypes.dict({k: pythonic_to_aron_ice(v) for k, v in value.items()})