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 is_signed: Optional[bool] = None 183 184 def __init__(self, name: str, key: int, is_signed: Optional[bool] = None): 185 self.name = name 186 self.key = key 187 self.is_signed = is_signed 188 189 def as_pb(self, klass: Type[ChannelEnumTypePb]) -> ChannelEnumTypePb: 190 return klass( 191 name=self.name, 192 key=self.key, 193 is_signed=False if self.is_signed is None else self.is_signed, 194 ) 195 196 @classmethod 197 def from_pb(cls, message: ChannelEnumTypePb) -> Self: 198 return cls(name=message.name, key=message.key, is_signed=message.is_signed) 199 200 201class ChannelDataTypeStrRep(Enum): 202 DOUBLE = "double" 203 STRING = "string" 204 ENUM = "enum" 205 BIT_FIELD = "bit_field" 206 BOOL = "bool" 207 FLOAT = "float" 208 INT_32 = "int32" 209 INT_64 = "int64" 210 UINT_32 = "uint32" 211 UINT_64 = "uint64" 212 213 @staticmethod 214 def from_api_format(val: str) -> Optional["ChannelDataTypeStrRep"]: 215 try: 216 return { 217 "CHANNEL_DATA_TYPE_DOUBLE": ChannelDataTypeStrRep.DOUBLE, 218 "CHANNEL_DATA_TYPE_STRING": ChannelDataTypeStrRep.STRING, 219 "CHANNEL_DATA_TYPE_ENUM": ChannelDataTypeStrRep.ENUM, 220 "CHANNEL_DATA_TYPE_BIT_FIELD": ChannelDataTypeStrRep.BIT_FIELD, 221 "CHANNEL_DATA_TYPE_BOOL": ChannelDataTypeStrRep.BOOL, 222 "CHANNEL_DATA_TYPE_FLOAT": ChannelDataTypeStrRep.FLOAT, 223 "CHANNEL_DATA_TYPE_INT_32": ChannelDataTypeStrRep.INT_32, 224 "CHANNEL_DATA_TYPE_INT_64": ChannelDataTypeStrRep.INT_64, 225 "CHANNEL_DATA_TYPE_UINT_32": ChannelDataTypeStrRep.UINT_32, 226 "CHANNEL_DATA_TYPE_UINT_64": ChannelDataTypeStrRep.UINT_64, 227 }[val] 228 except KeyError: 229 return None 230 231 232class ChannelDataType(Enum): 233 """ 234 Utility enum class to simplify working with channel data-types generated from protobuf 235 """ 236 237 DOUBLE = channel_pb.CHANNEL_DATA_TYPE_DOUBLE 238 STRING = channel_pb.CHANNEL_DATA_TYPE_STRING 239 ENUM = channel_pb.CHANNEL_DATA_TYPE_ENUM 240 BIT_FIELD = channel_pb.CHANNEL_DATA_TYPE_BIT_FIELD 241 BOOL = channel_pb.CHANNEL_DATA_TYPE_BOOL 242 FLOAT = channel_pb.CHANNEL_DATA_TYPE_FLOAT 243 INT_32 = channel_pb.CHANNEL_DATA_TYPE_INT_32 244 INT_64 = channel_pb.CHANNEL_DATA_TYPE_INT_64 245 UINT_32 = channel_pb.CHANNEL_DATA_TYPE_UINT_32 246 UINT_64 = channel_pb.CHANNEL_DATA_TYPE_UINT_64 247 248 @classmethod 249 def from_pb(cls, val: channel_pb.ChannelDataType.ValueType) -> "ChannelDataType": 250 if val == cls.DOUBLE.value: 251 return cls.DOUBLE 252 elif val == cls.STRING.value: 253 return cls.STRING 254 elif val == cls.ENUM.value: 255 return cls.ENUM 256 elif val == cls.BIT_FIELD.value: 257 return cls.BIT_FIELD 258 elif val == cls.BOOL.value: 259 return cls.BOOL 260 elif val == cls.FLOAT.value: 261 return cls.FLOAT 262 elif val == cls.INT_32.value: 263 return cls.INT_32 264 elif val == cls.INT_64.value: 265 return cls.INT_64 266 elif val == cls.UINT_32.value: 267 return cls.UINT_32 268 elif val == cls.UINT_64.value: 269 return cls.UINT_64 270 else: 271 raise ValueError(f"Unknown channel data type '{val}'.") 272 273 @classmethod 274 def from_str(cls, raw: str) -> Optional["ChannelDataType"]: 275 if raw.startswith("CHANNEL_DATA_TYPE_"): 276 val = ChannelDataTypeStrRep.from_api_format(raw) 277 if val is None: 278 return None 279 else: 280 try: 281 val = ChannelDataTypeStrRep(raw) 282 except ValueError: 283 return None 284 285 if val == ChannelDataTypeStrRep.DOUBLE: 286 return cls.DOUBLE 287 elif val == ChannelDataTypeStrRep.STRING: 288 return cls.STRING 289 elif val == ChannelDataTypeStrRep.ENUM: 290 return cls.ENUM 291 elif val == ChannelDataTypeStrRep.BIT_FIELD: 292 return cls.BIT_FIELD 293 elif val == ChannelDataTypeStrRep.BOOL: 294 return cls.BOOL 295 elif val == ChannelDataTypeStrRep.FLOAT: 296 return cls.FLOAT 297 elif val == ChannelDataTypeStrRep.INT_32: 298 return cls.INT_32 299 elif val == ChannelDataTypeStrRep.INT_64: 300 return cls.INT_64 301 elif val == ChannelDataTypeStrRep.UINT_32: 302 return cls.UINT_32 303 elif val == ChannelDataTypeStrRep.UINT_64: 304 return cls.UINT_64 305 else: 306 raise Exception("Unreachable") 307 308 def as_human_str(self, api_format: bool = False) -> str: 309 if self == ChannelDataType.DOUBLE: 310 return "CHANNEL_DATA_TYPE_DOUBLE" if api_format else ChannelDataTypeStrRep.DOUBLE.value 311 elif self == ChannelDataType.STRING: 312 return "CHANNEL_DATA_TYPE_STRING" if api_format else ChannelDataTypeStrRep.STRING.value 313 elif self == ChannelDataType.ENUM: 314 return "CHANNEL_DATA_TYPE_ENUM" if api_format else ChannelDataTypeStrRep.ENUM.value 315 elif self == ChannelDataType.BIT_FIELD: 316 return ( 317 "CHANNEL_DATA_TYPE_BIT_FIELD" 318 if api_format 319 else ChannelDataTypeStrRep.BIT_FIELD.value 320 ) 321 elif self == ChannelDataType.BOOL: 322 return "CHANNEL_DATA_TYPE_BOOL" if api_format else ChannelDataTypeStrRep.BOOL.value 323 elif self == ChannelDataType.FLOAT: 324 return "CHANNEL_DATA_TYPE_FLOAT" if api_format else ChannelDataTypeStrRep.FLOAT.value 325 elif self == ChannelDataType.INT_32: 326 return "CHANNEL_DATA_TYPE_INT_32" if api_format else ChannelDataTypeStrRep.INT_32.value 327 elif self == ChannelDataType.INT_64: 328 return "CHANNEL_DATA_TYPE_INT_64" if api_format else ChannelDataTypeStrRep.INT_64.value 329 elif self == ChannelDataType.UINT_32: 330 return ( 331 "CHANNEL_DATA_TYPE_UINT_32" if api_format else ChannelDataTypeStrRep.UINT_32.value 332 ) 333 elif self == ChannelDataType.UINT_64: 334 return ( 335 "CHANNEL_DATA_TYPE_UINT_64" if api_format else ChannelDataTypeStrRep.UINT_64.value 336 ) 337 else: 338 raise Exception("Unreachable.") 339 340 341class _AbstractChannel(TypedDict): 342 channel_name: str 343 component: Optional[str] # Deprecated 344 345 346def channel_fqn( 347 channel: Union[ 348 ChannelConfig, 349 ChannelConfigPb, 350 ChannelValue, 351 ChannelPb, 352 _AbstractChannel, 353 ], 354) -> str: 355 """ 356 Computes the fully qualified channel name. 357 358 NOTE: Component field of Channel is deprecated and should not be used. Function is left for code compatibility. 359 360 The fully-qualified channel name of a channel called 'voltage' is simply `voltage'. The 361 fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. 362 """ 363 364 if isinstance(channel, ChannelConfig): 365 if channel.component: 366 _component_deprecation_warning() 367 return _channel_fqn(channel.name, channel.component) 368 elif isinstance(channel, ChannelConfigPb): 369 return channel.name 370 elif isinstance(channel, ChannelPb): 371 return channel.name 372 else: 373 component = channel.get("component") 374 if component: 375 _component_deprecation_warning() 376 channel_name = channel["channel_name"] 377 return _channel_fqn(name=channel_name, component=component) 378 379 380def string_value(val: str) -> IngestWithConfigDataChannelValue: 381 return IngestWithConfigDataChannelValue(string=val) 382 383 384def double_value(val: float) -> IngestWithConfigDataChannelValue: 385 return IngestWithConfigDataChannelValue(double=val) 386 387 388def float_value(val: float) -> IngestWithConfigDataChannelValue: 389 return IngestWithConfigDataChannelValue(float=val) 390 391 392def bool_value(val: bool) -> IngestWithConfigDataChannelValue: 393 return IngestWithConfigDataChannelValue(bool=val) 394 395 396def int32_value(val: int) -> IngestWithConfigDataChannelValue: 397 return IngestWithConfigDataChannelValue(int32=val) 398 399 400def uint32_value(val: int) -> IngestWithConfigDataChannelValue: 401 return IngestWithConfigDataChannelValue(uint32=val) 402 403 404def int64_value(val: int) -> IngestWithConfigDataChannelValue: 405 return IngestWithConfigDataChannelValue(int64=val) 406 407 408def uint64_value(val: int) -> IngestWithConfigDataChannelValue: 409 return IngestWithConfigDataChannelValue(uint64=val) 410 411 412def bit_field_value(val: bytes) -> IngestWithConfigDataChannelValue: 413 return IngestWithConfigDataChannelValue(bit_field=val) 414 415 416def enum_value(val: int) -> IngestWithConfigDataChannelValue: 417 return IngestWithConfigDataChannelValue(enum=val) 418 419 420def empty_value() -> IngestWithConfigDataChannelValue: 421 return IngestWithConfigDataChannelValue(empty=Empty()) 422 423 424def is_data_type(val: IngestWithConfigDataChannelValue, target_type: ChannelDataType) -> bool: 425 if target_type == ChannelDataType.DOUBLE: 426 return val.HasField("double") 427 elif target_type == ChannelDataType.STRING: 428 return val.HasField("string") 429 elif target_type == ChannelDataType.ENUM: 430 return val.HasField("enum") 431 elif target_type == ChannelDataType.BIT_FIELD: 432 return val.HasField("bit_field") 433 elif target_type == ChannelDataType.BOOL: 434 return val.HasField("bool") 435 elif target_type == ChannelDataType.FLOAT: 436 return val.HasField("float") 437 elif target_type == ChannelDataType.INT_32: 438 return val.HasField("int32") 439 elif target_type == ChannelDataType.INT_64: 440 return val.HasField("int64") 441 elif target_type == ChannelDataType.UINT_32: 442 return val.HasField("uint32") 443 elif target_type == ChannelDataType.UINT_64: 444 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 is_signed: Optional[bool] = None 184 185 def __init__(self, name: str, key: int, is_signed: Optional[bool] = None): 186 self.name = name 187 self.key = key 188 self.is_signed = is_signed 189 190 def as_pb(self, klass: Type[ChannelEnumTypePb]) -> ChannelEnumTypePb: 191 return klass( 192 name=self.name, 193 key=self.key, 194 is_signed=False if self.is_signed is None else self.is_signed, 195 ) 196 197 @classmethod 198 def from_pb(cls, message: ChannelEnumTypePb) -> Self: 199 return cls(name=message.name, key=message.key, is_signed=message.is_signed)
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.
190 def as_pb(self, klass: Type[ChannelEnumTypePb]) -> ChannelEnumTypePb: 191 return klass( 192 name=self.name, 193 key=self.key, 194 is_signed=False if self.is_signed is None else self.is_signed, 195 )
Performs the conversion into a sub-type of ProtobufMessage
.
197 @classmethod 198 def from_pb(cls, message: ChannelEnumTypePb) -> Self: 199 return cls(name=message.name, key=message.key, is_signed=message.is_signed)
Converts a protobuf object to the type of the sub-class class.
202class ChannelDataTypeStrRep(Enum): 203 DOUBLE = "double" 204 STRING = "string" 205 ENUM = "enum" 206 BIT_FIELD = "bit_field" 207 BOOL = "bool" 208 FLOAT = "float" 209 INT_32 = "int32" 210 INT_64 = "int64" 211 UINT_32 = "uint32" 212 UINT_64 = "uint64" 213 214 @staticmethod 215 def from_api_format(val: str) -> Optional["ChannelDataTypeStrRep"]: 216 try: 217 return { 218 "CHANNEL_DATA_TYPE_DOUBLE": ChannelDataTypeStrRep.DOUBLE, 219 "CHANNEL_DATA_TYPE_STRING": ChannelDataTypeStrRep.STRING, 220 "CHANNEL_DATA_TYPE_ENUM": ChannelDataTypeStrRep.ENUM, 221 "CHANNEL_DATA_TYPE_BIT_FIELD": ChannelDataTypeStrRep.BIT_FIELD, 222 "CHANNEL_DATA_TYPE_BOOL": ChannelDataTypeStrRep.BOOL, 223 "CHANNEL_DATA_TYPE_FLOAT": ChannelDataTypeStrRep.FLOAT, 224 "CHANNEL_DATA_TYPE_INT_32": ChannelDataTypeStrRep.INT_32, 225 "CHANNEL_DATA_TYPE_INT_64": ChannelDataTypeStrRep.INT_64, 226 "CHANNEL_DATA_TYPE_UINT_32": ChannelDataTypeStrRep.UINT_32, 227 "CHANNEL_DATA_TYPE_UINT_64": ChannelDataTypeStrRep.UINT_64, 228 }[val] 229 except KeyError: 230 return None
An enumeration.
214 @staticmethod 215 def from_api_format(val: str) -> Optional["ChannelDataTypeStrRep"]: 216 try: 217 return { 218 "CHANNEL_DATA_TYPE_DOUBLE": ChannelDataTypeStrRep.DOUBLE, 219 "CHANNEL_DATA_TYPE_STRING": ChannelDataTypeStrRep.STRING, 220 "CHANNEL_DATA_TYPE_ENUM": ChannelDataTypeStrRep.ENUM, 221 "CHANNEL_DATA_TYPE_BIT_FIELD": ChannelDataTypeStrRep.BIT_FIELD, 222 "CHANNEL_DATA_TYPE_BOOL": ChannelDataTypeStrRep.BOOL, 223 "CHANNEL_DATA_TYPE_FLOAT": ChannelDataTypeStrRep.FLOAT, 224 "CHANNEL_DATA_TYPE_INT_32": ChannelDataTypeStrRep.INT_32, 225 "CHANNEL_DATA_TYPE_INT_64": ChannelDataTypeStrRep.INT_64, 226 "CHANNEL_DATA_TYPE_UINT_32": ChannelDataTypeStrRep.UINT_32, 227 "CHANNEL_DATA_TYPE_UINT_64": ChannelDataTypeStrRep.UINT_64, 228 }[val] 229 except KeyError: 230 return None
Inherited Members
- enum.Enum
- name
- value
233class ChannelDataType(Enum): 234 """ 235 Utility enum class to simplify working with channel data-types generated from protobuf 236 """ 237 238 DOUBLE = channel_pb.CHANNEL_DATA_TYPE_DOUBLE 239 STRING = channel_pb.CHANNEL_DATA_TYPE_STRING 240 ENUM = channel_pb.CHANNEL_DATA_TYPE_ENUM 241 BIT_FIELD = channel_pb.CHANNEL_DATA_TYPE_BIT_FIELD 242 BOOL = channel_pb.CHANNEL_DATA_TYPE_BOOL 243 FLOAT = channel_pb.CHANNEL_DATA_TYPE_FLOAT 244 INT_32 = channel_pb.CHANNEL_DATA_TYPE_INT_32 245 INT_64 = channel_pb.CHANNEL_DATA_TYPE_INT_64 246 UINT_32 = channel_pb.CHANNEL_DATA_TYPE_UINT_32 247 UINT_64 = channel_pb.CHANNEL_DATA_TYPE_UINT_64 248 249 @classmethod 250 def from_pb(cls, val: channel_pb.ChannelDataType.ValueType) -> "ChannelDataType": 251 if val == cls.DOUBLE.value: 252 return cls.DOUBLE 253 elif val == cls.STRING.value: 254 return cls.STRING 255 elif val == cls.ENUM.value: 256 return cls.ENUM 257 elif val == cls.BIT_FIELD.value: 258 return cls.BIT_FIELD 259 elif val == cls.BOOL.value: 260 return cls.BOOL 261 elif val == cls.FLOAT.value: 262 return cls.FLOAT 263 elif val == cls.INT_32.value: 264 return cls.INT_32 265 elif val == cls.INT_64.value: 266 return cls.INT_64 267 elif val == cls.UINT_32.value: 268 return cls.UINT_32 269 elif val == cls.UINT_64.value: 270 return cls.UINT_64 271 else: 272 raise ValueError(f"Unknown channel data type '{val}'.") 273 274 @classmethod 275 def from_str(cls, raw: str) -> Optional["ChannelDataType"]: 276 if raw.startswith("CHANNEL_DATA_TYPE_"): 277 val = ChannelDataTypeStrRep.from_api_format(raw) 278 if val is None: 279 return None 280 else: 281 try: 282 val = ChannelDataTypeStrRep(raw) 283 except ValueError: 284 return None 285 286 if val == ChannelDataTypeStrRep.DOUBLE: 287 return cls.DOUBLE 288 elif val == ChannelDataTypeStrRep.STRING: 289 return cls.STRING 290 elif val == ChannelDataTypeStrRep.ENUM: 291 return cls.ENUM 292 elif val == ChannelDataTypeStrRep.BIT_FIELD: 293 return cls.BIT_FIELD 294 elif val == ChannelDataTypeStrRep.BOOL: 295 return cls.BOOL 296 elif val == ChannelDataTypeStrRep.FLOAT: 297 return cls.FLOAT 298 elif val == ChannelDataTypeStrRep.INT_32: 299 return cls.INT_32 300 elif val == ChannelDataTypeStrRep.INT_64: 301 return cls.INT_64 302 elif val == ChannelDataTypeStrRep.UINT_32: 303 return cls.UINT_32 304 elif val == ChannelDataTypeStrRep.UINT_64: 305 return cls.UINT_64 306 else: 307 raise Exception("Unreachable") 308 309 def as_human_str(self, api_format: bool = False) -> str: 310 if self == ChannelDataType.DOUBLE: 311 return "CHANNEL_DATA_TYPE_DOUBLE" if api_format else ChannelDataTypeStrRep.DOUBLE.value 312 elif self == ChannelDataType.STRING: 313 return "CHANNEL_DATA_TYPE_STRING" if api_format else ChannelDataTypeStrRep.STRING.value 314 elif self == ChannelDataType.ENUM: 315 return "CHANNEL_DATA_TYPE_ENUM" if api_format else ChannelDataTypeStrRep.ENUM.value 316 elif self == ChannelDataType.BIT_FIELD: 317 return ( 318 "CHANNEL_DATA_TYPE_BIT_FIELD" 319 if api_format 320 else ChannelDataTypeStrRep.BIT_FIELD.value 321 ) 322 elif self == ChannelDataType.BOOL: 323 return "CHANNEL_DATA_TYPE_BOOL" if api_format else ChannelDataTypeStrRep.BOOL.value 324 elif self == ChannelDataType.FLOAT: 325 return "CHANNEL_DATA_TYPE_FLOAT" if api_format else ChannelDataTypeStrRep.FLOAT.value 326 elif self == ChannelDataType.INT_32: 327 return "CHANNEL_DATA_TYPE_INT_32" if api_format else ChannelDataTypeStrRep.INT_32.value 328 elif self == ChannelDataType.INT_64: 329 return "CHANNEL_DATA_TYPE_INT_64" if api_format else ChannelDataTypeStrRep.INT_64.value 330 elif self == ChannelDataType.UINT_32: 331 return ( 332 "CHANNEL_DATA_TYPE_UINT_32" if api_format else ChannelDataTypeStrRep.UINT_32.value 333 ) 334 elif self == ChannelDataType.UINT_64: 335 return ( 336 "CHANNEL_DATA_TYPE_UINT_64" if api_format else ChannelDataTypeStrRep.UINT_64.value 337 ) 338 else: 339 raise Exception("Unreachable.")
Utility enum class to simplify working with channel data-types generated from protobuf
249 @classmethod 250 def from_pb(cls, val: channel_pb.ChannelDataType.ValueType) -> "ChannelDataType": 251 if val == cls.DOUBLE.value: 252 return cls.DOUBLE 253 elif val == cls.STRING.value: 254 return cls.STRING 255 elif val == cls.ENUM.value: 256 return cls.ENUM 257 elif val == cls.BIT_FIELD.value: 258 return cls.BIT_FIELD 259 elif val == cls.BOOL.value: 260 return cls.BOOL 261 elif val == cls.FLOAT.value: 262 return cls.FLOAT 263 elif val == cls.INT_32.value: 264 return cls.INT_32 265 elif val == cls.INT_64.value: 266 return cls.INT_64 267 elif val == cls.UINT_32.value: 268 return cls.UINT_32 269 elif val == cls.UINT_64.value: 270 return cls.UINT_64 271 else: 272 raise ValueError(f"Unknown channel data type '{val}'.")
274 @classmethod 275 def from_str(cls, raw: str) -> Optional["ChannelDataType"]: 276 if raw.startswith("CHANNEL_DATA_TYPE_"): 277 val = ChannelDataTypeStrRep.from_api_format(raw) 278 if val is None: 279 return None 280 else: 281 try: 282 val = ChannelDataTypeStrRep(raw) 283 except ValueError: 284 return None 285 286 if val == ChannelDataTypeStrRep.DOUBLE: 287 return cls.DOUBLE 288 elif val == ChannelDataTypeStrRep.STRING: 289 return cls.STRING 290 elif val == ChannelDataTypeStrRep.ENUM: 291 return cls.ENUM 292 elif val == ChannelDataTypeStrRep.BIT_FIELD: 293 return cls.BIT_FIELD 294 elif val == ChannelDataTypeStrRep.BOOL: 295 return cls.BOOL 296 elif val == ChannelDataTypeStrRep.FLOAT: 297 return cls.FLOAT 298 elif val == ChannelDataTypeStrRep.INT_32: 299 return cls.INT_32 300 elif val == ChannelDataTypeStrRep.INT_64: 301 return cls.INT_64 302 elif val == ChannelDataTypeStrRep.UINT_32: 303 return cls.UINT_32 304 elif val == ChannelDataTypeStrRep.UINT_64: 305 return cls.UINT_64 306 else: 307 raise Exception("Unreachable")
309 def as_human_str(self, api_format: bool = False) -> str: 310 if self == ChannelDataType.DOUBLE: 311 return "CHANNEL_DATA_TYPE_DOUBLE" if api_format else ChannelDataTypeStrRep.DOUBLE.value 312 elif self == ChannelDataType.STRING: 313 return "CHANNEL_DATA_TYPE_STRING" if api_format else ChannelDataTypeStrRep.STRING.value 314 elif self == ChannelDataType.ENUM: 315 return "CHANNEL_DATA_TYPE_ENUM" if api_format else ChannelDataTypeStrRep.ENUM.value 316 elif self == ChannelDataType.BIT_FIELD: 317 return ( 318 "CHANNEL_DATA_TYPE_BIT_FIELD" 319 if api_format 320 else ChannelDataTypeStrRep.BIT_FIELD.value 321 ) 322 elif self == ChannelDataType.BOOL: 323 return "CHANNEL_DATA_TYPE_BOOL" if api_format else ChannelDataTypeStrRep.BOOL.value 324 elif self == ChannelDataType.FLOAT: 325 return "CHANNEL_DATA_TYPE_FLOAT" if api_format else ChannelDataTypeStrRep.FLOAT.value 326 elif self == ChannelDataType.INT_32: 327 return "CHANNEL_DATA_TYPE_INT_32" if api_format else ChannelDataTypeStrRep.INT_32.value 328 elif self == ChannelDataType.INT_64: 329 return "CHANNEL_DATA_TYPE_INT_64" if api_format else ChannelDataTypeStrRep.INT_64.value 330 elif self == ChannelDataType.UINT_32: 331 return ( 332 "CHANNEL_DATA_TYPE_UINT_32" if api_format else ChannelDataTypeStrRep.UINT_32.value 333 ) 334 elif self == ChannelDataType.UINT_64: 335 return ( 336 "CHANNEL_DATA_TYPE_UINT_64" if api_format else ChannelDataTypeStrRep.UINT_64.value 337 ) 338 else: 339 raise Exception("Unreachable.")
Inherited Members
- enum.Enum
- name
- value
347def channel_fqn( 348 channel: Union[ 349 ChannelConfig, 350 ChannelConfigPb, 351 ChannelValue, 352 ChannelPb, 353 _AbstractChannel, 354 ], 355) -> str: 356 """ 357 Computes the fully qualified channel name. 358 359 NOTE: Component field of Channel is deprecated and should not be used. Function is left for code compatibility. 360 361 The fully-qualified channel name of a channel called 'voltage' is simply `voltage'. The 362 fully qualified name of a channel called 'temperature' of component 'motor' is a `motor.temperature'. 363 """ 364 365 if isinstance(channel, ChannelConfig): 366 if channel.component: 367 _component_deprecation_warning() 368 return _channel_fqn(channel.name, channel.component) 369 elif isinstance(channel, ChannelConfigPb): 370 return channel.name 371 elif isinstance(channel, ChannelPb): 372 return channel.name 373 else: 374 component = channel.get("component") 375 if component: 376 _component_deprecation_warning() 377 channel_name = channel["channel_name"] 378 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'.
425def is_data_type(val: IngestWithConfigDataChannelValue, target_type: ChannelDataType) -> bool: 426 if target_type == ChannelDataType.DOUBLE: 427 return val.HasField("double") 428 elif target_type == ChannelDataType.STRING: 429 return val.HasField("string") 430 elif target_type == ChannelDataType.ENUM: 431 return val.HasField("enum") 432 elif target_type == ChannelDataType.BIT_FIELD: 433 return val.HasField("bit_field") 434 elif target_type == ChannelDataType.BOOL: 435 return val.HasField("bool") 436 elif target_type == ChannelDataType.FLOAT: 437 return val.HasField("float") 438 elif target_type == ChannelDataType.INT_32: 439 return val.HasField("int32") 440 elif target_type == ChannelDataType.INT_64: 441 return val.HasField("int64") 442 elif target_type == ChannelDataType.UINT_32: 443 return val.HasField("uint32") 444 elif target_type == ChannelDataType.UINT_64: 445 return val.HasField("uint64")