|
33 | 33 | AND_TERM = 'andTerm'
|
34 | 34 | OR_TERM = 'orTerm'
|
35 | 35 | NOT_TERM = 'notTerm'
|
| 36 | +GREATER_TERM = 'greaterTerm' |
36 | 37 | FEATURE_TAG = 'feature'
|
37 | 38 | ATTRIBUTE_TAG = 'attribute'
|
38 | 39 | COMMAND_TAG = 'command'
|
39 | 40 | CONDITION_TAG = 'condition'
|
| 41 | +LITERAL_TAG = 'literal' |
40 | 42 |
|
41 | 43 |
|
42 | 44 | class ConformanceException(Exception):
|
@@ -123,6 +125,18 @@ def __str__(self):
|
123 | 125 | return 'P'
|
124 | 126 |
|
125 | 127 |
|
| 128 | +class literal: |
| 129 | + def __init__(self, value: str): |
| 130 | + self.value = int(value) |
| 131 | + |
| 132 | + def __call__(self): |
| 133 | + # This should never be called |
| 134 | + raise ConformanceException('Literal conformance function should not be called - this is simply a value holder') |
| 135 | + |
| 136 | + def __str__(self): |
| 137 | + return str(self.value) |
| 138 | + |
| 139 | + |
126 | 140 | class feature:
|
127 | 141 | def __init__(self, requiredFeature: uint, code: str):
|
128 | 142 | self.requiredFeature = requiredFeature
|
@@ -267,8 +281,25 @@ def __str__(self):
|
267 | 281 | op_strs = [str(op) for op in self.op_list]
|
268 | 282 | return f'({" | ".join(op_strs)})'
|
269 | 283 |
|
270 |
| -# TODO: add xor operation once it's required |
271 |
| -# TODO: how would equal and unequal operations work here? |
| 284 | + |
| 285 | +class greater_operation: |
| 286 | + def _type_ok(self, op: Callable): |
| 287 | + return type(op) == attribute or type(op) == literal |
| 288 | + |
| 289 | + def __init__(self, op1: Callable, op2: Callable): |
| 290 | + if not self._type_ok(op1) or not self._type_ok(op2): |
| 291 | + raise ConformanceException('Arithmetic operations can only have attribute or literal value children') |
| 292 | + self.op1 = op1 |
| 293 | + self.op2 = op2 |
| 294 | + |
| 295 | + def __call__(self, feature_map: uint, attribute_list: list[uint], all_command_list: list[uint]) -> ConformanceDecision: |
| 296 | + # For now, this is fully optional, need to implement this properly later, but it requires access to the actual attribute values |
| 297 | + # We need to reach into the attribute, but can't use it directly because the attribute callable is an EXISTENCE check and |
| 298 | + # the arithmetic functions require a value. |
| 299 | + return ConformanceDecision.OPTIONAL |
| 300 | + |
| 301 | + def __str__(self): |
| 302 | + return f'{str(self.op1)} > {str(self.op2)}' |
272 | 303 |
|
273 | 304 |
|
274 | 305 | class otherwise:
|
@@ -325,6 +356,8 @@ def parse_callable_from_xml(element: ElementTree.Element, params: ConformancePar
|
325 | 356 | return command(params.command_map[element.get('name')], element.get('name'))
|
326 | 357 | elif element.tag == CONDITION_TAG and element.get('name').lower() == 'zigbee':
|
327 | 358 | return zigbee()
|
| 359 | + elif element.tag == LITERAL_TAG: |
| 360 | + return literal(element.get('value')) |
328 | 361 | else:
|
329 | 362 | raise ConformanceException(
|
330 | 363 | f'Unexpected xml conformance element with no children {str(element.tag)} {str(element.attrib)}')
|
@@ -354,5 +387,9 @@ def parse_callable_from_xml(element: ElementTree.Element, params: ConformancePar
|
354 | 387 | return not_operation(ops[0])
|
355 | 388 | elif element.tag == OTHERWISE_CONFORM:
|
356 | 389 | return otherwise(ops)
|
| 390 | + elif element.tag == GREATER_TERM: |
| 391 | + if len(ops) != 2: |
| 392 | + raise ConformanceException(f'Greater than term found with more than two subelements {list(element)}') |
| 393 | + return greater_operation(ops[0], ops[1]) |
357 | 394 | else:
|
358 | 395 | raise ConformanceException(f'Unexpected conformance tag with children {element}')
|
0 commit comments