Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ni_measurementlink_service/_internal/discovery_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def register_measurement_service(
service_descriptor.service_class = service_info.service_class
service_descriptor.description_url = service_info.description_url
service_descriptor.provided_interfaces.extend(service_info.provided_interfaces)
service_descriptor.annotations.update(service_info.annotations)

# Registration Request Creation
request = discovery_service_pb2.RegisterServiceRequest(
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,27 @@ class ServiceDescriptor(google.protobuf.message.Message):

DESCRIPTOR: google.protobuf.descriptor.Descriptor

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

KEY_FIELD_NUMBER: builtins.int
VALUE_FIELD_NUMBER: builtins.int
key: builtins.str
value: builtins.str
def __init__(
self,
*,
key: builtins.str = ...,
value: builtins.str = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ...

DISPLAY_NAME_FIELD_NUMBER: builtins.int
DESCRIPTION_URL_FIELD_NUMBER: builtins.int
PROVIDED_INTERFACES_FIELD_NUMBER: builtins.int
SERVICE_CLASS_FIELD_NUMBER: builtins.int
ANNOTATIONS_FIELD_NUMBER: builtins.int
display_name: builtins.str
"""Required. The user visible name of the service."""
description_url: builtins.str
Expand All @@ -43,15 +60,33 @@ class ServiceDescriptor(google.protobuf.message.Message):
"""Required. The "class" of a service. The value of this field should be unique for a given interface in provided_interfaces.
In effect, the .proto service declaration defines the interface, and this field defines a class or concrete type of the interface.
"""
@property
def annotations(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]:
"""Optional. Represents a set of annotations on the service.
Well-known annotations:
- Description
- Key: "ni/service.description"
- Expected format: string
- Example: "Measure inrush current with a shorted load and validate results against configured limits."
- Collection
- Key: "ni/service.collection"
- Expected format: "." delimited namespace/hierarchy case-insensitive string
- Example: "CurrentTests.Inrush"
- Tags
- Key: "ni/service.tags"
- Expected format: serialized JSON string of an array of strings
- Example: "[\\"powerup\\", \\"current\\"]"
"""
def __init__(
self,
*,
display_name: builtins.str = ...,
description_url: builtins.str = ...,
provided_interfaces: collections.abc.Iterable[builtins.str] | None = ...,
service_class: builtins.str = ...,
annotations: collections.abc.Mapping[builtins.str, builtins.str] | None = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["description_url", b"description_url", "display_name", b"display_name", "provided_interfaces", b"provided_interfaces", "service_class", b"service_class"]) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["annotations", b"annotations", "description_url", b"description_url", "display_name", b"display_name", "provided_interfaces", b"provided_interfaces", "service_class", b"service_class"]) -> None: ...

global___ServiceDescriptor = ServiceDescriptor

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ message ServiceDescriptor {
// Required. The "class" of a service. The value of this field should be unique for a given interface in provided_interfaces.
// In effect, the .proto service declaration defines the interface, and this field defines a class or concrete type of the interface.
string service_class = 4;

// Optional. Represents a set of annotations on the service.
// Well-known annotations:
// - Description
// - Key: "ni/service.description"
// - Expected format: string
// - Example: "Measure inrush current with a shorted load and validate results against configured limits."
// - Collection
// - Key: "ni/service.collection"
// - Expected format: "." delimited namespace/hierarchy case-insensitive string
// - Example: "CurrentTests.Inrush"
// - Tags
// - Key: "ni/service.tags"
// - Expected format: serialized JSON string of an array of strings
// - Example: "[\"powerup\", \"current\"]"
map<string, string> annotations = 5;
}

// Represents the location of a service. The location generally includes the IP address and port number for the service
Expand Down
12 changes: 8 additions & 4 deletions ni_measurementlink_service/measurement/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
from __future__ import annotations

import enum
import typing
from pathlib import Path
from typing import NamedTuple
from typing import Dict, List, NamedTuple

from google.protobuf import type_pb2

Expand All @@ -25,7 +24,7 @@ class MeasurementInfo(NamedTuple):

display_name: str
version: str
ui_file_paths: typing.List[Path]
ui_file_paths: List[Path]


class ServiceInfo(NamedTuple):
Expand All @@ -44,11 +43,16 @@ class ServiceInfo(NamedTuple):
For e.g., ni.measurementlink.measurement.v2.MeasurementService.
Defaults to ["ni.measurementlink.measurement.v1.MeasurementService"].

annotations (Dict[str,str]): Dict that contains extra information of the measurement.
As default we added a (str) description, (str) collection and a (List[str]) list of tags.
Feel free to add your own Annotations as needed.

"""

service_class: str
description_url: str
provided_interfaces: typing.List[str] = ["ni.measurementlink.measurement.v1.MeasurementService"]
provided_interfaces: List[str] = ["ni.measurementlink.measurement.v1.MeasurementService"]
annotations: Dict[str, str] = {}


class TypeSpecialization(enum.Enum):
Expand Down
6 changes: 6 additions & 0 deletions ni_measurementlink_service/measurement/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,16 @@ def __init__(
ui_file_paths=ui_file_paths,
)

service_annotations_string = {
key: json.dumps(value, separators=(",", ":"))
for key, value in service.get("annotations", {}).items()
}

self.service_info = ServiceInfo(
service_class=service["serviceClass"],
description_url=service["descriptionUrl"],
provided_interfaces=service["providedInterfaces"],
annotations=service_annotations_string,
)

self.configuration_parameter_list: list = []
Expand Down
15 changes: 4 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
[tool.black]
extend-exclude ='((.+_pb2_grpc)|(.+_pb2))+(.py)$' # Regex to files with _pb2_grpc.py and _pb2.py Auto Generated Code are excluded
extend_exclude = '_pb2(_grpc)?\.(py|pyi)$|ni_measurementlink_generator/'
line-length = 100
exclude = '''
(
\.git # root of the project
| \.venv
| venv
)
'''

[tool.ni-python-styleguide]
extend_exclude = '*_pb2_grpc.py,*_pb2_grpc.pyi,*_pb2.py,*_pb2.pyi,ni_measurementlink_generator/'

[tool.poetry]
name = "ni_measurementlink_service"
Expand Down Expand Up @@ -69,9 +65,6 @@ sphinx-click = "^4.1.0"
requires = ["poetry-core>=1.2.0"]
build-backend = "poetry.core.masonry.api"

[tool.ni-python-styleguide]
extend_exclude = "*_pb2_grpc.py,*_pb2.py,venv,ni_measurementlink_generator/*"

[tool.pytest.ini_options]
addopts = "--doctest-modules --strict-markers"
filterwarnings = ["always::ImportWarning", "always::ResourceWarning"]
Expand Down
22 changes: 22 additions & 0 deletions tests/assets/example.AllAnnotations.serviceconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"services": [
{
"displayName": "SampleMeasurement",
"serviceClass": "SampleMeasurement_Python",
"descriptionUrl": "https://www.example.com/SampleMeasurement.html",
"providedInterfaces": [
"ni.measurementlink.measurement.v2.MeasurementService"
],
"path": "start.bat",
"annotations": {
"ni/service.description": "Testing extra Client info",
"client/extra.NumberID" : 500,
"client/extra.Parts" : [ "A25898", "A25412" ],
"client/extra.GroupName" : {
"SpeakerType": "true",
"PhoneType": "false"
}
}
}
]
}
30 changes: 30 additions & 0 deletions tests/assets/example.CustomAnnotations.serviceconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"services": [
{
"displayName": "SampleMeasurement",
"serviceClass": "SampleMeasurement_Python",
"descriptionUrl": "https://www.example.com/SampleMeasurement.html",
"providedInterfaces": [
"ni.measurementlink.measurement.v1.MeasurementService"
],
"path": "NoFile.exe",
"annotations": {
"description": "An annotated test measurement service.",
"collection": "Tests.Measurements",
"tags": [
"test",
"measurement"
],
"custom": {
"foo": "bar",
"baz": [ "qux", "quux", "quuux" ],
"snork": {
"blarg": "flarp",
"oogle": [ "foogle", "boogle" ],
"ork": [ "zork", "gork", "bork" ]
}
}
}
}
]
}
17 changes: 17 additions & 0 deletions tests/assets/example.OnlyCollection.serviceconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"services": [
{
"displayName": "SampleMeasurement",
"serviceClass": "SampleMeasurement_Python",
"descriptionUrl": "https://www.example.com/SampleMeasurement.html",
"providedInterfaces": [
"ni.measurementlink.measurement.v1.MeasurementService",
"ni.measurementlink.measurement.v2.MeasurementService"
],
"path": "start.bat",
"annotations": {
"ni/service.collection": "CurrentTests.Inrush"
}
}
]
}
16 changes: 16 additions & 0 deletions tests/assets/example.OnlyTags.serviceconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"services": [
{
"displayName": "SampleMeasurement",
"serviceClass": "SampleMeasurement_Python",
"descriptionUrl": "https://www.example.com/SampleMeasurement.html",
"providedInterfaces": [
"ni.measurementlink.measurement.v2.MeasurementService"
],
"path": "start.bat",
"annotations": {
"ni/service.tags": [ "powerup", "current", "voltage" ]
}
}
]
}
7 changes: 6 additions & 1 deletion tests/assets/example.serviceconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
"ni.measurementlink.measurement.v1.MeasurementService",
"ni.measurementlink.measurement.v2.MeasurementService"
],
"path": "start.bat"
"path": "start.bat",
"annotations": {
"ni/service.description": "Measure inrush current with a shorted load and validate results against configured limits.",
"ni/service.collection": "CurrentTests.Inrush",
"ni/service.tags": [ "powerup", "current" ]
}
}
]
}
9 changes: 6 additions & 3 deletions tests/assets/test_pb2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ class _DifferentColor:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType

class _DifferentColorEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DifferentColor.ValueType], builtins.type):
class _DifferentColorEnumTypeWrapper(
google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DifferentColor.ValueType],
builtins.type,
):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
PURPLE: _DifferentColor.ValueType # 0
ORANGE: _DifferentColor.ValueType # 1
Expand Down Expand Up @@ -72,6 +75,8 @@ class MeasurementParameter(google.protobuf.message.Message):
uint64_data: builtins.int
bool_data: builtins.bool
string_data: builtins.str
enum_data: builtins.str
enum_array_data: builtins.list
@property
def double_array_data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: ...
@property
Expand All @@ -88,9 +93,7 @@ class MeasurementParameter(google.protobuf.message.Message):
def bool_array_data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bool]: ...
@property
def string_array_data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ...
enum_data: global___DifferentColor.ValueType
@property
def enum_array_data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___DifferentColor.ValueType]: ...
def __init__(
self,
*,
Expand Down
Loading