sift-stack-py 0.3.2__py3-none-any.whl
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.
- google/__init__.py +1 -0
- google/api/__init__.py +0 -0
- google/api/annotations_pb2.py +27 -0
- google/api/annotations_pb2.pyi +29 -0
- google/api/annotations_pb2_grpc.py +4 -0
- google/api/annotations_pb2_grpc.pyi +30 -0
- google/api/field_behavior_pb2.py +30 -0
- google/api/field_behavior_pb2.pyi +175 -0
- google/api/field_behavior_pb2_grpc.py +4 -0
- google/api/field_behavior_pb2_grpc.pyi +30 -0
- google/api/http_pb2.py +31 -0
- google/api/http_pb2.pyi +433 -0
- google/api/http_pb2_grpc.py +4 -0
- google/api/http_pb2_grpc.pyi +30 -0
- protoc_gen_openapiv2/__init__.py +0 -0
- protoc_gen_openapiv2/options/__init__.py +0 -0
- protoc_gen_openapiv2/options/annotations_pb2.py +27 -0
- protoc_gen_openapiv2/options/annotations_pb2.pyi +48 -0
- protoc_gen_openapiv2/options/annotations_pb2_grpc.py +4 -0
- protoc_gen_openapiv2/options/annotations_pb2_grpc.pyi +17 -0
- protoc_gen_openapiv2/options/openapiv2_pb2.py +132 -0
- protoc_gen_openapiv2/options/openapiv2_pb2.pyi +1533 -0
- protoc_gen_openapiv2/options/openapiv2_pb2_grpc.py +4 -0
- protoc_gen_openapiv2/options/openapiv2_pb2_grpc.pyi +17 -0
- sift/__init__.py +0 -0
- sift/annotation_logs/__init__.py +0 -0
- sift/annotation_logs/v1/__init__.py +0 -0
- sift/annotation_logs/v1/annotation_logs_pb2.py +115 -0
- sift/annotation_logs/v1/annotation_logs_pb2.pyi +370 -0
- sift/annotation_logs/v1/annotation_logs_pb2_grpc.py +135 -0
- sift/annotation_logs/v1/annotation_logs_pb2_grpc.pyi +84 -0
- sift/annotations/__init__.py +0 -0
- sift/annotations/v1/__init__.py +0 -0
- sift/annotations/v1/annotations_pb2.py +180 -0
- sift/annotations/v1/annotations_pb2.pyi +539 -0
- sift/annotations/v1/annotations_pb2_grpc.py +237 -0
- sift/annotations/v1/annotations_pb2_grpc.pyi +144 -0
- sift/assets/__init__.py +0 -0
- sift/assets/v1/__init__.py +0 -0
- sift/assets/v1/assets_pb2.py +90 -0
- sift/assets/v1/assets_pb2.pyi +235 -0
- sift/assets/v1/assets_pb2_grpc.py +168 -0
- sift/assets/v1/assets_pb2_grpc.pyi +101 -0
- sift/calculated_channels/__init__.py +0 -0
- sift/calculated_channels/v1/__init__.py +0 -0
- sift/calculated_channels/v1/calculated_channels_pb2.py +99 -0
- sift/calculated_channels/v1/calculated_channels_pb2.pyi +280 -0
- sift/calculated_channels/v1/calculated_channels_pb2_grpc.py +101 -0
- sift/calculated_channels/v1/calculated_channels_pb2_grpc.pyi +64 -0
- sift/campaigns/__init__.py +0 -0
- sift/campaigns/v1/__init__.py +0 -0
- sift/campaigns/v1/campaigns_pb2.py +144 -0
- sift/campaigns/v1/campaigns_pb2.pyi +383 -0
- sift/campaigns/v1/campaigns_pb2_grpc.py +169 -0
- sift/campaigns/v1/campaigns_pb2_grpc.pyi +104 -0
- sift/channel_schemas/__init__.py +0 -0
- sift/channel_schemas/v1/__init__.py +0 -0
- sift/channel_schemas/v1/channel_schemas_pb2.py +69 -0
- sift/channel_schemas/v1/channel_schemas_pb2.pyi +117 -0
- sift/channel_schemas/v1/channel_schemas_pb2_grpc.py +101 -0
- sift/channel_schemas/v1/channel_schemas_pb2_grpc.pyi +64 -0
- sift/channels/__init__.py +0 -0
- sift/channels/v2/__init__.py +0 -0
- sift/channels/v2/channels_pb2.py +88 -0
- sift/channels/v2/channels_pb2.pyi +183 -0
- sift/channels/v2/channels_pb2_grpc.py +101 -0
- sift/channels/v2/channels_pb2_grpc.pyi +64 -0
- sift/common/__init__.py +0 -0
- sift/common/type/__init__.py +0 -0
- sift/common/type/v1/__init__.py +0 -0
- sift/common/type/v1/channel_bit_field_element_pb2.py +34 -0
- sift/common/type/v1/channel_bit_field_element_pb2.pyi +33 -0
- sift/common/type/v1/channel_bit_field_element_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_bit_field_element_pb2_grpc.pyi +17 -0
- sift/common/type/v1/channel_data_type_pb2.py +29 -0
- sift/common/type/v1/channel_data_type_pb2.pyi +50 -0
- sift/common/type/v1/channel_data_type_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_data_type_pb2_grpc.pyi +17 -0
- sift/common/type/v1/channel_enum_type_pb2.py +32 -0
- sift/common/type/v1/channel_enum_type_pb2.pyi +29 -0
- sift/common/type/v1/channel_enum_type_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_enum_type_pb2_grpc.pyi +17 -0
- sift/common/type/v1/organization_pb2.py +27 -0
- sift/common/type/v1/organization_pb2.pyi +29 -0
- sift/common/type/v1/organization_pb2_grpc.py +4 -0
- sift/common/type/v1/organization_pb2_grpc.pyi +17 -0
- sift/common/type/v1/resource_identifier_pb2.py +46 -0
- sift/common/type/v1/resource_identifier_pb2.pyi +145 -0
- sift/common/type/v1/resource_identifier_pb2_grpc.py +4 -0
- sift/common/type/v1/resource_identifier_pb2_grpc.pyi +17 -0
- sift/common/type/v1/user_pb2.py +33 -0
- sift/common/type/v1/user_pb2.pyi +36 -0
- sift/common/type/v1/user_pb2_grpc.py +4 -0
- sift/common/type/v1/user_pb2_grpc.pyi +17 -0
- sift/data/__init__.py +0 -0
- sift/data/v1/__init__.py +0 -0
- sift/data/v1/data_pb2.py +212 -0
- sift/data/v1/data_pb2.pyi +745 -0
- sift/data/v1/data_pb2_grpc.py +67 -0
- sift/data/v1/data_pb2_grpc.pyi +44 -0
- sift/ingest/__init__.py +0 -0
- sift/ingest/v1/__init__.py +0 -0
- sift/ingest/v1/ingest_pb2.py +35 -0
- sift/ingest/v1/ingest_pb2.pyi +118 -0
- sift/ingest/v1/ingest_pb2_grpc.py +66 -0
- sift/ingest/v1/ingest_pb2_grpc.pyi +41 -0
- sift/ingestion_configs/__init__.py +0 -0
- sift/ingestion_configs/v1/__init__.py +0 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2.py +115 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2.pyi +332 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.py +203 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.pyi +124 -0
- sift/notifications/__init__.py +0 -0
- sift/notifications/v1/__init__.py +0 -0
- sift/notifications/v1/notifications_pb2.py +64 -0
- sift/notifications/v1/notifications_pb2.pyi +225 -0
- sift/notifications/v1/notifications_pb2_grpc.py +101 -0
- sift/notifications/v1/notifications_pb2_grpc.pyi +64 -0
- sift/ping/__init__.py +0 -0
- sift/ping/v1/__init__.py +0 -0
- sift/ping/v1/ping_pb2.py +38 -0
- sift/ping/v1/ping_pb2.pyi +36 -0
- sift/ping/v1/ping_pb2_grpc.py +66 -0
- sift/ping/v1/ping_pb2_grpc.pyi +41 -0
- sift/remote_files/__init__.py +0 -0
- sift/remote_files/v1/__init__.py +0 -0
- sift/remote_files/v1/remote_files_pb2.py +174 -0
- sift/remote_files/v1/remote_files_pb2.pyi +472 -0
- sift/remote_files/v1/remote_files_pb2_grpc.py +271 -0
- sift/remote_files/v1/remote_files_pb2_grpc.pyi +164 -0
- sift/report_templates/__init__.py +0 -0
- sift/report_templates/v1/__init__.py +0 -0
- sift/report_templates/v1/report_templates_pb2.py +146 -0
- sift/report_templates/v1/report_templates_pb2.pyi +381 -0
- sift/report_templates/v1/report_templates_pb2_grpc.py +169 -0
- sift/report_templates/v1/report_templates_pb2_grpc.pyi +104 -0
- sift/reports/__init__.py +0 -0
- sift/reports/v1/__init__.py +0 -0
- sift/reports/v1/reports_pb2.py +193 -0
- sift/reports/v1/reports_pb2.pyi +562 -0
- sift/reports/v1/reports_pb2_grpc.py +205 -0
- sift/reports/v1/reports_pb2_grpc.pyi +136 -0
- sift/rule_evaluation/__init__.py +0 -0
- sift/rule_evaluation/v1/__init__.py +0 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2.py +89 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2.pyi +263 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.py +101 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.pyi +64 -0
- sift/rules/__init__.py +0 -0
- sift/rules/v1/__init__.py +0 -0
- sift/rules/v1/rules_pb2.py +420 -0
- sift/rules/v1/rules_pb2.pyi +1355 -0
- sift/rules/v1/rules_pb2_grpc.py +577 -0
- sift/rules/v1/rules_pb2_grpc.pyi +351 -0
- sift/runs/__init__.py +0 -0
- sift/runs/v2/__init__.py +0 -0
- sift/runs/v2/runs_pb2.py +150 -0
- sift/runs/v2/runs_pb2.pyi +413 -0
- sift/runs/v2/runs_pb2_grpc.py +271 -0
- sift/runs/v2/runs_pb2_grpc.pyi +164 -0
- sift/saved_searches/__init__.py +0 -0
- sift/saved_searches/v1/__init__.py +0 -0
- sift/saved_searches/v1/saved_searches_pb2.py +144 -0
- sift/saved_searches/v1/saved_searches_pb2.pyi +385 -0
- sift/saved_searches/v1/saved_searches_pb2_grpc.py +237 -0
- sift/saved_searches/v1/saved_searches_pb2_grpc.pyi +144 -0
- sift/tags/__init__.py +0 -0
- sift/tags/v1/__init__.py +0 -0
- sift/tags/v1/tags_pb2.py +49 -0
- sift/tags/v1/tags_pb2.pyi +71 -0
- sift/tags/v1/tags_pb2_grpc.py +4 -0
- sift/tags/v1/tags_pb2_grpc.pyi +17 -0
- sift/users/__init__.py +0 -0
- sift/users/v2/__init__.py +0 -0
- sift/users/v2/users_pb2.py +61 -0
- sift/users/v2/users_pb2.pyi +142 -0
- sift/users/v2/users_pb2_grpc.py +135 -0
- sift/users/v2/users_pb2_grpc.pyi +84 -0
- sift/views/__init__.py +0 -0
- sift/views/v1/__init__.py +0 -0
- sift/views/v1/views_pb2.py +130 -0
- sift/views/v1/views_pb2.pyi +466 -0
- sift/views/v1/views_pb2_grpc.py +305 -0
- sift/views/v1/views_pb2_grpc.pyi +184 -0
- sift_grafana/py.typed +0 -0
- sift_grafana/sift_query_model.py +64 -0
- sift_py/__init__.py +923 -0
- sift_py/_internal/__init__.py +5 -0
- sift_py/_internal/cel.py +18 -0
- sift_py/_internal/channel.py +42 -0
- sift_py/_internal/convert/__init__.py +3 -0
- sift_py/_internal/convert/json.py +24 -0
- sift_py/_internal/convert/protobuf.py +34 -0
- sift_py/_internal/convert/timestamp.py +9 -0
- sift_py/_internal/test_util/__init__.py +0 -0
- sift_py/_internal/test_util/channel.py +136 -0
- sift_py/_internal/test_util/fn.py +14 -0
- sift_py/_internal/test_util/server_interceptor.py +62 -0
- sift_py/_internal/time.py +48 -0
- sift_py/_internal/user.py +39 -0
- sift_py/data/__init__.py +171 -0
- sift_py/data/_channel.py +38 -0
- sift_py/data/_deserialize.py +208 -0
- sift_py/data/_deserialize_test.py +134 -0
- sift_py/data/_service_test.py +276 -0
- sift_py/data/_validate.py +10 -0
- sift_py/data/error.py +5 -0
- sift_py/data/query.py +299 -0
- sift_py/data/service.py +497 -0
- sift_py/data_import/__init__.py +130 -0
- sift_py/data_import/_config.py +167 -0
- sift_py/data_import/_config_test.py +166 -0
- sift_py/data_import/_csv_test.py +395 -0
- sift_py/data_import/_status_test.py +176 -0
- sift_py/data_import/_tdms_test.py +238 -0
- sift_py/data_import/ch10.py +157 -0
- sift_py/data_import/config.py +19 -0
- sift_py/data_import/csv.py +259 -0
- sift_py/data_import/status.py +113 -0
- sift_py/data_import/tdms.py +206 -0
- sift_py/data_import/tempfile.py +30 -0
- sift_py/data_import/time_format.py +39 -0
- sift_py/error.py +11 -0
- sift_py/file_attachment/__init__.py +88 -0
- sift_py/file_attachment/_internal/__init__.py +0 -0
- sift_py/file_attachment/_internal/download.py +13 -0
- sift_py/file_attachment/_internal/upload.py +100 -0
- sift_py/file_attachment/_service_test.py +161 -0
- sift_py/file_attachment/entity.py +30 -0
- sift_py/file_attachment/metadata.py +107 -0
- sift_py/file_attachment/service.py +142 -0
- sift_py/grpc/__init__.py +15 -0
- sift_py/grpc/_async_interceptors/__init__.py +0 -0
- sift_py/grpc/_async_interceptors/base.py +72 -0
- sift_py/grpc/_async_interceptors/metadata.py +36 -0
- sift_py/grpc/_interceptors/__init__.py +0 -0
- sift_py/grpc/_interceptors/base.py +61 -0
- sift_py/grpc/_interceptors/context.py +25 -0
- sift_py/grpc/_interceptors/metadata.py +33 -0
- sift_py/grpc/_retry.py +70 -0
- sift_py/grpc/keepalive.py +34 -0
- sift_py/grpc/transport.py +250 -0
- sift_py/grpc/transport_test.py +170 -0
- sift_py/ingestion/__init__.py +6 -0
- sift_py/ingestion/_internal/__init__.py +6 -0
- sift_py/ingestion/_internal/channel.py +12 -0
- sift_py/ingestion/_internal/error.py +10 -0
- sift_py/ingestion/_internal/ingest.py +350 -0
- sift_py/ingestion/_internal/ingest_test.py +357 -0
- sift_py/ingestion/_internal/ingestion_config.py +130 -0
- sift_py/ingestion/_internal/run.py +46 -0
- sift_py/ingestion/_service_test.py +478 -0
- sift_py/ingestion/buffer.py +189 -0
- sift_py/ingestion/channel.py +422 -0
- sift_py/ingestion/config/__init__.py +3 -0
- sift_py/ingestion/config/telemetry.py +281 -0
- sift_py/ingestion/config/telemetry_test.py +405 -0
- sift_py/ingestion/config/yaml/__init__.py +0 -0
- sift_py/ingestion/config/yaml/error.py +44 -0
- sift_py/ingestion/config/yaml/load.py +126 -0
- sift_py/ingestion/config/yaml/spec.py +58 -0
- sift_py/ingestion/config/yaml/test_load.py +25 -0
- sift_py/ingestion/flow.py +73 -0
- sift_py/ingestion/manager.py +99 -0
- sift_py/ingestion/rule/__init__.py +4 -0
- sift_py/ingestion/rule/config.py +11 -0
- sift_py/ingestion/service.py +237 -0
- sift_py/py.typed +0 -0
- sift_py/report_templates/__init__.py +0 -0
- sift_py/report_templates/_config_test.py +34 -0
- sift_py/report_templates/_service_test.py +94 -0
- sift_py/report_templates/config.py +36 -0
- sift_py/report_templates/service.py +171 -0
- sift_py/rest.py +29 -0
- sift_py/rule/__init__.py +0 -0
- sift_py/rule/_config_test.py +109 -0
- sift_py/rule/_service_test.py +168 -0
- sift_py/rule/config.py +229 -0
- sift_py/rule/service.py +484 -0
- sift_py/yaml/__init__.py +0 -0
- sift_py/yaml/_channel_test.py +169 -0
- sift_py/yaml/_rule_test.py +207 -0
- sift_py/yaml/channel.py +224 -0
- sift_py/yaml/report_templates.py +73 -0
- sift_py/yaml/rule.py +321 -0
- sift_py/yaml/utils.py +15 -0
- sift_stack_py-0.3.2.dist-info/LICENSE +7 -0
- sift_stack_py-0.3.2.dist-info/METADATA +109 -0
- sift_stack_py-0.3.2.dist-info/RECORD +291 -0
- sift_stack_py-0.3.2.dist-info/WHEEL +5 -0
- sift_stack_py-0.3.2.dist-info/top_level.txt +5 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import List, Tuple, cast
|
|
4
|
+
|
|
5
|
+
from google.protobuf.any_pb2 import Any
|
|
6
|
+
from sift.data.v1.data_pb2 import (
|
|
7
|
+
BitFieldValues,
|
|
8
|
+
BoolValues,
|
|
9
|
+
DoubleValues,
|
|
10
|
+
EnumValues,
|
|
11
|
+
FloatValues,
|
|
12
|
+
Int32Values,
|
|
13
|
+
Int64Values,
|
|
14
|
+
Metadata,
|
|
15
|
+
StringValues,
|
|
16
|
+
Uint32Values,
|
|
17
|
+
Uint64Values,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from sift_py._internal.time import to_timestamp_nanos
|
|
21
|
+
from sift_py.data._channel import ChannelTimeSeries
|
|
22
|
+
from sift_py.error import SiftError
|
|
23
|
+
from sift_py.ingestion.channel import ChannelDataType
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ChannelValues(Enum):
|
|
27
|
+
DOUBLE_VALUES = "sift.data.v1.DoubleValues"
|
|
28
|
+
FLOAT_VALUES = "sift.data.v1.FloatValues"
|
|
29
|
+
STRING_VALUES = "sift.data.v1.StringValues"
|
|
30
|
+
ENUM_VALUES = "sift.data.v1.EnumValues"
|
|
31
|
+
BIT_FIELD_VALUES = "sift.data.v1.BitFieldValues"
|
|
32
|
+
BOOL_VALUES = "sift.data.v1.BoolValues"
|
|
33
|
+
INT32_VALUES = "sift.data.v1.Int32Values"
|
|
34
|
+
INT64_VALUES = "sift.data.v1.Int64Values"
|
|
35
|
+
UINT32_VALUES = "sift.data.v1.Uint32Values"
|
|
36
|
+
UINT64_VALUES = "sift.data.v1.Uint64Values"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def try_deserialize_channel_data(channel_values: Any) -> List[Tuple[Metadata, ChannelTimeSeries]]:
|
|
40
|
+
if ChannelValues.DOUBLE_VALUES.value in channel_values.type_url:
|
|
41
|
+
double_values = cast(DoubleValues, DoubleValues.FromString(channel_values.value))
|
|
42
|
+
metadata = double_values.metadata
|
|
43
|
+
|
|
44
|
+
time_column = []
|
|
45
|
+
double_value_column = []
|
|
46
|
+
|
|
47
|
+
for v in double_values.values:
|
|
48
|
+
time_column.append(to_timestamp_nanos(v.timestamp))
|
|
49
|
+
double_value_column.append(v.value)
|
|
50
|
+
|
|
51
|
+
time_series = ChannelTimeSeries(
|
|
52
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, double_value_column
|
|
53
|
+
)
|
|
54
|
+
return [(metadata, time_series)]
|
|
55
|
+
|
|
56
|
+
elif ChannelValues.FLOAT_VALUES.value in channel_values.type_url:
|
|
57
|
+
float_values = cast(FloatValues, FloatValues.FromString(channel_values.value))
|
|
58
|
+
metadata = float_values.metadata
|
|
59
|
+
|
|
60
|
+
time_column = []
|
|
61
|
+
float_value_column = []
|
|
62
|
+
|
|
63
|
+
for float_v in float_values.values:
|
|
64
|
+
time_column.append(to_timestamp_nanos(float_v.timestamp))
|
|
65
|
+
float_value_column.append(float_v.value)
|
|
66
|
+
|
|
67
|
+
time_series = ChannelTimeSeries(
|
|
68
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, float_value_column
|
|
69
|
+
)
|
|
70
|
+
return [(metadata, time_series)]
|
|
71
|
+
|
|
72
|
+
elif ChannelValues.STRING_VALUES.value in channel_values.type_url:
|
|
73
|
+
string_values = cast(StringValues, StringValues.FromString(channel_values.value))
|
|
74
|
+
metadata = string_values.metadata
|
|
75
|
+
|
|
76
|
+
time_column = []
|
|
77
|
+
string_value_column = []
|
|
78
|
+
|
|
79
|
+
for string_v in string_values.values:
|
|
80
|
+
time_column.append(to_timestamp_nanos(string_v.timestamp))
|
|
81
|
+
string_value_column.append(string_v.value)
|
|
82
|
+
|
|
83
|
+
time_series = ChannelTimeSeries(
|
|
84
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, string_value_column
|
|
85
|
+
)
|
|
86
|
+
return [(metadata, time_series)]
|
|
87
|
+
|
|
88
|
+
elif ChannelValues.ENUM_VALUES.value in channel_values.type_url:
|
|
89
|
+
enum_values = cast(EnumValues, EnumValues.FromString(channel_values.value))
|
|
90
|
+
metadata = enum_values.metadata
|
|
91
|
+
|
|
92
|
+
time_column = []
|
|
93
|
+
enum_value_column = []
|
|
94
|
+
|
|
95
|
+
for enum_v in enum_values.values:
|
|
96
|
+
time_column.append(to_timestamp_nanos(enum_v.timestamp))
|
|
97
|
+
enum_value_column.append(enum_v.value)
|
|
98
|
+
|
|
99
|
+
time_series = ChannelTimeSeries(
|
|
100
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, enum_value_column
|
|
101
|
+
)
|
|
102
|
+
return [(metadata, time_series)]
|
|
103
|
+
|
|
104
|
+
elif ChannelValues.BOOL_VALUES.value in channel_values.type_url:
|
|
105
|
+
bool_values = cast(BoolValues, BoolValues.FromString(channel_values.value))
|
|
106
|
+
metadata = bool_values.metadata
|
|
107
|
+
|
|
108
|
+
time_column = []
|
|
109
|
+
bool_value_column = []
|
|
110
|
+
|
|
111
|
+
for bool_v in bool_values.values:
|
|
112
|
+
time_column.append(to_timestamp_nanos(bool_v.timestamp))
|
|
113
|
+
bool_value_column.append(bool_v.value)
|
|
114
|
+
|
|
115
|
+
time_series = ChannelTimeSeries(
|
|
116
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, bool_value_column
|
|
117
|
+
)
|
|
118
|
+
return [(metadata, time_series)]
|
|
119
|
+
|
|
120
|
+
elif ChannelValues.INT32_VALUES.value in channel_values.type_url:
|
|
121
|
+
int32_values = cast(Int32Values, Int32Values.FromString(channel_values.value))
|
|
122
|
+
metadata = int32_values.metadata
|
|
123
|
+
|
|
124
|
+
time_column = []
|
|
125
|
+
int32_value_column = []
|
|
126
|
+
|
|
127
|
+
for int32_v in int32_values.values:
|
|
128
|
+
time_column.append(to_timestamp_nanos(int32_v.timestamp))
|
|
129
|
+
int32_value_column.append(int32_v.value)
|
|
130
|
+
|
|
131
|
+
time_series = ChannelTimeSeries(
|
|
132
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, int32_value_column
|
|
133
|
+
)
|
|
134
|
+
return [(metadata, time_series)]
|
|
135
|
+
|
|
136
|
+
elif ChannelValues.INT64_VALUES.value in channel_values.type_url:
|
|
137
|
+
int64_values = cast(Int64Values, Int64Values.FromString(channel_values.value))
|
|
138
|
+
metadata = int64_values.metadata
|
|
139
|
+
|
|
140
|
+
time_column = []
|
|
141
|
+
int64_value_column = []
|
|
142
|
+
|
|
143
|
+
for int64_v in int64_values.values:
|
|
144
|
+
time_column.append(to_timestamp_nanos(int64_v.timestamp))
|
|
145
|
+
int64_value_column.append(int64_v.value)
|
|
146
|
+
|
|
147
|
+
time_series = ChannelTimeSeries(
|
|
148
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, int64_value_column
|
|
149
|
+
)
|
|
150
|
+
return [(metadata, time_series)]
|
|
151
|
+
|
|
152
|
+
elif ChannelValues.UINT32_VALUES.value in channel_values.type_url:
|
|
153
|
+
uint32_values = cast(Uint32Values, Uint32Values.FromString(channel_values.value))
|
|
154
|
+
metadata = uint32_values.metadata
|
|
155
|
+
|
|
156
|
+
time_column = []
|
|
157
|
+
uint32_value_column = []
|
|
158
|
+
|
|
159
|
+
for uint32_v in uint32_values.values:
|
|
160
|
+
time_column.append(to_timestamp_nanos(uint32_v.timestamp))
|
|
161
|
+
uint32_value_column.append(uint32_v.value)
|
|
162
|
+
|
|
163
|
+
time_series = ChannelTimeSeries(
|
|
164
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, uint32_value_column
|
|
165
|
+
)
|
|
166
|
+
return [(metadata, time_series)]
|
|
167
|
+
|
|
168
|
+
elif ChannelValues.UINT64_VALUES.value in channel_values.type_url:
|
|
169
|
+
uint64_values = cast(Uint64Values, Uint64Values.FromString(channel_values.value))
|
|
170
|
+
metadata = uint64_values.metadata
|
|
171
|
+
|
|
172
|
+
time_column = []
|
|
173
|
+
uint64_value_column = []
|
|
174
|
+
|
|
175
|
+
for uint64_v in uint64_values.values:
|
|
176
|
+
time_column.append(to_timestamp_nanos(uint64_v.timestamp))
|
|
177
|
+
uint64_value_column.append(uint64_v.value)
|
|
178
|
+
|
|
179
|
+
time_series = ChannelTimeSeries(
|
|
180
|
+
ChannelDataType.from_pb(metadata.data_type), time_column, uint64_value_column
|
|
181
|
+
)
|
|
182
|
+
return [(metadata, time_series)]
|
|
183
|
+
|
|
184
|
+
elif ChannelValues.BIT_FIELD_VALUES.value in channel_values.type_url:
|
|
185
|
+
bit_field_values = cast(BitFieldValues, BitFieldValues.FromString(channel_values.value))
|
|
186
|
+
metadata = bit_field_values.metadata
|
|
187
|
+
data_type = ChannelDataType.from_pb(metadata.data_type)
|
|
188
|
+
channel_name = metadata.channel.name
|
|
189
|
+
|
|
190
|
+
parsed_data: List[Tuple[Metadata, ChannelTimeSeries]] = []
|
|
191
|
+
|
|
192
|
+
for bit_field_element in bit_field_values.values:
|
|
193
|
+
md_copy = deepcopy(bit_field_values.metadata)
|
|
194
|
+
md_copy.channel.name = f"{channel_name}.{bit_field_element.name}"
|
|
195
|
+
|
|
196
|
+
time_column = []
|
|
197
|
+
bit_field_el_column = []
|
|
198
|
+
|
|
199
|
+
for bf_v in bit_field_element.values:
|
|
200
|
+
time_column.append(to_timestamp_nanos(bf_v.timestamp))
|
|
201
|
+
bit_field_el_column.append(bf_v.value)
|
|
202
|
+
|
|
203
|
+
time_series = ChannelTimeSeries(data_type, time_column, bit_field_el_column)
|
|
204
|
+
parsed_data.append((md_copy, time_series))
|
|
205
|
+
|
|
206
|
+
return parsed_data
|
|
207
|
+
|
|
208
|
+
raise SiftError(f"Received an unknown channel-type '{channel_values.type_url}'.")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from google.protobuf.any_pb2 import Any
|
|
2
|
+
from sift.common.type.v1.channel_bit_field_element_pb2 import ChannelBitFieldElement
|
|
3
|
+
from sift.common.type.v1.channel_data_type_pb2 import (
|
|
4
|
+
CHANNEL_DATA_TYPE_BIT_FIELD,
|
|
5
|
+
CHANNEL_DATA_TYPE_DOUBLE,
|
|
6
|
+
)
|
|
7
|
+
from sift.data.v1.data_pb2 import (
|
|
8
|
+
BitFieldElementValues,
|
|
9
|
+
BitFieldValue,
|
|
10
|
+
BitFieldValues,
|
|
11
|
+
DoubleValue,
|
|
12
|
+
DoubleValues,
|
|
13
|
+
Metadata,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from sift_py._internal.time import to_timestamp_nanos, to_timestamp_pb
|
|
17
|
+
from sift_py.data._deserialize import try_deserialize_channel_data
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_try_deserialize_channel_data_double():
|
|
21
|
+
metadata = Metadata(
|
|
22
|
+
data_type=CHANNEL_DATA_TYPE_DOUBLE, channel=Metadata.Channel(name="double-channel")
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
time_a = "2024-07-04T18:09:08.555-07:00"
|
|
26
|
+
time_b = "2024-07-04T18:09:09.555-07:00"
|
|
27
|
+
|
|
28
|
+
double_values = DoubleValues(
|
|
29
|
+
metadata=metadata,
|
|
30
|
+
values=[
|
|
31
|
+
DoubleValue(
|
|
32
|
+
timestamp=to_timestamp_pb(time_a),
|
|
33
|
+
value=10,
|
|
34
|
+
),
|
|
35
|
+
DoubleValue(
|
|
36
|
+
timestamp=to_timestamp_pb(time_b),
|
|
37
|
+
value=11,
|
|
38
|
+
),
|
|
39
|
+
],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
raw_values = Any()
|
|
43
|
+
raw_values.Pack(double_values)
|
|
44
|
+
|
|
45
|
+
deserialized_data = try_deserialize_channel_data(raw_values)
|
|
46
|
+
|
|
47
|
+
assert len(deserialized_data) == 1
|
|
48
|
+
|
|
49
|
+
metadata, time_series = deserialized_data[0]
|
|
50
|
+
|
|
51
|
+
assert metadata.data_type == CHANNEL_DATA_TYPE_DOUBLE
|
|
52
|
+
assert metadata.channel.name == "double-channel"
|
|
53
|
+
assert len(time_series.time_column) == 2
|
|
54
|
+
assert len(time_series.value_column) == 2
|
|
55
|
+
assert time_series.value_column[0] == 10
|
|
56
|
+
assert time_series.value_column[1] == 11
|
|
57
|
+
assert time_series.time_column[0] == to_timestamp_nanos(time_a)
|
|
58
|
+
assert time_series.time_column[1] == to_timestamp_nanos(time_b)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_try_deserialize_channel_data_bit_field_elements():
|
|
62
|
+
metadata = Metadata(
|
|
63
|
+
data_type=CHANNEL_DATA_TYPE_BIT_FIELD,
|
|
64
|
+
channel=Metadata.Channel(
|
|
65
|
+
name="gpio",
|
|
66
|
+
bit_field_elements=[
|
|
67
|
+
ChannelBitFieldElement(
|
|
68
|
+
name="12v",
|
|
69
|
+
index=0,
|
|
70
|
+
bit_count=4,
|
|
71
|
+
),
|
|
72
|
+
ChannelBitFieldElement(
|
|
73
|
+
name="heater",
|
|
74
|
+
index=4,
|
|
75
|
+
bit_count=4,
|
|
76
|
+
),
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
time_a = "2024-07-04T18:09:08.555-07:00"
|
|
82
|
+
|
|
83
|
+
value_a = int("10000001", 2)
|
|
84
|
+
value_b = int("11110001", 2)
|
|
85
|
+
|
|
86
|
+
bit_field_values = BitFieldValues(
|
|
87
|
+
metadata=metadata,
|
|
88
|
+
values=[
|
|
89
|
+
BitFieldElementValues(
|
|
90
|
+
name="12v",
|
|
91
|
+
values=[
|
|
92
|
+
BitFieldValue(
|
|
93
|
+
timestamp=to_timestamp_pb(time_a),
|
|
94
|
+
value=value_a,
|
|
95
|
+
)
|
|
96
|
+
],
|
|
97
|
+
),
|
|
98
|
+
BitFieldElementValues(
|
|
99
|
+
name="heater",
|
|
100
|
+
values=[
|
|
101
|
+
BitFieldValue(
|
|
102
|
+
timestamp=to_timestamp_pb(time_a),
|
|
103
|
+
value=value_b,
|
|
104
|
+
)
|
|
105
|
+
],
|
|
106
|
+
),
|
|
107
|
+
],
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
raw_values = Any()
|
|
111
|
+
raw_values.Pack(bit_field_values)
|
|
112
|
+
|
|
113
|
+
deserialized_data = try_deserialize_channel_data(raw_values)
|
|
114
|
+
|
|
115
|
+
assert len(deserialized_data) == 2
|
|
116
|
+
|
|
117
|
+
metadata_12v, time_series_12v = deserialized_data[0]
|
|
118
|
+
metadata_heater, time_series_heater = deserialized_data[1]
|
|
119
|
+
|
|
120
|
+
assert metadata_12v.data_type == CHANNEL_DATA_TYPE_BIT_FIELD
|
|
121
|
+
assert metadata_heater.data_type == CHANNEL_DATA_TYPE_BIT_FIELD
|
|
122
|
+
|
|
123
|
+
assert metadata_12v.channel.name == "gpio.12v"
|
|
124
|
+
assert metadata_heater.channel.name == "gpio.heater"
|
|
125
|
+
|
|
126
|
+
assert len(time_series_12v.time_column) == 1
|
|
127
|
+
assert len(time_series_12v.value_column) == 1
|
|
128
|
+
assert len(time_series_heater.time_column) == 1
|
|
129
|
+
assert len(time_series_heater.value_column) == 1
|
|
130
|
+
|
|
131
|
+
assert time_series_12v.value_column[0] == value_a
|
|
132
|
+
assert time_series_heater.value_column[0] == value_b
|
|
133
|
+
assert time_series_12v.time_column[0] == to_timestamp_nanos(time_a)
|
|
134
|
+
assert time_series_heater.time_column[0] == to_timestamp_nanos(time_a)
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from datetime import datetime, timedelta, timezone
|
|
3
|
+
from typing import Dict, Iterator
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from google.protobuf.any_pb2 import Any
|
|
7
|
+
from pytest_mock import MockFixture, MockType
|
|
8
|
+
from sift.assets.v1.assets_pb2 import Asset
|
|
9
|
+
from sift.channels.v2.channels_pb2 import Channel
|
|
10
|
+
from sift.common.type.v1.channel_bit_field_element_pb2 import ChannelBitFieldElement
|
|
11
|
+
from sift.common.type.v1.channel_data_type_pb2 import (
|
|
12
|
+
CHANNEL_DATA_TYPE_BIT_FIELD,
|
|
13
|
+
CHANNEL_DATA_TYPE_DOUBLE,
|
|
14
|
+
)
|
|
15
|
+
from sift.data.v1.data_pb2 import (
|
|
16
|
+
BitFieldElementValues,
|
|
17
|
+
BitFieldValue,
|
|
18
|
+
BitFieldValues,
|
|
19
|
+
DoubleValue,
|
|
20
|
+
DoubleValues,
|
|
21
|
+
Metadata,
|
|
22
|
+
)
|
|
23
|
+
from sift.runs.v2.runs_pb2 import Run
|
|
24
|
+
|
|
25
|
+
from sift_py._internal.test_util.channel import MockAsyncChannel
|
|
26
|
+
from sift_py._internal.time import to_timestamp_pb
|
|
27
|
+
from sift_py.data.query import ChannelQuery, DataQuery
|
|
28
|
+
from sift_py.data.service import DataService
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.asyncio
|
|
32
|
+
async def test_data_service_execute_regular_channels(mocker: MockFixture):
|
|
33
|
+
with patch_grpc_calls_channels(mocker) as mocks:
|
|
34
|
+
channel = MockAsyncChannel()
|
|
35
|
+
data_service = DataService(channel)
|
|
36
|
+
|
|
37
|
+
start_time = datetime.now(timezone.utc)
|
|
38
|
+
end_time = start_time + timedelta(minutes=2)
|
|
39
|
+
|
|
40
|
+
query = DataQuery(
|
|
41
|
+
asset_name="NostromoLV428",
|
|
42
|
+
start_time=start_time,
|
|
43
|
+
end_time=end_time,
|
|
44
|
+
sample_ms=0,
|
|
45
|
+
channels=[
|
|
46
|
+
ChannelQuery(
|
|
47
|
+
channel_name="velocity",
|
|
48
|
+
component="mainmotor",
|
|
49
|
+
run_name="[NostromoLV426].1720141748.047512",
|
|
50
|
+
),
|
|
51
|
+
ChannelQuery(
|
|
52
|
+
channel_name="gpio",
|
|
53
|
+
run_name="[NostromoLV426].1720141748.047512",
|
|
54
|
+
),
|
|
55
|
+
ChannelQuery(
|
|
56
|
+
channel_name="valve.pressure",
|
|
57
|
+
run_name="[NostromoLV426].1720141748.047512",
|
|
58
|
+
),
|
|
59
|
+
],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
result = await data_service.execute(query)
|
|
63
|
+
|
|
64
|
+
mock_get_asset = mocks["mock_get_asset_by_name"]
|
|
65
|
+
mock_get_channels = mocks["mock_get_channels_by_asset_id"]
|
|
66
|
+
mock_get_runs = mocks["mock_get_runs_by_names"]
|
|
67
|
+
|
|
68
|
+
mock_get_asset.assert_called_once()
|
|
69
|
+
mock_get_channels.assert_called_once()
|
|
70
|
+
mock_get_runs.assert_called_once()
|
|
71
|
+
|
|
72
|
+
# bit field elements count as separate channels
|
|
73
|
+
assert len(result.all_channels()) == 4
|
|
74
|
+
assert not result.channel("velocity")
|
|
75
|
+
assert not result.channels("velocity")
|
|
76
|
+
assert len(result.channels("mainmotor.velocity")) == 1
|
|
77
|
+
|
|
78
|
+
velocity = result.channel("mainmotor.velocity")
|
|
79
|
+
assert velocity is not None
|
|
80
|
+
assert len(velocity.timestamps) == 2
|
|
81
|
+
assert len(velocity.time_column()["time"]) == 2
|
|
82
|
+
assert len(velocity.time_column("custom_column_name")["custom_column_name"]) == 2
|
|
83
|
+
assert len(velocity.value_column()["mainmotor.velocity"]) == 2
|
|
84
|
+
assert len(velocity.value_column("custom_column_name")["custom_column_name"]) == 2
|
|
85
|
+
|
|
86
|
+
all_columns = velocity.columns()
|
|
87
|
+
assert len(all_columns) == 2
|
|
88
|
+
assert len(all_columns["time"]) == 2
|
|
89
|
+
assert len(all_columns["mainmotor.velocity"]) == 2
|
|
90
|
+
|
|
91
|
+
all_columns_custom = velocity.columns(
|
|
92
|
+
time_column_name="ts",
|
|
93
|
+
value_column_name="velocity",
|
|
94
|
+
)
|
|
95
|
+
assert len(all_columns_custom) == 2
|
|
96
|
+
assert len(all_columns_custom["ts"]) == 2
|
|
97
|
+
assert len(all_columns_custom["velocity"]) == 2
|
|
98
|
+
|
|
99
|
+
gpio = result.channel("gpio")
|
|
100
|
+
assert not gpio
|
|
101
|
+
|
|
102
|
+
gpio_12v = result.channel("gpio.12v")
|
|
103
|
+
assert gpio_12v is not None
|
|
104
|
+
assert len(gpio_12v.timestamps) == 1
|
|
105
|
+
assert len(gpio_12v.time_column()["time"]) == 1
|
|
106
|
+
assert len(gpio_12v.time_column("custom_column_name")["custom_column_name"]) == 1
|
|
107
|
+
assert len(gpio_12v.value_column()["gpio.12v"]) == 1
|
|
108
|
+
assert len(gpio_12v.value_column("12v")["12v"]) == 1
|
|
109
|
+
|
|
110
|
+
gpio_heater = result.channel("gpio.heater")
|
|
111
|
+
assert gpio_heater is not None
|
|
112
|
+
assert len(gpio_heater.timestamps) == 1
|
|
113
|
+
assert len(gpio_heater.time_column()["time"]) == 1
|
|
114
|
+
assert len(gpio_heater.time_column("custom_column_name")["custom_column_name"]) == 1
|
|
115
|
+
assert len(gpio_heater.value_column()["gpio.heater"]) == 1
|
|
116
|
+
assert len(gpio_heater.value_column("heater")["heater"]) == 1
|
|
117
|
+
|
|
118
|
+
pressure = result.channel("valve.pressure")
|
|
119
|
+
assert pressure is not None
|
|
120
|
+
assert len(pressure.timestamps) == 2
|
|
121
|
+
assert len(pressure.time_column()["time"]) == 2
|
|
122
|
+
assert len(pressure.time_column("custom_column_name")["custom_column_name"]) == 2
|
|
123
|
+
assert len(pressure.value_column()["valve.pressure"]) == 2
|
|
124
|
+
assert len(pressure.value_column("custom_column_name")["custom_column_name"]) == 2
|
|
125
|
+
|
|
126
|
+
all_columns = pressure.columns()
|
|
127
|
+
assert len(all_columns) == 2
|
|
128
|
+
assert len(all_columns["time"]) == 2
|
|
129
|
+
assert len(all_columns["valve.pressure"]) == 2
|
|
130
|
+
|
|
131
|
+
all_columns_custom = pressure.columns(
|
|
132
|
+
time_column_name="ts",
|
|
133
|
+
value_column_name="valve.pressure",
|
|
134
|
+
)
|
|
135
|
+
assert len(all_columns_custom) == 2
|
|
136
|
+
assert len(all_columns_custom["ts"]) == 2
|
|
137
|
+
assert len(all_columns_custom["valve.pressure"]) == 2
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@contextmanager
|
|
141
|
+
def patch_grpc_calls_channels(mocker: MockFixture) -> Iterator[Dict[str, MockType]]:
|
|
142
|
+
mock__get_asset_by_name = mocker.patch.object(DataService, "_get_asset_by_name")
|
|
143
|
+
mock__get_asset_by_name.return_value = Asset(
|
|
144
|
+
asset_id="b7955799-9893-4acf-bf14-50052284020c", name="NostromoLV428"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
mock__get_channels_by_asset_id = mocker.patch.object(DataService, "_get_channels_by_asset_id")
|
|
148
|
+
mock__get_channels_by_asset_id.return_value = [
|
|
149
|
+
Channel(
|
|
150
|
+
channel_id="e8662647-12f7-465f-85dc-cb02513944e0",
|
|
151
|
+
name="velocity",
|
|
152
|
+
component="mainmotor",
|
|
153
|
+
data_type=CHANNEL_DATA_TYPE_DOUBLE,
|
|
154
|
+
),
|
|
155
|
+
Channel(
|
|
156
|
+
channel_id="97e25141-ed3e-4538-b063-c3eac30838ce",
|
|
157
|
+
name="gpio",
|
|
158
|
+
data_type=CHANNEL_DATA_TYPE_BIT_FIELD,
|
|
159
|
+
),
|
|
160
|
+
Channel(
|
|
161
|
+
channel_id="87e25141-ed3e-4538-b063-c3eac30838cd",
|
|
162
|
+
name="valve.pressure",
|
|
163
|
+
data_type=CHANNEL_DATA_TYPE_DOUBLE,
|
|
164
|
+
),
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
mock__get_runs_by_names = mocker.patch.object(DataService, "_get_runs_by_names")
|
|
168
|
+
mock__get_runs_by_names.return_value = [
|
|
169
|
+
Run(
|
|
170
|
+
run_id="9b7f6c5f-cabc-4481-b048-6f12fc6b5b68",
|
|
171
|
+
name="[NostromoLV426].1720141748.047512",
|
|
172
|
+
)
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
time_a = "2024-07-04T18:09:08.555-07:00"
|
|
176
|
+
time_b = "2024-07-04T18:09:09.555-07:00"
|
|
177
|
+
|
|
178
|
+
velocity_values = DoubleValues(
|
|
179
|
+
metadata=Metadata(
|
|
180
|
+
data_type=CHANNEL_DATA_TYPE_DOUBLE,
|
|
181
|
+
channel=Metadata.Channel(name="velocity", component="mainmotor"),
|
|
182
|
+
),
|
|
183
|
+
values=[
|
|
184
|
+
DoubleValue(
|
|
185
|
+
timestamp=to_timestamp_pb(time_a),
|
|
186
|
+
value=10,
|
|
187
|
+
),
|
|
188
|
+
DoubleValue(
|
|
189
|
+
timestamp=to_timestamp_pb(time_b),
|
|
190
|
+
value=11,
|
|
191
|
+
),
|
|
192
|
+
],
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
raw_velocity_values = Any()
|
|
196
|
+
raw_velocity_values.Pack(velocity_values)
|
|
197
|
+
|
|
198
|
+
time_a = "2024-07-04T18:09:08.555-07:00"
|
|
199
|
+
time_b = "2024-07-04T18:09:09.555-07:00"
|
|
200
|
+
|
|
201
|
+
pressure_values = DoubleValues(
|
|
202
|
+
metadata=Metadata(
|
|
203
|
+
data_type=CHANNEL_DATA_TYPE_DOUBLE,
|
|
204
|
+
channel=Metadata.Channel(name="valve.pressure"),
|
|
205
|
+
),
|
|
206
|
+
values=[
|
|
207
|
+
DoubleValue(
|
|
208
|
+
timestamp=to_timestamp_pb(time_a),
|
|
209
|
+
value=10,
|
|
210
|
+
),
|
|
211
|
+
DoubleValue(
|
|
212
|
+
timestamp=to_timestamp_pb(time_b),
|
|
213
|
+
value=11,
|
|
214
|
+
),
|
|
215
|
+
],
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
raw_pressure_values = Any()
|
|
219
|
+
raw_pressure_values.Pack(pressure_values)
|
|
220
|
+
|
|
221
|
+
bit_field_values = BitFieldValues(
|
|
222
|
+
metadata=Metadata(
|
|
223
|
+
data_type=CHANNEL_DATA_TYPE_BIT_FIELD,
|
|
224
|
+
channel=Metadata.Channel(
|
|
225
|
+
name="gpio",
|
|
226
|
+
bit_field_elements=[
|
|
227
|
+
ChannelBitFieldElement(
|
|
228
|
+
name="12v",
|
|
229
|
+
index=0,
|
|
230
|
+
bit_count=4,
|
|
231
|
+
),
|
|
232
|
+
ChannelBitFieldElement(
|
|
233
|
+
name="heater",
|
|
234
|
+
index=4,
|
|
235
|
+
bit_count=4,
|
|
236
|
+
),
|
|
237
|
+
],
|
|
238
|
+
),
|
|
239
|
+
),
|
|
240
|
+
values=[
|
|
241
|
+
BitFieldElementValues(
|
|
242
|
+
name="12v",
|
|
243
|
+
values=[
|
|
244
|
+
BitFieldValue(
|
|
245
|
+
timestamp=to_timestamp_pb(time_a),
|
|
246
|
+
value=int("10000001", 2),
|
|
247
|
+
)
|
|
248
|
+
],
|
|
249
|
+
),
|
|
250
|
+
BitFieldElementValues(
|
|
251
|
+
name="heater",
|
|
252
|
+
values=[
|
|
253
|
+
BitFieldValue(
|
|
254
|
+
timestamp=to_timestamp_pb(time_a),
|
|
255
|
+
value=int("11110001", 2),
|
|
256
|
+
)
|
|
257
|
+
],
|
|
258
|
+
),
|
|
259
|
+
],
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
raw_bit_field_values = Any()
|
|
263
|
+
raw_bit_field_values.Pack(bit_field_values)
|
|
264
|
+
|
|
265
|
+
mock__get_data = mocker.patch.object(DataService, "_get_data")
|
|
266
|
+
mock__get_data.side_effect = [
|
|
267
|
+
[[raw_velocity_values]],
|
|
268
|
+
[[raw_bit_field_values]],
|
|
269
|
+
[[raw_pressure_values]],
|
|
270
|
+
]
|
|
271
|
+
yield {
|
|
272
|
+
"mock_get_asset_by_name": mock__get_asset_by_name,
|
|
273
|
+
"mock_get_runs_by_names": mock__get_runs_by_names,
|
|
274
|
+
"mock_get_channels_by_asset_id": mock__get_channels_by_asset_id,
|
|
275
|
+
"mock_get_data": mock__get_data,
|
|
276
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
CHANNEL_REFERENCE_REGEX = re.compile(r"^\$\d+$")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def validate_channel_reference(ref: str):
|
|
7
|
+
if CHANNEL_REFERENCE_REGEX.match(ref) is None:
|
|
8
|
+
raise ValueError(
|
|
9
|
+
f"Invalid channel reference key '{ref}'. Expected an integer prefixed with '$' e.g. '$1', '$2', and so on."
|
|
10
|
+
)
|