sift_py.ingestion.channel
1from __future__ import annotations 2 3from enum import Enum 4from typing import List, Optional, Type, TypedDict, Union 5 6import sift.common.type.v1.channel_data_type_pb2 as channel_pb 7from google.protobuf.empty_pb2 import Empty 8from sift.channels.v3.channels_pb2 import Channel as ChannelPb 9from sift.common.type.v1.channel_bit_field_element_pb2 import ( 10 ChannelBitFieldElement as ChannelBitFieldElementPb, 11) 12from sift.common.type.v1.channel_enum_type_pb2 import ( 13 ChannelEnumType as ChannelEnumTypePb, 14) 15from sift.ingest.v1.ingest_pb2 import IngestWithConfigDataChannelValue 16from sift.ingestion_configs.v2.ingestion_configs_pb2 import ChannelConfig as ChannelConfigPb 17from typing_extensions import NotRequired, Self 18 19from sift_py._internal.channel import channel_fqn as _channel_fqn 20from sift_py._internal.convert.protobuf import AsProtobuf 21from sift_py.error import _component_deprecation_warning 22 23 24class ChannelValue(TypedDict): 25 """ 26 Represents a fully qualified data point for a channel 27 """ 28 29 channel_name: str 30 component: NotRequired[str] # Deprecated 31 value: IngestWithConfigDataChannelValue 32 33 34class ChannelConfig(AsProtobuf): 35 """ 36 A description for a channel 37 """ 38 39 name: str 40 data_type: ChannelDataType 41 description: Optional[str] 42 unit: Optional[str] 43 component: Optional[str] # Deprecated 44 bit_field_elements: List[ChannelBitFieldElement] 45 enum_types: List[ChannelEnumType] 46 identifier: str 47 48 def __init__( 49 self, 50 name: str, 51 data_type: ChannelDataType, 52 description: Optional[str] = None, 53 unit: Optional[str] = None, 54 component: Optional[str] = None, # Deprecated 55 bit_field_elements: List[ChannelBitFieldElement] = [], 56 enum_types: List[ChannelEnumType] = [], 57 ): 58 self.name = name 59 self.data_type = data_type 60 self.description = description 61 self.unit = unit 62 63 self.component = None # Field kept for backwards compatibility 64 if component: 65 _component_deprecation_warning() 66 self.name = _channel_fqn(name=self.name, component=component) 67 self.component = None 68 69 self.bit_field_elements = bit_field_elements 70 self.enum_types = enum_types 71 self.identifier = self.fqn() 72 73 def value_from( 74 self, value: Optional[Union[int, float, bool, str]] 75 ) -> Optional[IngestWithConfigDataChannelValue]: 76 """ 77 Like `try_value_from` except will return `None` there is a failure to produce a channel value due to a type mismatch. 78 """ 79 try: 80 return self.try_value_from(value) 81 except ValueError: 82 return None 83 84 def try_value_from( 85 self, value: Optional[Union[int, float, bool, str]] 86 ) -> IngestWithConfigDataChannelValue: 87 """ 88 Generate a channel value for this particular channel configuration. This will raise an exception 89 if there is a type match, namely, if `value` isn't consistent with the channel's data-type. For a version 90 of this function that does not raise an exception and simply ignores type mistmatches, see `value_from`. If `value` 91 is `None` then an empty value will be generated. 92 """ 93 if value is None: 94 return empty_value() 95 96 if isinstance(value, bool) and self.data_type == ChannelDataType.BOOL: 97 return bool_value(value) 98 elif isinstance(value, int) or isinstance(value, float): 99 if self.data_type == ChannelDataType.INT_32: 100 return int32_value(int(value)) 101 elif self.data_type == ChannelDataType.INT_64: 102 return int64_value(int(value)) 103 elif self.data_type == ChannelDataType.UINT_32: 104 return uint32_value(int(value)) 105 elif self.data_type == ChannelDataType.UINT_64: 106 return uint64_value(int(value)) 107 elif self.data_type == ChannelDataType.FLOAT: 108 return float_value(float(value)) 109 elif self.data_type == ChannelDataType.DOUBLE: 110 return double_value(float(value)) 111 elif self.data_type == ChannelDataType.ENUM: 112 return enum_value(int(value)) 113 elif isinstance(value, str) and self.data_type == ChannelDataType.STRING: 114 return string_value(value) 115 116 raise ValueError(f"Failed to cast value of type {type(value)} to {self.data_type}") 117 118 def as_pb(self, klass: Type[ChannelConfigPb]) -> ChannelConfigPb: 119 return klass( 120 name=self.name, 121 unit=self.unit or "", 122 description=self.description or "", 123 data_type=self.data_type.value, 124 enum_types=[etype.as_pb(ChannelEnumTypePb) for etype in self.enum_types], 125 bit_field_elements=[ 126 el.as_pb(ChannelBitFieldElementPb) for el in self.bit_field_elements 127 ], 128 ) 129 130 @classmethod 131 def from_pb(cls, message: ChannelConfigPb) -> Self: 132 return cls( 133 name=message.name, 134 data_type=ChannelDataType.from_pb(message.data_type), 135 description=message.description, 136 unit=message.unit, 137 bit_field_elements=[ 138 ChannelBitFieldElement.from_pb(el) for el in message.bit_field_elements 139 ], 140 enum_types=[ChannelEnumType.from_pb(etype) for etype in message.enum_types], 141 ) 142 143 def fqn(self) -> str: 144 """ 145 NOTE: Component field of Channel has been deprecated. Function kept for backwards compatibility. 146 147 The fully-qualified channel name of a channel called 'voltage' is simply `voltage`. The 148 fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. 149 """ 150 return channel_fqn(self) 151 152 153class ChannelBitFieldElement(AsProtobuf): 154 name: str 155 index: int 156 bit_count: int 157 158 def __init__(self, name: str, index: int, bit_count: int): 159 self.name = name 160 self.index = index 161 self.bit_count = bit_count 162 163 def as_pb(self, klass: Type[ChannelBitFieldElementPb]) -> ChannelBitFieldElementPb: 164 return klass( 165 name=self.name, 166 index=self.index, 167 bit_count=self.bit_count, 168 ) 169 170 @classmethod 171 def from_pb(cls, message: ChannelBitFieldElementPb) -> Self: 172 return cls( 173 name=message.name, 174 index=message.index, 175 bit_count=message.bit_count, 176 ) 177 178 179class ChannelEnumType(AsProtobuf): 180 name: str 181 key: int 182 183 def __init__(self, name: str, key: int): 184 self.name = name 185 self.key = key 186 187 def as_pb(self, klass: Type[ChannelEnumTypePb]) -> ChannelEnumTypePb: 188 return klass(name=self.name, key=self.key) 189 190 @classmethod 191 def from_pb(cls, message: ChannelEnumTypePb) -> Self: 192 return cls(name=message.name, key=message.key) 193 194 195class ChannelDataTypeStrRep(Enum): 196 DOUBLE = "double" 197 STRING = "string" 198 ENUM = "enum" 199 BIT_FIELD = "bit_field" 200 BOOL = "bool" 201 FLOAT = "float" 202 INT_32 = "int32" 203 INT_64 = "int64" 204 UINT_32 = "uint32" 205 UINT_64 = "uint64" 206 207 @staticmethod 208 def from_api_format(val: str) -> Optional["ChannelDataTypeStrRep"]: 209 try: 210 return { 211 "CHANNEL_DATA_TYPE_DOUBLE": ChannelDataTypeStrRep.DOUBLE, 212 "CHANNEL_DATA_TYPE_STRING": ChannelDataTypeStrRep.STRING, 213 "CHANNEL_DATA_TYPE_ENUM": ChannelDataTypeStrRep.ENUM, 214 "CHANNEL_DATA_TYPE_BIT_FIELD": ChannelDataTypeStrRep.BIT_FIELD, 215 "CHANNEL_DATA_TYPE_BOOL": ChannelDataTypeStrRep.BOOL, 216 "CHANNEL_DATA_TYPE_FLOAT": ChannelDataTypeStrRep.FLOAT, 217 "CHANNEL_DATA_TYPE_INT_32": ChannelDataTypeStrRep.INT_32, 218 "CHANNEL_DATA_TYPE_INT_64": ChannelDataTypeStrRep.INT_64, 219 "CHANNEL_DATA_TYPE_UINT_32": ChannelDataTypeStrRep.UINT_32, 220 "CHANNEL_DATA_TYPE_UINT_64": ChannelDataTypeStrRep.UINT_64, 221 }[val] 222 except KeyError: 223 return None 224 225 226class ChannelDataType(Enum): 227 """ 228 Utility enum class to simplify working with channel data-types generated from protobuf 229 """ 230 231 DOUBLE = channel_pb.CHANNEL_DATA_TYPE_DOUBLE 232 STRING = channel_pb.CHANNEL_DATA_TYPE_STRING 233 ENUM = channel_pb.CHANNEL_DATA_TYPE_ENUM 234 BIT_FIELD = channel_pb.CHANNEL_DATA_TYPE_BIT_FIELD 235 BOOL = channel_pb.CHANNEL_DATA_TYPE_BOOL 236 FLOAT = channel_pb.CHANNEL_DATA_TYPE_FLOAT 237 INT_32 = channel_pb.CHANNEL_DATA_TYPE_INT_32 238 INT_64 = channel_pb.CHANNEL_DATA_TYPE_INT_64 239 UINT_32 = channel_pb.CHANNEL_DATA_TYPE_UINT_32 240 UINT_64 = channel_pb.CHANNEL_DATA_TYPE_UINT_64 241 242 @classmethod 243 def from_pb(cls, val: channel_pb.ChannelDataType.ValueType) -> "ChannelDataType": 244 if val == cls.DOUBLE.value: 245 return cls.DOUBLE 246 elif val == cls.STRING.value: 247 return cls.STRING 248 elif val == cls.ENUM.value: 249 return cls.ENUM 250 elif val == cls.BIT_FIELD.value: 251 return cls.BIT_FIELD 252 elif val == cls.BOOL.value: 253 return cls.BOOL 254 elif val == cls.FLOAT.value: 255 return cls.FLOAT 256 elif val == cls.INT_32.value: 257 return cls.INT_32 258 elif val == cls.INT_64.value: 259 return cls.INT_64 260 elif val == cls.UINT_32.value: 261 return cls.UINT_32 262 elif val == cls.UINT_64.value: 263 return cls.UINT_64 264 else: 265 raise ValueError(f"Unknown channel data type '{val}'.") 266 267 @classmethod 268 def from_str(cls, raw: str) -> Optional["ChannelDataType"]: 269 if raw.startswith("CHANNEL_DATA_TYPE_"): 270 val = ChannelDataTypeStrRep.from_api_format(raw) 271 if val is None: 272 return None 273 else: 274 try: 275 val = ChannelDataTypeStrRep(raw) 276 except ValueError: 277 return None 278 279 if val == ChannelDataTypeStrRep.DOUBLE: 280 return cls.DOUBLE 281 elif val == ChannelDataTypeStrRep.STRING: 282 return cls.STRING 283 elif val == ChannelDataTypeStrRep.ENUM: 284 return cls.ENUM 285 elif val == ChannelDataTypeStrRep.BIT_FIELD: 286 return cls.BIT_FIELD 287 elif val == ChannelDataTypeStrRep.BOOL: 288 return cls.BOOL 289 elif val == ChannelDataTypeStrRep.FLOAT: 290 return cls.FLOAT 291 elif val == ChannelDataTypeStrRep.INT_32: 292 return cls.INT_32 293 elif val == ChannelDataTypeStrRep.INT_64: 294 return cls.INT_64 295 elif val == ChannelDataTypeStrRep.UINT_32: 296 return cls.UINT_32 297 elif val == ChannelDataTypeStrRep.UINT_64: 298 return cls.UINT_64 299 else: 300 raise Exception("Unreachable") 301 302 def as_human_str(self, api_format: bool = False) -> str: 303 if self == ChannelDataType.DOUBLE: 304 return "CHANNEL_DATA_TYPE_DOUBLE" if api_format else ChannelDataTypeStrRep.DOUBLE.value 305 elif self == ChannelDataType.STRING: 306 return "CHANNEL_DATA_TYPE_STRING" if api_format else ChannelDataTypeStrRep.STRING.value 307 elif self == ChannelDataType.ENUM: 308 return "CHANNEL_DATA_TYPE_ENUM" if api_format else ChannelDataTypeStrRep.ENUM.value 309 elif self == ChannelDataType.BIT_FIELD: 310 return ( 311 "CHANNEL_DATA_TYPE_BIT_FIELD" 312 if api_format 313 else ChannelDataTypeStrRep.BIT_FIELD.value 314 ) 315 elif self == ChannelDataType.BOOL: 316 return "CHANNEL_DATA_TYPE_BOOL" if api_format else ChannelDataTypeStrRep.BOOL.value 317 elif self == ChannelDataType.FLOAT: 318 return "CHANNEL_DATA_TYPE_FLOAT" if api_format else ChannelDataTypeStrRep.FLOAT.value 319 elif self == ChannelDataType.INT_32: 320 return "CHANNEL_DATA_TYPE_INT_32" if api_format else ChannelDataTypeStrRep.INT_32.value 321 elif self == ChannelDataType.INT_64: 322 return "CHANNEL_DATA_TYPE_INT_64" if api_format else ChannelDataTypeStrRep.INT_64.value 323 elif self == ChannelDataType.UINT_32: 324 return ( 325 "CHANNEL_DATA_TYPE_UINT_32" if api_format else ChannelDataTypeStrRep.UINT_32.value 326 ) 327 elif self == ChannelDataType.UINT_64: 328 return ( 329 "CHANNEL_DATA_TYPE_UINT_64" if api_format else ChannelDataTypeStrRep.UINT_64.value 330 ) 331 else: 332 raise Exception("Unreachable.") 333 334 335class _AbstractChannel(TypedDict): 336 channel_name: str 337 component: Optional[str] # Deprecated 338 339 340def channel_fqn( 341 channel: Union[ 342 ChannelConfig, 343 ChannelConfigPb, 344 ChannelValue, 345 ChannelPb, 346 _AbstractChannel, 347 ], 348) -> str: 349 """ 350 Computes the fully qualified channel name. 351 352 NOTE: Component field of Channel is deprecated and should not be used. Function is left for code compatibility. 353 354 The fully-qualified channel name of a channel called 'voltage' is simply `voltage'. The 355 fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. 356 """ 357 358 if isinstance(channel, ChannelConfig): 359 if channel.component: 360 _component_deprecation_warning() 361 return _channel_fqn(channel.name, channel.component) 362 elif isinstance(channel, ChannelConfigPb): 363 return channel.name 364 elif isinstance(channel, ChannelPb): 365 return channel.name 366 else: 367 component = channel.get("component") 368 if component: 369 _component_deprecation_warning() 370 channel_name = channel["channel_name"] 371 return _channel_fqn(name=channel_name, component=component) 372 373 374def string_value(val: str) -> IngestWithConfigDataChannelValue: 375 return IngestWithConfigDataChannelValue(string=val) 376 377 378def double_value(val: float) -> IngestWithConfigDataChannelValue: 379 return IngestWithConfigDataChannelValue(double=val) 380 381 382def float_value(val: float) -> IngestWithConfigDataChannelValue: 383 return IngestWithConfigDataChannelValue(float=val) 384 385 386def bool_value(val: bool) -> IngestWithConfigDataChannelValue: 387 return IngestWithConfigDataChannelValue(bool=val) 388 389 390def int32_value(val: int) -> IngestWithConfigDataChannelValue: 391 return IngestWithConfigDataChannelValue(int32=val) 392 393 394def uint32_value(val: int) -> IngestWithConfigDataChannelValue: 395 return IngestWithConfigDataChannelValue(uint32=val) 396 397 398def int64_value(val: int) -> IngestWithConfigDataChannelValue: 399 return IngestWithConfigDataChannelValue(int64=val) 400 401 402def uint64_value(val: int) -> IngestWithConfigDataChannelValue: 403 return IngestWithConfigDataChannelValue(uint64=val) 404 405 406def bit_field_value(val: bytes) -> IngestWithConfigDataChannelValue: 407 return IngestWithConfigDataChannelValue(bit_field=val) 408 409 410def enum_value(val: int) -> IngestWithConfigDataChannelValue: 411 return IngestWithConfigDataChannelValue(enum=val) 412 413 414def empty_value() -> IngestWithConfigDataChannelValue: 415 return IngestWithConfigDataChannelValue(empty=Empty()) 416 417 418def is_data_type(val: IngestWithConfigDataChannelValue, target_type: ChannelDataType) -> bool: 419 if target_type == ChannelDataType.DOUBLE: 420 return val.HasField("double") 421 elif target_type == ChannelDataType.STRING: 422 return val.HasField("string") 423 elif target_type == ChannelDataType.ENUM: 424 return val.HasField("enum") 425 elif target_type == ChannelDataType.BIT_FIELD: 426 return val.HasField("bit_field") 427 elif target_type == ChannelDataType.BOOL: 428 return val.HasField("bool") 429 elif target_type == ChannelDataType.FLOAT: 430 return val.HasField("float") 431 elif target_type == ChannelDataType.INT_32: 432 return val.HasField("int32") 433 elif target_type == ChannelDataType.INT_64: 434 return val.HasField("int64") 435 elif target_type == ChannelDataType.UINT_32: 436 return val.HasField("uint32") 437 elif target_type == ChannelDataType.UINT_64: 438 return val.HasField("uint64")
25class ChannelValue(TypedDict): 26 """ 27 Represents a fully qualified data point for a channel 28 """ 29 30 channel_name: str 31 component: NotRequired[str] # Deprecated 32 value: IngestWithConfigDataChannelValue
Represents a fully qualified data point for a channel
Inherited Members
- builtins.dict
- get
- setdefault
- pop
- popitem
- keys
- items
- values
- update
- fromkeys
- clear
- copy
35class ChannelConfig(AsProtobuf): 36 """ 37 A description for a channel 38 """ 39 40 name: str 41 data_type: ChannelDataType 42 description: Optional[str] 43 unit: Optional[str] 44 component: Optional[str] # Deprecated 45 bit_field_elements: List[ChannelBitFieldElement] 46 enum_types: List[ChannelEnumType] 47 identifier: str 48 49 def __init__( 50 self, 51 name: str, 52 data_type: ChannelDataType, 53 description: Optional[str] = None, 54 unit: Optional[str] = None, 55 component: Optional[str] = None, # Deprecated 56 bit_field_elements: List[ChannelBitFieldElement] = [], 57 enum_types: List[ChannelEnumType] = [], 58 ): 59 self.name = name 60 self.data_type = data_type 61 self.description = description 62 self.unit = unit 63 64 self.component = None # Field kept for backwards compatibility 65 if component: 66 _component_deprecation_warning() 67 self.name = _channel_fqn(name=self.name, component=component) 68 self.component = None 69 70 self.bit_field_elements = bit_field_elements 71 self.enum_types = enum_types 72 self.identifier = self.fqn() 73 74 def value_from( 75 self, value: Optional[Union[int, float, bool, str]] 76 ) -> Optional[IngestWithConfigDataChannelValue]: 77 """ 78 Like `try_value_from` except will return `None` there is a failure to produce a channel value due to a type mismatch. 79 """ 80 try: 81 return self.try_value_from(value) 82 except ValueError: 83 return None 84 85 def try_value_from( 86 self, value: Optional[Union[int, float, bool, str]] 87 ) -> IngestWithConfigDataChannelValue: 88 """ 89 Generate a channel value for this particular channel configuration. This will raise an exception 90 if there is a type match, namely, if `value` isn't consistent with the channel's data-type. For a version 91 of this function that does not raise an exception and simply ignores type mistmatches, see `value_from`. If `value` 92 is `None` then an empty value will be generated. 93 """ 94 if value is None: 95 return empty_value() 96 97 if isinstance(value, bool) and self.data_type == ChannelDataType.BOOL: 98 return bool_value(value) 99 elif isinstance(value, int) or isinstance(value, float): 100 if self.data_type == ChannelDataType.INT_32: 101 return int32_value(int(value)) 102 elif self.data_type == ChannelDataType.INT_64: 103 return int64_value(int(value)) 104 elif self.data_type == ChannelDataType.UINT_32: 105 return uint32_value(int(value)) 106 elif self.data_type == ChannelDataType.UINT_64: 107 return uint64_value(int(value)) 108 elif self.data_type == ChannelDataType.FLOAT: 109 return float_value(float(value)) 110 elif self.data_type == ChannelDataType.DOUBLE: 111 return double_value(float(value)) 112 elif self.data_type == ChannelDataType.ENUM: 113 return enum_value(int(value)) 114 elif isinstance(value, str) and self.data_type == ChannelDataType.STRING: 115 return string_value(value) 116 117 raise ValueError(f"Failed to cast value of type {type(value)} to {self.data_type}") 118 119 def as_pb(self, klass: Type[ChannelConfigPb]) -> ChannelConfigPb: 120 return klass( 121 name=self.name, 122 unit=self.unit or "", 123 description=self.description or "", 124 data_type=self.data_type.value, 125 enum_types=[etype.as_pb(ChannelEnumTypePb) for etype in self.enum_types], 126 bit_field_elements=[ 127 el.as_pb(ChannelBitFieldElementPb) for el in self.bit_field_elements 128 ], 129 ) 130 131 @classmethod 132 def from_pb(cls, message: ChannelConfigPb) -> Self: 133 return cls( 134 name=message.name, 135 data_type=ChannelDataType.from_pb(message.data_type), 136 description=message.description, 137 unit=message.unit, 138 bit_field_elements=[ 139 ChannelBitFieldElement.from_pb(el) for el in message.bit_field_elements 140 ], 141 enum_types=[ChannelEnumType.from_pb(etype) for etype in message.enum_types], 142 ) 143 144 def fqn(self) -> str: 145 """ 146 NOTE: Component field of Channel has been deprecated. Function kept for backwards compatibility. 147 148 The fully-qualified channel name of a channel called 'voltage' is simply `voltage`. The 149 fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. 150 """ 151 return channel_fqn(self)
A description for a channel
49 def __init__( 50 self, 51 name: str, 52 data_type: ChannelDataType, 53 description: Optional[str] = None, 54 unit: Optional[str] = None, 55 component: Optional[str] = None, # Deprecated 56 bit_field_elements: List[ChannelBitFieldElement] = [], 57 enum_types: List[ChannelEnumType] = [], 58 ): 59 self.name = name 60 self.data_type = data_type 61 self.description = description 62 self.unit = unit 63 64 self.component = None # Field kept for backwards compatibility 65 if component: 66 _component_deprecation_warning() 67 self.name = _channel_fqn(name=self.name, component=component) 68 self.component = None 69 70 self.bit_field_elements = bit_field_elements 71 self.enum_types = enum_types 72 self.identifier = self.fqn()
74 def value_from( 75 self, value: Optional[Union[int, float, bool, str]] 76 ) -> Optional[IngestWithConfigDataChannelValue]: 77 """ 78 Like `try_value_from` except will return `None` there is a failure to produce a channel value due to a type mismatch. 79 """ 80 try: 81 return self.try_value_from(value) 82 except ValueError: 83 return None
Like try_value_from
except will return None
there is a failure to produce a channel value due to a type mismatch.
85 def try_value_from( 86 self, value: Optional[Union[int, float, bool, str]] 87 ) -> IngestWithConfigDataChannelValue: 88 """ 89 Generate a channel value for this particular channel configuration. This will raise an exception 90 if there is a type match, namely, if `value` isn't consistent with the channel's data-type. For a version 91 of this function that does not raise an exception and simply ignores type mistmatches, see `value_from`. If `value` 92 is `None` then an empty value will be generated. 93 """ 94 if value is None: 95 return empty_value() 96 97 if isinstance(value, bool) and self.data_type == ChannelDataType.BOOL: 98 return bool_value(value) 99 elif isinstance(value, int) or isinstance(value, float): 100 if self.data_type == ChannelDataType.INT_32: 101 return int32_value(int(value)) 102 elif self.data_type == ChannelDataType.INT_64: 103 return int64_value(int(value)) 104 elif self.data_type == ChannelDataType.UINT_32: 105 return uint32_value(int(value)) 106 elif self.data_type == ChannelDataType.UINT_64: 107 return uint64_value(int(value)) 108 elif self.data_type == ChannelDataType.FLOAT: 109 return float_value(float(value)) 110 elif self.data_type == ChannelDataType.DOUBLE: 111 return double_value(float(value)) 112 elif self.data_type == ChannelDataType.ENUM: 113 return enum_value(int(value)) 114 elif isinstance(value, str) and self.data_type == ChannelDataType.STRING: 115 return string_value(value) 116 117 raise ValueError(f"Failed to cast value of type {type(value)} to {self.data_type}")
Generate a channel value for this particular channel configuration. This will raise an exception
if there is a type match, namely, if value
isn't consistent with the channel's data-type. For a version
of this function that does not raise an exception and simply ignores type mistmatches, see value_from
. If value
is None
then an empty value will be generated.
119 def as_pb(self, klass: Type[ChannelConfigPb]) -> ChannelConfigPb: 120 return klass( 121 name=self.name, 122 unit=self.unit or "", 123 description=self.description or "", 124 data_type=self.data_type.value, 125 enum_types=[etype.as_pb(ChannelEnumTypePb) for etype in self.enum_types], 126 bit_field_elements=[ 127 el.as_pb(ChannelBitFieldElementPb) for el in self.bit_field_elements 128 ], 129 )
Performs the conversion into a sub-type of ProtobufMessage
.
131 @classmethod 132 def from_pb(cls, message: ChannelConfigPb) -> Self: 133 return cls( 134 name=message.name, 135 data_type=ChannelDataType.from_pb(message.data_type), 136 description=message.description, 137 unit=message.unit, 138 bit_field_elements=[ 139 ChannelBitFieldElement.from_pb(el) for el in message.bit_field_elements 140 ], 141 enum_types=[ChannelEnumType.from_pb(etype) for etype in message.enum_types], 142 )
Converts a protobuf object to the type of the sub-class class.
144 def fqn(self) -> str: 145 """ 146 NOTE: Component field of Channel has been deprecated. Function kept for backwards compatibility. 147 148 The fully-qualified channel name of a channel called 'voltage' is simply `voltage`. The 149 fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. 150 """ 151 return channel_fqn(self)
NOTE: Component field of Channel has been deprecated. Function kept for backwards compatibility.
The fully-qualified channel name of a channel called 'voltage' is simply voltage
. The
fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'.
154class ChannelBitFieldElement(AsProtobuf): 155 name: str 156 index: int 157 bit_count: int 158 159 def __init__(self, name: str, index: int, bit_count: int): 160 self.name = name 161 self.index = index 162 self.bit_count = bit_count 163 164 def as_pb(self, klass: Type[ChannelBitFieldElementPb]) -> ChannelBitFieldElementPb: 165 return klass( 166 name=self.name, 167 index=self.index, 168 bit_count=self.bit_count, 169 ) 170 171 @classmethod 172 def from_pb(cls, message: ChannelBitFieldElementPb) -> Self: 173 return cls( 174 name=message.name, 175 index=message.index, 176 bit_count=message.bit_count, 177 )
Abstract base class used to create create sub-types that can be treated
as an object that can be converted into an instance of ProtobufMessage
.
If there are multiple possible protobuf targets then as_pb
may be overloaded.
164 def as_pb(self, klass: Type[ChannelBitFieldElementPb]) -> ChannelBitFieldElementPb: 165 return klass( 166 name=self.name, 167 index=self.index, 168 bit_count=self.bit_count, 169 )
Performs the conversion into a sub-type of ProtobufMessage
.
171 @classmethod 172 def from_pb(cls, message: ChannelBitFieldElementPb) -> Self: 173 return cls( 174 name=message.name, 175 index=message.index, 176 bit_count=message.bit_count, 177 )
Converts a protobuf object to the type of the sub-class class.
180class ChannelEnumType(AsProtobuf): 181 name: str 182 key: int 183 184 def __init__(self, name: str, key: int): 185 self.name = name 186 self.key = key 187 188 def as_pb(self, klass: Type[ChannelEnumTypePb]) -> ChannelEnumTypePb: 189 return klass(name=self.name, key=self.key) 190 191 @classmethod 192 def from_pb(cls, message: ChannelEnumTypePb) -> Self: 193 return cls(name=message.name, key=message.key)
Abstract base class used to create create sub-types that can be treated
as an object that can be converted into an instance of ProtobufMessage
.
If there are multiple possible protobuf targets then as_pb
may be overloaded.
188 def as_pb(self, klass: Type[ChannelEnumTypePb]) -> ChannelEnumTypePb: 189 return klass(name=self.name, key=self.key)
Performs the conversion into a sub-type of ProtobufMessage
.
191 @classmethod 192 def from_pb(cls, message: ChannelEnumTypePb) -> Self: 193 return cls(name=message.name, key=message.key)
Converts a protobuf object to the type of the sub-class class.
196class ChannelDataTypeStrRep(Enum): 197 DOUBLE = "double" 198 STRING = "string" 199 ENUM = "enum" 200 BIT_FIELD = "bit_field" 201 BOOL = "bool" 202 FLOAT = "float" 203 INT_32 = "int32" 204 INT_64 = "int64" 205 UINT_32 = "uint32" 206 UINT_64 = "uint64" 207 208 @staticmethod 209 def from_api_format(val: str) -> Optional["ChannelDataTypeStrRep"]: 210 try: 211 return { 212 "CHANNEL_DATA_TYPE_DOUBLE": ChannelDataTypeStrRep.DOUBLE, 213 "CHANNEL_DATA_TYPE_STRING": ChannelDataTypeStrRep.STRING, 214 "CHANNEL_DATA_TYPE_ENUM": ChannelDataTypeStrRep.ENUM, 215 "CHANNEL_DATA_TYPE_BIT_FIELD": ChannelDataTypeStrRep.BIT_FIELD, 216 "CHANNEL_DATA_TYPE_BOOL": ChannelDataTypeStrRep.BOOL, 217 "CHANNEL_DATA_TYPE_FLOAT": ChannelDataTypeStrRep.FLOAT, 218 "CHANNEL_DATA_TYPE_INT_32": ChannelDataTypeStrRep.INT_32, 219 "CHANNEL_DATA_TYPE_INT_64": ChannelDataTypeStrRep.INT_64, 220 "CHANNEL_DATA_TYPE_UINT_32": ChannelDataTypeStrRep.UINT_32, 221 "CHANNEL_DATA_TYPE_UINT_64": ChannelDataTypeStrRep.UINT_64, 222 }[val] 223 except KeyError: 224 return None
An enumeration.
208 @staticmethod 209 def from_api_format(val: str) -> Optional["ChannelDataTypeStrRep"]: 210 try: 211 return { 212 "CHANNEL_DATA_TYPE_DOUBLE": ChannelDataTypeStrRep.DOUBLE, 213 "CHANNEL_DATA_TYPE_STRING": ChannelDataTypeStrRep.STRING, 214 "CHANNEL_DATA_TYPE_ENUM": ChannelDataTypeStrRep.ENUM, 215 "CHANNEL_DATA_TYPE_BIT_FIELD": ChannelDataTypeStrRep.BIT_FIELD, 216 "CHANNEL_DATA_TYPE_BOOL": ChannelDataTypeStrRep.BOOL, 217 "CHANNEL_DATA_TYPE_FLOAT": ChannelDataTypeStrRep.FLOAT, 218 "CHANNEL_DATA_TYPE_INT_32": ChannelDataTypeStrRep.INT_32, 219 "CHANNEL_DATA_TYPE_INT_64": ChannelDataTypeStrRep.INT_64, 220 "CHANNEL_DATA_TYPE_UINT_32": ChannelDataTypeStrRep.UINT_32, 221 "CHANNEL_DATA_TYPE_UINT_64": ChannelDataTypeStrRep.UINT_64, 222 }[val] 223 except KeyError: 224 return None
Inherited Members
- enum.Enum
- name
- value
227class ChannelDataType(Enum): 228 """ 229 Utility enum class to simplify working with channel data-types generated from protobuf 230 """ 231 232 DOUBLE = channel_pb.CHANNEL_DATA_TYPE_DOUBLE 233 STRING = channel_pb.CHANNEL_DATA_TYPE_STRING 234 ENUM = channel_pb.CHANNEL_DATA_TYPE_ENUM 235 BIT_FIELD = channel_pb.CHANNEL_DATA_TYPE_BIT_FIELD 236 BOOL = channel_pb.CHANNEL_DATA_TYPE_BOOL 237 FLOAT = channel_pb.CHANNEL_DATA_TYPE_FLOAT 238 INT_32 = channel_pb.CHANNEL_DATA_TYPE_INT_32 239 INT_64 = channel_pb.CHANNEL_DATA_TYPE_INT_64 240 UINT_32 = channel_pb.CHANNEL_DATA_TYPE_UINT_32 241 UINT_64 = channel_pb.CHANNEL_DATA_TYPE_UINT_64 242 243 @classmethod 244 def from_pb(cls, val: channel_pb.ChannelDataType.ValueType) -> "ChannelDataType": 245 if val == cls.DOUBLE.value: 246 return cls.DOUBLE 247 elif val == cls.STRING.value: 248 return cls.STRING 249 elif val == cls.ENUM.value: 250 return cls.ENUM 251 elif val == cls.BIT_FIELD.value: 252 return cls.BIT_FIELD 253 elif val == cls.BOOL.value: 254 return cls.BOOL 255 elif val == cls.FLOAT.value: 256 return cls.FLOAT 257 elif val == cls.INT_32.value: 258 return cls.INT_32 259 elif val == cls.INT_64.value: 260 return cls.INT_64 261 elif val == cls.UINT_32.value: 262 return cls.UINT_32 263 elif val == cls.UINT_64.value: 264 return cls.UINT_64 265 else: 266 raise ValueError(f"Unknown channel data type '{val}'.") 267 268 @classmethod 269 def from_str(cls, raw: str) -> Optional["ChannelDataType"]: 270 if raw.startswith("CHANNEL_DATA_TYPE_"): 271 val = ChannelDataTypeStrRep.from_api_format(raw) 272 if val is None: 273 return None 274 else: 275 try: 276 val = ChannelDataTypeStrRep(raw) 277 except ValueError: 278 return None 279 280 if val == ChannelDataTypeStrRep.DOUBLE: 281 return cls.DOUBLE 282 elif val == ChannelDataTypeStrRep.STRING: 283 return cls.STRING 284 elif val == ChannelDataTypeStrRep.ENUM: 285 return cls.ENUM 286 elif val == ChannelDataTypeStrRep.BIT_FIELD: 287 return cls.BIT_FIELD 288 elif val == ChannelDataTypeStrRep.BOOL: 289 return cls.BOOL 290 elif val == ChannelDataTypeStrRep.FLOAT: 291 return cls.FLOAT 292 elif val == ChannelDataTypeStrRep.INT_32: 293 return cls.INT_32 294 elif val == ChannelDataTypeStrRep.INT_64: 295 return cls.INT_64 296 elif val == ChannelDataTypeStrRep.UINT_32: 297 return cls.UINT_32 298 elif val == ChannelDataTypeStrRep.UINT_64: 299 return cls.UINT_64 300 else: 301 raise Exception("Unreachable") 302 303 def as_human_str(self, api_format: bool = False) -> str: 304 if self == ChannelDataType.DOUBLE: 305 return "CHANNEL_DATA_TYPE_DOUBLE" if api_format else ChannelDataTypeStrRep.DOUBLE.value 306 elif self == ChannelDataType.STRING: 307 return "CHANNEL_DATA_TYPE_STRING" if api_format else ChannelDataTypeStrRep.STRING.value 308 elif self == ChannelDataType.ENUM: 309 return "CHANNEL_DATA_TYPE_ENUM" if api_format else ChannelDataTypeStrRep.ENUM.value 310 elif self == ChannelDataType.BIT_FIELD: 311 return ( 312 "CHANNEL_DATA_TYPE_BIT_FIELD" 313 if api_format 314 else ChannelDataTypeStrRep.BIT_FIELD.value 315 ) 316 elif self == ChannelDataType.BOOL: 317 return "CHANNEL_DATA_TYPE_BOOL" if api_format else ChannelDataTypeStrRep.BOOL.value 318 elif self == ChannelDataType.FLOAT: 319 return "CHANNEL_DATA_TYPE_FLOAT" if api_format else ChannelDataTypeStrRep.FLOAT.value 320 elif self == ChannelDataType.INT_32: 321 return "CHANNEL_DATA_TYPE_INT_32" if api_format else ChannelDataTypeStrRep.INT_32.value 322 elif self == ChannelDataType.INT_64: 323 return "CHANNEL_DATA_TYPE_INT_64" if api_format else ChannelDataTypeStrRep.INT_64.value 324 elif self == ChannelDataType.UINT_32: 325 return ( 326 "CHANNEL_DATA_TYPE_UINT_32" if api_format else ChannelDataTypeStrRep.UINT_32.value 327 ) 328 elif self == ChannelDataType.UINT_64: 329 return ( 330 "CHANNEL_DATA_TYPE_UINT_64" if api_format else ChannelDataTypeStrRep.UINT_64.value 331 ) 332 else: 333 raise Exception("Unreachable.")
Utility enum class to simplify working with channel data-types generated from protobuf
243 @classmethod 244 def from_pb(cls, val: channel_pb.ChannelDataType.ValueType) -> "ChannelDataType": 245 if val == cls.DOUBLE.value: 246 return cls.DOUBLE 247 elif val == cls.STRING.value: 248 return cls.STRING 249 elif val == cls.ENUM.value: 250 return cls.ENUM 251 elif val == cls.BIT_FIELD.value: 252 return cls.BIT_FIELD 253 elif val == cls.BOOL.value: 254 return cls.BOOL 255 elif val == cls.FLOAT.value: 256 return cls.FLOAT 257 elif val == cls.INT_32.value: 258 return cls.INT_32 259 elif val == cls.INT_64.value: 260 return cls.INT_64 261 elif val == cls.UINT_32.value: 262 return cls.UINT_32 263 elif val == cls.UINT_64.value: 264 return cls.UINT_64 265 else: 266 raise ValueError(f"Unknown channel data type '{val}'.")
268 @classmethod 269 def from_str(cls, raw: str) -> Optional["ChannelDataType"]: 270 if raw.startswith("CHANNEL_DATA_TYPE_"): 271 val = ChannelDataTypeStrRep.from_api_format(raw) 272 if val is None: 273 return None 274 else: 275 try: 276 val = ChannelDataTypeStrRep(raw) 277 except ValueError: 278 return None 279 280 if val == ChannelDataTypeStrRep.DOUBLE: 281 return cls.DOUBLE 282 elif val == ChannelDataTypeStrRep.STRING: 283 return cls.STRING 284 elif val == ChannelDataTypeStrRep.ENUM: 285 return cls.ENUM 286 elif val == ChannelDataTypeStrRep.BIT_FIELD: 287 return cls.BIT_FIELD 288 elif val == ChannelDataTypeStrRep.BOOL: 289 return cls.BOOL 290 elif val == ChannelDataTypeStrRep.FLOAT: 291 return cls.FLOAT 292 elif val == ChannelDataTypeStrRep.INT_32: 293 return cls.INT_32 294 elif val == ChannelDataTypeStrRep.INT_64: 295 return cls.INT_64 296 elif val == ChannelDataTypeStrRep.UINT_32: 297 return cls.UINT_32 298 elif val == ChannelDataTypeStrRep.UINT_64: 299 return cls.UINT_64 300 else: 301 raise Exception("Unreachable")
303 def as_human_str(self, api_format: bool = False) -> str: 304 if self == ChannelDataType.DOUBLE: 305 return "CHANNEL_DATA_TYPE_DOUBLE" if api_format else ChannelDataTypeStrRep.DOUBLE.value 306 elif self == ChannelDataType.STRING: 307 return "CHANNEL_DATA_TYPE_STRING" if api_format else ChannelDataTypeStrRep.STRING.value 308 elif self == ChannelDataType.ENUM: 309 return "CHANNEL_DATA_TYPE_ENUM" if api_format else ChannelDataTypeStrRep.ENUM.value 310 elif self == ChannelDataType.BIT_FIELD: 311 return ( 312 "CHANNEL_DATA_TYPE_BIT_FIELD" 313 if api_format 314 else ChannelDataTypeStrRep.BIT_FIELD.value 315 ) 316 elif self == ChannelDataType.BOOL: 317 return "CHANNEL_DATA_TYPE_BOOL" if api_format else ChannelDataTypeStrRep.BOOL.value 318 elif self == ChannelDataType.FLOAT: 319 return "CHANNEL_DATA_TYPE_FLOAT" if api_format else ChannelDataTypeStrRep.FLOAT.value 320 elif self == ChannelDataType.INT_32: 321 return "CHANNEL_DATA_TYPE_INT_32" if api_format else ChannelDataTypeStrRep.INT_32.value 322 elif self == ChannelDataType.INT_64: 323 return "CHANNEL_DATA_TYPE_INT_64" if api_format else ChannelDataTypeStrRep.INT_64.value 324 elif self == ChannelDataType.UINT_32: 325 return ( 326 "CHANNEL_DATA_TYPE_UINT_32" if api_format else ChannelDataTypeStrRep.UINT_32.value 327 ) 328 elif self == ChannelDataType.UINT_64: 329 return ( 330 "CHANNEL_DATA_TYPE_UINT_64" if api_format else ChannelDataTypeStrRep.UINT_64.value 331 ) 332 else: 333 raise Exception("Unreachable.")
Inherited Members
- enum.Enum
- name
- value
341def channel_fqn( 342 channel: Union[ 343 ChannelConfig, 344 ChannelConfigPb, 345 ChannelValue, 346 ChannelPb, 347 _AbstractChannel, 348 ], 349) -> str: 350 """ 351 Computes the fully qualified channel name. 352 353 NOTE: Component field of Channel is deprecated and should not be used. Function is left for code compatibility. 354 355 The fully-qualified channel name of a channel called 'voltage' is simply `voltage'. The 356 fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. 357 """ 358 359 if isinstance(channel, ChannelConfig): 360 if channel.component: 361 _component_deprecation_warning() 362 return _channel_fqn(channel.name, channel.component) 363 elif isinstance(channel, ChannelConfigPb): 364 return channel.name 365 elif isinstance(channel, ChannelPb): 366 return channel.name 367 else: 368 component = channel.get("component") 369 if component: 370 _component_deprecation_warning() 371 channel_name = channel["channel_name"] 372 return _channel_fqn(name=channel_name, component=component)
Computes the fully qualified channel name.
NOTE: Component field of Channel is deprecated and should not be used. Function is left for code compatibility.
The fully-qualified channel name of a channel called 'voltage' is simply voltage'. The
fully qualified name of a channel called 'temperature' of component 'motor' is a
motor.temperature'.
419def is_data_type(val: IngestWithConfigDataChannelValue, target_type: ChannelDataType) -> bool: 420 if target_type == ChannelDataType.DOUBLE: 421 return val.HasField("double") 422 elif target_type == ChannelDataType.STRING: 423 return val.HasField("string") 424 elif target_type == ChannelDataType.ENUM: 425 return val.HasField("enum") 426 elif target_type == ChannelDataType.BIT_FIELD: 427 return val.HasField("bit_field") 428 elif target_type == ChannelDataType.BOOL: 429 return val.HasField("bool") 430 elif target_type == ChannelDataType.FLOAT: 431 return val.HasField("float") 432 elif target_type == ChannelDataType.INT_32: 433 return val.HasField("int32") 434 elif target_type == ChannelDataType.INT_64: 435 return val.HasField("int64") 436 elif target_type == ChannelDataType.UINT_32: 437 return val.HasField("uint32") 438 elif target_type == ChannelDataType.UINT_64: 439 return val.HasField("uint64")