PyAres 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyares-0.3.2 → pyares-0.4.0}/PKG-INFO +2 -4
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Analyzing/analysis_service.py +3 -3
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/device_test.py +6 -4
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/rotary_mixer.py +19 -1
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Device/device_models.py +20 -4
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Device/device_service.py +8 -5
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Models/__init__.py +4 -2
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Models/ares_data_models.py +25 -2
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Planning/planning_service.py +2 -10
- pyares-0.4.0/PyAres/Utils/ares_data_schema_utils.py +59 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/ares_data_type_utils.py +9 -1
- pyares-0.4.0/PyAres/Utils/ares_device_command_utils.py +65 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/ares_value_utils.py +43 -13
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/__init__.py +4 -1
- {pyares-0.3.2 → pyares-0.4.0}/pyproject.toml +2 -4
- {pyares-0.3.2 → pyares-0.4.0}/tests/test_analyzer.py +2 -2
- {pyares-0.3.2 → pyares-0.4.0}/tests/test_ares_data_schema_utils.py +1 -3
- {pyares-0.3.2 → pyares-0.4.0}/tests/test_ares_device.py +1 -1
- pyares-0.3.2/PyAres/Utils/ares_data_schema_utils.py +0 -49
- pyares-0.3.2/PyAres/Utils/ares_device_command_utils.py +0 -43
- {pyares-0.3.2 → pyares-0.4.0}/.github/dependabot.yml +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/.github/workflows/pypi.yaml +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/.github/workflows/python-tests.yaml +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/.gitignore +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/LICENSE +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Analyzing/__init__.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Analyzing/analyzer_models.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/analyzer_test.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/analyzer_test_tools_demo.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/analyzer_wiki.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/hotplate.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/planner_test.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/planner_test_tools_demo.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Demo/planner_wiki.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Device/__init__.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Device/device_warnings.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Planning/__init__.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Planning/planner_models.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/ares_outcome_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/ares_struct_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/param_history_info_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/plan_request_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/plan_response_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/Utils/planning_param_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/test_tools/__init__.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/test_tools/analyzer_tester.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/PyAres/test_tools/planner_tester.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/README.md +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/setup.cfg +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/setup.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/tests/__init__.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/tests/mock_grpc_context.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/tests/test_ares_data_type_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/tests/test_ares_struct_utils.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/tests/test_ares_value_type_conversions.py +0 -0
- {pyares-0.3.2 → pyares-0.4.0}/tests/test_planner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyAres
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: The official Python library for ARES.
|
|
5
5
|
Project-URL: Homepage, https://github.com/AFRL-ARES/PyAres
|
|
6
6
|
Author-email: AFRL <AFRL.RX.AREScontact@us.af.mil>
|
|
@@ -9,9 +9,7 @@ License-File: LICENSE
|
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
|
-
Requires-Dist: ares-datamodel>=0.
|
|
13
|
-
Requires-Dist: grpcio-tools>=1.75.1
|
|
14
|
-
Requires-Dist: grpcio<2.0.0,>=1.71.0
|
|
12
|
+
Requires-Dist: ares-datamodel>=0.20.1
|
|
15
13
|
Description-Content-Type: text/markdown
|
|
16
14
|
|
|
17
15
|
# PyAres
|
|
@@ -36,8 +36,8 @@ class AresAnalyzerServiceWrapper(analyzer_service_grpc.AresRemoteAnalyzerService
|
|
|
36
36
|
self._info = info
|
|
37
37
|
self._timeout = timeout
|
|
38
38
|
self._custom_analysis_logic = custom_analysis_logic
|
|
39
|
-
self._settings: Dict[str, ares_data_schema_pb2.
|
|
40
|
-
self._analysis_parameters: Dict[str, ares_data_schema_pb2.
|
|
39
|
+
self._settings: Dict[str, ares_data_schema_pb2.AresValueSchema] = {}
|
|
40
|
+
self._analysis_parameters: Dict[str, ares_data_schema_pb2.AresValueSchema] = {}
|
|
41
41
|
|
|
42
42
|
def GetInfo(self, request, context) -> connection_info_pb2.InfoResponse:
|
|
43
43
|
print("Info Requested!")
|
|
@@ -138,7 +138,7 @@ class AresAnalyzerServiceWrapper(analyzer_service_grpc.AresRemoteAnalyzerService
|
|
|
138
138
|
|
|
139
139
|
def ValidateInputs(self, request: analyzer_service.ParameterValidationRequest, context):
|
|
140
140
|
response = analyzer_service.ParameterValidationResult(success=True)
|
|
141
|
-
provided_params: Mapping[str, ares_data_schema_pb2.
|
|
141
|
+
provided_params: Mapping[str, ares_data_schema_pb2.AresValueSchema] = request.input_schema.fields
|
|
142
142
|
|
|
143
143
|
for stored_key, stored_schema in self._analysis_parameters.items():
|
|
144
144
|
if stored_key in provided_params:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
from PyAres import
|
|
1
|
+
from PyAres import *
|
|
2
2
|
from typing import Dict
|
|
3
|
+
import pint
|
|
3
4
|
import time
|
|
4
5
|
|
|
5
6
|
class DemoDevice:
|
|
@@ -25,20 +26,21 @@ class DemoDevice:
|
|
|
25
26
|
device = DemoDevice()
|
|
26
27
|
|
|
27
28
|
if __name__ == "__main__":
|
|
28
|
-
# Basic information about my device
|
|
29
|
+
ureg = pint.UnitRegistry() # Basic information about my device
|
|
29
30
|
device_name = "Demo Device"
|
|
30
31
|
description = "A device to demonstrate the PyAres device capabilities"
|
|
31
32
|
version = "1.0.0"
|
|
32
33
|
device_service = AresDeviceService(device.enter_safe_mode, device.get_device_state, device_name, description, version)
|
|
33
34
|
|
|
34
35
|
# Create the "Set Temperature" Command
|
|
35
|
-
|
|
36
|
+
quantity_schema = QuantitySchema(ureg.degree_Celsius, 40.0, 125.0)
|
|
37
|
+
parameter_schema = DeviceSchemaEntry(AresDataType.QUANTITY, "A numeric temperature value", quantity_schema=quantity_schema)
|
|
36
38
|
input_schema = { "temperature": parameter_schema }
|
|
37
39
|
set_temp_descriptor = DeviceCommandDescriptor("Set Temperature", "Set's the temperature of the demo device to the provided value.", input_schema, {})
|
|
38
40
|
device_service.add_new_command(set_temp_descriptor, device.set_temperature)
|
|
39
41
|
|
|
40
42
|
# Create the "Get Temperature" Command
|
|
41
|
-
output_schema = {"temperature": DeviceSchemaEntry(AresDataType.
|
|
43
|
+
output_schema = {"temperature": DeviceSchemaEntry(AresDataType.QUANTITY, "The current temperature of the device", quantity_schema=quantity_schema)}
|
|
42
44
|
get_temp_desc = DeviceCommandDescriptor("Get Temperature", "Get's the current temperature of the demo device.", {}, output_schema)
|
|
43
45
|
device_service.add_new_command(get_temp_desc, device.get_temperature)
|
|
44
46
|
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
from PyAres import AresDeviceService, AresDataType, DeviceSchemaEntry, DeviceCommandDescriptor
|
|
2
2
|
|
|
3
|
+
class RotaryMixer:
|
|
4
|
+
def __init__(self, speed):
|
|
5
|
+
self.speed = speed
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
mixer = RotaryMixer(1200)
|
|
9
|
+
|
|
3
10
|
# 1. Define your hardware logic
|
|
4
11
|
def set_speed(rpm: float):
|
|
5
12
|
print(f"Setting motor speed to {rpm}")
|
|
13
|
+
mixer.speed = rpm
|
|
6
14
|
# Hardware communication goes here...
|
|
7
15
|
return {} # Return empty dict if no data needs to be sent back
|
|
8
16
|
|
|
17
|
+
def get_speed():
|
|
18
|
+
print("Hey I got speed")
|
|
19
|
+
return { "rpm": mixer.speed }
|
|
20
|
+
|
|
9
21
|
def get_status():
|
|
10
22
|
# Return a dictionary matching your state schema
|
|
11
|
-
return { "rpm":
|
|
23
|
+
return { "rpm": mixer.speed }
|
|
12
24
|
|
|
13
25
|
def safe_mode():
|
|
14
26
|
print("Stopping motor immediately!")
|
|
@@ -30,8 +42,14 @@ input_schema = {
|
|
|
30
42
|
}
|
|
31
43
|
cmd_descriptor = DeviceCommandDescriptor("Set Speed", "Sets mixer speed", input_schema, {})
|
|
32
44
|
|
|
45
|
+
output_schema = {
|
|
46
|
+
"rpm": DeviceSchemaEntry(AresDataType.NUMBER, "Speed in RPM", "RPM")
|
|
47
|
+
}
|
|
48
|
+
get_speed_descriptor = DeviceCommandDescriptor("Get Speed", "Gets mixer speed", {}, output_schema)
|
|
49
|
+
|
|
33
50
|
# 4. Register the command
|
|
34
51
|
service.add_new_command(cmd_descriptor, set_speed)
|
|
52
|
+
service.add_new_command(get_speed_descriptor, get_speed)
|
|
35
53
|
|
|
36
54
|
# 5. Start
|
|
37
55
|
service.start()
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
from typing import Dict, Union
|
|
1
|
+
from typing import Dict, Union, Optional
|
|
2
2
|
from ..Models import ares_data_models
|
|
3
3
|
|
|
4
4
|
class DeviceSchemaEntry:
|
|
5
5
|
""" A class that describes an input or output parameter for a device command """
|
|
6
6
|
|
|
7
|
-
def __init__(self, type: ares_data_models.AresDataType, description: str = "", unit: str = "", optional: bool = False,
|
|
7
|
+
def __init__(self, type: ares_data_models.AresDataType, description: str = "", unit: str = "", optional: bool = False,
|
|
8
|
+
constraints: Union[list[int], list[float], list[str]] = [],
|
|
9
|
+
quantity_schema: Optional[ares_data_models.QuantitySchema] = None,
|
|
10
|
+
struct_schema: Optional[Dict[str, 'DeviceSchemaEntry']] = None,
|
|
11
|
+
list_element_schema: Optional['DeviceSchemaEntry'] = None,
|
|
12
|
+
min_number_value: Optional[float] = None,
|
|
13
|
+
max_number_value: Optional[float] = None):
|
|
8
14
|
"""
|
|
9
15
|
Initializes a new DeviceSchemaEntry
|
|
10
16
|
|
|
@@ -13,14 +19,24 @@ class DeviceSchemaEntry:
|
|
|
13
19
|
description (str): A description of the given schema entry
|
|
14
20
|
unit (str): The unit associated with this schema entry
|
|
15
21
|
optional (bool): A boolean value that determines whether or not this schema entry's inclusion is optional
|
|
16
|
-
|
|
22
|
+
constraints (Union[list[int], list[float], list[str]]): An optional list of constraints to limit the number of choices available for this schema entry
|
|
23
|
+
quantity_schema (ares_data_models.QuantitySchema): Optional metadata for quantity types
|
|
24
|
+
struct_schema (Dict[str, DeviceSchemaEntry]): Optional schema for struct types
|
|
25
|
+
list_element_schema (DeviceSchemaEntry): Optional schema for list elements
|
|
26
|
+
min_number_value (float): Optional minimum value for numeric types
|
|
27
|
+
max_number_value (float): Optional maximum value for numeric types
|
|
17
28
|
"""
|
|
18
29
|
|
|
19
30
|
self.type = type
|
|
20
31
|
self.optional = optional
|
|
21
32
|
self.description = description
|
|
22
33
|
self.unit = unit
|
|
23
|
-
self.
|
|
34
|
+
self.constraints = constraints
|
|
35
|
+
self.quantity_schema = quantity_schema
|
|
36
|
+
self.struct_schema = struct_schema
|
|
37
|
+
self.list_element_schema = list_element_schema
|
|
38
|
+
self.min_number_value = min_number_value
|
|
39
|
+
self.max_number_value = max_number_value
|
|
24
40
|
|
|
25
41
|
class DeviceCommandDescriptor:
|
|
26
42
|
""" A class that contains all the necessary information to describe a device command """
|
|
@@ -37,9 +37,9 @@ class AresDeviceServiceWrapper(device_service_grpc.AresRemoteDeviceServiceServic
|
|
|
37
37
|
self.version = version
|
|
38
38
|
self._enter_safe_mode = enter_safe_mode
|
|
39
39
|
self._update_device_state = update_device_state
|
|
40
|
-
self._setting_schema: Dict[str, ares_data_schema_pb2.
|
|
41
|
-
self._current_settings: Dict[str, ares_struct_pb2.
|
|
42
|
-
self._state_schema: Dict[str, ares_data_schema_pb2.
|
|
40
|
+
self._setting_schema: Dict[str, ares_data_schema_pb2.AresValueSchema] = {}
|
|
41
|
+
self._current_settings: Dict[str, ares_struct_pb2.AresStruct] = {}
|
|
42
|
+
self._state_schema: Dict[str, ares_data_schema_pb2.AresValueSchema] = {}
|
|
43
43
|
self._commands: list[DeviceCommandDescriptor] = []
|
|
44
44
|
self._command_methods: Dict[str, Callable] = {}
|
|
45
45
|
|
|
@@ -85,8 +85,11 @@ class AresDeviceServiceWrapper(device_service_grpc.AresRemoteDeviceServiceServic
|
|
|
85
85
|
provided_param_dict = ares_struct_utils.ares_struct_to_dict(request.arguments)
|
|
86
86
|
result : Dict[str, Any] = method(**provided_param_dict)
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
if isinstance(result, dict):
|
|
89
|
+
for key, value in result.items():
|
|
90
|
+
ares_struct_utils.add_value_to_struct(response.result.struct_value, key, ares_value_utils.create_ares_value(value))
|
|
91
|
+
else:
|
|
92
|
+
response.result.CopyFrom(ares_value_utils.create_ares_value(result))
|
|
90
93
|
|
|
91
94
|
response.success = True
|
|
92
95
|
return response
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
from .ares_data_models import AresDataType, Outcome, RequestMetadata, AresSchemaEntry
|
|
1
|
+
from .ares_data_models import AresDataType, Outcome, RequestMetadata, AresSchemaEntry, Quantity, QuantitySchema
|
|
2
2
|
|
|
3
3
|
__all__ = [
|
|
4
4
|
"AresDataType",
|
|
5
5
|
"Outcome",
|
|
6
6
|
"RequestMetadata",
|
|
7
|
-
"AresSchemaEntry"
|
|
7
|
+
"AresSchemaEntry",
|
|
8
|
+
"Quantity",
|
|
9
|
+
"QuantitySchema"
|
|
8
10
|
]
|
|
@@ -2,6 +2,7 @@ from ares_datamodel import request_metadata_pb2
|
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from typing import Union, List, Optional, Dict
|
|
5
|
+
import pint
|
|
5
6
|
|
|
6
7
|
class AresDataType(Enum):
|
|
7
8
|
UNKNOWN = 0
|
|
@@ -14,6 +15,10 @@ class AresDataType(Enum):
|
|
|
14
15
|
LIST = 7
|
|
15
16
|
STRUCT = 8
|
|
16
17
|
BYTE_ARRAY = 9
|
|
18
|
+
ANY = 10
|
|
19
|
+
UNIT = 11
|
|
20
|
+
FUNCTION = 12
|
|
21
|
+
QUANTITY = 13
|
|
17
22
|
|
|
18
23
|
class Outcome(Enum):
|
|
19
24
|
UNSPECIFIED_OUTCOME = 0
|
|
@@ -27,7 +32,9 @@ class RequestMetadata():
|
|
|
27
32
|
self.system_name = proto_metadata.system_name
|
|
28
33
|
self.campaign_name = proto_metadata.campaign_name
|
|
29
34
|
self.campaign_id = proto_metadata.campaign_id
|
|
30
|
-
self.experiment_id = proto_metadata.experiment_id
|
|
35
|
+
self.experiment_id = proto_metadata.experiment_id
|
|
36
|
+
dt = proto_metadata.experiment_start_time.ToDatetime()
|
|
37
|
+
self.experiment_start_time = dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
31
38
|
|
|
32
39
|
@classmethod
|
|
33
40
|
def from_default_values(cls):
|
|
@@ -35,6 +42,18 @@ class RequestMetadata():
|
|
|
35
42
|
default = request_metadata_pb2.RequestMetadata(system_name="TEST SYSTEM", campaign_name="TEST CAMPAIGN", campaign_id="TEST ID", experiment_id="TEST EXPERIMENT ID")
|
|
36
43
|
return cls(default)
|
|
37
44
|
|
|
45
|
+
@dataclass
|
|
46
|
+
class Quantity:
|
|
47
|
+
scalar: float
|
|
48
|
+
type: int # QuantityType enum value
|
|
49
|
+
unit: str
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class QuantitySchema:
|
|
53
|
+
bounds_unit: pint.Unit = None
|
|
54
|
+
min_scalar_value: Optional[float] = None
|
|
55
|
+
max_scalar_value: Optional[float] = None
|
|
56
|
+
|
|
38
57
|
@dataclass
|
|
39
58
|
class AresSchemaEntry:
|
|
40
59
|
type: AresDataType
|
|
@@ -42,4 +61,8 @@ class AresSchemaEntry:
|
|
|
42
61
|
description: str = ""
|
|
43
62
|
unit: str = ""
|
|
44
63
|
choices: Union[List[str], List[int], List[float]] = field(default_factory=list)
|
|
45
|
-
|
|
64
|
+
quantity_schema: Optional[QuantitySchema] = None
|
|
65
|
+
struct_schema: Optional[Dict[str, 'AresSchemaEntry']] = None
|
|
66
|
+
list_element_schema: Optional['AresSchemaEntry'] = None
|
|
67
|
+
min_number_value: Optional[float] = None
|
|
68
|
+
max_number_value: Optional[float] = None
|
|
@@ -37,7 +37,7 @@ class AresPlannerServiceWrapper(planner_service_grpc.AresRemotePlannerServiceSer
|
|
|
37
37
|
self._service_name: str = service_name
|
|
38
38
|
self._description: str = description
|
|
39
39
|
self._version: str = version
|
|
40
|
-
self._settings: Dict[str, ares_data_schema_pb2.
|
|
40
|
+
self._settings: Dict[str, ares_data_schema_pb2.AresValueSchema] = {}
|
|
41
41
|
self._planner_options: list[planner_pb2.Planner] = []
|
|
42
42
|
self._supported_types: list[ares_data_type_pb2.AresDataType] = []
|
|
43
43
|
self._timeout: int = timeout
|
|
@@ -54,15 +54,7 @@ class AresPlannerServiceWrapper(planner_service_grpc.AresRemotePlannerServiceSer
|
|
|
54
54
|
capabilities.available_planners.extend(self._planner_options)
|
|
55
55
|
|
|
56
56
|
for(key, value) in self._settings.items():
|
|
57
|
-
|
|
58
|
-
settings_entry.type = value.type
|
|
59
|
-
settings_entry.optional = value.optional
|
|
60
|
-
|
|
61
|
-
if len(value.string_choices.strings) != 0:
|
|
62
|
-
settings_entry.string_choices.strings.extend(value.string_choices.strings)
|
|
63
|
-
|
|
64
|
-
elif len(value.number_choices.numbers) != 0:
|
|
65
|
-
settings_entry.number_choices.numbers.extend(value.number_choices.numbers)
|
|
57
|
+
capabilities.settings_schema.fields[key].CopyFrom(value)
|
|
66
58
|
|
|
67
59
|
print("Capabilites Sent!")
|
|
68
60
|
return capabilities
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Union, Dict, Optional, List
|
|
2
|
+
from ares_datamodel import ares_data_schema_pb2
|
|
3
|
+
from ..Models import ares_data_models
|
|
4
|
+
from ..Models.ares_data_models import AresSchemaEntry
|
|
5
|
+
|
|
6
|
+
def convert_ares_schema_entry_to_proto(entry: AresSchemaEntry) -> ares_data_schema_pb2.AresValueSchema:
|
|
7
|
+
proto_entry = create_settings_schema_entry(entry.type, entry.optional, entry.choices, entry.struct_schema)
|
|
8
|
+
proto_entry.description = entry.description
|
|
9
|
+
|
|
10
|
+
if entry.quantity_schema:
|
|
11
|
+
proto_entry.quantity_schema.quantity_type = entry.quantity_schema.quantity_type
|
|
12
|
+
proto_entry.quantity_schema.bounds_unit = entry.quantity_schema.bounds_unit
|
|
13
|
+
if entry.quantity_schema.min_scalar_value is not None:
|
|
14
|
+
proto_entry.quantity_schema.min_scalar_value = entry.quantity_schema.min_scalar_value
|
|
15
|
+
if entry.quantity_schema.max_scalar_value is not None:
|
|
16
|
+
proto_entry.quantity_schema.max_scalar_value = entry.quantity_schema.max_scalar_value
|
|
17
|
+
|
|
18
|
+
if entry.list_element_schema:
|
|
19
|
+
proto_entry.list_element_schema.CopyFrom(convert_ares_schema_entry_to_proto(entry.list_element_schema))
|
|
20
|
+
|
|
21
|
+
if entry.min_number_value is not None:
|
|
22
|
+
proto_entry.min_number_value = entry.min_number_value
|
|
23
|
+
if entry.max_number_value is not None:
|
|
24
|
+
proto_entry.max_number_value = entry.max_number_value
|
|
25
|
+
|
|
26
|
+
return proto_entry
|
|
27
|
+
|
|
28
|
+
def create_settings_schema_entry(
|
|
29
|
+
setting_type: ares_data_models.AresDataType,
|
|
30
|
+
optional: bool,
|
|
31
|
+
choices: Union[list[str], list[int], list[float]],
|
|
32
|
+
struct_schema: Optional[Dict[str, AresSchemaEntry]] = None) -> ares_data_schema_pb2.AresValueSchema:
|
|
33
|
+
"""
|
|
34
|
+
Creates a protobuf AresValueSchema message from the provided setting details.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
setting_type (AresDataType): The data type of the setting.
|
|
38
|
+
optional (bool): Whether the setting is optional.
|
|
39
|
+
choices (Union[list[str], list[int], list[float]]): A list of valid choices for the setting.
|
|
40
|
+
struct_schema (Optional[Dict[str, AresSchemaEntry]]): Nested schema definition for STRUCT types.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
(AresValueSchema): A new AresValueSchema message.
|
|
44
|
+
"""
|
|
45
|
+
schema_entry = ares_data_schema_pb2.AresValueSchema()
|
|
46
|
+
schema_entry.type = setting_type.value
|
|
47
|
+
schema_entry.optional = optional
|
|
48
|
+
|
|
49
|
+
if isinstance(choices, list) and len(choices) > 0:
|
|
50
|
+
if all(isinstance(item, str) for item in choices):
|
|
51
|
+
schema_entry.string_choices.strings.extend(choices)
|
|
52
|
+
elif all(isinstance(item, (int, float)) for item in choices):
|
|
53
|
+
schema_entry.number_choices.numbers.extend(choices)
|
|
54
|
+
|
|
55
|
+
if struct_schema is not None:
|
|
56
|
+
for key, value in struct_schema.items():
|
|
57
|
+
schema_entry.struct_schema.fields[key].CopyFrom(convert_ares_schema_entry_to_proto(value))
|
|
58
|
+
|
|
59
|
+
return schema_entry
|
|
@@ -11,7 +11,7 @@ def proto_ares_type_to_python_ares_type(proto_value: ares_data_type_pb2.AresData
|
|
|
11
11
|
""" A method to convert from the protobuf AresDataType class to the python version """
|
|
12
12
|
return AresDataType(proto_value)
|
|
13
13
|
|
|
14
|
-
def determine_python_ares_data_type(value: Union[int, float, str, bool, list]):
|
|
14
|
+
def determine_python_ares_data_type(value: Union[int, float, str, bool, list, dict, bytes]):
|
|
15
15
|
""" A method that takes in a value and returns the corresponding `PyAres.Models.AresDataType`"""
|
|
16
16
|
match value:
|
|
17
17
|
case str():
|
|
@@ -22,6 +22,10 @@ def determine_python_ares_data_type(value: Union[int, float, str, bool, list]):
|
|
|
22
22
|
return AresDataType.NUMBER
|
|
23
23
|
case float():
|
|
24
24
|
return AresDataType.NUMBER
|
|
25
|
+
case bytes():
|
|
26
|
+
return AresDataType.BYTE_ARRAY
|
|
27
|
+
case dict():
|
|
28
|
+
return AresDataType.STRUCT
|
|
25
29
|
case list():
|
|
26
30
|
if(all(isinstance(x, bool) for x in value)):
|
|
27
31
|
return AresDataType.LIST
|
|
@@ -31,5 +35,9 @@ def determine_python_ares_data_type(value: Union[int, float, str, bool, list]):
|
|
|
31
35
|
return AresDataType.NUMBER_ARRAY
|
|
32
36
|
else:
|
|
33
37
|
return AresDataType.LIST
|
|
38
|
+
case None:
|
|
39
|
+
return AresDataType.NULL
|
|
34
40
|
case _:
|
|
41
|
+
# If it has specific attributes, it might be a Quantity or other complex type
|
|
42
|
+
# This part might need more refinement as we define how Quantity looks in Python
|
|
35
43
|
return AresDataType.UNKNOWN
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from ..Device import DeviceCommandDescriptor
|
|
4
|
+
from ..Device import DeviceSchemaEntry
|
|
5
|
+
from ares_datamodel.device import device_command_descriptor_pb2
|
|
6
|
+
from ares_datamodel import ares_data_schema_pb2, ares_data_type_pb2
|
|
7
|
+
from ares_datamodel import to_quantity_schema
|
|
8
|
+
|
|
9
|
+
from . import ares_data_type_utils
|
|
10
|
+
|
|
11
|
+
def python_command_description_to_proto(python_description: DeviceCommandDescriptor) -> device_command_descriptor_pb2.DeviceCommandDescriptor:
|
|
12
|
+
proto_description = device_command_descriptor_pb2.DeviceCommandDescriptor()
|
|
13
|
+
|
|
14
|
+
# Update input_schema (AresStructSchema)
|
|
15
|
+
for key, value in python_description.input_schema.items():
|
|
16
|
+
proto_description.input_schema.fields[key].CopyFrom(python_device_schema_entry_to_proto(value))
|
|
17
|
+
|
|
18
|
+
# Update output_schema (AresValueSchema)
|
|
19
|
+
# If there is only one output and the key is empty or "output", we can use it directly.
|
|
20
|
+
# Otherwise, if there are multiple outputs, we should probably wrap them in a struct.
|
|
21
|
+
# However, the proto says output_schema is a single AresValueSchema.
|
|
22
|
+
# For now, let's assume we use the first one if there's only one, or wrap in a struct if there are multiple.
|
|
23
|
+
if len(python_description.output_schema) == 1:
|
|
24
|
+
first_val = list(python_description.output_schema.values())[0]
|
|
25
|
+
proto_description.output_schema.CopyFrom(python_device_schema_entry_to_proto(first_val))
|
|
26
|
+
elif len(python_description.output_schema) > 1:
|
|
27
|
+
proto_description.output_schema.type = ares_data_type_pb2.AresDataType.STRUCT
|
|
28
|
+
for key, value in python_description.output_schema.items():
|
|
29
|
+
proto_description.output_schema.struct_schema.fields[key].CopyFrom(python_device_schema_entry_to_proto(value))
|
|
30
|
+
|
|
31
|
+
proto_description.name = python_description.name
|
|
32
|
+
proto_description.description = python_description.description
|
|
33
|
+
|
|
34
|
+
return proto_description
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def python_device_schema_entry_to_proto(entry: DeviceSchemaEntry) -> ares_data_schema_pb2.AresValueSchema:
|
|
38
|
+
proto_schema = ares_data_schema_pb2.AresValueSchema()
|
|
39
|
+
proto_schema.type = ares_data_type_utils.python_ares_type_to_proto_ares_type(entry.type)
|
|
40
|
+
proto_schema.optional = entry.optional
|
|
41
|
+
proto_schema.description = entry.description
|
|
42
|
+
|
|
43
|
+
if entry.constraints:
|
|
44
|
+
if all(isinstance(x, (int, float)) for x in entry.constraints):
|
|
45
|
+
proto_schema.number_choices.numbers.extend(entry.constraints)
|
|
46
|
+
elif all(isinstance(x, str) for x in entry.constraints):
|
|
47
|
+
proto_schema.string_choices.strings.extend(entry.constraints)
|
|
48
|
+
|
|
49
|
+
# If a quantity schema is provided, utilize it. Use datamodel helpers to create the protobuf representation
|
|
50
|
+
if entry.quantity_schema:
|
|
51
|
+
proto_schema.quantity_schema.CopyFrom(to_quantity_schema(entry.quantity_schema.bounds_unit, entry.quantity_schema.min_scalar_value, entry.quantity_schema.max_scalar_value))
|
|
52
|
+
|
|
53
|
+
if entry.struct_schema:
|
|
54
|
+
for key, sub_entry in entry.struct_schema.items():
|
|
55
|
+
proto_schema.struct_schema.fields[key].CopyFrom(python_device_schema_entry_to_proto(sub_entry))
|
|
56
|
+
|
|
57
|
+
if entry.list_element_schema:
|
|
58
|
+
proto_schema.list_element_schema.CopyFrom(python_device_schema_entry_to_proto(entry.list_element_schema))
|
|
59
|
+
|
|
60
|
+
if entry.min_number_value is not None:
|
|
61
|
+
proto_schema.min_number_value = entry.min_number_value
|
|
62
|
+
if entry.max_number_value is not None:
|
|
63
|
+
proto_schema.max_number_value = entry.max_number_value
|
|
64
|
+
|
|
65
|
+
return proto_schema
|
|
@@ -3,23 +3,33 @@ from ares_datamodel import ares_data_type_pb2
|
|
|
3
3
|
from typing import Union, Any, Dict
|
|
4
4
|
|
|
5
5
|
from . import ares_data_type_utils
|
|
6
|
-
from ..Models import AresDataType
|
|
6
|
+
from ..Models import AresDataType, Quantity
|
|
7
7
|
|
|
8
8
|
def ares_value_to_py(ares_value: ares_struct_pb2.AresValue):
|
|
9
9
|
"""Converts an AresValue protobuf message to a Python native type."""
|
|
10
|
-
type_map = {
|
|
11
|
-
"null_value": None,
|
|
12
|
-
"number_value": ares_value.number_value,
|
|
13
|
-
"string_value": ares_value.string_value,
|
|
14
|
-
"bool_value": ares_value.bool_value,
|
|
15
|
-
"string_array_value": ares_value.string_array_value.strings,
|
|
16
|
-
"number_array_value": ares_value.number_array_value.numbers,
|
|
17
|
-
"bytes_value": ares_value.bytes_value
|
|
18
|
-
}
|
|
19
|
-
|
|
20
10
|
field = ares_value.WhichOneof("kind")
|
|
21
|
-
if field
|
|
22
|
-
return
|
|
11
|
+
if field == "null_value":
|
|
12
|
+
return None
|
|
13
|
+
elif field == "number_value":
|
|
14
|
+
return ares_value.number_value
|
|
15
|
+
elif field == "string_value":
|
|
16
|
+
return ares_value.string_value
|
|
17
|
+
elif field == "bool_value":
|
|
18
|
+
return ares_value.bool_value
|
|
19
|
+
elif field == "string_array_value":
|
|
20
|
+
return list(ares_value.string_array_value.strings)
|
|
21
|
+
elif field == "number_array_value":
|
|
22
|
+
return list(ares_value.number_array_value.numbers)
|
|
23
|
+
elif field == "bytes_value":
|
|
24
|
+
return ares_value.bytes_value
|
|
25
|
+
elif field == "list_value":
|
|
26
|
+
return [ares_value_to_py(v) for v in ares_value.list_value.values]
|
|
27
|
+
elif field == "struct_value":
|
|
28
|
+
return {k: ares_value_to_py(v) for k, v in ares_value.struct_value.fields.items()}
|
|
29
|
+
elif field == "quantity_value":
|
|
30
|
+
return Quantity(scalar=ares_value.quantity_value.scalar,
|
|
31
|
+
type=ares_value.quantity_value.type,
|
|
32
|
+
unit=ares_value.quantity_value.unit)
|
|
23
33
|
return None
|
|
24
34
|
|
|
25
35
|
def py_to_ares_value(py_value, ares_value: ares_struct_pb2.AresValue):
|
|
@@ -32,6 +42,13 @@ def py_to_ares_value(py_value, ares_value: ares_struct_pb2.AresValue):
|
|
|
32
42
|
ares_value.number_value = py_value
|
|
33
43
|
elif isinstance(py_value, bytes):
|
|
34
44
|
ares_value.bytes_value = py_value
|
|
45
|
+
elif isinstance(py_value, Quantity):
|
|
46
|
+
ares_value.quantity_value.scalar = py_value.scalar
|
|
47
|
+
ares_value.quantity_value.type = py_value.type
|
|
48
|
+
ares_value.quantity_value.unit = py_value.unit
|
|
49
|
+
elif isinstance(py_value, dict):
|
|
50
|
+
for k, v in py_value.items():
|
|
51
|
+
py_to_ares_value(v, ares_value.struct_value.fields[k])
|
|
35
52
|
elif isinstance(py_value, list):
|
|
36
53
|
if(all(isinstance(x, str) for x in py_value)):
|
|
37
54
|
ares_value.string_array_value.strings.extend(py_value)
|
|
@@ -47,6 +64,14 @@ def py_to_ares_value(py_value, ares_value: ares_struct_pb2.AresValue):
|
|
|
47
64
|
else:
|
|
48
65
|
raise TypeError(f"Unsupported type for AresValue: {type(py_value)}")
|
|
49
66
|
|
|
67
|
+
def create_quantity(value: Quantity) -> ares_struct_pb2.AresValue:
|
|
68
|
+
""" Creates a new AresValue, initialized to the provided Quantity. """
|
|
69
|
+
ares_val = ares_struct_pb2.AresValue()
|
|
70
|
+
ares_val.quantity_value.scalar = value.scalar
|
|
71
|
+
ares_val.quantity_value.type = value.type
|
|
72
|
+
ares_val.quantity_value.unit = value.unit
|
|
73
|
+
return ares_val
|
|
74
|
+
|
|
50
75
|
def create_number(value: Union[int, float]) -> ares_struct_pb2.AresValue:
|
|
51
76
|
"""
|
|
52
77
|
Creates a new AresValue, initialized to the provided number value.
|
|
@@ -203,6 +228,8 @@ def create_default(python_datatype: AresDataType) -> ares_struct_pb2.AresValue:
|
|
|
203
228
|
|
|
204
229
|
elif(dataType == ares_data_type_pb2.AresDataType.STRUCT):
|
|
205
230
|
return create_struct({})
|
|
231
|
+
elif(dataType == ares_data_type_pb2.AresDataType.QUANTITY):
|
|
232
|
+
return create_quantity(Quantity(0, 0, ""))
|
|
206
233
|
else:
|
|
207
234
|
return create_null()
|
|
208
235
|
|
|
@@ -229,6 +256,9 @@ def create_ares_value(value: Any) -> ares_struct_pb2.AresValue:
|
|
|
229
256
|
elif(isinstance(value, bytes)):
|
|
230
257
|
return create_bytes(value)
|
|
231
258
|
|
|
259
|
+
elif(isinstance(value, Quantity)):
|
|
260
|
+
return create_quantity(value)
|
|
261
|
+
|
|
232
262
|
elif(isinstance(value, list)):
|
|
233
263
|
if all(isinstance(x, bool) for x in value):
|
|
234
264
|
return create_array(value)
|
|
@@ -11,4 +11,7 @@ from .Device import AresDeviceService
|
|
|
11
11
|
from .Device import DeviceCommandDescriptor
|
|
12
12
|
from .Device import DeviceSchemaEntry
|
|
13
13
|
from .Models import AresDataType
|
|
14
|
-
from .Models import Outcome
|
|
14
|
+
from .Models import Outcome
|
|
15
|
+
from .Models import AresSchemaEntry
|
|
16
|
+
from .Models import Quantity
|
|
17
|
+
from .Models import QuantitySchema
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"
|
|
2
|
+
requires = ["hatchling"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -16,9 +16,7 @@ classifiers = [
|
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
dependencies = [
|
|
19
|
-
"
|
|
20
|
-
"grpcio >= 1.71.0, < 2.0.0",
|
|
21
|
-
"ares-datamodel >= 0.16.0",
|
|
19
|
+
"ares-datamodel >= 0.20.1",
|
|
22
20
|
]
|
|
23
21
|
|
|
24
22
|
[project.urls]
|
|
@@ -107,7 +107,7 @@ class TestAresAnalyzerService(unittest.TestCase):
|
|
|
107
107
|
# Case A: Success (Matches Schema)
|
|
108
108
|
req = analyzer_service.ParameterValidationRequest()
|
|
109
109
|
entry = req.input_schema.fields["Voltage"]
|
|
110
|
-
new_schema_entry = ares_data_schema_pb2.
|
|
110
|
+
new_schema_entry = ares_data_schema_pb2.AresValueSchema(type=ares_data_type_pb2.AresDataType.NUMBER, optional=False, description="Voltage value")
|
|
111
111
|
entry.CopyFrom(new_schema_entry)
|
|
112
112
|
resp = self.service._service_wrapper.ValidateInputs(req, None)
|
|
113
113
|
self.assertTrue(resp.success, "Validation should pass for correct schema")
|
|
@@ -115,7 +115,7 @@ class TestAresAnalyzerService(unittest.TestCase):
|
|
|
115
115
|
# Case B: Failure (Wrong Type)
|
|
116
116
|
req_bad_type = analyzer_service.ParameterValidationRequest()
|
|
117
117
|
bad_entry = req_bad_type.input_schema.fields["Voltage"]
|
|
118
|
-
bad_schema_entry = ares_data_schema_pb2.
|
|
118
|
+
bad_schema_entry = ares_data_schema_pb2.AresValueSchema(type=ares_data_type_pb2.AresDataType.STRING, optional=False, description="Voltage value")
|
|
119
119
|
bad_entry.CopyFrom(bad_schema_entry)
|
|
120
120
|
resp_bad = self.service._service_wrapper.ValidateInputs(req_bad_type, None)
|
|
121
121
|
self.assertFalse(resp_bad.success, "Validation should fail for type mismatch")
|
|
@@ -48,15 +48,13 @@ class TestAresDataSchemaUtils(unittest.TestCase):
|
|
|
48
48
|
type=AresDataType.NUMBER,
|
|
49
49
|
optional=True,
|
|
50
50
|
description="Test Desc",
|
|
51
|
-
unit="m/s",
|
|
52
51
|
choices=[1.0, 2.0]
|
|
53
52
|
)
|
|
54
53
|
proto = ares_data_schema_utils.convert_ares_schema_entry_to_proto(py_entry)
|
|
55
54
|
self.assertEqual(proto.type, ares_data_type_pb2.AresDataType.NUMBER)
|
|
56
55
|
self.assertTrue(proto.optional)
|
|
57
56
|
self.assertEqual(proto.description, "Test Desc")
|
|
58
|
-
self.assertEqual(proto.
|
|
59
|
-
self.assertEqual(proto.number_choices.numbers, [1.0, 2.0])
|
|
57
|
+
self.assertEqual(list(proto.number_choices.numbers), [1.0, 2.0])
|
|
60
58
|
|
|
61
59
|
def test_nested_struct_schema(self):
|
|
62
60
|
nested_field = AresSchemaEntry(type=AresDataType.STRING, description="Inner")
|
|
@@ -111,7 +111,7 @@ class TestAresDeviceService(unittest.TestCase):
|
|
|
111
111
|
response = self.service._service_wrapper.ExecuteCommand(req, None)
|
|
112
112
|
|
|
113
113
|
self.assertTrue(response.success, response.error)
|
|
114
|
-
result_dict = ares_struct_utils.ares_struct_to_dict(response.result)
|
|
114
|
+
result_dict = ares_struct_utils.ares_struct_to_dict(response.result.struct_value)
|
|
115
115
|
self.assertEqual(result_dict["axis"], "X")
|
|
116
116
|
self.assertEqual(result_dict["final_speed"], 50.0)
|
|
117
117
|
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
from typing import Union, Dict, Optional, List
|
|
2
|
-
from ares_datamodel import ares_data_schema_pb2
|
|
3
|
-
from ..Models import ares_data_models
|
|
4
|
-
from ..Models.ares_data_models import AresSchemaEntry
|
|
5
|
-
|
|
6
|
-
def convert_ares_schema_entry_to_proto(entry: AresSchemaEntry) -> ares_data_schema_pb2.SchemaEntry:
|
|
7
|
-
proto_entry = create_settings_schema_entry(entry.type, entry.optional, entry.choices, entry.struct_schema)
|
|
8
|
-
proto_entry.description = entry.description
|
|
9
|
-
proto_entry.unit = entry.unit
|
|
10
|
-
return proto_entry
|
|
11
|
-
|
|
12
|
-
def create_settings_schema_entry(
|
|
13
|
-
setting_type: ares_data_models.AresDataType,
|
|
14
|
-
optional: bool,
|
|
15
|
-
choices: Union[list[str], list[int], list[float]],
|
|
16
|
-
struct_schema: Optional[Dict[str, AresSchemaEntry]] = None) -> ares_data_schema_pb2.SchemaEntry:
|
|
17
|
-
"""
|
|
18
|
-
Creates a protobuf SchemaEntry message from the provided setting details.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
setting_type (AresDataType): The data type of the setting.
|
|
22
|
-
optional (bool): Whether the setting is optional.
|
|
23
|
-
choices (Union[list[str], list[int], list[float]]): A list of valid choices for the setting.
|
|
24
|
-
struct_schema (Optional[Dict[str, AresSchemaEntry]]): Nested schema definition for STRUCT types.
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
(SchemaEntry): A new SchemaEntry message.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
if(isinstance(choices, list)):
|
|
31
|
-
if(len(choices) == 0):
|
|
32
|
-
schema_entry = ares_data_schema_pb2.SchemaEntry(type=setting_type.value, optional=optional)
|
|
33
|
-
|
|
34
|
-
elif(all(isinstance(item, str) for item in choices)):
|
|
35
|
-
schema_entry = ares_data_schema_pb2.SchemaEntry(type=setting_type.value, optional=optional)
|
|
36
|
-
schema_entry.string_choices.strings.extend(choices)
|
|
37
|
-
|
|
38
|
-
elif(all(isinstance(item, (int, float)) for item in choices)):
|
|
39
|
-
schema_entry = ares_data_schema_pb2.SchemaEntry(type=setting_type.value, optional=optional)
|
|
40
|
-
schema_entry.number_choices.numbers.extend(choices)
|
|
41
|
-
|
|
42
|
-
else:
|
|
43
|
-
schema_entry = ares_data_schema_pb2.SchemaEntry(type=setting_type.value, optional=optional)
|
|
44
|
-
|
|
45
|
-
if struct_schema is not None:
|
|
46
|
-
for key, value in struct_schema.items():
|
|
47
|
-
schema_entry.struct_schema.fields[key].CopyFrom(convert_ares_schema_entry_to_proto(value))
|
|
48
|
-
|
|
49
|
-
return schema_entry
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from typing import Union
|
|
2
|
-
|
|
3
|
-
from ..Device import DeviceCommandDescriptor
|
|
4
|
-
from ..Device import DeviceSchemaEntry
|
|
5
|
-
from ares_datamodel.device import device_command_descriptor_pb2
|
|
6
|
-
from ares_datamodel import ares_data_schema_pb2
|
|
7
|
-
|
|
8
|
-
from . import ares_data_type_utils
|
|
9
|
-
|
|
10
|
-
def python_command_description_to_proto(python_description: DeviceCommandDescriptor) -> device_command_descriptor_pb2.DeviceCommandDescriptor:
|
|
11
|
-
proto_description = device_command_descriptor_pb2.DeviceCommandDescriptor()
|
|
12
|
-
|
|
13
|
-
#First, we need to transform our Python DeviceSchemaEntry classes into the protobuf equivalent. Then add them to our new proto message
|
|
14
|
-
transformed_input_schema = {key: python_device_schema_entry_to_proto(value) for key, value in python_description.input_schema.items()}
|
|
15
|
-
for key, value in transformed_input_schema.items():
|
|
16
|
-
new_entry: ares_data_schema_pb2.SchemaEntry = proto_description.input_schema.fields[key]
|
|
17
|
-
new_entry.CopyFrom(value)
|
|
18
|
-
|
|
19
|
-
transformed_output_schema = {key: python_device_schema_entry_to_proto(value) for key, value in python_description.output_schema.items()}
|
|
20
|
-
for key, value in transformed_output_schema.items():
|
|
21
|
-
new_entry: ares_data_schema_pb2.SchemaEntry = proto_description.output_schema.fields[key]
|
|
22
|
-
new_entry.CopyFrom(value)
|
|
23
|
-
|
|
24
|
-
proto_description.name = python_description.name
|
|
25
|
-
proto_description.description = python_description.description
|
|
26
|
-
|
|
27
|
-
return proto_description
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def python_device_schema_entry_to_proto(entry: DeviceSchemaEntry) -> ares_data_schema_pb2.SchemaEntry:
|
|
31
|
-
proto_schema = ares_data_schema_pb2.SchemaEntry()
|
|
32
|
-
proto_schema.type = ares_data_type_utils.python_ares_type_to_proto_ares_type(entry.type)
|
|
33
|
-
proto_schema.optional = entry.optional
|
|
34
|
-
proto_schema.description = entry.description
|
|
35
|
-
proto_schema.unit = entry.unit
|
|
36
|
-
|
|
37
|
-
if all(isinstance(x, (int, float)) for x in entry.contraints):
|
|
38
|
-
proto_schema.number_choices.numbers.extend(entry.contraints)
|
|
39
|
-
|
|
40
|
-
elif all(isinstance(x, str) for x in entry.contraints):
|
|
41
|
-
proto_schema.string_choices.strings.extend(entry.contraints)
|
|
42
|
-
|
|
43
|
-
return proto_schema
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|