Skip to content
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

Add Unit test for cfg_time_start_trig #488

Merged
merged 21 commits into from
Feb 20, 2024
1 change: 1 addition & 0 deletions generated/nidaqmx/_grpc_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Callable, Generic, Optional, TypeVar

import google.protobuf.message
from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp
import grpc
import numpy

Expand Down
7 changes: 5 additions & 2 deletions generated/nidaqmx/_grpc_time.py
DeborahOoi96 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
_YS_PER_FS = 10**9


def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTimestamp) -> None:
def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional[GrpcTimestamp] = None) -> GrpcTimestamp:
utc_dt = dt.astimezone(tz=timezone.utc)
seconds = int(utc_dt.timestamp())
if ts is None:
ts = GrpcTimestamp()

if isinstance(dt, ht_datetime):
total_yoctoseconds = dt.yoctosecond
Expand All @@ -30,6 +32,7 @@ def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTime
nanos = utc_dt.microsecond * _NS_PER_US

ts.FromNanoseconds(seconds * _NS_PER_S + nanos)
return ts


def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: Optional[timezone] = None) -> ht_datetime:
Expand All @@ -47,4 +50,4 @@ def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: Optional[timezone] = No
# Add in precision
dt = dt.replace(microsecond=microsecond, femtosecond=femtosecond, yoctosecond=yoctosecond)
# Then convert to requested timezone
return dt.astimezone(tz=tzinfo)
return dt.astimezone(tz=tzinfo)
2 changes: 1 addition & 1 deletion generated/nidaqmx/_lib_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,4 @@ def __lt__(self, other) -> bool:
if self.msb == other.msb:
return self.lsb < other.lsb
else:
return self.msb < other.msb
return self.msb < other.msb
2 changes: 1 addition & 1 deletion generated/nidaqmx/_library_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3684,7 +3684,7 @@ def get_trig_attribute_timestamp(self, task, attribute):
error_code = cfunc(
task, attribute, ctypes.byref(value))
self.check_for_error(error_code)
return value
return value.to_datetime()

def get_trig_attribute_uint32(self, task, attribute):
value = ctypes.c_uint32()
Expand Down
1 change: 1 addition & 0 deletions src/codegen/templates/_grpc_interpreter.py.mako
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import warnings
from typing import Callable, Generic, Optional, TypeVar

import google.protobuf.message
from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp
import grpc
import numpy

Expand Down
4 changes: 3 additions & 1 deletion src/codegen/utilities/interpreter_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,10 @@ def get_return_values(func):
return_values.append(param.parameter_name)
else:
return_values.append(f"{param.parameter_name}.tolist()")
elif param.type == "TaskHandle" or param.type == "CVIAbsoluteTime":
elif param.type == "TaskHandle":
return_values.append(param.parameter_name)
elif param.type == "CVIAbsoluteTime":
return_values.append(f"{param.parameter_name}.to_datetime()")
else:
return_values.append(f"{param.parameter_name}.value")
if func.is_init_method:
Expand Down
7 changes: 5 additions & 2 deletions src/handwritten/_grpc_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
_YS_PER_FS = 10**9


def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTimestamp) -> None:
def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: Optional[GrpcTimestamp] = None) -> GrpcTimestamp:
utc_dt = dt.astimezone(tz=timezone.utc)
seconds = int(utc_dt.timestamp())
if ts is None:
ts = GrpcTimestamp()

if isinstance(dt, ht_datetime):
total_yoctoseconds = dt.yoctosecond
Expand All @@ -30,6 +32,7 @@ def convert_time_to_timestamp(dt: Union[std_datetime, ht_datetime], ts: GrpcTime
nanos = utc_dt.microsecond * _NS_PER_US

ts.FromNanoseconds(seconds * _NS_PER_S + nanos)
return ts


def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: Optional[timezone] = None) -> ht_datetime:
Expand All @@ -47,4 +50,4 @@ def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: Optional[timezone] = No
# Add in precision
dt = dt.replace(microsecond=microsecond, femtosecond=femtosecond, yoctosecond=yoctosecond)
# Then convert to requested timezone
return dt.astimezone(tz=tzinfo)
return dt.astimezone(tz=tzinfo)
2 changes: 1 addition & 1 deletion src/handwritten/_lib_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,4 @@ def __lt__(self, other) -> bool:
if self.msb == other.msb:
return self.lsb < other.lsb
else:
return self.msb < other.msb
return self.msb < other.msb
53 changes: 53 additions & 0 deletions tests/component/_task_modules/test_triggers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from datetime import timedelta

import pytest
from hightime import datetime as ht_datetime

from nidaqmx.constants import Timescale
from nidaqmx.task import Task


@pytest.fixture()
def ai_voltage_field_daq_task(task, sim_field_daq_device):
"""Gets AI voltage task."""
task.ai_channels.add_ai_voltage_chan(sim_field_daq_device.ai_physical_chans[0].name)
yield task


def test___default_arguments___cfg_time_start_trig___no_errors(
ai_voltage_field_daq_task: Task,
):
ai_voltage_field_daq_task.timing.cfg_samp_clk_timing(1000)
trigger_time = ht_datetime.now() + timedelta(seconds=10)

ai_voltage_field_daq_task.triggers.start_trigger.cfg_time_start_trig(trigger_time)

when_value = ai_voltage_field_daq_task.triggers.start_trigger.trig_when
timescale_value = ai_voltage_field_daq_task.triggers.start_trigger.timestamp_timescale
assert timescale_value == Timescale.USE_HOST
assert when_value.year == trigger_time.year
assert when_value.month == trigger_time.month
assert when_value.day == trigger_time.day
assert when_value.hour == trigger_time.hour
assert when_value.minute == trigger_time.minute
assert when_value.second == trigger_time.second


def test___arguments_provided___cfg_time_start_trig___no_errors(
ai_voltage_field_daq_task: Task,
):
ai_voltage_field_daq_task.timing.cfg_samp_clk_timing(1000)
trigger_time = ht_datetime.now() + timedelta(seconds=10)
# simulated devices don't support setting timescale to USE_IO_DEVICE
timescale = Timescale.USE_HOST

ai_voltage_field_daq_task.triggers.start_trigger.cfg_time_start_trig(trigger_time, timescale)

when_value = ai_voltage_field_daq_task.triggers.start_trigger.trig_when
assert when_value.year == trigger_time.year
assert when_value.month == trigger_time.month
assert when_value.day == trigger_time.day
assert when_value.hour == trigger_time.hour
assert when_value.minute == trigger_time.minute
assert when_value.second == trigger_time.second
assert ai_voltage_field_daq_task.triggers.start_trigger.timestamp_timescale == timescale
72 changes: 72 additions & 0 deletions tests/component/_task_modules/test_triggers_properties.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from datetime import timezone

import pytest
from hightime import datetime as ht_datetime

from nidaqmx.constants import TaskMode, TriggerType
from nidaqmx.error_codes import DAQmxErrors
from nidaqmx.errors import DaqError
from nidaqmx.task import Task
from tests.unit._time_utils import JAN_01_1904_HIGHTIME, JAN_01_2002_HIGHTIME


@pytest.fixture()
Expand All @@ -13,6 +17,20 @@ def ai_voltage_task(task, any_x_series_device):
yield task


@pytest.fixture()
def ai_voltage_field_daq_task(task, sim_field_daq_device):
"""Gets AI voltage task."""
task.ai_channels.add_ai_voltage_chan(sim_field_daq_device.ai_physical_chans[0].name)
yield task


def convert_to_local_timezone(expected_time_utc):
current_time_utc = ht_datetime.now(timezone.utc)
local_timezone_offset = current_time_utc.astimezone().utcoffset()
local_expected_time = expected_time_utc + local_timezone_offset
return local_expected_time


def test___ai_task___get_float_property___returns_default_value(ai_voltage_task: Task):
assert ai_voltage_task.triggers.start_trigger.dig_edge_dig_fltr_timebase_rate == 0.0

Expand Down Expand Up @@ -99,3 +117,57 @@ def test___ai_task___reset_uint32_property___returns_default_value(ai_voltage_ta
del ai_voltage_task.triggers.reference_trigger.pretrig_samples

assert ai_voltage_task.triggers.reference_trigger.pretrig_samples == 2


@pytest.mark.xfail(reason="Timestamp conversion doesn't work on dates before 1970", raises=OSError)
def test___ai_voltage_field_daq_task___get_timestamp_property___returns_default_value(
ai_voltage_field_daq_task: Task,
):
ai_voltage_field_daq_task.timing.cfg_samp_clk_timing(1000)

when_value = ai_voltage_field_daq_task.triggers.start_trigger.trig_when

localized_default_value = convert_to_local_timezone(JAN_01_1904_HIGHTIME)
assert when_value.year == localized_default_value.year
assert when_value.month == localized_default_value.month
assert when_value.day == localized_default_value.day
assert when_value.hour == localized_default_value.hour
assert when_value.minute == localized_default_value.minute
assert when_value.second == localized_default_value.second


def test___ai_voltage_field_daq_task___set_timestamp_property___returns_assigned_value(
ai_voltage_field_daq_task: Task,
):
value_to_test = JAN_01_2002_HIGHTIME
ai_voltage_field_daq_task.timing.cfg_samp_clk_timing(1000)

ai_voltage_field_daq_task.triggers.start_trigger.trig_when = value_to_test

when_value = ai_voltage_field_daq_task.triggers.start_trigger.trig_when
localized_value_to_test = convert_to_local_timezone(value_to_test)
assert when_value.year == localized_value_to_test.year
assert when_value.month == localized_value_to_test.month
assert when_value.day == localized_value_to_test.day
assert when_value.hour == localized_value_to_test.hour
assert when_value.minute == localized_value_to_test.minute
assert when_value.second == localized_value_to_test.second


@pytest.mark.xfail(reason="Timestamp conversion doesn't work on dates before 1970", raises=OSError)
def test___ai_voltage_field_daq_task___reset_timestamp_property___returns_default_value(
ai_voltage_field_daq_task: Task,
):
ai_voltage_field_daq_task.timing.cfg_samp_clk_timing(1000)
ai_voltage_field_daq_task.triggers.start_trigger.trig_when = JAN_01_2002_HIGHTIME

del ai_voltage_field_daq_task.triggers.start_trigger.trig_when

when_value = ai_voltage_field_daq_task.triggers.start_trigger.trig_when
localized_default_value = convert_to_local_timezone(JAN_01_1904_HIGHTIME)
assert when_value.year == localized_default_value.year
assert when_value.month == localized_default_value.month
assert when_value.day == localized_default_value.day
assert when_value.hour == localized_default_value.hour
assert when_value.minute == localized_default_value.minute
assert when_value.second == localized_default_value.second
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def _device_by_product_type(
device_type == DeviceType.ANY
or (device_type == DeviceType.REAL and not device.is_simulated)
or (device_type == DeviceType.SIMULATED and device.is_simulated)
and len(device.ai_physical_chans) >= 1
)
if device_type_match and device.product_type == product_type:
return device
Expand Down Expand Up @@ -159,6 +160,12 @@ def sim_6363_device(system: nidaqmx.system.System) -> nidaqmx.system.Device:
return _device_by_product_type("PCIe-6363", DeviceType.SIMULATED, system)


@pytest.fixture(scope="function")
def sim_field_daq_device(system):
"""Gets simulated Field DAQ device information."""
return _device_by_product_type("FD-11601", DeviceType.SIMULATED, system)


@pytest.fixture(scope="function")
def sim_ts_chassis(system: nidaqmx.system.System) -> nidaqmx.system.Device:
"""Gets simulated TestScale chassis information."""
Expand Down
24 changes: 24 additions & 0 deletions tests/max_config/nidaqmxMaxConfig.ini
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,27 @@ DevIsSimulated = 1
CompactDAQ.ChassisDevName = cdaqChassisTester
CompactDAQ.SlotNum = 1

[DAQmxFieldDAQ FieldDAQ1]
ProductType = FD-11601
DevSerialNum = 0x0
DevIsSimulated = 1
BusType = TCP/IP
TCPIP.Hostname =
TCPIP.EthernetIP = 0.0.0.0
TCPIP.EthernetMAC = 00:00:00:00:00:00
TCPIP.EthernetMDNSServiceInstance =
TCPIP.DevIsReserved = 0

[DAQmxFieldDAQBank FieldDAQ1-Bank1]
ProductType = FD-11601
DevSerialNum = 0x0
DevIsSimulated = 1
FieldDAQ.BankNum = 1
FieldDAQ.DevName = FieldDAQ1

[DAQmxFieldDAQBank FieldDAQ1-Bank2]
ProductType = FD-11601
DevSerialNum = 0x0
DevIsSimulated = 1
FieldDAQ.BankNum = 2
FieldDAQ.DevName = FieldDAQ1
2 changes: 2 additions & 0 deletions tests/unit/_time_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@

JAN_01_2002_DATETIME = std_datetime(2002, 1, 1, tzinfo=timezone.utc)
JAN_01_2002_HIGHTIME = ht_datetime(2002, 1, 1, tzinfo=timezone.utc)
JAN_01_1904_DATETIME = std_datetime(1904, 1, 1, tzinfo=timezone.utc)
JAN_01_1904_HIGHTIME = ht_datetime(1904, 1, 1, tzinfo=timezone.utc)
18 changes: 7 additions & 11 deletions tests/unit/test_grpc_time.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from datetime import datetime as std_datetime
from datetime import timedelta, timezone
from datetime import datetime as std_datetime, timedelta, timezone

import pytest
from hightime import datetime as ht_datetime

from tests.unit._time_utils import (
JAN_01_2002_TIMESTAMP_1970_EPOCH,
JAN_01_2002_DATETIME,
JAN_01_2002_HIGHTIME,
JAN_01_2002_TIMESTAMP_1970_EPOCH,
)

try:
from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp

import nidaqmx._grpc_time as grpc_time
import nidaqmx._stubs.nidaqmx_pb2 as nidaqmx_pb2
except ImportError:
Expand All @@ -22,8 +22,7 @@

@pytest.mark.parametrize("from_dt", [(JAN_01_2002_DATETIME), (JAN_01_2002_HIGHTIME)])
def test___utc_datetime___convert_to_timestamp___is_reversible(from_dt):
to_ts = GrpcTimestamp()
grpc_time.convert_time_to_timestamp(from_dt, to_ts)
to_ts = grpc_time.convert_time_to_timestamp(from_dt)
roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=timezone.utc)

total_nanoseconds = to_ts.ToNanoseconds()
Expand Down Expand Up @@ -73,8 +72,7 @@ def test___tz_datetime___convert_to_timestamp___is_reversible(
):
from_dt = datetime_cls(2002, 1, 1, tzinfo=tzinfo)

to_ts = GrpcTimestamp()
grpc_time.convert_time_to_timestamp(from_dt, to_ts)
to_ts = grpc_time.convert_time_to_timestamp(from_dt)
roundtrip_dt = grpc_time.convert_timestamp_to_time(to_ts, tzinfo=tzinfo)

assert to_ts.seconds == JAN_01_2002_TIMESTAMP_1970_EPOCH + expected_offset
Expand Down Expand Up @@ -104,8 +102,7 @@ def test___datetime_with_microseconds___convert_to_timestamp___is_reversible(
):
from_dt = base_dt.replace(microsecond=microsecond)

to_ts = GrpcTimestamp()
grpc_time.convert_time_to_timestamp(from_dt, to_ts)
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_2002_TIMESTAMP_1970_EPOCH
Expand Down Expand Up @@ -135,8 +132,7 @@ def test___datetime_with_femtoseconds___convert_to_timestamp___is_reversible(
):
from_dt = base_dt.replace(femtosecond=femtosecond)

to_ts = GrpcTimestamp()
grpc_time.convert_time_to_timestamp(from_dt, to_ts)
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_2002_TIMESTAMP_1970_EPOCH
Expand Down
Loading