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,189 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from types import TracebackType
|
|
4
|
+
from typing import Callable, Generic, List, Optional, Type, TypeVar
|
|
5
|
+
|
|
6
|
+
from sift.ingest.v1.ingest_pb2 import IngestWithConfigDataStreamRequest
|
|
7
|
+
from typing_extensions import Self, TypeAlias
|
|
8
|
+
|
|
9
|
+
from sift_py.ingestion._internal.ingest import _IngestionServiceImpl
|
|
10
|
+
from sift_py.ingestion.flow import Flow, FlowOrderedChannelValues
|
|
11
|
+
|
|
12
|
+
DEFAULT_BUFFER_SIZE = 1_000
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T", bound=_IngestionServiceImpl)
|
|
15
|
+
|
|
16
|
+
FlushCallback: TypeAlias = Callable[[], None]
|
|
17
|
+
OnErrorCallback: TypeAlias = Callable[
|
|
18
|
+
[BaseException, List[IngestWithConfigDataStreamRequest], FlushCallback], None
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BufferedIngestionService(Generic[T]):
|
|
23
|
+
"""
|
|
24
|
+
See `sift_py.ingestion.service.IngestionService.buffered_ingestion`
|
|
25
|
+
for more information and how to leverage buffered ingestion.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
_buffer: List[IngestWithConfigDataStreamRequest]
|
|
29
|
+
_buffer_size: int
|
|
30
|
+
_ingestion_service: T
|
|
31
|
+
_flush_interval_sec: Optional[float]
|
|
32
|
+
_flush_timer: Optional[threading.Timer]
|
|
33
|
+
_lock: Optional[threading.Lock]
|
|
34
|
+
_on_error: Optional[OnErrorCallback]
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
ingestion_service: T,
|
|
39
|
+
buffer_size: Optional[int],
|
|
40
|
+
flush_interval_sec: Optional[float],
|
|
41
|
+
on_error: Optional[OnErrorCallback],
|
|
42
|
+
):
|
|
43
|
+
self._buffer = []
|
|
44
|
+
self._buffer_size = buffer_size or DEFAULT_BUFFER_SIZE
|
|
45
|
+
self._ingestion_service = ingestion_service
|
|
46
|
+
self._on_error = on_error
|
|
47
|
+
self._flush_timer = None
|
|
48
|
+
|
|
49
|
+
if flush_interval_sec:
|
|
50
|
+
self._flush_interval_sec = flush_interval_sec
|
|
51
|
+
self._lock = threading.Lock()
|
|
52
|
+
self._start_flush_timer()
|
|
53
|
+
else:
|
|
54
|
+
self._flush_interval_sec = None
|
|
55
|
+
self._lock = None
|
|
56
|
+
|
|
57
|
+
def __enter__(self) -> Self:
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
def __exit__(
|
|
61
|
+
self,
|
|
62
|
+
exc_type: Optional[Type[BaseException]],
|
|
63
|
+
exc_val: Optional[BaseException],
|
|
64
|
+
exc_tb: Optional[TracebackType],
|
|
65
|
+
) -> bool:
|
|
66
|
+
self._cancel_flush_timer()
|
|
67
|
+
|
|
68
|
+
if exc_val is not None:
|
|
69
|
+
if self._on_error is not None:
|
|
70
|
+
self._on_error(exc_val, self._buffer, self.flush)
|
|
71
|
+
else:
|
|
72
|
+
self.flush()
|
|
73
|
+
|
|
74
|
+
raise exc_val
|
|
75
|
+
else:
|
|
76
|
+
self.flush()
|
|
77
|
+
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
def ingest_flows(self, *flows: FlowOrderedChannelValues):
|
|
81
|
+
"""
|
|
82
|
+
Ingests flows in batches for each request generated from a flow.
|
|
83
|
+
See `sift_py.ingestion.service.IngestionService.create_ingestion_request`
|
|
84
|
+
for more information.
|
|
85
|
+
"""
|
|
86
|
+
with self._use_lock():
|
|
87
|
+
lhs_cursor = 0
|
|
88
|
+
rhs_cursor = min(
|
|
89
|
+
self._buffer_size - len(self._buffer),
|
|
90
|
+
len(flows),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
while lhs_cursor < len(flows):
|
|
94
|
+
for flow in flows[lhs_cursor:rhs_cursor]:
|
|
95
|
+
flow_name = flow["flow_name"]
|
|
96
|
+
timestamp = flow["timestamp"]
|
|
97
|
+
channel_values = flow["channel_values"]
|
|
98
|
+
|
|
99
|
+
req = self._ingestion_service.create_ingestion_request(
|
|
100
|
+
flow_name=flow_name,
|
|
101
|
+
timestamp=timestamp,
|
|
102
|
+
channel_values=channel_values,
|
|
103
|
+
)
|
|
104
|
+
self._buffer.append(req)
|
|
105
|
+
|
|
106
|
+
if len(self._buffer) >= self._buffer_size:
|
|
107
|
+
self._flush()
|
|
108
|
+
|
|
109
|
+
lhs_cursor = rhs_cursor
|
|
110
|
+
rhs_cursor = min(
|
|
111
|
+
rhs_cursor + (self._buffer_size - len(self._buffer)),
|
|
112
|
+
len(flows),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def try_ingest_flows(self, *flows: Flow):
|
|
116
|
+
"""
|
|
117
|
+
Ingests flows in batches and performs client-side validations for each request
|
|
118
|
+
generated from a flow. See `sift_py.ingestion.service.IngestionService.try_create_ingestion_request`
|
|
119
|
+
for more information.
|
|
120
|
+
"""
|
|
121
|
+
with self._use_lock():
|
|
122
|
+
lhs_cursor = 0
|
|
123
|
+
rhs_cursor = min(
|
|
124
|
+
self._buffer_size - len(self._buffer),
|
|
125
|
+
len(flows),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
while lhs_cursor < len(flows):
|
|
129
|
+
for flow in flows[lhs_cursor:rhs_cursor]:
|
|
130
|
+
flow_name = flow["flow_name"]
|
|
131
|
+
timestamp = flow["timestamp"]
|
|
132
|
+
channel_values = flow["channel_values"]
|
|
133
|
+
|
|
134
|
+
req = self._ingestion_service.try_create_ingestion_request(
|
|
135
|
+
flow_name=flow_name,
|
|
136
|
+
timestamp=timestamp,
|
|
137
|
+
channel_values=channel_values,
|
|
138
|
+
)
|
|
139
|
+
self._buffer.append(req)
|
|
140
|
+
|
|
141
|
+
if len(self._buffer) >= self._buffer_size:
|
|
142
|
+
self._flush()
|
|
143
|
+
|
|
144
|
+
lhs_cursor = rhs_cursor
|
|
145
|
+
rhs_cursor = min(
|
|
146
|
+
rhs_cursor + (self._buffer_size - len(self._buffer)),
|
|
147
|
+
len(flows),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def flush(self):
|
|
151
|
+
"""
|
|
152
|
+
Flush and ingest all requests in buffer.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
if self._flush_timer and self._lock:
|
|
156
|
+
with self._lock:
|
|
157
|
+
self._flush()
|
|
158
|
+
self._restart_flush_timer()
|
|
159
|
+
else:
|
|
160
|
+
self._flush()
|
|
161
|
+
|
|
162
|
+
def _flush(self):
|
|
163
|
+
if len(self._buffer) > 0:
|
|
164
|
+
self._ingestion_service.ingest(*self._buffer)
|
|
165
|
+
self._buffer.clear()
|
|
166
|
+
|
|
167
|
+
def _start_flush_timer(self):
|
|
168
|
+
if self._flush_interval_sec:
|
|
169
|
+
self._flush_timer = threading.Timer(self._flush_interval_sec, self.flush)
|
|
170
|
+
self._flush_timer.start()
|
|
171
|
+
|
|
172
|
+
def _cancel_flush_timer(self):
|
|
173
|
+
if self._flush_timer:
|
|
174
|
+
self._flush_timer.cancel()
|
|
175
|
+
self._flush_timer = None
|
|
176
|
+
|
|
177
|
+
def _restart_flush_timer(self):
|
|
178
|
+
self._cancel_flush_timer()
|
|
179
|
+
self._start_flush_timer()
|
|
180
|
+
|
|
181
|
+
@contextmanager
|
|
182
|
+
def _use_lock(self):
|
|
183
|
+
try:
|
|
184
|
+
if self._lock:
|
|
185
|
+
self._lock.acquire()
|
|
186
|
+
yield
|
|
187
|
+
finally:
|
|
188
|
+
if self._lock:
|
|
189
|
+
self._lock.release()
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import List, Optional, Type, TypedDict, Union
|
|
5
|
+
|
|
6
|
+
import sift.common.type.v1.channel_data_type_pb2 as channel_pb
|
|
7
|
+
from google.protobuf.empty_pb2 import Empty
|
|
8
|
+
from sift.channels.v2.channels_pb2 import Channel as ChannelPb
|
|
9
|
+
from sift.common.type.v1.channel_bit_field_element_pb2 import (
|
|
10
|
+
ChannelBitFieldElement as ChannelBitFieldElementPb,
|
|
11
|
+
)
|
|
12
|
+
from sift.common.type.v1.channel_enum_type_pb2 import (
|
|
13
|
+
ChannelEnumType as ChannelEnumTypePb,
|
|
14
|
+
)
|
|
15
|
+
from sift.ingest.v1.ingest_pb2 import IngestWithConfigDataChannelValue
|
|
16
|
+
from sift.ingestion_configs.v1.ingestion_configs_pb2 import ChannelConfig as ChannelConfigPb
|
|
17
|
+
from typing_extensions import NotRequired, Self
|
|
18
|
+
|
|
19
|
+
from sift_py._internal.channel import channel_fqn as _channel_fqn
|
|
20
|
+
from sift_py._internal.convert.protobuf import AsProtobuf
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ChannelValue(TypedDict):
|
|
24
|
+
"""
|
|
25
|
+
Represents a fully qualified data point for a channel
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
channel_name: str
|
|
29
|
+
component: NotRequired[str]
|
|
30
|
+
value: IngestWithConfigDataChannelValue
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ChannelConfig(AsProtobuf):
|
|
34
|
+
"""
|
|
35
|
+
A description for a channel
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
name: str
|
|
39
|
+
data_type: ChannelDataType
|
|
40
|
+
description: Optional[str]
|
|
41
|
+
unit: Optional[str]
|
|
42
|
+
component: Optional[str]
|
|
43
|
+
bit_field_elements: List[ChannelBitFieldElement]
|
|
44
|
+
enum_types: List[ChannelEnumType]
|
|
45
|
+
identifier: str
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
name: str,
|
|
50
|
+
data_type: ChannelDataType,
|
|
51
|
+
description: Optional[str] = None,
|
|
52
|
+
unit: Optional[str] = None,
|
|
53
|
+
component: Optional[str] = None,
|
|
54
|
+
bit_field_elements: List[ChannelBitFieldElement] = [],
|
|
55
|
+
enum_types: List[ChannelEnumType] = [],
|
|
56
|
+
):
|
|
57
|
+
self.name = name
|
|
58
|
+
self.data_type = data_type
|
|
59
|
+
self.description = description
|
|
60
|
+
self.unit = unit
|
|
61
|
+
self.component = component
|
|
62
|
+
self.bit_field_elements = bit_field_elements
|
|
63
|
+
self.enum_types = enum_types
|
|
64
|
+
self.identifier = self.fqn()
|
|
65
|
+
|
|
66
|
+
def value_from(
|
|
67
|
+
self, value: Optional[Union[int, float, bool, str]]
|
|
68
|
+
) -> Optional[IngestWithConfigDataChannelValue]:
|
|
69
|
+
"""
|
|
70
|
+
Like `try_value_from` except will return `None` there is a failure to produce a channel value due to a type mismatch.
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
return self.try_value_from(value)
|
|
74
|
+
except ValueError:
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
def try_value_from(
|
|
78
|
+
self, value: Optional[Union[int, float, bool, str]]
|
|
79
|
+
) -> IngestWithConfigDataChannelValue:
|
|
80
|
+
"""
|
|
81
|
+
Generate a channel value for this particular channel configuration. This will raise an exception
|
|
82
|
+
if there is a type match, namely, if `value` isn't consistent with the channel's data-type. For a version
|
|
83
|
+
of this function that does not raise an exception and simply ignores type mistmatches, see `value_from`. If `value`
|
|
84
|
+
is `None` then an empty value will be generated.
|
|
85
|
+
"""
|
|
86
|
+
if value is None:
|
|
87
|
+
return empty_value()
|
|
88
|
+
|
|
89
|
+
if isinstance(value, int) or isinstance(value, float):
|
|
90
|
+
if self.data_type == ChannelDataType.INT_32:
|
|
91
|
+
return int32_value(int(value))
|
|
92
|
+
elif self.data_type == ChannelDataType.INT_64:
|
|
93
|
+
return int64_value(int(value))
|
|
94
|
+
elif self.data_type == ChannelDataType.UINT_32:
|
|
95
|
+
return uint32_value(int(value))
|
|
96
|
+
elif self.data_type == ChannelDataType.UINT_64:
|
|
97
|
+
return uint64_value(int(value))
|
|
98
|
+
elif self.data_type == ChannelDataType.FLOAT:
|
|
99
|
+
return float_value(float(value))
|
|
100
|
+
elif self.data_type == ChannelDataType.DOUBLE:
|
|
101
|
+
return double_value(float(value))
|
|
102
|
+
elif self.data_type == ChannelDataType.ENUM:
|
|
103
|
+
return enum_value(int(value))
|
|
104
|
+
elif isinstance(value, str) and self.data_type == ChannelDataType.STRING:
|
|
105
|
+
return string_value(value)
|
|
106
|
+
elif isinstance(value, bool) and self.data_type == ChannelDataType.BOOL:
|
|
107
|
+
return bool_value(value)
|
|
108
|
+
|
|
109
|
+
raise ValueError(f"Failed to cast value of type {type(value)} to {self.data_type}")
|
|
110
|
+
|
|
111
|
+
def as_pb(self, klass: Type[ChannelConfigPb]) -> ChannelConfigPb:
|
|
112
|
+
return klass(
|
|
113
|
+
name=self.name,
|
|
114
|
+
component=self.component or "",
|
|
115
|
+
unit=self.unit or "",
|
|
116
|
+
description=self.description or "",
|
|
117
|
+
data_type=self.data_type.value,
|
|
118
|
+
enum_types=[etype.as_pb(ChannelEnumTypePb) for etype in self.enum_types],
|
|
119
|
+
bit_field_elements=[
|
|
120
|
+
el.as_pb(ChannelBitFieldElementPb) for el in self.bit_field_elements
|
|
121
|
+
],
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def from_pb(cls, message: ChannelConfigPb) -> Self:
|
|
126
|
+
return cls(
|
|
127
|
+
name=message.name,
|
|
128
|
+
data_type=ChannelDataType.from_pb(message.data_type),
|
|
129
|
+
description=message.description,
|
|
130
|
+
unit=message.unit,
|
|
131
|
+
component=message.component,
|
|
132
|
+
bit_field_elements=[
|
|
133
|
+
ChannelBitFieldElement.from_pb(el) for el in message.bit_field_elements
|
|
134
|
+
],
|
|
135
|
+
enum_types=[ChannelEnumType.from_pb(etype) for etype in message.enum_types],
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def fqn(self) -> str:
|
|
139
|
+
"""
|
|
140
|
+
The fully-qualified channel name of a channel called 'voltage' is simply `voltage`. The
|
|
141
|
+
fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'.
|
|
142
|
+
"""
|
|
143
|
+
return channel_fqn(self)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class ChannelBitFieldElement(AsProtobuf):
|
|
147
|
+
name: str
|
|
148
|
+
index: int
|
|
149
|
+
bit_count: int
|
|
150
|
+
|
|
151
|
+
def __init__(self, name: str, index: int, bit_count: int):
|
|
152
|
+
self.name = name
|
|
153
|
+
self.index = index
|
|
154
|
+
self.bit_count = bit_count
|
|
155
|
+
|
|
156
|
+
def as_pb(self, klass: Type[ChannelBitFieldElementPb]) -> ChannelBitFieldElementPb:
|
|
157
|
+
return klass(
|
|
158
|
+
name=self.name,
|
|
159
|
+
index=self.index,
|
|
160
|
+
bit_count=self.bit_count,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def from_pb(cls, message: ChannelBitFieldElementPb) -> Self:
|
|
165
|
+
return cls(
|
|
166
|
+
name=message.name,
|
|
167
|
+
index=message.index,
|
|
168
|
+
bit_count=message.bit_count,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ChannelEnumType(AsProtobuf):
|
|
173
|
+
name: str
|
|
174
|
+
key: int
|
|
175
|
+
|
|
176
|
+
def __init__(self, name: str, key: int):
|
|
177
|
+
self.name = name
|
|
178
|
+
self.key = key
|
|
179
|
+
|
|
180
|
+
def as_pb(self, klass: Type[ChannelEnumTypePb]) -> ChannelEnumTypePb:
|
|
181
|
+
return klass(name=self.name, key=self.key)
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def from_pb(cls, message: ChannelEnumTypePb) -> Self:
|
|
185
|
+
return cls(name=message.name, key=message.key)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class ChannelDataTypeStrRep(Enum):
|
|
189
|
+
DOUBLE = "double"
|
|
190
|
+
STRING = "string"
|
|
191
|
+
ENUM = "enum"
|
|
192
|
+
BIT_FIELD = "bit_field"
|
|
193
|
+
BOOL = "bool"
|
|
194
|
+
FLOAT = "float"
|
|
195
|
+
INT_32 = "int32"
|
|
196
|
+
INT_64 = "int64"
|
|
197
|
+
UINT_32 = "uint32"
|
|
198
|
+
UINT_64 = "uint64"
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def from_api_format(val: str) -> Optional["ChannelDataTypeStrRep"]:
|
|
202
|
+
try:
|
|
203
|
+
return {
|
|
204
|
+
"CHANNEL_DATA_TYPE_DOUBLE": ChannelDataTypeStrRep.DOUBLE,
|
|
205
|
+
"CHANNEL_DATA_TYPE_STRING": ChannelDataTypeStrRep.STRING,
|
|
206
|
+
"CHANNEL_DATA_TYPE_ENUM": ChannelDataTypeStrRep.ENUM,
|
|
207
|
+
"CHANNEL_DATA_TYPE_BIT_FIELD": ChannelDataTypeStrRep.BIT_FIELD,
|
|
208
|
+
"CHANNEL_DATA_TYPE_BOOL": ChannelDataTypeStrRep.BOOL,
|
|
209
|
+
"CHANNEL_DATA_TYPE_FLOAT": ChannelDataTypeStrRep.FLOAT,
|
|
210
|
+
"CHANNEL_DATA_TYPE_INT_32": ChannelDataTypeStrRep.INT_32,
|
|
211
|
+
"CHANNEL_DATA_TYPE_INT_64": ChannelDataTypeStrRep.INT_64,
|
|
212
|
+
"CHANNEL_DATA_TYPE_UINT_32": ChannelDataTypeStrRep.UINT_32,
|
|
213
|
+
"CHANNEL_DATA_TYPE_UINT_64": ChannelDataTypeStrRep.UINT_64,
|
|
214
|
+
}[val]
|
|
215
|
+
except KeyError:
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class ChannelDataType(Enum):
|
|
220
|
+
"""
|
|
221
|
+
Utility enum class to simplify working with channel data-types generated from protobuf
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
DOUBLE = channel_pb.CHANNEL_DATA_TYPE_DOUBLE
|
|
225
|
+
STRING = channel_pb.CHANNEL_DATA_TYPE_STRING
|
|
226
|
+
ENUM = channel_pb.CHANNEL_DATA_TYPE_ENUM
|
|
227
|
+
BIT_FIELD = channel_pb.CHANNEL_DATA_TYPE_BIT_FIELD
|
|
228
|
+
BOOL = channel_pb.CHANNEL_DATA_TYPE_BOOL
|
|
229
|
+
FLOAT = channel_pb.CHANNEL_DATA_TYPE_FLOAT
|
|
230
|
+
INT_32 = channel_pb.CHANNEL_DATA_TYPE_INT_32
|
|
231
|
+
INT_64 = channel_pb.CHANNEL_DATA_TYPE_INT_64
|
|
232
|
+
UINT_32 = channel_pb.CHANNEL_DATA_TYPE_UINT_32
|
|
233
|
+
UINT_64 = channel_pb.CHANNEL_DATA_TYPE_UINT_64
|
|
234
|
+
|
|
235
|
+
@classmethod
|
|
236
|
+
def from_pb(cls, val: channel_pb.ChannelDataType.ValueType) -> "ChannelDataType":
|
|
237
|
+
if val == cls.DOUBLE.value:
|
|
238
|
+
return cls.DOUBLE
|
|
239
|
+
elif val == cls.STRING.value:
|
|
240
|
+
return cls.STRING
|
|
241
|
+
elif val == cls.ENUM.value:
|
|
242
|
+
return cls.ENUM
|
|
243
|
+
elif val == cls.BIT_FIELD.value:
|
|
244
|
+
return cls.BIT_FIELD
|
|
245
|
+
elif val == cls.BOOL.value:
|
|
246
|
+
return cls.BOOL
|
|
247
|
+
elif val == cls.FLOAT.value:
|
|
248
|
+
return cls.FLOAT
|
|
249
|
+
elif val == cls.INT_32.value:
|
|
250
|
+
return cls.INT_32
|
|
251
|
+
elif val == cls.INT_64.value:
|
|
252
|
+
return cls.INT_64
|
|
253
|
+
elif val == cls.UINT_32.value:
|
|
254
|
+
return cls.UINT_32
|
|
255
|
+
elif val == cls.UINT_64.value:
|
|
256
|
+
return cls.UINT_64
|
|
257
|
+
else:
|
|
258
|
+
raise ValueError(f"Unknown channel data type '{val}'.")
|
|
259
|
+
|
|
260
|
+
@classmethod
|
|
261
|
+
def from_str(cls, raw: str) -> Optional["ChannelDataType"]:
|
|
262
|
+
if raw.startswith("CHANNEL_DATA_TYPE_"):
|
|
263
|
+
val = ChannelDataTypeStrRep.from_api_format(raw)
|
|
264
|
+
if val is None:
|
|
265
|
+
return None
|
|
266
|
+
else:
|
|
267
|
+
try:
|
|
268
|
+
val = ChannelDataTypeStrRep(raw)
|
|
269
|
+
except ValueError:
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
if val == ChannelDataTypeStrRep.DOUBLE:
|
|
273
|
+
return cls.DOUBLE
|
|
274
|
+
elif val == ChannelDataTypeStrRep.STRING:
|
|
275
|
+
return cls.STRING
|
|
276
|
+
elif val == ChannelDataTypeStrRep.ENUM:
|
|
277
|
+
return cls.ENUM
|
|
278
|
+
elif val == ChannelDataTypeStrRep.BIT_FIELD:
|
|
279
|
+
return cls.BIT_FIELD
|
|
280
|
+
elif val == ChannelDataTypeStrRep.BOOL:
|
|
281
|
+
return cls.BOOL
|
|
282
|
+
elif val == ChannelDataTypeStrRep.FLOAT:
|
|
283
|
+
return cls.FLOAT
|
|
284
|
+
elif val == ChannelDataTypeStrRep.INT_32:
|
|
285
|
+
return cls.INT_32
|
|
286
|
+
elif val == ChannelDataTypeStrRep.INT_64:
|
|
287
|
+
return cls.INT_64
|
|
288
|
+
elif val == ChannelDataTypeStrRep.UINT_32:
|
|
289
|
+
return cls.UINT_32
|
|
290
|
+
elif val == ChannelDataTypeStrRep.UINT_64:
|
|
291
|
+
return cls.UINT_64
|
|
292
|
+
else:
|
|
293
|
+
raise Exception("Unreachable")
|
|
294
|
+
|
|
295
|
+
def as_human_str(self, api_format: bool = False) -> str:
|
|
296
|
+
if self == ChannelDataType.DOUBLE:
|
|
297
|
+
return "CHANNEL_DATA_TYPE_DOUBLE" if api_format else ChannelDataTypeStrRep.DOUBLE.value
|
|
298
|
+
elif self == ChannelDataType.STRING:
|
|
299
|
+
return "CHANNEL_DATA_TYPE_STRING" if api_format else ChannelDataTypeStrRep.STRING.value
|
|
300
|
+
elif self == ChannelDataType.ENUM:
|
|
301
|
+
return "CHANNEL_DATA_TYPE_ENUM" if api_format else ChannelDataTypeStrRep.ENUM.value
|
|
302
|
+
elif self == ChannelDataType.BIT_FIELD:
|
|
303
|
+
return (
|
|
304
|
+
"CHANNEL_DATA_TYPE_BIT_FIELD"
|
|
305
|
+
if api_format
|
|
306
|
+
else ChannelDataTypeStrRep.BIT_FIELD.value
|
|
307
|
+
)
|
|
308
|
+
elif self == ChannelDataType.BOOL:
|
|
309
|
+
return "CHANNEL_DATA_TYPE_BOOL" if api_format else ChannelDataTypeStrRep.BOOL.value
|
|
310
|
+
elif self == ChannelDataType.FLOAT:
|
|
311
|
+
return "CHANNEL_DATA_TYPE_FLOAT" if api_format else ChannelDataTypeStrRep.FLOAT.value
|
|
312
|
+
elif self == ChannelDataType.INT_32:
|
|
313
|
+
return "CHANNEL_DATA_TYPE_INT_32" if api_format else ChannelDataTypeStrRep.INT_32.value
|
|
314
|
+
elif self == ChannelDataType.INT_64:
|
|
315
|
+
return "CHANNEL_DATA_TYPE_INT_64" if api_format else ChannelDataTypeStrRep.INT_64.value
|
|
316
|
+
elif self == ChannelDataType.UINT_32:
|
|
317
|
+
return (
|
|
318
|
+
"CHANNEL_DATA_TYPE_UINT_32" if api_format else ChannelDataTypeStrRep.UINT_32.value
|
|
319
|
+
)
|
|
320
|
+
elif self == ChannelDataType.UINT_64:
|
|
321
|
+
return (
|
|
322
|
+
"CHANNEL_DATA_TYPE_UINT_64" if api_format else ChannelDataTypeStrRep.UINT_64.value
|
|
323
|
+
)
|
|
324
|
+
else:
|
|
325
|
+
raise Exception("Unreachable.")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class _AbstractChannel(TypedDict):
|
|
329
|
+
channel_name: str
|
|
330
|
+
component: NotRequired[str]
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def channel_fqn(
|
|
334
|
+
channel: Union[ChannelConfig, ChannelConfigPb, ChannelValue, ChannelPb, _AbstractChannel],
|
|
335
|
+
) -> str:
|
|
336
|
+
"""
|
|
337
|
+
Computes the fully qualified channel name.
|
|
338
|
+
|
|
339
|
+
The fully-qualified channel name of a channel called 'voltage' is simply `voltage'. The
|
|
340
|
+
fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'.
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
if isinstance(channel, ChannelConfig):
|
|
344
|
+
return _channel_fqn(channel.name, channel.component)
|
|
345
|
+
elif isinstance(channel, ChannelConfigPb):
|
|
346
|
+
return _channel_fqn(channel.name, channel.component)
|
|
347
|
+
elif isinstance(channel, ChannelPb):
|
|
348
|
+
return _channel_fqn(channel.name, channel.component)
|
|
349
|
+
else:
|
|
350
|
+
component = channel.get("component", "")
|
|
351
|
+
channel_name = channel["channel_name"]
|
|
352
|
+
if len(component) == 0:
|
|
353
|
+
return channel_name
|
|
354
|
+
else:
|
|
355
|
+
return f"{component}.{channel_name}"
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def string_value(val: str) -> IngestWithConfigDataChannelValue:
|
|
359
|
+
return IngestWithConfigDataChannelValue(string=val)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def double_value(val: float) -> IngestWithConfigDataChannelValue:
|
|
363
|
+
return IngestWithConfigDataChannelValue(double=val)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def float_value(val: float) -> IngestWithConfigDataChannelValue:
|
|
367
|
+
return IngestWithConfigDataChannelValue(float=val)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def bool_value(val: bool) -> IngestWithConfigDataChannelValue:
|
|
371
|
+
return IngestWithConfigDataChannelValue(bool=val)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def int32_value(val: int) -> IngestWithConfigDataChannelValue:
|
|
375
|
+
return IngestWithConfigDataChannelValue(int32=val)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def uint32_value(val: int) -> IngestWithConfigDataChannelValue:
|
|
379
|
+
return IngestWithConfigDataChannelValue(uint32=val)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def int64_value(val: int) -> IngestWithConfigDataChannelValue:
|
|
383
|
+
return IngestWithConfigDataChannelValue(int64=val)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def uint64_value(val: int) -> IngestWithConfigDataChannelValue:
|
|
387
|
+
return IngestWithConfigDataChannelValue(uint64=val)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def bit_field_value(val: bytes) -> IngestWithConfigDataChannelValue:
|
|
391
|
+
return IngestWithConfigDataChannelValue(bit_field=val)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def enum_value(val: int) -> IngestWithConfigDataChannelValue:
|
|
395
|
+
return IngestWithConfigDataChannelValue(enum=val)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def empty_value() -> IngestWithConfigDataChannelValue:
|
|
399
|
+
return IngestWithConfigDataChannelValue(empty=Empty())
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def is_data_type(val: IngestWithConfigDataChannelValue, target_type: ChannelDataType) -> bool:
|
|
403
|
+
if target_type == ChannelDataType.DOUBLE:
|
|
404
|
+
return val.HasField("double")
|
|
405
|
+
elif target_type == ChannelDataType.STRING:
|
|
406
|
+
return val.HasField("string")
|
|
407
|
+
elif target_type == ChannelDataType.ENUM:
|
|
408
|
+
return val.HasField("enum")
|
|
409
|
+
elif target_type == ChannelDataType.BIT_FIELD:
|
|
410
|
+
return val.HasField("bit_field")
|
|
411
|
+
elif target_type == ChannelDataType.BOOL:
|
|
412
|
+
return val.HasField("bool")
|
|
413
|
+
elif target_type == ChannelDataType.FLOAT:
|
|
414
|
+
return val.HasField("float")
|
|
415
|
+
elif target_type == ChannelDataType.INT_32:
|
|
416
|
+
return val.HasField("int32")
|
|
417
|
+
elif target_type == ChannelDataType.INT_64:
|
|
418
|
+
return val.HasField("int64")
|
|
419
|
+
elif target_type == ChannelDataType.UINT_32:
|
|
420
|
+
return val.HasField("uint32")
|
|
421
|
+
elif target_type == ChannelDataType.UINT_64:
|
|
422
|
+
return val.HasField("uint64")
|