Skip to content

Update hightime library #560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
8 changes: 4 additions & 4 deletions generated/nidaqmx/_grpc_time.py
Original file line number Diff line number Diff line change
@@ -20,14 +20,13 @@
_EPOCH_1970 = ht_datetime(1970, 1, 1, tzinfo=timezone.utc)

def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional[GrpcTimestamp] = None) -> GrpcTimestamp:
seconds_since_1970 = int((dt - _EPOCH_1970).total_seconds())
# We need to add one more negative second if applicable to compensate for a non-zero microsecond.
if dt.microsecond and (dt < _EPOCH_1970):
seconds_since_1970 -=1
seconds_since_1970 = 0

if ts is None:
ts = GrpcTimestamp()

if isinstance(dt, ht_datetime):
seconds_since_1970 = int((dt - _EPOCH_1970).precision_total_seconds())
total_yoctoseconds = dt.yoctosecond
total_yoctoseconds += dt.femtosecond * _YS_PER_FS
total_yoctoseconds += dt.microsecond * _YS_PER_US
@@ -36,6 +35,7 @@ def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional
if remainder_yoctoseconds >= _YS_PER_NS / 2:
nanos += 1
else:
seconds_since_1970 = int((dt - _EPOCH_1970).total_seconds())
nanos = dt.microsecond * _NS_PER_US

ts.FromNanoseconds(seconds_since_1970 * _NS_PER_S + nanos)
4 changes: 3 additions & 1 deletion generated/nidaqmx/_lib_time.py
Original file line number Diff line number Diff line change
@@ -35,17 +35,19 @@ class AbsoluteTime(ctypes.Structure):

@classmethod
def from_datetime(cls, dt: Union[std_datetime, ht_datetime]) -> AbsoluteTime:
seconds_since_1904 = int((dt - AbsoluteTime._EPOCH_1904).total_seconds())
seconds_since_1904 = 0

# Convert the subseconds.
if isinstance(dt, ht_datetime):
seconds_since_1904 = int((dt - AbsoluteTime._EPOCH_1904).precision_total_seconds())
total_yoctoseconds = dt.yoctosecond
total_yoctoseconds += dt.femtosecond * AbsoluteTime._YS_PER_FS
total_yoctoseconds += dt.microsecond * AbsoluteTime._YS_PER_US
lsb = int(
round(AbsoluteTime._NUM_SUBSECONDS * total_yoctoseconds / AbsoluteTime._YS_PER_S)
)
else:
seconds_since_1904 = int((dt - AbsoluteTime._EPOCH_1904).total_seconds())
lsb = int(
round(AbsoluteTime._NUM_SUBSECONDS * dt.microsecond / AbsoluteTime._US_PER_S)
)
6,087 changes: 3,044 additions & 3,043 deletions generated/nidaqmx/_stubs/nidaqmx_pb2.pyi

Large diffs are not rendered by default.

1,199 changes: 1,195 additions & 4 deletions generated/nidaqmx/_stubs/nidaqmx_pb2_grpc.pyi

Large diffs are not rendered by default.

49 changes: 25 additions & 24 deletions generated/nidaqmx/_stubs/session_pb2.pyi
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
"""

import builtins
import collections.abc
import google.protobuf.descriptor
@@ -35,7 +36,7 @@ SESSION_INITIALIZATION_BEHAVIOR_INITIALIZE_NEW: SessionInitializationBehavior.Va
SESSION_INITIALIZATION_BEHAVIOR_ATTACH_TO_EXISTING: SessionInitializationBehavior.ValueType # 2
global___SessionInitializationBehavior = SessionInitializationBehavior

@typing_extensions.final
@typing.final
class Session(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -49,13 +50,13 @@ class Session(google.protobuf.message.Message):
name: builtins.str = ...,
id: builtins.int = ...,
) -> None: ...
def HasField(self, field_name: typing_extensions.Literal["id", b"id", "name", b"name", "session", b"session"]) -> builtins.bool: ...
def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "name", b"name", "session", b"session"]) -> None: ...
def WhichOneof(self, oneof_group: typing_extensions.Literal["session", b"session"]) -> typing_extensions.Literal["name", "id"] | None: ...
def HasField(self, field_name: typing.Literal["id", b"id", "name", b"name", "session", b"session"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["id", b"id", "name", b"name", "session", b"session"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["session", b"session"]) -> typing.Literal["name", "id"] | None: ...

global___Session = Session

@typing_extensions.final
@typing.final
class DeviceProperties(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -78,11 +79,11 @@ class DeviceProperties(google.protobuf.message.Message):
serial_number: builtins.str = ...,
product_id: builtins.int = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["model", b"model", "name", b"name", "product_id", b"product_id", "serial_number", b"serial_number", "vendor", b"vendor"]) -> None: ...
def ClearField(self, field_name: typing.Literal["model", b"model", "name", b"name", "product_id", b"product_id", "serial_number", b"serial_number", "vendor", b"vendor"]) -> None: ...

global___DeviceProperties = DeviceProperties

@typing_extensions.final
@typing.final
class EnumerateDevicesRequest(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -92,7 +93,7 @@ class EnumerateDevicesRequest(google.protobuf.message.Message):

global___EnumerateDevicesRequest = EnumerateDevicesRequest

@typing_extensions.final
@typing.final
class EnumerateDevicesResponse(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -104,11 +105,11 @@ class EnumerateDevicesResponse(google.protobuf.message.Message):
*,
devices: collections.abc.Iterable[global___DeviceProperties] | None = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["devices", b"devices"]) -> None: ...
def ClearField(self, field_name: typing.Literal["devices", b"devices"]) -> None: ...

global___EnumerateDevicesResponse = EnumerateDevicesResponse

@typing_extensions.final
@typing.final
class ReserveRequest(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -124,11 +125,11 @@ class ReserveRequest(google.protobuf.message.Message):
reservation_id: builtins.str = ...,
client_id: builtins.str = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["client_id", b"client_id", "reservation_id", b"reservation_id"]) -> None: ...
def ClearField(self, field_name: typing.Literal["client_id", b"client_id", "reservation_id", b"reservation_id"]) -> None: ...

global___ReserveRequest = ReserveRequest

@typing_extensions.final
@typing.final
class ReserveResponse(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -139,11 +140,11 @@ class ReserveResponse(google.protobuf.message.Message):
*,
is_reserved: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["is_reserved", b"is_reserved"]) -> None: ...
def ClearField(self, field_name: typing.Literal["is_reserved", b"is_reserved"]) -> None: ...

global___ReserveResponse = ReserveResponse

@typing_extensions.final
@typing.final
class IsReservedByClientRequest(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -159,11 +160,11 @@ class IsReservedByClientRequest(google.protobuf.message.Message):
reservation_id: builtins.str = ...,
client_id: builtins.str = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["client_id", b"client_id", "reservation_id", b"reservation_id"]) -> None: ...
def ClearField(self, field_name: typing.Literal["client_id", b"client_id", "reservation_id", b"reservation_id"]) -> None: ...

global___IsReservedByClientRequest = IsReservedByClientRequest

@typing_extensions.final
@typing.final
class IsReservedByClientResponse(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -174,11 +175,11 @@ class IsReservedByClientResponse(google.protobuf.message.Message):
*,
is_reserved: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["is_reserved", b"is_reserved"]) -> None: ...
def ClearField(self, field_name: typing.Literal["is_reserved", b"is_reserved"]) -> None: ...

global___IsReservedByClientResponse = IsReservedByClientResponse

@typing_extensions.final
@typing.final
class UnreserveRequest(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -194,11 +195,11 @@ class UnreserveRequest(google.protobuf.message.Message):
reservation_id: builtins.str = ...,
client_id: builtins.str = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["client_id", b"client_id", "reservation_id", b"reservation_id"]) -> None: ...
def ClearField(self, field_name: typing.Literal["client_id", b"client_id", "reservation_id", b"reservation_id"]) -> None: ...

global___UnreserveRequest = UnreserveRequest

@typing_extensions.final
@typing.final
class UnreserveResponse(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -209,11 +210,11 @@ class UnreserveResponse(google.protobuf.message.Message):
*,
is_unreserved: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["is_unreserved", b"is_unreserved"]) -> None: ...
def ClearField(self, field_name: typing.Literal["is_unreserved", b"is_unreserved"]) -> None: ...

global___UnreserveResponse = UnreserveResponse

@typing_extensions.final
@typing.final
class ResetServerRequest(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -223,7 +224,7 @@ class ResetServerRequest(google.protobuf.message.Message):

global___ResetServerRequest = ResetServerRequest

@typing_extensions.final
@typing.final
class ResetServerResponse(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

@@ -234,6 +235,6 @@ class ResetServerResponse(google.protobuf.message.Message):
*,
is_server_reset: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["is_server_reset", b"is_server_reset"]) -> None: ...
def ClearField(self, field_name: typing.Literal["is_server_reset", b"is_server_reset"]) -> None: ...

global___ResetServerResponse = ResetServerResponse
20 changes: 16 additions & 4 deletions generated/nidaqmx/_stubs/session_pb2_grpc.pyi
Original file line number Diff line number Diff line change
@@ -2,19 +2,19 @@
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
"""

import abc
import collections.abc
import grpc
import grpc.aio
from nidaqmx._stubs import session_pb2
import typing

_T = typing.TypeVar('_T')
_T = typing.TypeVar("_T")

class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta):
...
class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ...

class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore
class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg]
...

class SessionUtilitiesStub:
@@ -24,23 +24,27 @@ class SessionUtilitiesStub:
session_pb2.EnumerateDevicesResponse,
]
"""Provides a list of devices or chassis connected to server under localhost"""

Reserve: grpc.UnaryUnaryMultiCallable[
session_pb2.ReserveRequest,
session_pb2.ReserveResponse,
]
"""Reserve a set of client defined resources for exclusive use"""

IsReservedByClient: grpc.UnaryUnaryMultiCallable[
session_pb2.IsReservedByClientRequest,
session_pb2.IsReservedByClientResponse,
]
"""Determines if a set of client defined resources is currently reserved by a
specific client
"""

Unreserve: grpc.UnaryUnaryMultiCallable[
session_pb2.UnreserveRequest,
session_pb2.UnreserveResponse,
]
"""Unreserves a previously reserved resource"""

ResetServer: grpc.UnaryUnaryMultiCallable[
session_pb2.ResetServerRequest,
session_pb2.ResetServerResponse,
@@ -53,23 +57,27 @@ class SessionUtilitiesAsyncStub:
session_pb2.EnumerateDevicesResponse,
]
"""Provides a list of devices or chassis connected to server under localhost"""

Reserve: grpc.aio.UnaryUnaryMultiCallable[
session_pb2.ReserveRequest,
session_pb2.ReserveResponse,
]
"""Reserve a set of client defined resources for exclusive use"""

IsReservedByClient: grpc.aio.UnaryUnaryMultiCallable[
session_pb2.IsReservedByClientRequest,
session_pb2.IsReservedByClientResponse,
]
"""Determines if a set of client defined resources is currently reserved by a
specific client
"""

Unreserve: grpc.aio.UnaryUnaryMultiCallable[
session_pb2.UnreserveRequest,
session_pb2.UnreserveResponse,
]
"""Unreserves a previously reserved resource"""

ResetServer: grpc.aio.UnaryUnaryMultiCallable[
session_pb2.ResetServerRequest,
session_pb2.ResetServerResponse,
@@ -84,13 +92,15 @@ class SessionUtilitiesServicer(metaclass=abc.ABCMeta):
context: _ServicerContext,
) -> typing.Union[session_pb2.EnumerateDevicesResponse, collections.abc.Awaitable[session_pb2.EnumerateDevicesResponse]]:
"""Provides a list of devices or chassis connected to server under localhost"""

@abc.abstractmethod
def Reserve(
self,
request: session_pb2.ReserveRequest,
context: _ServicerContext,
) -> typing.Union[session_pb2.ReserveResponse, collections.abc.Awaitable[session_pb2.ReserveResponse]]:
"""Reserve a set of client defined resources for exclusive use"""

@abc.abstractmethod
def IsReservedByClient(
self,
@@ -100,13 +110,15 @@ class SessionUtilitiesServicer(metaclass=abc.ABCMeta):
"""Determines if a set of client defined resources is currently reserved by a
specific client
"""

@abc.abstractmethod
def Unreserve(
self,
request: session_pb2.UnreserveRequest,
context: _ServicerContext,
) -> typing.Union[session_pb2.UnreserveResponse, collections.abc.Awaitable[session_pb2.UnreserveResponse]]:
"""Unreserves a previously reserved resource"""

@abc.abstractmethod
def ResetServer(
self,
1,798 changes: 863 additions & 935 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ Sphinx = {version=">=5.0", optional=true}
sphinx_rtd_theme = {version=">=1.0", optional=true}
grpcio = {version=">=1.49.0,<2.0", optional = true}
protobuf = { version = "^4.21", optional = true }
hightime = "^0.2.1"
hightime = "^0.2.2"
tzlocal = "^5.0"
python-decouple = ">=3.8"
click = ">=8.0.0"
8 changes: 4 additions & 4 deletions src/handwritten/_grpc_time.py
Original file line number Diff line number Diff line change
@@ -20,14 +20,13 @@
_EPOCH_1970 = ht_datetime(1970, 1, 1, tzinfo=timezone.utc)

def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional[GrpcTimestamp] = None) -> GrpcTimestamp:
seconds_since_1970 = int((dt - _EPOCH_1970).total_seconds())
# We need to add one more negative second if applicable to compensate for a non-zero microsecond.
if dt.microsecond and (dt < _EPOCH_1970):
seconds_since_1970 -=1
seconds_since_1970 = 0

if ts is None:
ts = GrpcTimestamp()

if isinstance(dt, ht_datetime):
seconds_since_1970 = int((dt - _EPOCH_1970).precision_total_seconds())
total_yoctoseconds = dt.yoctosecond
total_yoctoseconds += dt.femtosecond * _YS_PER_FS
total_yoctoseconds += dt.microsecond * _YS_PER_US
@@ -36,6 +35,7 @@ def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional
if remainder_yoctoseconds >= _YS_PER_NS / 2:
nanos += 1
else:
seconds_since_1970 = int((dt - _EPOCH_1970).total_seconds())
nanos = dt.microsecond * _NS_PER_US

ts.FromNanoseconds(seconds_since_1970 * _NS_PER_S + nanos)
4 changes: 3 additions & 1 deletion src/handwritten/_lib_time.py
Original file line number Diff line number Diff line change
@@ -35,17 +35,19 @@ class AbsoluteTime(ctypes.Structure):

@classmethod
def from_datetime(cls, dt: Union[std_datetime, ht_datetime]) -> AbsoluteTime:
seconds_since_1904 = int((dt - AbsoluteTime._EPOCH_1904).total_seconds())
seconds_since_1904 = 0

# Convert the subseconds.
if isinstance(dt, ht_datetime):
seconds_since_1904 = int((dt - AbsoluteTime._EPOCH_1904).precision_total_seconds())
total_yoctoseconds = dt.yoctosecond
total_yoctoseconds += dt.femtosecond * AbsoluteTime._YS_PER_FS
total_yoctoseconds += dt.microsecond * AbsoluteTime._YS_PER_US
lsb = int(
round(AbsoluteTime._NUM_SUBSECONDS * total_yoctoseconds / AbsoluteTime._YS_PER_S)
)
else:
seconds_since_1904 = int((dt - AbsoluteTime._EPOCH_1904).total_seconds())
lsb = int(
round(AbsoluteTime._NUM_SUBSECONDS * dt.microsecond / AbsoluteTime._US_PER_S)
)
14 changes: 12 additions & 2 deletions tests/unit/test_grpc_time.py
Original file line number Diff line number Diff line change
@@ -241,7 +241,12 @@ def test___datetime_before_1970_with_microseconds___convert_to_timestamp___is_re
to_ts = grpc_time.convert_time_to_timestamp(from_dt)
roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=timezone.utc)

assert to_ts.seconds == JAN_01_1850_TIMESTAMP_1970_EPOCH
if microsecond:
# with a change of non-zero subsecond value, the seconds value is off by 1
# because of negative seconds value
assert to_ts.seconds == JAN_01_1850_TIMESTAMP_1970_EPOCH + 1
else:
assert to_ts.seconds == JAN_01_1850_TIMESTAMP_1970_EPOCH
assert to_ts.nanos == nanoseconds
assert roundtrip_dt.microsecond == microsecond

@@ -302,7 +307,12 @@ def test___datetime_before_1970_with_femtoseconds___convert_to_timestamp___is_re
to_ts = grpc_time.convert_time_to_timestamp(from_dt)
roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=timezone.utc)

assert to_ts.seconds == JAN_01_1850_TIMESTAMP_1970_EPOCH
if femtosecond:
# with a change of non-zero subsecond value, the seconds value is off by 1
# because of negative seconds value
assert to_ts.seconds == JAN_01_1850_TIMESTAMP_1970_EPOCH + 1
else:
assert to_ts.seconds == JAN_01_1850_TIMESTAMP_1970_EPOCH
assert to_ts.nanos == nanoseconds
# we lost femtosecond precision coercing to nanoseconds.
assert roundtrip_dt.femtosecond == nanoseconds * (10**6)
16 changes: 14 additions & 2 deletions tests/unit/test_lib_time.py
Original file line number Diff line number Diff line change
@@ -225,6 +225,8 @@ def test___datetime_before_1904_with_microseconds___convert_to_timestamp___is_re
roundtrip_dt = to_ts.to_datetime(tzinfo=timezone.utc)

if microsecond:
# with a change of non-zero subsecond value, the seconds value is off by 1
# because of negative seconds value
assert to_ts.msb == JAN_01_1850_LIB.msb + 1
else:
assert to_ts.msb == JAN_01_1850_LIB.msb
@@ -274,7 +276,12 @@ def test___datetime_before_1904_with_femtoseconds___convert_to_timestamp___is_re
ts = LibTimestamp.from_datetime(from_dt)
roundtrip_dt = ts.to_datetime(tzinfo=timezone.utc)

assert ts.msb == JAN_01_1850_LIB.msb
if femtosecond:
# with a change of non-zero subsecond value, the seconds value is off by 1
# because of negative seconds value
assert ts.msb == JAN_01_1850_LIB.msb + 1
else:
assert ts.msb == JAN_01_1850_LIB.msb
assert ts.lsb == subseconds
# comparison is tricky since imprecision in the conversion to NI-BTF are
# caught by the higher precision values in hightime, so we round here.
@@ -323,6 +330,11 @@ def test___datetime_before_1904_with_yoctoseconds___convert_to_timestamp___is_re
ts = LibTimestamp.from_datetime(from_dt)
to_dt = ts.to_datetime(tzinfo=timezone.utc)

assert ts.msb == JAN_01_1850_LIB.msb
if yoctosecond:
# with a change of non-zero subsecond value, the seconds value is off by 1
# because of negative seconds value
assert ts.msb == JAN_01_1850_LIB.msb + 1
else:
assert ts.msb == JAN_01_1850_LIB.msb
assert ts.lsb == subseconds
assert to_dt.yoctosecond == yoctosecond_round_trip