diff --git a/docs/reference/ni-data-store.md b/docs/reference/ni-data-store.md index 2a32887..942bafb 100644 --- a/docs/reference/ni-data-store.md +++ b/docs/reference/ni-data-store.md @@ -59,7 +59,6 @@ Each step belongs to a TestResult and can contain multiple measurements and cond A **PublishedMeasurement** represents actual measurement data captured during a test step. This is the core data entity that stores the measured values, whether they are simple scalars, complex waveforms, or other data types. **Fields:** -- `moniker` (Moniker) - Data location identifier for retrieving the actual measurement values - `published_conditions` (list) - Environmental conditions present during measurement - `id` (string) - Unique identifier for this measurement - `test_result_id` (string) - ID of the associated TestResult @@ -98,7 +97,6 @@ A **PublishedMeasurement** represents actual measurement data captured during a A **PublishedCondition** represents environmental or contextual information that was present during test execution. Conditions capture the state of the test environment, input parameters, or other contextual data that might affect measurement results. **Fields:** -- `moniker` (Moniker) - Data location identifier for retrieving the condition value - `id` (string) - Unique identifier for this condition - `name` (string) - Name of the condition (e.g., "Temperature", "Supply Voltage") - `condition_type` (string) - Type/category of the condition (e.g., "Environment", "Input Parameter") diff --git a/examples/notebooks/extension-attributes/extension_attributes.ipynb b/examples/notebooks/extension-attributes/extension_attributes.ipynb index 55224a7..793d0ad 100644 --- a/examples/notebooks/extension-attributes/extension_attributes.ipynb +++ b/examples/notebooks/extension-attributes/extension_attributes.ipynb @@ -173,7 +173,7 @@ "published_measurements = data_store_client.query_measurements(\"$filter=name eq 'scope reading'\")\n", "found_measurement = next(iter(published_measurements), None)\n", "if found_measurement is not None:\n", - " waveform = data_store_client.read_data(found_measurement, expected_type=AnalogWaveform)\n", + " waveform = data_store_client.read_measurement_value(found_measurement, expected_type=AnalogWaveform)\n", " print(f\"published data is: {waveform.raw_data}\")" ] }, diff --git a/examples/notebooks/overview/publish_measurement.ipynb b/examples/notebooks/overview/publish_measurement.ipynb index ffa7acc..8aa5b34 100644 --- a/examples/notebooks/overview/publish_measurement.ipynb +++ b/examples/notebooks/overview/publish_measurement.ipynb @@ -266,7 +266,7 @@ " badge_number = operator.extension[\"badge_number\"]\n", " print(f\"operator {operator.name}'s badge number is: {badge_number}\")\n", "\n", - " waveform = data_store_client.read_data(found_measurement, expected_type=AnalogWaveform)\n", + " waveform = data_store_client.read_measurement_value(found_measurement, expected_type=AnalogWaveform)\n", " print(f\"published data is: {waveform.raw_data}\")" ] }, @@ -295,7 +295,7 @@ ], "metadata": { "kernelspec": { - "display_name": "ni-datastore-py3.10", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -309,7 +309,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/examples/notebooks/voltage-regulator/publish_waveforms.ipynb b/examples/notebooks/voltage-regulator/publish_waveforms.ipynb index 8617e42..2446a78 100644 --- a/examples/notebooks/voltage-regulator/publish_waveforms.ipynb +++ b/examples/notebooks/voltage-regulator/publish_waveforms.ipynb @@ -240,14 +240,14 @@ "condition = next(iter(conditions), None)\n", "condition_values = []\n", "if condition is not None:\n", - " condition_data = data_store_client.read_data(condition, expected_type=Vector)\n", + " condition_data = data_store_client.read_condition_value(condition, expected_type=Vector)\n", " units = condition_data.units\n", " for val in condition_data:\n", " condition_values.append(str(val) + units)\n", "measurements = data_store_client.query_measurements(odata_query=f\"$filter=stepid eq {step_id}\")\n", "sorted_measurements = sorted(measurements, key=lambda m: m.parametric_index)\n", "for measurement in sorted_measurements:\n", - " waveform = data_store_client.read_data(measurement, expected_type=AnalogWaveform)\n", + " waveform = data_store_client.read_measurement_value(measurement, expected_type=AnalogWaveform)\n", " color = colors[measurement.parametric_index]\n", " timing = waveform.timing\n", " fig.add_trace(go.Scatter(\n", diff --git a/examples/overview/poetry.lock b/examples/overview/poetry.lock index a21685f..7ddc150 100644 --- a/examples/overview/poetry.lock +++ b/examples/overview/poetry.lock @@ -188,7 +188,7 @@ version = "1.76.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc"}, {file = "grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde"}, @@ -544,7 +544,7 @@ version = "1.0.0" description = "Hightime Python API" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "hightime-1.0.0-py3-none-any.whl", hash = "sha256:ba86d42976c36451b14e11c736e61f296f9f00dbb79c8488e18d70c6b2dbb395"}, {file = "hightime-1.0.0.tar.gz", hash = "sha256:480d2a03e2c3ed44916d2406d40ab6d10a276ed7f101619fc3fcc1e00c46aacf"}, @@ -738,29 +738,13 @@ files = [ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] -[[package]] -name = "ni-datamonikers-v1-client" -version = "1.0.0" -description = "gRPC Client for NI Data Moniker Service" -optional = false -python-versions = "<4.0,>=3.10" -groups = ["main"] -files = [ - {file = "ni_datamonikers_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:e7ce750986c55f415d02ad5690afff0621db86f2e27bdec6b97a59ee18f98c2a"}, - {file = "ni_datamonikers_v1_client-1.0.0.tar.gz", hash = "sha256:b16eb188b9ac6f3c1693f89191612961484917e2390b0dca07511c6434a25334"}, -] - -[package.dependencies] -ni-datamonikers-v1-proto = ">=1.0.0" -ni-grpc-extensions = ">=1.1.0" - [[package]] name = "ni-datamonikers-v1-proto" version = "1.0.0" description = "Protobuf data types and service stub for NI data moniker gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_datamonikers_v1_proto-1.0.0-py3-none-any.whl", hash = "sha256:77d078d810656b3e90152a065047c6203140e0998e8cbdf9d2dbb6e9f477840e"}, {file = "ni_datamonikers_v1_proto-1.0.0.tar.gz", hash = "sha256:2e4cf30f9dee343af4a5f328fb785320d2eb30705abc15f0695057177afd5f00"}, @@ -771,31 +755,32 @@ protobuf = ">=4.21" [[package]] name = "ni-datastore" -version = "1.0.0" +version = "2.0.0.dev0" description = "APIs for publishing and retrieving data from NI Measurement Data Services" optional = false -python-versions = "<4.0,>=3.10" -groups = ["main"] -files = [ - {file = "ni_datastore-1.0.0-py3-none-any.whl", hash = "sha256:4471c9f9ae0e3a59b7fb116ccd9e11575b2769c44c44387f932fbad3f6a205e5"}, - {file = "ni_datastore-1.0.0.tar.gz", hash = "sha256:d4ec36c5664cc10233c21cb80765a539aeaf5c5d14588fb2ff7a01b67bd6df9f"}, -] +python-versions = "^3.10" +groups = ["main", "dev"] +files = [] +develop = true [package.dependencies] hightime = ">=1.0.0" -ni-datamonikers-v1-client = ">=1.0.0" -ni-measurements-data-v1-client = ">=1.0.0" +ni-measurements-data-v1-client = ">=1.1.0dev0" ni-measurements-metadata-v1-client = ">=1.0.0" ni-protobuf-types = ">=1.1.0" protobuf = ">=4.21" +[package.source] +type = "directory" +url = "../.." + [[package]] name = "ni-grpc-extensions" version = "1.1.0" description = "gRPC Extensions" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_grpc_extensions-1.1.0-py3-none-any.whl", hash = "sha256:db0357acd244854f4acccf202c89fe6462b4283d264ed639f4e248e6cc86bc9b"}, {file = "ni_grpc_extensions-1.1.0.tar.gz", hash = "sha256:028ea33e5c5234bc050bf5dc99f5b61611531de8f012293e9d4c6985b7b37afb"}, @@ -811,7 +796,7 @@ version = "1.1.0" description = "gRPC Client for NI Discovery Service" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurementlink_discovery_v1_client-1.1.0-py3-none-any.whl", hash = "sha256:366dcc3b93627ed1ede488955637e0768b29cb7a375e59ac1020f4c53892d00c"}, {file = "ni_measurementlink_discovery_v1_client-1.1.0.tar.gz", hash = "sha256:831b6145cf8def0021cb00579b08a2ad1da5a19fdeedea4522a3cb4a30978c48"}, @@ -829,7 +814,7 @@ version = "1.1.0" description = "Protobuf data types for NI discovery gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurementlink_discovery_v1_proto-1.1.0-py3-none-any.whl", hash = "sha256:6a3061ca858d3ee887987dc5130074fc439ce6c2b26fe6b3f9401da023461d43"}, {file = "ni_measurementlink_discovery_v1_proto-1.1.0.tar.gz", hash = "sha256:f9a9b4572ac5d169fad21ab56e2639abdb77979cf0dc3a88cdb71b2c783d009c"}, @@ -840,30 +825,30 @@ protobuf = ">=4.21" [[package]] name = "ni-measurements-data-v1-client" -version = "1.0.0" +version = "1.1.0.dev0" description = "gRPC Client for NI Data Store Service" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "ni_measurements_data_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:33832370474dc0d6481f1d90623db898f4e051695ae65b66adb14ca8b37d2686"}, - {file = "ni_measurements_data_v1_client-1.0.0.tar.gz", hash = "sha256:095dc4ece2df8015486ed2bdbbf2dde3dfab02695d5b67eb94c0f0314e5cdb30"}, + {file = "ni_measurements_data_v1_client-1.1.0.dev0-py3-none-any.whl", hash = "sha256:d41f93ff1584461ef45dd4ea25c3ceaf763da53c0a2cc5e48c64f675e4ba3c00"}, + {file = "ni_measurements_data_v1_client-1.1.0.dev0.tar.gz", hash = "sha256:3043ef784d6dec4f476f3065e781ce8a1f2db38900c3a0ef5d90d6c08a9fa466"}, ] [package.dependencies] ni-measurementlink-discovery-v1-client = ">=1.1.0" -ni-measurements-data-v1-proto = ">=1.0.0" +ni-measurements-data-v1-proto = ">=1.1.0.dev0" [[package]] name = "ni-measurements-data-v1-proto" -version = "1.0.0" +version = "1.1.0.dev0" description = "Protobuf data types and service stubs for NI data store gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "ni_measurements_data_v1_proto-1.0.0-py3-none-any.whl", hash = "sha256:34f2c05b7fd86e3842554a4a31944e381927371a0ff12cf2c5512e37c3eb5dcb"}, - {file = "ni_measurements_data_v1_proto-1.0.0.tar.gz", hash = "sha256:7c9f58054b5156f65e4ae7bbc0615f58dfcd023a1dabeddbd6a9583bb6fc5df9"}, + {file = "ni_measurements_data_v1_proto-1.1.0.dev0-py3-none-any.whl", hash = "sha256:c1e5ad669ab978c7202f58fac7eda2e19be2a6fcc4b07bc13f1904a5aad43809"}, + {file = "ni_measurements_data_v1_proto-1.1.0.dev0.tar.gz", hash = "sha256:99102d9e785031ce0797efa435fce975e9a0de8547b76079aaa7e35c871e7da4"}, ] [package.dependencies] @@ -878,7 +863,7 @@ version = "1.0.0" description = "gRPC Client for NI Metadata Store Service" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurements_metadata_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:c697ce4e98105b810f4da844a598e86e359ff6c90ca7a832e1e23a70327551a7"}, {file = "ni_measurements_metadata_v1_client-1.0.0.tar.gz", hash = "sha256:9f9a10810c4c6693239081abbc026414a18df3e3d04daa3cd59010b1eed07f51"}, @@ -894,7 +879,7 @@ version = "1.0.0" description = "Protobuf data types and service stub for NI metadata store gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurements_metadata_v1_proto-1.0.0-py3-none-any.whl", hash = "sha256:8844806d6775ac65144100fcd03099aea0c17ed55de696683d0663166f45cee3"}, {file = "ni_measurements_metadata_v1_proto-1.0.0.tar.gz", hash = "sha256:3283cf7f0c452812a311b2b9274b5ba1e549f994f6a978a125f1746b85746e6f"}, @@ -909,7 +894,7 @@ version = "1.1.0" description = "Protobuf data types for NI gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_protobuf_types-1.1.0-py3-none-any.whl", hash = "sha256:0c21c096cf8577483dade081c571305fe8d4cc759ce2c780e7437129a375942c"}, {file = "ni_protobuf_types-1.1.0.tar.gz", hash = "sha256:98f0583405e219f6e128133c2f6c033f03cd83ebd3ce8098ad74ab99b8a253c1"}, @@ -956,7 +941,7 @@ version = "1.1.0.dev1" description = "Data types for NI Python APIs" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "nitypes-1.1.0.dev1-py3-none-any.whl", hash = "sha256:d98ad6e3f8b92db76b5c1c584431fa27d3e74cce6e20464e43ee0117b02fe089"}, {file = "nitypes-1.1.0.dev1.tar.gz", hash = "sha256:50b23e00cc6960996656c4c9ef0ca71dd267fc5c9ca481077b682c29190aa2d3"}, @@ -976,7 +961,7 @@ version = "2.2.6" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version < \"3.12\"" files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, @@ -1042,7 +1027,7 @@ version = "2.3.5" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.11" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version >= \"3.12\"" files = [ {file = "numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10"}, @@ -1183,7 +1168,7 @@ version = "4.25.8" description = "" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version <= \"3.12\"" files = [ {file = "protobuf-4.25.8-cp310-abi3-win32.whl", hash = "sha256:504435d831565f7cfac9f0714440028907f1975e4bed228e58e72ecfff58a1e0"}, @@ -1205,7 +1190,7 @@ version = "5.29.5" description = "" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version == \"3.13\"" files = [ {file = "protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079"}, @@ -1227,7 +1212,7 @@ version = "6.33.1" description = "" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version >= \"3.14\"" files = [ {file = "protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b"}, @@ -1333,7 +1318,7 @@ version = "311" description = "Python for Window Extensions" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "dev"] markers = "sys_platform == \"win32\"" files = [ {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, @@ -1462,7 +1447,7 @@ version = "1.0.1" description = "Generates Event Tracing for Windows events using TraceLogging" optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["main", "dev"] markers = "sys_platform == \"win32\"" files = [ {file = "traceloggingdynamic-1.0.1-py3-none-any.whl", hash = "sha256:0e19da491a8960725b3622366487ae35f49d8f595bb2e4e5ce1795eb5928db7c"}, @@ -1499,7 +1484,7 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "lint"] +groups = ["main", "dev", "lint"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, @@ -1508,4 +1493,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "e2817aa3b427681441b58a1b018dceb0e86924943010d634c9629e81f1d2654d" +content-hash = "bc6ef6b0df0d64e132dde217e8eb3e02223f3928a56a8fb0a3da726b9db874bc" diff --git a/examples/overview/pyproject.toml b/examples/overview/pyproject.toml index ddc9ca8..4604910 100644 --- a/examples/overview/pyproject.toml +++ b/examples/overview/pyproject.toml @@ -55,7 +55,7 @@ grpcio-tools = [ types-grpcio = ">=1.0" types-protobuf = ">=4.21" # Uncomment to use local ni-datastore code -# ni-datastore = {path = "../..", develop = true} +ni-datastore = {path = "../..", develop = true} datastore-utilities = { path = "../../utilities", develop = true } [tool.poetry.group.lint.dependencies] diff --git a/examples/overview/src/overview.py b/examples/overview/src/overview.py index efdb2a7..a67dfe6 100644 --- a/examples/overview/src/overview.py +++ b/examples/overview/src/overview.py @@ -111,7 +111,9 @@ def query_data(published_measurement_id: str) -> None: operator = metadata_store_client.get_operator(test_result.operator_id) print(f"operator: {operator}") - waveform = data_store_client.read_data(found_measurement, expected_type=AnalogWaveform) + waveform = data_store_client.read_measurement_value( + found_measurement, expected_type=AnalogWaveform + ) print(f"published data is: {waveform.raw_data}") diff --git a/examples/system/poetry.lock b/examples/system/poetry.lock index 26340b1..815bd5b 100644 --- a/examples/system/poetry.lock +++ b/examples/system/poetry.lock @@ -188,7 +188,7 @@ version = "1.76.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc"}, {file = "grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde"}, @@ -265,7 +265,7 @@ version = "1.0.0" description = "Hightime Python API" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "hightime-1.0.0-py3-none-any.whl", hash = "sha256:ba86d42976c36451b14e11c736e61f296f9f00dbb79c8488e18d70c6b2dbb395"}, {file = "hightime-1.0.0.tar.gz", hash = "sha256:480d2a03e2c3ed44916d2406d40ab6d10a276ed7f101619fc3fcc1e00c46aacf"}, @@ -459,29 +459,13 @@ files = [ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] -[[package]] -name = "ni-datamonikers-v1-client" -version = "1.0.0" -description = "gRPC Client for NI Data Moniker Service" -optional = false -python-versions = "<4.0,>=3.10" -groups = ["main"] -files = [ - {file = "ni_datamonikers_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:e7ce750986c55f415d02ad5690afff0621db86f2e27bdec6b97a59ee18f98c2a"}, - {file = "ni_datamonikers_v1_client-1.0.0.tar.gz", hash = "sha256:b16eb188b9ac6f3c1693f89191612961484917e2390b0dca07511c6434a25334"}, -] - -[package.dependencies] -ni-datamonikers-v1-proto = ">=1.0.0" -ni-grpc-extensions = ">=1.1.0" - [[package]] name = "ni-datamonikers-v1-proto" version = "1.0.0" description = "Protobuf data types and service stub for NI data moniker gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_datamonikers_v1_proto-1.0.0-py3-none-any.whl", hash = "sha256:77d078d810656b3e90152a065047c6203140e0998e8cbdf9d2dbb6e9f477840e"}, {file = "ni_datamonikers_v1_proto-1.0.0.tar.gz", hash = "sha256:2e4cf30f9dee343af4a5f328fb785320d2eb30705abc15f0695057177afd5f00"}, @@ -492,31 +476,32 @@ protobuf = ">=4.21" [[package]] name = "ni-datastore" -version = "1.0.0" +version = "2.0.0.dev0" description = "APIs for publishing and retrieving data from NI Measurement Data Services" optional = false -python-versions = "<4.0,>=3.10" -groups = ["main"] -files = [ - {file = "ni_datastore-1.0.0-py3-none-any.whl", hash = "sha256:4471c9f9ae0e3a59b7fb116ccd9e11575b2769c44c44387f932fbad3f6a205e5"}, - {file = "ni_datastore-1.0.0.tar.gz", hash = "sha256:d4ec36c5664cc10233c21cb80765a539aeaf5c5d14588fb2ff7a01b67bd6df9f"}, -] +python-versions = "^3.10" +groups = ["main", "dev"] +files = [] +develop = true [package.dependencies] hightime = ">=1.0.0" -ni-datamonikers-v1-client = ">=1.0.0" -ni-measurements-data-v1-client = ">=1.0.0" +ni-measurements-data-v1-client = ">=1.1.0dev0" ni-measurements-metadata-v1-client = ">=1.0.0" ni-protobuf-types = ">=1.1.0" protobuf = ">=4.21" +[package.source] +type = "directory" +url = "../.." + [[package]] name = "ni-grpc-extensions" version = "1.1.0" description = "gRPC Extensions" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_grpc_extensions-1.1.0-py3-none-any.whl", hash = "sha256:db0357acd244854f4acccf202c89fe6462b4283d264ed639f4e248e6cc86bc9b"}, {file = "ni_grpc_extensions-1.1.0.tar.gz", hash = "sha256:028ea33e5c5234bc050bf5dc99f5b61611531de8f012293e9d4c6985b7b37afb"}, @@ -532,7 +517,7 @@ version = "1.1.0" description = "gRPC Client for NI Discovery Service" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurementlink_discovery_v1_client-1.1.0-py3-none-any.whl", hash = "sha256:366dcc3b93627ed1ede488955637e0768b29cb7a375e59ac1020f4c53892d00c"}, {file = "ni_measurementlink_discovery_v1_client-1.1.0.tar.gz", hash = "sha256:831b6145cf8def0021cb00579b08a2ad1da5a19fdeedea4522a3cb4a30978c48"}, @@ -550,7 +535,7 @@ version = "1.1.0" description = "Protobuf data types for NI discovery gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurementlink_discovery_v1_proto-1.1.0-py3-none-any.whl", hash = "sha256:6a3061ca858d3ee887987dc5130074fc439ce6c2b26fe6b3f9401da023461d43"}, {file = "ni_measurementlink_discovery_v1_proto-1.1.0.tar.gz", hash = "sha256:f9a9b4572ac5d169fad21ab56e2639abdb77979cf0dc3a88cdb71b2c783d009c"}, @@ -561,30 +546,30 @@ protobuf = ">=4.21" [[package]] name = "ni-measurements-data-v1-client" -version = "1.0.0" +version = "1.1.0.dev0" description = "gRPC Client for NI Data Store Service" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "ni_measurements_data_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:33832370474dc0d6481f1d90623db898f4e051695ae65b66adb14ca8b37d2686"}, - {file = "ni_measurements_data_v1_client-1.0.0.tar.gz", hash = "sha256:095dc4ece2df8015486ed2bdbbf2dde3dfab02695d5b67eb94c0f0314e5cdb30"}, + {file = "ni_measurements_data_v1_client-1.1.0.dev0-py3-none-any.whl", hash = "sha256:d41f93ff1584461ef45dd4ea25c3ceaf763da53c0a2cc5e48c64f675e4ba3c00"}, + {file = "ni_measurements_data_v1_client-1.1.0.dev0.tar.gz", hash = "sha256:3043ef784d6dec4f476f3065e781ce8a1f2db38900c3a0ef5d90d6c08a9fa466"}, ] [package.dependencies] ni-measurementlink-discovery-v1-client = ">=1.1.0" -ni-measurements-data-v1-proto = ">=1.0.0" +ni-measurements-data-v1-proto = ">=1.1.0.dev0" [[package]] name = "ni-measurements-data-v1-proto" -version = "1.0.0" +version = "1.1.0.dev0" description = "Protobuf data types and service stubs for NI data store gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "ni_measurements_data_v1_proto-1.0.0-py3-none-any.whl", hash = "sha256:34f2c05b7fd86e3842554a4a31944e381927371a0ff12cf2c5512e37c3eb5dcb"}, - {file = "ni_measurements_data_v1_proto-1.0.0.tar.gz", hash = "sha256:7c9f58054b5156f65e4ae7bbc0615f58dfcd023a1dabeddbd6a9583bb6fc5df9"}, + {file = "ni_measurements_data_v1_proto-1.1.0.dev0-py3-none-any.whl", hash = "sha256:c1e5ad669ab978c7202f58fac7eda2e19be2a6fcc4b07bc13f1904a5aad43809"}, + {file = "ni_measurements_data_v1_proto-1.1.0.dev0.tar.gz", hash = "sha256:99102d9e785031ce0797efa435fce975e9a0de8547b76079aaa7e35c871e7da4"}, ] [package.dependencies] @@ -599,7 +584,7 @@ version = "1.0.0" description = "gRPC Client for NI Metadata Store Service" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurements_metadata_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:c697ce4e98105b810f4da844a598e86e359ff6c90ca7a832e1e23a70327551a7"}, {file = "ni_measurements_metadata_v1_client-1.0.0.tar.gz", hash = "sha256:9f9a10810c4c6693239081abbc026414a18df3e3d04daa3cd59010b1eed07f51"}, @@ -615,7 +600,7 @@ version = "1.0.0" description = "Protobuf data types and service stub for NI metadata store gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_measurements_metadata_v1_proto-1.0.0-py3-none-any.whl", hash = "sha256:8844806d6775ac65144100fcd03099aea0c17ed55de696683d0663166f45cee3"}, {file = "ni_measurements_metadata_v1_proto-1.0.0.tar.gz", hash = "sha256:3283cf7f0c452812a311b2b9274b5ba1e549f994f6a978a125f1746b85746e6f"}, @@ -630,7 +615,7 @@ version = "1.1.0" description = "Protobuf data types for NI gRPC APIs" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "ni_protobuf_types-1.1.0-py3-none-any.whl", hash = "sha256:0c21c096cf8577483dade081c571305fe8d4cc759ce2c780e7437129a375942c"}, {file = "ni_protobuf_types-1.1.0.tar.gz", hash = "sha256:98f0583405e219f6e128133c2f6c033f03cd83ebd3ce8098ad74ab99b8a253c1"}, @@ -692,7 +677,7 @@ version = "1.1.0.dev1" description = "Data types for NI Python APIs" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "nitypes-1.1.0.dev1-py3-none-any.whl", hash = "sha256:d98ad6e3f8b92db76b5c1c584431fa27d3e74cce6e20464e43ee0117b02fe089"}, {file = "nitypes-1.1.0.dev1.tar.gz", hash = "sha256:50b23e00cc6960996656c4c9ef0ca71dd267fc5c9ca481077b682c29190aa2d3"}, @@ -712,7 +697,7 @@ version = "2.2.6" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version < \"3.12\"" files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, @@ -778,7 +763,7 @@ version = "2.3.5" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.11" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version >= \"3.12\"" files = [ {file = "numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10"}, @@ -919,7 +904,7 @@ version = "6.33.1" description = "" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b"}, {file = "protobuf-6.33.1-cp310-abi3-win_amd64.whl", hash = "sha256:923aa6d27a92bf44394f6abf7ea0500f38769d4b07f4be41cb52bd8b1123b9ed"}, @@ -1024,7 +1009,7 @@ version = "311" description = "Python for Window Extensions" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "dev"] markers = "sys_platform == \"win32\"" files = [ {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, @@ -1165,7 +1150,7 @@ version = "1.0.1" description = "Generates Event Tracing for Windows events using TraceLogging" optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["main", "dev"] markers = "sys_platform == \"win32\"" files = [ {file = "traceloggingdynamic-1.0.1-py3-none-any.whl", hash = "sha256:0e19da491a8960725b3622366487ae35f49d8f595bb2e4e5ce1795eb5928db7c"}, @@ -1178,7 +1163,7 @@ version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "lint"] +groups = ["main", "dev", "lint"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, @@ -1187,4 +1172,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "d73890f64fdac868ca09c67d4f6d5bddcd1663050ee733fa354369788e19757f" +content-hash = "ff35dc51e9e494e352f1218538718ffcbd66880a6d7dbd7700b401d04ed44927" diff --git a/examples/system/pyproject.toml b/examples/system/pyproject.toml index 7f78664..05687fa 100644 --- a/examples/system/pyproject.toml +++ b/examples/system/pyproject.toml @@ -47,7 +47,7 @@ nisyscfg = { version = ">=0.2.1" } [tool.poetry.group.dev.dependencies] # Uncomment to use local ni-datastore code -# ni-datastore = {path = "../..", develop = true} +ni-datastore = {path = "../..", develop = true} datastore-utilities = { path = "../../utilities", develop = true } [tool.poetry.group.lint.dependencies] diff --git a/poetry.lock b/poetry.lock index e4da95e..77a8c6f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2370,22 +2370,6 @@ files = [ {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, ] -[[package]] -name = "ni-datamonikers-v1-client" -version = "1.0.0" -description = "gRPC Client for NI Data Moniker Service" -optional = false -python-versions = "<4.0,>=3.10" -groups = ["main"] -files = [ - {file = "ni_datamonikers_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:e7ce750986c55f415d02ad5690afff0621db86f2e27bdec6b97a59ee18f98c2a"}, - {file = "ni_datamonikers_v1_client-1.0.0.tar.gz", hash = "sha256:b16eb188b9ac6f3c1693f89191612961484917e2390b0dca07511c6434a25334"}, -] - -[package.dependencies] -ni-datamonikers-v1-proto = ">=1.0.0" -ni-grpc-extensions = ">=1.1.0" - [[package]] name = "ni-datamonikers-v1-proto" version = "1.0.0" @@ -2452,30 +2436,30 @@ protobuf = ">=4.21" [[package]] name = "ni-measurements-data-v1-client" -version = "1.0.0" +version = "1.1.0.dev0" description = "gRPC Client for NI Data Store Service" optional = false python-versions = "<4.0,>=3.10" groups = ["main"] files = [ - {file = "ni_measurements_data_v1_client-1.0.0-py3-none-any.whl", hash = "sha256:33832370474dc0d6481f1d90623db898f4e051695ae65b66adb14ca8b37d2686"}, - {file = "ni_measurements_data_v1_client-1.0.0.tar.gz", hash = "sha256:095dc4ece2df8015486ed2bdbbf2dde3dfab02695d5b67eb94c0f0314e5cdb30"}, + {file = "ni_measurements_data_v1_client-1.1.0.dev0-py3-none-any.whl", hash = "sha256:d41f93ff1584461ef45dd4ea25c3ceaf763da53c0a2cc5e48c64f675e4ba3c00"}, + {file = "ni_measurements_data_v1_client-1.1.0.dev0.tar.gz", hash = "sha256:3043ef784d6dec4f476f3065e781ce8a1f2db38900c3a0ef5d90d6c08a9fa466"}, ] [package.dependencies] ni-measurementlink-discovery-v1-client = ">=1.1.0" -ni-measurements-data-v1-proto = ">=1.0.0" +ni-measurements-data-v1-proto = ">=1.1.0.dev0" [[package]] name = "ni-measurements-data-v1-proto" -version = "1.0.0" +version = "1.1.0.dev0" description = "Protobuf data types and service stubs for NI data store gRPC APIs" optional = false python-versions = "<4.0,>=3.10" groups = ["main"] files = [ - {file = "ni_measurements_data_v1_proto-1.0.0-py3-none-any.whl", hash = "sha256:34f2c05b7fd86e3842554a4a31944e381927371a0ff12cf2c5512e37c3eb5dcb"}, - {file = "ni_measurements_data_v1_proto-1.0.0.tar.gz", hash = "sha256:7c9f58054b5156f65e4ae7bbc0615f58dfcd023a1dabeddbd6a9583bb6fc5df9"}, + {file = "ni_measurements_data_v1_proto-1.1.0.dev0-py3-none-any.whl", hash = "sha256:c1e5ad669ab978c7202f58fac7eda2e19be2a6fcc4b07bc13f1904a5aad43809"}, + {file = "ni_measurements_data_v1_proto-1.1.0.dev0.tar.gz", hash = "sha256:99102d9e785031ce0797efa435fce975e9a0de8547b76079aaa7e35c871e7da4"}, ] [package.dependencies] @@ -4478,4 +4462,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "4d8bbb151fc7827e0cad5e9d3f0eb297ea2004a363fb06c25678d85d6cf76066" +content-hash = "7b3289fda614c93cbdfd701d40173d131938304eb1870c23d40bb934fdb723b1" diff --git a/pyproject.toml b/pyproject.toml index c5fb10e..04f3ceb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ni.datastore" -version = "1.1.0.dev0" +version = "2.0.0.dev0" license = "MIT" description = "APIs for publishing and retrieving data from NI Measurement Data Services" authors = [{name = "NI", email = "opensource@ni.com"}] @@ -40,8 +40,7 @@ requires-poetry = '>=2.1,<3.0' [tool.poetry.dependencies] python = "^3.10" protobuf = {version=">=4.21"} -ni-datamonikers-v1-client = { version = ">=1.0.0" } -ni-measurements-data-v1-client = { version = ">=1.0.0" } +ni-measurements-data-v1-client = { version = ">=1.1.0dev0", allow-prereleases = true } ni-measurements-metadata-v1-client = { version = ">=1.0.0" } ni-protobuf-types = { version = ">=1.1.0" } hightime = { version = ">=1.0.0" } diff --git a/src/ni/datastore/data/__init__.py b/src/ni/datastore/data/__init__.py index 7a68d62..76d7d86 100644 --- a/src/ni/datastore/data/__init__.py +++ b/src/ni/datastore/data/__init__.py @@ -2,7 +2,6 @@ from ni.datastore.data._data_store_client import DataStoreClient from ni.datastore.data._types._error_information import ErrorInformation -from ni.datastore.data._types._moniker import Moniker from ni.datastore.data._types._outcome import Outcome from ni.datastore.data._types._published_condition import PublishedCondition from ni.datastore.data._types._published_measurement import PublishedMeasurement @@ -12,7 +11,6 @@ __all__ = [ "DataStoreClient", "ErrorInformation", - "Moniker", "Outcome", "PublishedCondition", "PublishedMeasurement", @@ -23,7 +21,6 @@ # Hide that it was not defined in this top-level package DataStoreClient.__module__ = __name__ ErrorInformation.__module__ = __name__ -Moniker.__module__ = __name__ Outcome.__module__ = __name__ PublishedCondition.__module__ = __name__ PublishedMeasurement.__module__ = __name__ diff --git a/src/ni/datastore/data/_data_store_client.py b/src/ni/datastore/data/_data_store_client.py index 9fa4ab2..3705a38 100644 --- a/src/ni/datastore/data/_data_store_client.py +++ b/src/ni/datastore/data/_data_store_client.py @@ -8,21 +8,19 @@ from threading import Lock from types import TracebackType from typing import TYPE_CHECKING, Type, TypeVar, overload -from urllib.parse import urlparse import hightime as ht from grpc import Channel -from ni.datamonikers.v1.client import MonikerClient from ni.datastore.data._grpc_conversion import ( + convert_read_condition_response_from_protobuf, + convert_read_measurement_response_from_protobuf, get_publish_measurement_timestamp, populate_publish_condition_batch_request_values, populate_publish_condition_request_value, populate_publish_measurement_batch_request_values, populate_publish_measurement_request_value, - unpack_and_convert_from_protobuf_any, ) from ni.datastore.data._types._error_information import ErrorInformation -from ni.datastore.data._types._moniker import Moniker from ni.datastore.data._types._outcome import Outcome from ni.datastore.data._types._published_condition import PublishedCondition from ni.datastore.data._types._published_measurement import PublishedMeasurement @@ -45,6 +43,8 @@ QueryMeasurementsRequest, QueryStepsRequest, QueryTestResultsRequest, + ReadConditionValueRequest, + ReadMeasurementValueRequest, ) from ni.protobuf.types.precision_timestamp_conversion import ( hightime_datetime_to_protobuf, @@ -72,8 +72,6 @@ class DataStoreClient: "_grpc_channel_pool", "_data_store_client", "_data_store_client_lock", - "_moniker_clients_by_service_location", - "_moniker_clients_lock", ) _DATA_STORE_CLIENT_CLOSED_ERROR = ( @@ -86,9 +84,7 @@ class DataStoreClient: _grpc_channel: Channel | None _grpc_channel_pool: GrpcChannelPool | None _data_store_client: DataStoreServiceClient | None - _moniker_clients_by_service_location: dict[str, MonikerClient] _data_store_client_lock: Lock - _moniker_clients_lock: Lock def __init__( self, @@ -102,9 +98,7 @@ def __init__( discovery_client: An optional discovery client (recommended). grpc_channel: An optional data store gRPC channel. Providing this channel will bypass - discovery service resolution of the data store. (Note: Reading data from a moniker - will still always use a channel corresponding to the service location specified by - that moniker.) + discovery service resolution of the data store. grpc_channel_pool: An optional gRPC channel pool (recommended). """ @@ -113,10 +107,8 @@ def __init__( self._grpc_channel_pool = grpc_channel_pool self._data_store_client = None - self._moniker_clients_by_service_location = {} self._data_store_client_lock = Lock() - self._moniker_clients_lock = Lock() self._closed = False @@ -142,11 +134,6 @@ def close(self) -> None: self._data_store_client.close() self._data_store_client = None - with self._moniker_clients_lock: - for _, moniker_client in self._moniker_clients_by_service_location.items(): - moniker_client.close() - self._moniker_clients_by_service_location.clear() - def publish_condition( self, name: str, @@ -375,30 +362,72 @@ def publish_measurement_batch( return publish_response.measurement_ids @overload - def read_data( + def read_condition_value( self, - moniker_source: Moniker | PublishedMeasurement | PublishedCondition, + read_source: PublishedCondition, expected_type: Type[TRead], ) -> TRead: ... @overload - def read_data( + def read_condition_value( self, - moniker_source: Moniker | PublishedMeasurement | PublishedCondition, + read_source: PublishedCondition, ) -> object: ... - def read_data( + def read_condition_value( self, - moniker_source: Moniker | PublishedMeasurement | PublishedCondition, + read_source: PublishedCondition, expected_type: Type[TRead] | None = None, ) -> TRead | object: """Read data published to the data store. Args: - moniker_source: The source from which to read data. Can be: - - A Moniker (wrapper type) directly - - A PublishedMeasurement (uses its moniker) - - A PublishedCondition (uses its moniker) + read_source: The source from which to read data (PublishedCondition). + + expected_type: Optional type to validate the returned data against. + If provided, a TypeError will be raised if the actual data type + doesn't match. + + Returns: + The data retrieved from the data store. The return type depends on + what was originally published: + - Scalar conditions return as Vectors + - Other types are returned as originally published + If expected_type is specified, the return value is guaranteed to be + of that type. + + Raises: + TypeError: If expected_type is provided and the actual data type + doesn't match. + """ + read_value = self._read_condition(read_source) + if expected_type is not None and not isinstance(read_value, expected_type): + raise TypeError(f"Expected type {expected_type}, got {type(read_value)}") + + return read_value + + @overload + def read_measurement_value( + self, + read_source: PublishedMeasurement, + expected_type: Type[TRead], + ) -> TRead: ... + + @overload + def read_measurement_value( + self, + read_source: PublishedMeasurement, + ) -> object: ... + + def read_measurement_value( + self, + read_source: PublishedMeasurement, + expected_type: Type[TRead] | None = None, + ) -> TRead | object: + """Read data published to the data store. + + Args: + read_source: The source from which to read data (PublishedMeasurement). expected_type: Optional type to validate the returned data against. If provided, a TypeError will be raised if the actual data type @@ -413,33 +442,14 @@ def read_data( of that type. Raises: - ValueError: If the moniker_source doesn't have a valid moniker. TypeError: If expected_type is provided and the actual data type doesn't match. """ - from ni.datamonikers.v1.data_moniker_pb2 import Moniker as MonikerProto - - moniker_proto: MonikerProto - - if isinstance(moniker_source, Moniker): - moniker_proto = moniker_source.to_protobuf() - elif isinstance(moniker_source, PublishedMeasurement): - if moniker_source.moniker is None: - raise ValueError("PublishedMeasurement must have a Moniker to read data") - moniker_proto = moniker_source.moniker.to_protobuf() - elif isinstance(moniker_source, PublishedCondition): - if moniker_source.moniker is None: - raise ValueError("PublishedCondition must have a Moniker to read data") - moniker_proto = moniker_source.moniker.to_protobuf() - else: - raise TypeError(f"Unsupported moniker_source type: {type(moniker_source)}") - - moniker_client = self._get_moniker_client(moniker_proto.service_location) - read_result = moniker_client.read_from_moniker(moniker_proto) - converted_data = unpack_and_convert_from_protobuf_any(read_result.value) - if expected_type is not None and not isinstance(converted_data, expected_type): - raise TypeError(f"Expected type {expected_type}, got {type(converted_data)}") - return converted_data + read_value = self._read_measurement(read_source) + if expected_type is not None and not isinstance(read_value, expected_type): + raise TypeError(f"Expected type {expected_type}, got {type(read_value)}") + + return read_value def create_step(self, step: Step) -> str: """Create a new step in the data store. @@ -540,7 +550,7 @@ def query_conditions(self, odata_query: str = "") -> Sequence[PublishedCondition Returns: Sequence[PublishedCondition]: The list of matching conditions. Each - item contains a moniker for retrieving the condition + item contains an id for retrieving the condition measurements, as well as the metadata associated with the condition. """ @@ -563,7 +573,7 @@ def query_measurements(self, odata_query: str = "") -> Sequence[PublishedMeasure Returns: Sequence[PublishedMeasurement]: The list of matching measurements. - Each item contains a moniker for retrieving the measurement, as + Each item contains an id for retrieving the measurement, as well as the metadata associated with the measurement. """ query_request = QueryMeasurementsRequest(odata_query=odata_query) @@ -629,21 +639,12 @@ def _instantiate_data_store_client(self) -> DataStoreServiceClient: grpc_channel_pool=self._grpc_channel_pool, ) - def _get_moniker_client(self, service_location: str) -> MonikerClient: - if self._closed: - raise RuntimeError(self._DATA_STORE_CLIENT_CLOSED_ERROR) + def _read_measurement(self, published_measurement: PublishedMeasurement) -> object: + request = ReadMeasurementValueRequest(measurement_id=published_measurement.id) + response = self._get_data_store_client().read_measurement_value(request) + return convert_read_measurement_response_from_protobuf(response) - parsed_service_location = urlparse(service_location).netloc - if parsed_service_location not in self._moniker_clients_by_service_location: - with self._moniker_clients_lock: - if parsed_service_location not in self._moniker_clients_by_service_location: - self._moniker_clients_by_service_location[parsed_service_location] = ( - self._instantiate_moniker_client(parsed_service_location) - ) - return self._moniker_clients_by_service_location[parsed_service_location] - - def _instantiate_moniker_client(self, parsed_service_location: str) -> MonikerClient: - return MonikerClient( - service_location=parsed_service_location, - grpc_channel_pool=self._grpc_channel_pool, - ) + def _read_condition(self, published_condition: PublishedCondition) -> object: + request = ReadConditionValueRequest(condition_id=published_condition.id) + response = self._get_data_store_client().read_condition_value(request) + return convert_read_condition_response_from_protobuf(response) diff --git a/src/ni/datastore/data/_grpc_conversion.py b/src/ni/datastore/data/_grpc_conversion.py index c87fbd8..2fcab11 100644 --- a/src/ni/datastore/data/_grpc_conversion.py +++ b/src/ni/datastore/data/_grpc_conversion.py @@ -8,12 +8,13 @@ import hightime as ht import numpy as np -from google.protobuf.any_pb2 import Any from ni.measurements.data.v1.data_store_service_pb2 import ( PublishConditionBatchRequest, PublishConditionRequest, PublishMeasurementBatchRequest, PublishMeasurementRequest, + ReadConditionValueResponse, + ReadMeasurementValueResponse, ) from ni.protobuf.types.precision_timestamp_conversion import ( hightime_datetime_to_protobuf, @@ -21,7 +22,6 @@ from ni.protobuf.types.precision_timestamp_pb2 import PrecisionTimestamp from ni.protobuf.types.scalar_conversion import scalar_to_protobuf from ni.protobuf.types.vector_conversion import vector_from_protobuf, vector_to_protobuf -from ni.protobuf.types.vector_pb2 import Vector as VectorProto from ni.protobuf.types.waveform_conversion import ( digital_waveform_from_protobuf, digital_waveform_to_protobuf, @@ -36,19 +36,10 @@ int16_complex_waveform_from_protobuf, int16_complex_waveform_to_protobuf, ) -from ni.protobuf.types.waveform_pb2 import ( - DigitalWaveform as DigitalWaveformProto, - DoubleAnalogWaveform, - DoubleComplexWaveform, - DoubleSpectrum, - I16AnalogWaveform, - I16ComplexWaveform, -) from ni.protobuf.types.xydata_conversion import ( float64_xydata_from_protobuf, float64_xydata_to_protobuf, ) -from ni.protobuf.types.xydata_pb2 import DoubleXYData from nitypes.complex import ComplexInt32DType from nitypes.scalar import Scalar from nitypes.vector import Vector @@ -187,43 +178,38 @@ def populate_publish_measurement_batch_request_values( ) -def unpack_and_convert_from_protobuf_any(read_value: Any) -> object: - """Convert from a packed pb.Any to the appropriate python object.""" - value_type = read_value.TypeName() - if value_type == DoubleAnalogWaveform.DESCRIPTOR.full_name: - double_analog_waveform = DoubleAnalogWaveform() - read_value.Unpack(double_analog_waveform) - return float64_analog_waveform_from_protobuf(double_analog_waveform) - elif value_type == I16AnalogWaveform.DESCRIPTOR.full_name: - i16_analog_waveform = I16AnalogWaveform() - read_value.Unpack(i16_analog_waveform) - return int16_analog_waveform_from_protobuf(i16_analog_waveform) - elif value_type == DoubleComplexWaveform.DESCRIPTOR.full_name: - double_complex_waveform = DoubleComplexWaveform() - read_value.Unpack(double_complex_waveform) - return float64_complex_waveform_from_protobuf(double_complex_waveform) - elif value_type == I16ComplexWaveform.DESCRIPTOR.full_name: - i16_complex_waveform = I16ComplexWaveform() - read_value.Unpack(i16_complex_waveform) - return int16_complex_waveform_from_protobuf(i16_complex_waveform) - elif value_type == DoubleSpectrum.DESCRIPTOR.full_name: - spectrum = DoubleSpectrum() - read_value.Unpack(spectrum) - return float64_spectrum_from_protobuf(spectrum) - elif value_type == DigitalWaveformProto.DESCRIPTOR.full_name: - digital_waveform = DigitalWaveformProto() - read_value.Unpack(digital_waveform) - return digital_waveform_from_protobuf(digital_waveform) - elif value_type == DoubleXYData.DESCRIPTOR.full_name: - xydata = DoubleXYData() - read_value.Unpack(xydata) - return float64_xydata_from_protobuf(xydata) - elif value_type == VectorProto.DESCRIPTOR.full_name: - vector = VectorProto() - read_value.Unpack(vector) - return vector_from_protobuf(vector) +def convert_read_measurement_response_from_protobuf( + response: ReadMeasurementValueResponse, +) -> object: + """Convert the value in the ReadMeasurementValueResponse from protobuf and return it.""" + read_data_type = response.WhichOneof("value") + if read_data_type == "digital_waveform": + return digital_waveform_from_protobuf(response.digital_waveform) + elif read_data_type == "double_analog_waveform": + return float64_analog_waveform_from_protobuf(response.double_analog_waveform) + elif read_data_type == "double_complex_waveform": + return float64_complex_waveform_from_protobuf(response.double_complex_waveform) + elif read_data_type == "double_spectrum": + return float64_spectrum_from_protobuf(response.double_spectrum) + elif read_data_type == "i16_analog_waveform": + return int16_analog_waveform_from_protobuf(response.i16_analog_waveform) + elif read_data_type == "i16_complex_waveform": + return int16_complex_waveform_from_protobuf(response.i16_complex_waveform) + elif read_data_type == "vector": + return vector_from_protobuf(response.vector) + elif read_data_type == "x_y_data": + return float64_xydata_from_protobuf(response.x_y_data) + else: + raise TypeError(f"Invalid read type: {read_data_type}") + + +def convert_read_condition_response_from_protobuf(response: ReadConditionValueResponse) -> object: + """Convert the value in the ReadConditionValueResponse from protobuf and return it.""" + read_data_type = response.WhichOneof("value") + if read_data_type == "vector": + return vector_from_protobuf(response.vector) else: - raise TypeError(f"Unsupported data type Name: {value_type}") + raise TypeError(f"Invalid read type: {read_data_type}") def get_publish_measurement_timestamp( diff --git a/src/ni/datastore/data/_types/_moniker.py b/src/ni/datastore/data/_types/_moniker.py deleted file mode 100644 index fb38859..0000000 --- a/src/ni/datastore/data/_types/_moniker.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Moniker data type for the Data Store Client.""" - -from __future__ import annotations - -from ni.datamonikers.v1.data_moniker_pb2 import Moniker as MonikerProto - - -class Moniker: - """Represents a data moniker for retrieving published data. - - A moniker provides the necessary information to locate and retrieve data - from the data store, including the service location, data source, and - data instance identifiers. - """ - - __slots__ = ( - "service_location", - "data_source", - "data_instance", - ) - - def __init__( - self, - *, - service_location: str = "", - data_source: str = "", - data_instance: int = 0, - ) -> None: - """Initialize a Moniker instance. - - Args: - service_location: The location of the service that stores the data. - data_source: The identifier for the data source. - data_instance: The instance number of the data. - """ - self.service_location = service_location - self.data_source = data_source - self.data_instance = data_instance - - @staticmethod - def from_protobuf(moniker_proto: MonikerProto) -> "Moniker": - """Create a Moniker instance from a protobuf Moniker message.""" - return Moniker( - service_location=moniker_proto.service_location, - data_source=moniker_proto.data_source, - data_instance=moniker_proto.data_instance, - ) - - def to_protobuf(self) -> MonikerProto: - """Convert this Moniker instance to a protobuf Moniker message.""" - return MonikerProto( - service_location=self.service_location, - data_source=self.data_source, - data_instance=self.data_instance, - ) - - def __eq__(self, other: object) -> bool: - """Determine equality.""" - if not isinstance(other, Moniker): - return NotImplemented - return ( - self.service_location == other.service_location - and self.data_source == other.data_source - and self.data_instance == other.data_instance - ) - - def __str__(self) -> str: - """Return a string representation of the Moniker.""" - return str(self.to_protobuf()) diff --git a/src/ni/datastore/data/_types/_published_condition.py b/src/ni/datastore/data/_types/_published_condition.py index 9108b27..3cd729c 100644 --- a/src/ni/datastore/data/_types/_published_condition.py +++ b/src/ni/datastore/data/_types/_published_condition.py @@ -2,7 +2,6 @@ from __future__ import annotations -from ni.datastore.data._types._moniker import Moniker from ni.measurements.data.v1.data_store_pb2 import ( PublishedCondition as PublishedConditionProto, ) @@ -12,12 +11,11 @@ class PublishedCondition: """Represents a condition that has been published to the data store. A published condition contains metadata about a condition value that was - published, including a moniker for data retrieval and associated metadata + published, including an id for data retrieval and associated metadata like condition name, type, and associated step/test result IDs. """ __slots__ = ( - "moniker", "id", "name", "condition_type", @@ -28,7 +26,6 @@ class PublishedCondition: def __init__( self, *, - moniker: Moniker | None = None, id: str = "", name: str = "", condition_type: str = "", @@ -38,8 +35,6 @@ def __init__( """Initialize a PublishedCondition instance. Args: - moniker: The moniker of the condition that this value is associated - with. This moniker returns a Vector when read. id: The unique identifier of the condition. This can be used to reference and find the condition in the data store. name: The name of the condition. @@ -49,7 +44,6 @@ def __init__( test_result_id: The ID of the test result with which this condition is associated. """ - self.moniker = moniker self.id = id self.name = name self.condition_type = condition_type @@ -60,11 +54,6 @@ def __init__( def from_protobuf(published_condition_proto: PublishedConditionProto) -> "PublishedCondition": """Create a PublishedCondition instance from a protobuf PublishedCondition message.""" return PublishedCondition( - moniker=( - Moniker.from_protobuf(published_condition_proto.moniker) - if published_condition_proto.HasField("moniker") - else None - ), id=published_condition_proto.id, name=published_condition_proto.name, condition_type=published_condition_proto.condition_type, @@ -75,7 +64,6 @@ def from_protobuf(published_condition_proto: PublishedConditionProto) -> "Publis def to_protobuf(self) -> PublishedConditionProto: """Convert this PublishedCondition instance to a protobuf PublishedCondition message.""" return PublishedConditionProto( - moniker=self.moniker.to_protobuf() if self.moniker is not None else None, id=self.id, name=self.name, condition_type=self.condition_type, @@ -88,8 +76,7 @@ def __eq__(self, other: object) -> bool: if not isinstance(other, PublishedCondition): return NotImplemented return ( - self.moniker == other.moniker - and self.id == other.id + self.id == other.id and self.name == other.name and self.condition_type == other.condition_type and self.step_id == other.step_id diff --git a/src/ni/datastore/data/_types/_published_measurement.py b/src/ni/datastore/data/_types/_published_measurement.py index 7ce6084..084f9ed 100644 --- a/src/ni/datastore/data/_types/_published_measurement.py +++ b/src/ni/datastore/data/_types/_published_measurement.py @@ -6,7 +6,6 @@ import hightime as ht from ni.datastore.data._types._error_information import ErrorInformation -from ni.datastore.data._types._moniker import Moniker from ni.datastore.data._types._outcome import Outcome from ni.datastore.data._types._published_condition import PublishedCondition from ni.measurements.data.v1.data_store_pb2 import PublishedMeasurement as PublishedMeasurementProto @@ -25,7 +24,6 @@ class PublishedMeasurement: """ __slots__ = ( - "moniker", "_published_conditions", "id", "test_result_id", @@ -66,7 +64,6 @@ def test_adapter_ids(self) -> MutableSequence[str]: def __init__( self, *, - moniker: Moniker | None = None, published_conditions: Iterable[PublishedCondition] | None = None, id: str = "", test_result_id: str = "", @@ -86,8 +83,6 @@ def __init__( """Initialize a PublishedMeasurement instance. Args: - moniker: The moniker of the measurement that this value is - associated with. published_conditions: The published conditions associated with this measurement from the test step. id: The unique identifier of the measurement. @@ -116,7 +111,6 @@ def __init__( error_information: Error or exception information in case of measurement failure. """ - self.moniker = moniker self._published_conditions: MutableSequence[PublishedCondition] = ( list(published_conditions) if published_conditions is not None else [] ) @@ -147,11 +141,6 @@ def from_protobuf( ) -> "PublishedMeasurement": """Create a PublishedMeasurement instance from a protobuf PublishedMeasurement message.""" return PublishedMeasurement( - moniker=( - Moniker.from_protobuf(published_measurement_proto.moniker) - if published_measurement_proto.HasField("moniker") - else None - ), published_conditions=[ PublishedCondition.from_protobuf(cond) for cond in published_measurement_proto.published_conditions @@ -187,7 +176,6 @@ def from_protobuf( def to_protobuf(self) -> PublishedMeasurementProto: """Convert this PublishedMeasurement instance to a protobuf PublishedMeasurement message.""" return PublishedMeasurementProto( - moniker=self.moniker.to_protobuf() if self.moniker is not None else None, published_conditions=[ condition.to_protobuf() for condition in self.published_conditions ], @@ -222,8 +210,7 @@ def __eq__(self, other: object) -> bool: if not isinstance(other, PublishedMeasurement): return NotImplemented return ( - self.moniker == other.moniker - and self.published_conditions == other.published_conditions + self.published_conditions == other.published_conditions and self.id == other.id and self.test_result_id == other.test_result_id and self.step_id == other.step_id diff --git a/tests/acceptance/test_publish_condition_and_read_data.py b/tests/acceptance/test_publish_condition_and_read_data.py index 5a0c6f7..b0b61b3 100644 --- a/tests/acceptance/test_publish_condition_and_read_data.py +++ b/tests/acceptance/test_publish_condition_and_read_data.py @@ -10,7 +10,7 @@ from utilities import DataStoreContext -def test___publish_float_condition___read_data_returns_vector( +def test___publish_float_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -24,13 +24,13 @@ def test___publish_float_condition___read_data_returns_vector( # A published float will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert len(vector) == 1 assert vector[0] == 123.45 assert vector.units == "" -def test___publish_integer_condition___read_data_returns_vector( +def test___publish_integer_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -44,13 +44,13 @@ def test___publish_integer_condition___read_data_returns_vector( # A published integer will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert len(vector) == 1 assert vector[0] == 123 assert vector.units == "" -def test___publish_bool_condition___read_data_returns_vector( +def test___publish_bool_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -64,13 +64,13 @@ def test___publish_bool_condition___read_data_returns_vector( # A published bool will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert len(vector) == 1 assert vector[0] is True assert vector.units == "" -def test___publish_str_condition___read_data_returns_vector( +def test___publish_str_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -84,13 +84,13 @@ def test___publish_str_condition___read_data_returns_vector( # A published str will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert len(vector) == 1 assert vector[0] == "condition value" assert vector.units == "" -def test___publish_scalar_condition___read_data_returns_vector( +def test___publish_scalar_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -105,7 +105,7 @@ def test___publish_scalar_condition___read_data_returns_vector( # A published Scalar will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert vector[0] == expected_scalar.value assert vector.units == expected_scalar.units diff --git a/tests/acceptance/test_publish_condition_batch_and_read_data.py b/tests/acceptance/test_publish_condition_batch_and_read_data.py index c16ffa8..df8b693 100644 --- a/tests/acceptance/test_publish_condition_batch_and_read_data.py +++ b/tests/acceptance/test_publish_condition_batch_and_read_data.py @@ -9,7 +9,7 @@ from utilities import DataStoreContext -def test___publish_batch_float_condition___read_data_returns_vector( +def test___publish_batch_float_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: expected_value = [1.0, 2.0, 3.0] @@ -24,12 +24,12 @@ def test___publish_batch_float_condition___read_data_returns_vector( # A batch published float will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert vector._values == expected_value assert vector.units == "" -def test___publish_batch_integer_condition___read_data_returns_vector( +def test___publish_batch_integer_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: expected_value = [5, 6, 7, 8] @@ -44,12 +44,12 @@ def test___publish_batch_integer_condition___read_data_returns_vector( # A batch published integer will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert vector._values == expected_value assert vector.units == "" -def test___publish_batch_bool_condition___read_data_returns_vector( +def test___publish_batch_bool_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: expected_value = [True, False, True] @@ -64,12 +64,12 @@ def test___publish_batch_bool_condition___read_data_returns_vector( # A batch published bool will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert vector._values == expected_value assert vector.units == "" -def test___publish_batch_str_condition___read_data_returns_vector( +def test___publish_batch_str_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: expected_value = ["one", "two", "three"] @@ -84,12 +84,12 @@ def test___publish_batch_str_condition___read_data_returns_vector( # A published str will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert vector._values == expected_value assert vector.units == "" -def test___publish_batch_vector_condition___read_data_returns_vector( +def test___publish_batch_vector_condition___read_condition_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -104,7 +104,7 @@ def test___publish_batch_vector_condition___read_data_returns_vector( # A batch published Vector will be read back as a Vector. published_condition = data_store_client.get_condition(published_condition_id) - vector = data_store_client.read_data(published_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(published_condition, expected_type=Vector) assert vector == expected_vector diff --git a/tests/acceptance/test_publish_measurement_and_read_data.py b/tests/acceptance/test_publish_measurement_and_read_data.py index 7e15a66..30033f2 100644 --- a/tests/acceptance/test_publish_measurement_and_read_data.py +++ b/tests/acceptance/test_publish_measurement_and_read_data.py @@ -13,7 +13,7 @@ from utilities import DataStoreContext -def test___publish_float___read_data_returns_vector( +def test___publish_float___read_measurement_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -26,12 +26,14 @@ def test___publish_float___read_data_returns_vector( # A published integer will be read back as a Vector. published_measurement = data_store_client.get_measurement(published_measurement_id) - vector = data_store_client.read_data(published_measurement, expected_type=Vector) + vector = data_store_client.read_measurement_value( + published_measurement, expected_type=Vector + ) assert vector[0] == 123.45 assert vector.units == "" -def test___publish_scalar___read_data_returns_vector( +def test___publish_scalar___read_measurement_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -45,12 +47,14 @@ def test___publish_scalar___read_data_returns_vector( # A published Scalar will be read back as a Vector. published_measurement = data_store_client.get_measurement(published_measurement_id) - vector = data_store_client.read_data(published_measurement, expected_type=Vector) + vector = data_store_client.read_measurement_value( + published_measurement, expected_type=Vector + ) assert vector[0] == expected_scalar.value assert vector.units == expected_scalar.units -def test___publish_xydata___read_data_returns_xydata( +def test___publish_xydata___read_measurement_value_returns_xydata( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -69,11 +73,13 @@ def test___publish_xydata___read_data_returns_xydata( ) published_measurement = data_store_client.get_measurement(published_measurement_id) - xydata = data_store_client.read_data(published_measurement, expected_type=XYData) + xydata = data_store_client.read_measurement_value( + published_measurement, expected_type=XYData + ) assert xydata == expected_xydata -def test___publish_spectrum___read_data_returns_spectrum( +def test___publish_spectrum___read_measurement_value_returns_spectrum( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -92,11 +98,13 @@ def test___publish_spectrum___read_data_returns_spectrum( ) published_measurement = data_store_client.get_measurement(published_measurement_id) - spectrum = data_store_client.read_data(published_measurement, expected_type=Spectrum) + spectrum = data_store_client.read_measurement_value( + published_measurement, expected_type=Spectrum + ) assert spectrum == expected_spectrum -def test___publish_analog_waveform___read_data_returns_analog_waveform( +def test___publish_analog_waveform___read_measurement_value_returns_analog_waveform( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -113,11 +121,13 @@ def test___publish_analog_waveform___read_data_returns_analog_waveform( ) published_measurement = data_store_client.get_measurement(published_measurement_id) - waveform = data_store_client.read_data(published_measurement, expected_type=AnalogWaveform) + waveform = data_store_client.read_measurement_value( + published_measurement, expected_type=AnalogWaveform + ) assert waveform == expected_waveform -def test___publish_digital_waveform___read_data_returns_digital_waveform( +def test___publish_digital_waveform___read_measurement_value_returns_digital_waveform( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -130,11 +140,13 @@ def test___publish_digital_waveform___read_data_returns_digital_waveform( ) published_measurement = data_store_client.get_measurement(published_measurement_id) - waveform = data_store_client.read_data(published_measurement, expected_type=DigitalWaveform) + waveform = data_store_client.read_measurement_value( + published_measurement, expected_type=DigitalWaveform + ) assert waveform == expected_waveform -def test___publish_complex_waveform___read_data_returns_complex_waveform( +def test___publish_complex_waveform___read_measurement_value_returns_complex_waveform( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -147,7 +159,9 @@ def test___publish_complex_waveform___read_data_returns_complex_waveform( ) published_measurement = data_store_client.get_measurement(published_measurement_id) - waveform = data_store_client.read_data(published_measurement, expected_type=ComplexWaveform) + waveform = data_store_client.read_measurement_value( + published_measurement, expected_type=ComplexWaveform + ) assert waveform == expected_waveform diff --git a/tests/acceptance/test_publish_measurement_batch_and_read_data.py b/tests/acceptance/test_publish_measurement_batch_and_read_data.py index 1ebee73..d148a51 100644 --- a/tests/acceptance/test_publish_measurement_batch_and_read_data.py +++ b/tests/acceptance/test_publish_measurement_batch_and_read_data.py @@ -9,7 +9,7 @@ from utilities import DataStoreContext -def test___publish_float___read_data_returns_vector( +def test___publish_float___read_measurement_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -33,12 +33,14 @@ def test___publish_float___read_data_returns_vector( published_measurement = data_store_client.get_measurement(published_measurement_id) # A published batch floats will be read back as a Vector. - vector = data_store_client.read_data(published_measurement, expected_type=Vector) + vector = data_store_client.read_measurement_value( + published_measurement, expected_type=Vector + ) assert vector._values == [1.0, 2.0, 3.0, 4.0] assert vector.units == "" -def test___publish_batch_vector___read_data_returns_vector( +def test___publish_batch_vector___read_measurement_value_returns_vector( acceptance_test_context: DataStoreContext, ) -> None: with DataStoreClient() as data_store_client: @@ -65,5 +67,7 @@ def test___publish_batch_vector___read_data_returns_vector( published_measurement = data_store_client.get_measurement(published_measurement_id) # A batch published Vector will be read back as a Vector. - vector = data_store_client.read_data(published_measurement, expected_type=Vector) + vector = data_store_client.read_measurement_value( + published_measurement, expected_type=Vector + ) assert vector == expected_vector diff --git a/tests/acceptance/test_publish_with_metadata.py b/tests/acceptance/test_publish_with_metadata.py index 9c1ba3c..2bdca0e 100644 --- a/tests/acceptance/test_publish_with_metadata.py +++ b/tests/acceptance/test_publish_with_metadata.py @@ -315,5 +315,7 @@ def test___waveform_with_all_metadata___publish___query_read_returns_correct_dat assert found_hw_item.link == hardware_item.link assert found_hw_item.extension == hardware_item.extension - waveform = data_store_client.read_data(found_measurement, expected_type=AnalogWaveform) + waveform = data_store_client.read_measurement_value( + found_measurement, expected_type=AnalogWaveform + ) assert waveform == expected_waveform diff --git a/tests/acceptance/test_query_conditions.py b/tests/acceptance/test_query_conditions.py index 890e2a8..369a8d6 100644 --- a/tests/acceptance/test_query_conditions.py +++ b/tests/acceptance/test_query_conditions.py @@ -34,7 +34,7 @@ def test___query_conditions___filter_by_id___single_condition_returned( assert first_condition.name == condition_name # Check the value of the queried condition. - vector = data_store_client.read_data(first_condition, expected_type=Vector) + vector = data_store_client.read_condition_value(first_condition, expected_type=Vector) assert len(vector) == 1 assert vector[0] == 123.45 assert vector.units == "" @@ -77,6 +77,6 @@ def test___query_conditions___filter_by_name___correct_conditions_returned( # Check the value of each queried condition. for condition in queried_conditions: assert condition is not None - vector = data_store_client.read_data(condition, expected_type=Vector) + vector = data_store_client.read_condition_value(condition, expected_type=Vector) assert condition.name == f"{condition_name_base} {vector[0]}" assert vector.units == "" diff --git a/tests/acceptance/test_query_measurements.py b/tests/acceptance/test_query_measurements.py index c0c4543..b219de4 100644 --- a/tests/acceptance/test_query_measurements.py +++ b/tests/acceptance/test_query_measurements.py @@ -33,7 +33,7 @@ def test___query_measurements___filter_by_id___single_measurement_returned( assert first_measurement.name == measurement_name # Check the value of the queried measurement. - vector = data_store_client.read_data(first_measurement, expected_type=Vector) + vector = data_store_client.read_measurement_value(first_measurement, expected_type=Vector) assert vector[0] == 123.45 assert vector.units == "" @@ -73,6 +73,6 @@ def test___query_measurements___filter_by_name___correct_measurements_returned( # Check the value of each queried measurement. for measurement in queried_measurements: assert measurement is not None - vector = data_store_client.read_data(measurement, expected_type=Vector) + vector = data_store_client.read_measurement_value(measurement, expected_type=Vector) assert measurement.name == f"{measurement_name_base} {vector[0]}" assert vector.units == "" diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index f4f343e..3a45041 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -15,7 +15,6 @@ @pytest.fixture def data_store_client( mocked_data_store_service_client: NonCallableMock, - mocked_moniker_client: NonCallableMock, mocker: MockerFixture, ) -> DataStoreClient: """Returns the pytest fixture for the data store client.""" @@ -24,9 +23,6 @@ def data_store_client( "_instantiate_data_store_client", return_value=mocked_data_store_service_client, ) - mocker.patch.object( - DataStoreClient, "_instantiate_moniker_client", return_value=mocked_moniker_client - ) return DataStoreClient() @@ -64,14 +60,6 @@ def mocked_metadata_store_service_client(mocker: MockerFixture) -> Any: return mock_metadatastore_instance -@pytest.fixture -def mocked_moniker_client(mocker: MockerFixture) -> Any: - """Returns the pytest fixture for a mocked moniker client.""" - mock_moniker_client = mocker.patch("ni.datamonikers.v1.client.MonikerClient", autospec=True) - mock_moniker_instance = mock_moniker_client.return_value - return mock_moniker_instance - - @pytest.fixture(scope="module") def schemas_directory(test_assets_directory: Path) -> Path: """Returns the test assets directory containing schemas.""" diff --git a/tests/unit/data/test_close_client.py b/tests/unit/data/test_close_client.py index f7d31a1..11c2226 100644 --- a/tests/unit/data/test_close_client.py +++ b/tests/unit/data/test_close_client.py @@ -5,10 +5,7 @@ from unittest.mock import NonCallableMock import pytest -from google.protobuf.any_pb2 import Any as gpAny -from ni.datamonikers.v1.data_moniker_pb2 import ReadFromMonikerResult -from ni.datastore.data import DataStoreClient, Moniker -from ni.protobuf.types import waveform_pb2 +from ni.datastore.data import DataStoreClient def test___exit_data_store_client_context___calls_close_on_data_store_service_client( @@ -23,20 +20,6 @@ def test___exit_data_store_client_context___calls_close_on_data_store_service_cl mocked_data_store_service_client.close.assert_called_once() -def test___exit_data_store_client_context___calls_close_on_moniker_client( - data_store_client: DataStoreClient, - mocked_moniker_client: NonCallableMock, -) -> None: - - mocked_moniker_client.read_from_moniker.return_value = _create_read_from_moniker_result() - data_store_client.read_data(Moniker(service_location="http://localhost:50051")) - - with data_store_client: - pass - - mocked_moniker_client.close.assert_called_once() - - def test___close_data_store_client___calls_close_on_data_store_service_client( data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock, @@ -48,18 +31,6 @@ def test___close_data_store_client___calls_close_on_data_store_service_client( mocked_data_store_service_client.close.assert_called_once() -def test___close_data_store_client___calls_close_on_moniker_client( - data_store_client: DataStoreClient, - mocked_moniker_client: NonCallableMock, -) -> None: - mocked_moniker_client.read_from_moniker.return_value = _create_read_from_moniker_result() - data_store_client.read_data(Moniker(service_location="http://localhost:50051")) - - data_store_client.close() - - mocked_moniker_client.close.assert_called_once() - - def test___exit_data_store_client_context___call_method___raises_error( data_store_client: DataStoreClient, ) -> None: @@ -81,11 +52,3 @@ def test___close_data_store_client___call_method___raises_error( data_store_client.query_measurements() assert exc.value.args[0] == DataStoreClient._DATA_STORE_CLIENT_CLOSED_ERROR - - -def _create_read_from_moniker_result() -> ReadFromMonikerResult: - read_result = ReadFromMonikerResult() - value_to_read = gpAny() - value_to_read.Pack(waveform_pb2.DoubleAnalogWaveform(y_data=[1.0, 2.0, 3.0])) - read_result.value.CopyFrom(value_to_read) - return read_result diff --git a/tests/unit/data/test_grpc_conversion.py b/tests/unit/data/test_grpc_conversion.py index 08e8f82..c8893bb 100644 --- a/tests/unit/data/test_grpc_conversion.py +++ b/tests/unit/data/test_grpc_conversion.py @@ -1,13 +1,10 @@ import numpy as np import pytest -from google.protobuf import any_pb2 -from google.protobuf.message import Message from ni.datastore.data._grpc_conversion import ( populate_publish_condition_batch_request_values, populate_publish_condition_request_value, populate_publish_measurement_batch_request_values, populate_publish_measurement_request_value, - unpack_and_convert_from_protobuf_any, ) from ni.measurements.data.v1.data_store_service_pb2 import ( PublishConditionBatchRequest, @@ -16,8 +13,6 @@ PublishMeasurementRequest, ) from ni.protobuf.types import ( - array_pb2, - attribute_value_pb2, scalar_pb2, vector_pb2, waveform_pb2, @@ -223,147 +218,3 @@ def test___python_vector_object___populate_measurement_batch___condition_updated assert isinstance(request.scalar_values, vector_pb2.Vector) assert list(request.scalar_values.double_array.values) == [1.0, 2.0, 3.0] assert request.scalar_values.attributes["NI_UnitDescription"].string_value == "amps" - - -# ======================================================== -# Convert from protobuf -# ======================================================== -def test___vector_proto___convert_from_protobuf___valid_python_vector() -> None: - attrs = {"NI_UnitDescription": attribute_value_pb2.AttributeValue(string_value="amps")} - pb_value = vector_pb2.Vector( - attributes=attrs, - double_array=array_pb2.DoubleArray(values=[1.0, 2.0, 3.0]), - ) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, Vector) - assert list(result) == [1.0, 2.0, 3.0] - assert result.units == "amps" - - -def test___double_analog_waveform_proto___convert_from_protobuf___valid_python_float64_analog_waveform() -> ( - None -): - pb_value = waveform_pb2.DoubleAnalogWaveform(y_data=[0.0, 0.0, 0.0]) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, AnalogWaveform) - assert result.sample_count == result.capacity == len(result.raw_data) == 3 - assert result.dtype == np.float64 - - -def test___i16_analog_waveform_proto___convert_from_protobuf___valid_python_int16_analog_waveform() -> ( - None -): - pb_value = waveform_pb2.I16AnalogWaveform(y_data=[0, 0, 0]) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, AnalogWaveform) - assert result.sample_count == result.capacity == len(result.raw_data) == 3 - assert result.dtype == np.int16 - - -def test___double_complex_waveform_proto___convert_from_protobuf___valid_python_float64_complex_waveform() -> ( - None -): - pb_value = waveform_pb2.DoubleComplexWaveform(y_data=[0.0, 0.0, 0.0, 0.0]) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, ComplexWaveform) - assert result.sample_count == result.capacity == len(result.raw_data) == 2 - assert result.dtype == np.complex128 - - -def test___i16_complex_waveform_proto___convert_from_protobuf___valid_python_int16_complex_waveform() -> ( - None -): - pb_value = waveform_pb2.I16ComplexWaveform(y_data=[0, 0, 0, 0]) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, ComplexWaveform) - assert result.sample_count == result.capacity == len(result.raw_data) == 2 - assert result.dtype == ComplexInt32DType - - -def test___digital_waveform_proto___convert_from_protobuf___valid_python_bool_digital_waveform() -> ( - None -): - data = np.array([[0, 1, 0], [1, 0, 1]], dtype=np.bool) - pb_value = waveform_pb2.DigitalWaveform(y_data=data.tobytes(), signal_count=3) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, DigitalWaveform) - assert np.array_equal(result.data, data) - assert result.signal_count == 3 - - -def test___digital_waveform_proto___convert_from_protobuf___valid_python_uint8_digital_waveform() -> ( - None -): - data = np.array([[0, 1, 0], [1, 0, 1]], dtype=np.uint8) - pb_value = waveform_pb2.DigitalWaveform(y_data=data.tobytes(), signal_count=3) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, DigitalWaveform) - assert np.array_equal(result.data, data) - assert result.signal_count == 3 - - -def test___double_spectrum_proto___convert_from_protobuf___valid_python_spectrum() -> None: - pb_value = waveform_pb2.DoubleSpectrum( - data=[1.0, 2.0, 3.0], - start_frequency=100.0, - frequency_increment=10.0, - ) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, Spectrum) - assert list(result.data) == [1.0, 2.0, 3.0] - assert result.start_frequency == 100.0 - assert result.frequency_increment == 10.0 - - -def test___xydata_proto___convert_from_protobuf___valid_python_xydata() -> None: - attrs = { - "NI_UnitDescription_X": attribute_value_pb2.AttributeValue(string_value="amps"), - "NI_UnitDescription_Y": attribute_value_pb2.AttributeValue(string_value="seconds"), - } - pb_value = xydata_pb2.DoubleXYData( - x_data=[1.0, 2.0], - y_data=[3.0, 4.0], - attributes=attrs, - ) - packed_any = _pack_into_any(pb_value) - - result = unpack_and_convert_from_protobuf_any(packed_any) - - assert isinstance(result, XYData) - assert list(result.x_data) == [1.0, 2.0] - assert list(result.y_data) == [3.0, 4.0] - assert result.x_units == "amps" - assert result.y_units == "seconds" - - -# ======================================================== -# Pack/Unpack Helpers -# ======================================================== -def _pack_into_any(proto_value: Message) -> any_pb2.Any: - as_any = any_pb2.Any() - as_any.Pack(proto_value) - return as_any diff --git a/tests/unit/data/test_moniker.py b/tests/unit/data/test_moniker.py deleted file mode 100644 index ed9849b..0000000 --- a/tests/unit/data/test_moniker.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Tests for the Moniker wrapper type.""" - -from __future__ import annotations - -import pytest -from ni.datamonikers.v1.data_moniker_pb2 import Moniker as MonikerProto -from ni.datastore.data import Moniker - - -def test___init___with_defaults() -> None: - """Test creating Moniker with default values.""" - moniker = Moniker() - - assert moniker.service_location == "" - assert moniker.data_source == "" - assert moniker.data_instance == 0 - - -def test___init___with_values() -> None: - """Test creating Moniker with specific values.""" - moniker = Moniker( - service_location="localhost:8080", data_source="test_datasource", data_instance=42 - ) - - assert moniker.service_location == "localhost:8080" - assert moniker.data_source == "test_datasource" - assert moniker.data_instance == 42 - - -def test___init___with_keyword_arguments() -> None: - """Test creating Moniker using keyword arguments in different order.""" - moniker = Moniker( - data_instance=123, service_location="example.com:9090", data_source="my_source" - ) - - assert moniker.service_location == "example.com:9090" - assert moniker.data_source == "my_source" - assert moniker.data_instance == 123 - - -def test___from_protobuf___creates_instance_from_protobuf() -> None: - """Test creating Moniker from protobuf message.""" - proto = MonikerProto( - service_location="proto.server:8080", data_source="proto_source", data_instance=789 - ) - - moniker = Moniker.from_protobuf(proto) - - assert moniker.service_location == "proto.server:8080" - assert moniker.data_source == "proto_source" - assert moniker.data_instance == 789 - - -def test___from_protobuf___with_empty_protobuf() -> None: - """Test creating Moniker from empty protobuf message.""" - proto = MonikerProto() - - moniker = Moniker.from_protobuf(proto) - - assert moniker.service_location == "" - assert moniker.data_source == "" - assert moniker.data_instance == 0 - - -def test___to_protobuf___converts_to_protobuf_message() -> None: - """Test converting Moniker to protobuf message.""" - moniker = Moniker( - service_location="test.service:443", data_source="test_source", data_instance=555 - ) - - proto = moniker.to_protobuf() - - assert isinstance(proto, MonikerProto) - assert proto.service_location == "test.service:443" - assert proto.data_source == "test_source" - assert proto.data_instance == 555 - - -def test___to_protobuf___with_defaults() -> None: - """Test converting Moniker with default values to protobuf.""" - moniker = Moniker() - - proto = moniker.to_protobuf() - - assert isinstance(proto, MonikerProto) - assert proto.service_location == "" - assert proto.data_source == "" - assert proto.data_instance == 0 - - -def test___round_trip_conversion___preserves_values() -> None: - """Test that converting to protobuf and back preserves all values.""" - original = Moniker( - service_location="roundtrip.test:8080", data_source="roundtrip_source", data_instance=999 - ) - - proto = original.to_protobuf() - converted_back = Moniker.from_protobuf(proto) - - assert converted_back.service_location == original.service_location - assert converted_back.data_source == original.data_source - assert converted_back.data_instance == original.data_instance - - -def test___equality___same_values() -> None: - """Test equality comparison with same values.""" - moniker1 = Moniker( - service_location="same.server:8080", data_source="same_source", data_instance=100 - ) - moniker2 = Moniker( - service_location="same.server:8080", data_source="same_source", data_instance=100 - ) - - assert moniker1 == moniker2 - assert moniker2 == moniker1 - - -def test___equality___different_values() -> None: - """Test equality comparison with different values.""" - moniker1 = Moniker(service_location="server1:8080", data_source="source1", data_instance=1) - moniker2 = Moniker(service_location="server2:8080", data_source="source1", data_instance=1) - moniker3 = Moniker(service_location="server1:8080", data_source="source2", data_instance=1) - moniker4 = Moniker(service_location="server1:8080", data_source="source1", data_instance=2) - - assert moniker1 != moniker2 - assert moniker1 != moniker3 - assert moniker1 != moniker4 - - -def test___equality___with_defaults() -> None: - """Test equality comparison with default values.""" - moniker1 = Moniker() - moniker2 = Moniker() - - assert moniker1 == moniker2 - - -def test___equality___with_non_moniker_object() -> None: - """Test equality comparison with non-Moniker object.""" - moniker = Moniker() - - assert moniker != "not a Moniker" - assert moniker != 42 - assert moniker is not None - assert moniker != {} - - -def test___str___returns_protobuf_string() -> None: - """Test string representation returns protobuf string.""" - moniker = Moniker(service_location="str.test:8080", data_source="str_source", data_instance=777) - - str_repr = str(moniker) - proto_str = str(moniker.to_protobuf()) - - assert str_repr == proto_str - assert "str.test:8080" in str_repr - assert "str_source" in str_repr - assert "777" in str_repr - - -def test___str___with_empty_values() -> None: - """Test string representation with empty values.""" - moniker = Moniker() - - str_repr = str(moniker) - - # Should be a string (may be empty for empty protobuf) - assert isinstance(str_repr, str) - # Should match the protobuf string representation - assert str_repr == str(moniker.to_protobuf()) - - -def test___slots___attribute() -> None: - """Test that __slots__ is properly defined.""" - moniker = Moniker() - - # Should have the three expected slots - expected_slots = ("service_location", "data_source", "data_instance") - assert hasattr(Moniker, "__slots__") - assert Moniker.__slots__ == expected_slots - - # Should not allow arbitrary attribute assignment - with pytest.raises(AttributeError): - setattr(moniker, "unexpected_attribute", "this should fail") - - -def test___attribute_assignment___after_initialization() -> None: - """Test that attributes can be modified after initialization.""" - moniker = Moniker(service_location="initial:8080", data_source="initial", data_instance=1) - - # Should be able to modify existing attributes - moniker.service_location = "modified:9090" - moniker.data_source = "modified" - moniker.data_instance = 2 - - assert moniker.service_location == "modified:9090" - assert moniker.data_source == "modified" - assert moniker.data_instance == 2 - - -def test___data_instance___accepts_zero_and_negative_values() -> None: - """Test that data_instance can be zero or negative.""" - moniker_zero = Moniker(data_instance=0) - moniker_negative = Moniker(data_instance=-1) - - assert moniker_zero.data_instance == 0 - assert moniker_negative.data_instance == -1 - - -def test___empty_strings___are_valid() -> None: - """Test that empty strings are valid for string fields.""" - moniker = Moniker(service_location="", data_source="") - - assert moniker.service_location == "" - assert moniker.data_source == "" - - # Should round-trip properly - proto = moniker.to_protobuf() - converted_back = Moniker.from_protobuf(proto) - assert converted_back.service_location == "" - assert converted_back.data_source == "" diff --git a/tests/unit/data/test_read.py b/tests/unit/data/test_read.py index 6b3f22c..d708dea 100644 --- a/tests/unit/data/test_read.py +++ b/tests/unit/data/test_read.py @@ -2,13 +2,11 @@ from __future__ import annotations -from typing import cast -from unittest.mock import NonCallableMock +from unittest.mock import Mock, NonCallableMock import numpy as np -from google.protobuf.any_pb2 import Any as gpAny -from ni.datamonikers.v1.data_moniker_pb2 import ReadFromMonikerResult -from ni.datastore.data import DataStoreClient, Moniker +import pytest +from ni.datastore.data import DataStoreClient, PublishedCondition, PublishedMeasurement from ni.protobuf.types import ( array_pb2, attribute_value_pb2, @@ -22,76 +20,77 @@ from nitypes.xy_data import XYData -def test___read_data___calls_moniker_client( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock +def test___read_measurement_value___calls_data_store_client( + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-123") expected_waveform = waveform_pb2.DoubleAnalogWaveform(y_data=[1.0, 2.0, 3.0]) - value_to_read.Pack(expected_waveform) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "double_analog_waveform" + response.double_analog_waveform = expected_waveform + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_waveform = data_store_client.read_data(moniker, AnalogWaveform) - - args, __ = mocked_moniker_client.read_from_moniker.call_args - from ni.datamonikers.v1.data_moniker_pb2 import Moniker as GrpcMoniker + actual_waveform = data_store_client.read_measurement_value( + published_measurement, AnalogWaveform + ) - requested_moniker = cast(GrpcMoniker, args[0]) - assert requested_moniker.service_location == moniker.service_location - assert requested_moniker.data_instance == moniker.data_instance - assert requested_moniker.data_source == moniker.data_source + mocked_data_store_service_client.read_measurement_value.assert_called_once() + args, __ = mocked_data_store_service_client.read_measurement_value.call_args + request = args[0] + assert request.measurement_id == "measurement-123" assert isinstance(actual_waveform, AnalogWaveform) assert list(actual_waveform.scaled_data) == list(expected_waveform.y_data) def test___read_double_analog_waveform___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-123") expected_waveform = waveform_pb2.DoubleAnalogWaveform(y_data=[1.0, 2.0, 3.0]) - value_to_read.Pack(expected_waveform) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "double_analog_waveform" + response.double_analog_waveform = expected_waveform + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_waveform = data_store_client.read_data(moniker, AnalogWaveform) + actual_waveform = data_store_client.read_measurement_value( + published_measurement, AnalogWaveform + ) assert isinstance(actual_waveform, AnalogWaveform) assert list(actual_waveform.scaled_data) == list(expected_waveform.y_data) def test___read_i16_analog_waveform___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-456") expected_waveform = waveform_pb2.I16AnalogWaveform(y_data=[1, 2, 3]) - value_to_read.Pack(expected_waveform) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "i16_analog_waveform" + response.i16_analog_waveform = expected_waveform + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_waveform = data_store_client.read_data(moniker, AnalogWaveform) + actual_waveform = data_store_client.read_measurement_value( + published_measurement, AnalogWaveform + ) assert isinstance(actual_waveform, AnalogWaveform) assert list(actual_waveform.raw_data) == list(expected_waveform.y_data) def test___read_double_complex_waveform___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-789") expected_waveform = waveform_pb2.DoubleComplexWaveform(y_data=[1.0, 2.0, 3.0, 4.0]) - value_to_read.Pack(expected_waveform) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "double_complex_waveform" + response.double_complex_waveform = expected_waveform + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_waveform = data_store_client.read_data(moniker, ComplexWaveform) + actual_waveform = data_store_client.read_measurement_value( + published_measurement, ComplexWaveform + ) assert isinstance(actual_waveform, ComplexWaveform) assert actual_waveform.sample_count == actual_waveform.capacity == 2 @@ -100,17 +99,18 @@ def test___read_double_complex_waveform___value_correct( def test___read_i16_complex_waveform___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-101") expected_waveform = waveform_pb2.I16ComplexWaveform(y_data=[1, 2, 3, 4]) - value_to_read.Pack(expected_waveform) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "i16_complex_waveform" + response.i16_complex_waveform = expected_waveform + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_waveform = data_store_client.read_data(moniker, ComplexWaveform) + actual_waveform = data_store_client.read_measurement_value( + published_measurement, ComplexWaveform + ) assert isinstance(actual_waveform, ComplexWaveform) assert actual_waveform.sample_count == actual_waveform.capacity == 2 @@ -119,18 +119,19 @@ def test___read_i16_complex_waveform___value_correct( def test___read_digital_waveform___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-202") data = np.array([[0, 1, 0], [1, 0, 1]], dtype=np.bool) expected_waveform = waveform_pb2.DigitalWaveform(y_data=data.tobytes(), signal_count=3) - value_to_read.Pack(expected_waveform) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "digital_waveform" + response.digital_waveform = expected_waveform + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_waveform = data_store_client.read_data(moniker, DigitalWaveform) + actual_waveform = data_store_client.read_measurement_value( + published_measurement, DigitalWaveform + ) assert isinstance(actual_waveform, DigitalWaveform) assert np.array_equal(actual_waveform.data, data) @@ -138,21 +139,20 @@ def test___read_digital_waveform___value_correct( def test___read_double_spectrum___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-303") expected_waveform = waveform_pb2.DoubleSpectrum( data=[1.0, 2.0, 3.0], start_frequency=100.0, frequency_increment=10.0, ) - value_to_read.Pack(expected_waveform) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "double_spectrum" + response.double_spectrum = expected_waveform + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_waveform = data_store_client.read_data(moniker, Spectrum) + actual_waveform = data_store_client.read_measurement_value(published_measurement, Spectrum) assert isinstance(actual_waveform, Spectrum) assert list(actual_waveform.data) == [1.0, 2.0, 3.0] @@ -161,21 +161,20 @@ def test___read_double_spectrum___value_correct( def test___read_vector___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-404") attrs = {"NI_UnitDescription": attribute_value_pb2.AttributeValue(string_value="amps")} expected_vector = vector_pb2.Vector( attributes=attrs, double_array=array_pb2.DoubleArray(values=[1.0, 2.0, 3.0]), ) - value_to_read.Pack(expected_vector) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "vector" + response.vector = expected_vector + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_vector = data_store_client.read_data(moniker, Vector) + actual_vector = data_store_client.read_measurement_value(published_measurement, Vector) assert isinstance(actual_vector, Vector) assert list(actual_vector) == [1.0, 2.0, 3.0] @@ -183,11 +182,9 @@ def test___read_vector___value_correct( def test___read_xydata___value_correct( - data_store_client: DataStoreClient, mocked_moniker_client: NonCallableMock + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock ) -> None: - moniker = _init_moniker() - result = ReadFromMonikerResult() - value_to_read = gpAny() + published_measurement = PublishedMeasurement(id="measurement-505") attrs = { "NI_UnitDescription_X": attribute_value_pb2.AttributeValue(string_value="amps"), "NI_UnitDescription_Y": attribute_value_pb2.AttributeValue(string_value="seconds"), @@ -197,11 +194,12 @@ def test___read_xydata___value_correct( y_data=[3.0, 4.0], attributes=attrs, ) - value_to_read.Pack(expected_xydata) - result.value.CopyFrom(value_to_read) - mocked_moniker_client.read_from_moniker.return_value = result + response = Mock() + response.WhichOneof.return_value = "x_y_data" + response.x_y_data = expected_xydata + mocked_data_store_service_client.read_measurement_value.return_value = response - actual_xydata = data_store_client.read_data(moniker, XYData) + actual_xydata = data_store_client.read_measurement_value(published_measurement, XYData) assert isinstance(actual_xydata, XYData) assert list(actual_xydata.x_data) == [1.0, 2.0] @@ -210,7 +208,103 @@ def test___read_xydata___value_correct( assert actual_xydata.y_units == "seconds" -def _init_moniker() -> Moniker: - return Moniker( - data_instance=12, data_source="ABCD123", service_location="http://localhost:50051" +def test___read_condition_value___value_correct( + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock +) -> None: + published_condition = PublishedCondition(id="condition-789") + attrs = {"NI_UnitDescription": attribute_value_pb2.AttributeValue(string_value="volts")} + expected_vector = vector_pb2.Vector( + attributes=attrs, + double_array=array_pb2.DoubleArray(values=[5.0, 6.0, 7.0]), + ) + response = Mock() + response.WhichOneof.return_value = "vector" + response.vector = expected_vector + mocked_data_store_service_client.read_condition_value.return_value = response + + actual_vector = data_store_client.read_condition_value(published_condition, Vector) + + mocked_data_store_service_client.read_condition_value.assert_called_once() + args, __ = mocked_data_store_service_client.read_condition_value.call_args + request = args[0] + assert request.condition_id == "condition-789" + assert isinstance(actual_vector, Vector) + assert list(actual_vector) == [5.0, 6.0, 7.0] + assert actual_vector.units == "volts" + + +def test___read_measurement_value___without_expected_type___returns_object( + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock +) -> None: + published_measurement = PublishedMeasurement(id="measurement-999") + expected_vector = vector_pb2.Vector( + double_array=array_pb2.DoubleArray(values=[1.0, 2.0, 3.0]), + ) + response = Mock() + response.WhichOneof.return_value = "vector" + response.vector = expected_vector + mocked_data_store_service_client.read_measurement_value.return_value = response + + actual_value = data_store_client.read_measurement_value(published_measurement) + + # Without expected_type, it returns the converted Python object + assert isinstance(actual_value, Vector) + assert list(actual_value) == [1.0, 2.0, 3.0] + + +def test___read_measurement_value___with_matching_expected_type___returns_typed_value( + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock +) -> None: + published_measurement = PublishedMeasurement(id="measurement-888") + expected_vector = vector_pb2.Vector( + double_array=array_pb2.DoubleArray(values=[1.0, 2.0, 3.0]), + ) + response = Mock() + response.WhichOneof.return_value = "vector" + response.vector = expected_vector + mocked_data_store_service_client.read_measurement_value.return_value = response + + actual_value = data_store_client.read_measurement_value(published_measurement, Vector) + + assert isinstance(actual_value, Vector) + assert list(actual_value) == [1.0, 2.0, 3.0] + + +def test___read_measurement_value___with_mismatched_expected_type___raises_type_error( + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock +) -> None: + published_measurement = PublishedMeasurement(id="measurement-777") + expected_vector = vector_pb2.Vector( + double_array=array_pb2.DoubleArray(values=[1.0, 2.0, 3.0]), ) + response = Mock() + response.WhichOneof.return_value = "vector" + response.vector = expected_vector + mocked_data_store_service_client.read_measurement_value.return_value = response + + with pytest.raises(TypeError, match="Expected type.*AnalogWaveform.*got"): + data_store_client.read_measurement_value(published_measurement, AnalogWaveform) + + +def test___read_measurement_value___unsupported_type___raises_type_error( + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock +) -> None: + published_measurement = PublishedMeasurement(id="measurement-666") + response = Mock() + response.WhichOneof.return_value = "unknown_type" + mocked_data_store_service_client.read_measurement_value.return_value = response + + with pytest.raises(TypeError, match="Invalid read type: unknown_type"): + data_store_client.read_measurement_value(published_measurement) + + +def test___read_condition_value___unsupported_type___raises_type_error( + data_store_client: DataStoreClient, mocked_data_store_service_client: NonCallableMock +) -> None: + published_condition = PublishedCondition(id="condition-555") + response = Mock() + response.WhichOneof.return_value = "unknown_type" + mocked_data_store_service_client.read_condition_value.return_value = response + + with pytest.raises(TypeError, match="Invalid read type: unknown_type"): + data_store_client.read_condition_value(published_condition)