statable 3.1.0__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.
- codegen/__init__.py +38 -0
- codegen/c_code_generator.py +1752 -0
- codegen/code_merger.py +371 -0
- codegen/code_templates.py +509 -0
- codegen/config.py +167 -0
- codegen/enum_generator.py +377 -0
- codegen/event_queue_generator.py +303 -0
- codegen/interrupt_generator.py +345 -0
- codegen/naming_convention.py +171 -0
- codegen/osal_generator.py +282 -0
- codegen/role_function_generator.py +1244 -0
- codegen/sample_data.py +269 -0
- codegen/state_actions_generator.py +395 -0
- codegen/struct_generator.py +721 -0
- codegen/timer_generator.py +299 -0
- codegen/transition_generator.py +907 -0
- codegen/type_mapper.py +69 -0
- codegen/validate/__init__.py +38 -0
- codegen/validate/change_actions.py +90 -0
- codegen/validate/change_applier.py +416 -0
- codegen/validate/clipboard_manager.py +47 -0
- codegen/validate/data/__init__.py +30 -0
- codegen/validate/data/action_definitions.py +200 -0
- codegen/validate/data/keywords.py +46 -0
- codegen/validate/data/prompt_templates.py +176 -0
- codegen/validate/data/validation_rules.py +205 -0
- codegen/validate/items/__init__.py +30 -0
- codegen/validate/items/base_validator.py +25 -0
- codegen/validate/items/cell_validator.py +275 -0
- codegen/validate/items/custom_type_validator.py +75 -0
- codegen/validate/items/event_validator.py +78 -0
- codegen/validate/items/flag_validator.py +74 -0
- codegen/validate/items/interrupt_validator.py +75 -0
- codegen/validate/items/queue_validator.py +73 -0
- codegen/validate/items/role_function_validator.py +175 -0
- codegen/validate/items/state_validator.py +88 -0
- codegen/validate/items/timer_validator.py +120 -0
- codegen/validate/items/transition_validator.py +113 -0
- codegen/validate/items/variable_validator.py +132 -0
- codegen/validate/logger.py +52 -0
- codegen/validate/models.py +177 -0
- codegen/validate/prompt_generator.py +724 -0
- codegen/validate/response_parser.py +154 -0
- codegen/validate/response_validator.py +275 -0
- codegen/validate/validation_dialog.py +322 -0
- codegen/validate/validator.py +94 -0
- codegen/variable_generator.py +419 -0
- statable/__init__.py +71 -0
- statable/__main__.py +49 -0
- statable/cli.py +251 -0
- statable/global_defs.py +256 -0
- statable/mermaid_gen.py +144 -0
- statable/model.py +426 -0
- statable/parser.py +64 -0
- statable/sample_data.py +435 -0
- statable/shared/__init__.py +24 -0
- statable/shared/condition_library.py +63 -0
- statable/shared/literal_library.py +66 -0
- statable/shared/role_function_library.py +150 -0
- statable/state_machine.py +121 -0
- statable/xml_io.py +1164 -0
- statable-3.1.0.dist-info/METADATA +151 -0
- statable-3.1.0.dist-info/RECORD +117 -0
- statable-3.1.0.dist-info/WHEEL +5 -0
- statable-3.1.0.dist-info/entry_points.txt +2 -0
- statable-3.1.0.dist-info/top_level.txt +3 -0
- statable_gui/__init__.py +23 -0
- statable_gui/action_edit_dialog.py +214 -0
- statable_gui/code_generation_dialog.py +486 -0
- statable_gui/code_generation_settings_dialog.py +522 -0
- statable_gui/common_widgets.py +537 -0
- statable_gui/condition_builder_dialog.py +500 -0
- statable_gui/condition_edit_dialog.py +123 -0
- statable_gui/condition_template_dialog.py +58 -0
- statable_gui/config.py +21 -0
- statable_gui/dialogs.py +439 -0
- statable_gui/event_definition_dialog.py +605 -0
- statable_gui/event_delivery_settings_dialog.py +191 -0
- statable_gui/event_queue_dialog.py +245 -0
- statable_gui/global_defs.py +167 -0
- statable_gui/global_defs_dialog.py +796 -0
- statable_gui/i18n/__init__.py +118 -0
- statable_gui/interrupt_handler_edit_dialog.py +810 -0
- statable_gui/layer_settings_dialog.py +180 -0
- statable_gui/libcntrl/__init__.py +23 -0
- statable_gui/libcntrl/condition_library.py +13 -0
- statable_gui/libcntrl/literal_library.py +13 -0
- statable_gui/libcntrl/literal_management_dialog.py +185 -0
- statable_gui/libcntrl/role_function_edit_dialog.py +145 -0
- statable_gui/libcntrl/role_function_library.py +13 -0
- statable_gui/literal_definition_dialog.py +62 -0
- statable_gui/logger.py +83 -0
- statable_gui/main_window.py +1380 -0
- statable_gui/matrix_table.py +413 -0
- statable_gui/preference_keys.py +18 -0
- statable_gui/preferences.py +74 -0
- statable_gui/role_function_dialog.py +244 -0
- statable_gui/sample_data.py +11 -0
- statable_gui/state_actions_dialog.py +398 -0
- statable_gui/symbol_picker.py +170 -0
- statable_gui/traceball.py +37 -0
- statable_gui/transition_editor_direct/__init__.py +15 -0
- statable_gui/transition_editor_direct/actions_tab.py +856 -0
- statable_gui/transition_editor_direct/code_widget.py +344 -0
- statable_gui/transition_editor_direct/coverage_analyzer.py +163 -0
- statable_gui/transition_editor_direct/dialog.py +335 -0
- statable_gui/transition_editor_direct/draft.py +270 -0
- statable_gui/transition_editor_direct/edit_dialogs.py +160 -0
- statable_gui/transition_editor_direct/overview_tab.py +173 -0
- statable_gui/transition_editor_direct/palette_widget.py +233 -0
- statable_gui/transition_editor_direct/relations_edit_dialog.py +156 -0
- statable_gui/transition_editor_direct/relations_tab.py +230 -0
- statable_gui/transition_editor_direct/system_global_dialog.py +52 -0
- statable_gui/transition_editor_direct/transition_actions_dialog.py +109 -0
- statable_gui/transition_editor_direct/transitions_tab.py +620 -0
- statable_gui/validation_dialog.py +285 -0
- statable_gui/widgets.py +1315 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# codegen/validate/items/variable_validator.py
|
|
2
|
+
"""\nVariable validation\n"""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
9
|
+
|
|
10
|
+
from ..logger import logger
|
|
11
|
+
from ..models import ValidationIssue, ValidationSeverity, ValidationContext
|
|
12
|
+
from ..data.validation_rules import VALIDATION_RULES
|
|
13
|
+
from .base_validator import BaseValidator
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class VariableValidator(BaseValidator):
|
|
17
|
+
"""Variable validation class"""
|
|
18
|
+
|
|
19
|
+
category = "variable"
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
logger.debug("VariableValidator.__init__ started")
|
|
23
|
+
self.rules_data = VALIDATION_RULES.get(self.category, {})
|
|
24
|
+
self.rules = {
|
|
25
|
+
'VAR_DUPLICATE_NAME': self._check_duplicate_names,
|
|
26
|
+
'VAR_INVALID_TYPE': self._check_invalid_type,
|
|
27
|
+
'VAR_INVALID_ARRAY_SIZE': self._check_invalid_array_size,
|
|
28
|
+
}
|
|
29
|
+
logger.debug(f"VariableValidator.__init__ completed: {len(self.rules)} rules")
|
|
30
|
+
|
|
31
|
+
def validate(self, context: ValidationContext) -> List[ValidationIssue]:
|
|
32
|
+
logger.debug(f"VariableValidator.validate started")
|
|
33
|
+
issues = []
|
|
34
|
+
for code, rule_func in self.rules.items():
|
|
35
|
+
try:
|
|
36
|
+
result = rule_func(context)
|
|
37
|
+
if result:
|
|
38
|
+
issues.extend(result)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.error(f"Rule '{code}' failed: {e}", exc_info=True)
|
|
41
|
+
return issues
|
|
42
|
+
|
|
43
|
+
def _create_issue(self, code: str, **kwargs) -> ValidationIssue:
|
|
44
|
+
rule = self.rules_data.get(code, {})
|
|
45
|
+
message = rule.get('message', '').format(**kwargs)
|
|
46
|
+
severity = ValidationSeverity.from_string(rule.get('severity', 'info'))
|
|
47
|
+
return ValidationIssue(
|
|
48
|
+
category=self.category,
|
|
49
|
+
code=code,
|
|
50
|
+
message=message,
|
|
51
|
+
severity=severity,
|
|
52
|
+
suggestion=rule.get('suggestion', ''),
|
|
53
|
+
target=kwargs.get('name', ''),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def _check_duplicate_names(self, context):
|
|
57
|
+
issues = []
|
|
58
|
+
seen = set()
|
|
59
|
+
for var in context.variables:
|
|
60
|
+
name = getattr(var, 'name', '')
|
|
61
|
+
if name in seen:
|
|
62
|
+
issues.append(self._create_issue('VAR_DUPLICATE_NAME', name=name))
|
|
63
|
+
else:
|
|
64
|
+
seen.add(name)
|
|
65
|
+
return issues
|
|
66
|
+
|
|
67
|
+
def _check_invalid_type(self, context):
|
|
68
|
+
"""[v2.7.2 fix] Accept stdint.h types and strip qualifiers.
|
|
69
|
+
|
|
70
|
+
Previous behavior:
|
|
71
|
+
- 'uint32_t' / 'uint8_t' were flagged (only legacy aliases
|
|
72
|
+
'uint32' / 'uint8' were in the set)
|
|
73
|
+
- 'volatile uint32_t' was flagged
|
|
74
|
+
"""
|
|
75
|
+
issues = []
|
|
76
|
+
basic_types = {
|
|
77
|
+
# Plain C
|
|
78
|
+
'int', 'short', 'long', 'unsigned', 'signed',
|
|
79
|
+
'float', 'double', 'bool', 'char', 'string', 'void',
|
|
80
|
+
# stdint.h (C99)
|
|
81
|
+
'int8_t', 'int16_t', 'int32_t', 'int64_t',
|
|
82
|
+
'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t',
|
|
83
|
+
'intptr_t', 'uintptr_t', 'size_t',
|
|
84
|
+
# Legacy aliases (without _t)
|
|
85
|
+
'int8', 'int16', 'int32', 'int64',
|
|
86
|
+
'uint', 'uint8', 'uint16', 'uint32', 'uint64',
|
|
87
|
+
}
|
|
88
|
+
qualifiers = {
|
|
89
|
+
'volatile', 'const', 'static', 'register',
|
|
90
|
+
'extern', 'inline', 'unsigned', 'signed',
|
|
91
|
+
'long', 'short',
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
def normalize(t: str) -> str:
|
|
95
|
+
parts = (t or '').strip().split()
|
|
96
|
+
base = [p for p in parts if p not in qualifiers]
|
|
97
|
+
return ' '.join(base) if base else (t or '').strip()
|
|
98
|
+
|
|
99
|
+
def strip_extras(t: str) -> str:
|
|
100
|
+
t = t.rstrip('*').strip()
|
|
101
|
+
t = t.split('[')[0].strip()
|
|
102
|
+
return t
|
|
103
|
+
|
|
104
|
+
custom_type_names = {
|
|
105
|
+
getattr(ct, 'name', '') for ct in context.custom_types
|
|
106
|
+
}
|
|
107
|
+
for var in context.variables:
|
|
108
|
+
var_type = getattr(var, 'type', '') or ''
|
|
109
|
+
name = getattr(var, 'name', '')
|
|
110
|
+
if not var_type:
|
|
111
|
+
continue
|
|
112
|
+
normalized = normalize(var_type)
|
|
113
|
+
candidates = {
|
|
114
|
+
var_type, normalized,
|
|
115
|
+
strip_extras(var_type), strip_extras(normalized),
|
|
116
|
+
}
|
|
117
|
+
if candidates & basic_types:
|
|
118
|
+
continue
|
|
119
|
+
if candidates & custom_type_names:
|
|
120
|
+
continue
|
|
121
|
+
issues.append(self._create_issue(
|
|
122
|
+
'VAR_INVALID_TYPE', name=name, type=var_type))
|
|
123
|
+
return issues
|
|
124
|
+
|
|
125
|
+
def _check_invalid_array_size(self, context):
|
|
126
|
+
issues = []
|
|
127
|
+
for var in context.variables:
|
|
128
|
+
array_size = getattr(var, 'array_size', 0)
|
|
129
|
+
name = getattr(var, 'name', '')
|
|
130
|
+
if array_size < 0:
|
|
131
|
+
issues.append(self._create_issue('VAR_INVALID_ARRAY_SIZE', name=name))
|
|
132
|
+
return issues
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# codegen/validate/logger.py
|
|
2
|
+
"""\nLogger setup for validation module\n"""
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def setup_logger(name: str = "validate", log_dir: str = None) -> logging.Logger:
|
|
10
|
+
"""Setup logger"""
|
|
11
|
+
logger = logging.getLogger(name)
|
|
12
|
+
|
|
13
|
+
if logger.handlers:
|
|
14
|
+
return logger
|
|
15
|
+
|
|
16
|
+
logger.setLevel(logging.DEBUG)
|
|
17
|
+
|
|
18
|
+
# Console handler
|
|
19
|
+
console_handler = logging.StreamHandler()
|
|
20
|
+
console_handler.setLevel(logging.DEBUG)
|
|
21
|
+
console_format = logging.Formatter(
|
|
22
|
+
'%(asctime)s - %(name)s - %(levelname)s - [%(funcName)s:%(lineno)d] - %(message)s',
|
|
23
|
+
datefmt='%H:%M:%S'
|
|
24
|
+
)
|
|
25
|
+
console_handler.setFormatter(console_format)
|
|
26
|
+
logger.addHandler(console_handler)
|
|
27
|
+
|
|
28
|
+
# File handler
|
|
29
|
+
if log_dir is None:
|
|
30
|
+
log_dir = os.path.join(
|
|
31
|
+
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
|
|
32
|
+
'logs'
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
36
|
+
|
|
37
|
+
log_file = os.path.join(log_dir, f'validate_{datetime.now().strftime("%Y%m%d")}.log')
|
|
38
|
+
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
|
39
|
+
file_handler.setLevel(logging.DEBUG)
|
|
40
|
+
file_format = logging.Formatter(
|
|
41
|
+
'%(asctime)s - %(name)s - %(levelname)s - [%(funcName)s:%(lineno)d] - %(message)s'
|
|
42
|
+
)
|
|
43
|
+
file_handler.setFormatter(file_format)
|
|
44
|
+
logger.addHandler(file_handler)
|
|
45
|
+
|
|
46
|
+
logger.debug(f"Logger setup completed: {name} -> {log_file}")
|
|
47
|
+
return logger
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Default logger
|
|
51
|
+
validate_logger = setup_logger()
|
|
52
|
+
logger = validate_logger
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# codegen/validate/models.py
|
|
2
|
+
"""\nValidation data model definitions\n"""
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import List, Dict, Any, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ValidationSeverity(Enum):
|
|
10
|
+
"""Validation severity"""
|
|
11
|
+
ERROR = "error"
|
|
12
|
+
WARNING = "warning"
|
|
13
|
+
INFO = "info"
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_string(cls, value: str) -> 'ValidationSeverity':
|
|
17
|
+
"""Convert from string"""
|
|
18
|
+
value_map = {
|
|
19
|
+
'error': cls.ERROR,
|
|
20
|
+
'warning': cls.WARNING,
|
|
21
|
+
'info': cls.INFO,
|
|
22
|
+
'ERROR': cls.ERROR,
|
|
23
|
+
'WARNING': cls.WARNING,
|
|
24
|
+
'INFO': cls.INFO,
|
|
25
|
+
'Error': cls.ERROR,
|
|
26
|
+
'Warning': cls.WARNING,
|
|
27
|
+
'Info': cls.INFO,
|
|
28
|
+
}
|
|
29
|
+
return value_map.get(value, cls.INFO)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ValidationIssue:
|
|
34
|
+
"""Validation issue"""
|
|
35
|
+
category: str
|
|
36
|
+
code: str
|
|
37
|
+
message: str
|
|
38
|
+
severity: ValidationSeverity
|
|
39
|
+
target: str = ""
|
|
40
|
+
suggestion: str = ""
|
|
41
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
42
|
+
|
|
43
|
+
def to_dict(self) -> Dict:
|
|
44
|
+
"""Convert to dictionary"""
|
|
45
|
+
return {
|
|
46
|
+
'category': self.category,
|
|
47
|
+
'code': self.code,
|
|
48
|
+
'message': self.message,
|
|
49
|
+
'severity': self.severity.value,
|
|
50
|
+
'target': self.target,
|
|
51
|
+
'suggestion': self.suggestion,
|
|
52
|
+
'details': self.details,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_dict(cls, data: Dict) -> 'ValidationIssue':
|
|
57
|
+
"""Restore from dictionary"""
|
|
58
|
+
return cls(
|
|
59
|
+
category=data.get('category', ''),
|
|
60
|
+
code=data.get('code', ''),
|
|
61
|
+
message=data.get('message', ''),
|
|
62
|
+
severity=ValidationSeverity.from_string(data.get('severity', 'info')),
|
|
63
|
+
target=data.get('target', ''),
|
|
64
|
+
suggestion=data.get('suggestion', ''),
|
|
65
|
+
details=data.get('details', {}),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def __str__(self) -> str:
|
|
69
|
+
return f"[{self.severity.value.upper()}] {self.category}: {self.message}"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class ValidationResult:
|
|
74
|
+
"""Validation result"""
|
|
75
|
+
issues: List[ValidationIssue] = field(default_factory=list)
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def passed(self) -> bool:
|
|
79
|
+
return self.error_count == 0
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def error_count(self) -> int:
|
|
83
|
+
return sum(1 for i in self.issues if i.severity == ValidationSeverity.ERROR)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def warning_count(self) -> int:
|
|
87
|
+
return sum(1 for i in self.issues if i.severity == ValidationSeverity.WARNING)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def info_count(self) -> int:
|
|
91
|
+
return sum(1 for i in self.issues if i.severity == ValidationSeverity.INFO)
|
|
92
|
+
|
|
93
|
+
def get_errors(self) -> List[ValidationIssue]:
|
|
94
|
+
return [i for i in self.issues if i.severity == ValidationSeverity.ERROR]
|
|
95
|
+
|
|
96
|
+
def get_warnings(self) -> List[ValidationIssue]:
|
|
97
|
+
return [i for i in self.issues if i.severity == ValidationSeverity.WARNING]
|
|
98
|
+
|
|
99
|
+
def get_infos(self) -> List[ValidationIssue]:
|
|
100
|
+
return [i for i in self.issues if i.severity == ValidationSeverity.INFO]
|
|
101
|
+
|
|
102
|
+
def get_by_category(self, category: str) -> List[ValidationIssue]:
|
|
103
|
+
return [i for i in self.issues if i.category == category]
|
|
104
|
+
|
|
105
|
+
def to_dict(self) -> Dict:
|
|
106
|
+
return {
|
|
107
|
+
'passed': self.passed,
|
|
108
|
+
'error_count': self.error_count,
|
|
109
|
+
'warning_count': self.warning_count,
|
|
110
|
+
'info_count': self.info_count,
|
|
111
|
+
'issues': [i.to_dict() for i in self.issues],
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def from_dict(cls, data: Dict) -> 'ValidationResult':
|
|
116
|
+
return cls(
|
|
117
|
+
issues=[ValidationIssue.from_dict(i) for i in data.get('issues', [])]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def __str__(self) -> str:
|
|
121
|
+
return (f"ValidationResult(errors={self.error_count}, "
|
|
122
|
+
f"warnings={self.warning_count}, infos={self.info_count})")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class ValidationContext:
|
|
127
|
+
"""Validation context"""
|
|
128
|
+
state_machine: Any = None
|
|
129
|
+
global_defs: Any = None
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def states(self) -> Dict:
|
|
133
|
+
return self.state_machine.states if self.state_machine else {}
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def events(self) -> Dict:
|
|
137
|
+
return self.state_machine.events if self.state_machine else {}
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def transitions(self) -> List:
|
|
141
|
+
return self.state_machine.transitions if self.state_machine else []
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def role_functions(self) -> Dict:
|
|
145
|
+
return self.state_machine.role_functions if self.state_machine else {}
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def initial_state(self) -> Optional[str]:
|
|
149
|
+
return self.state_machine.initial_state if self.state_machine else None
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def variables(self) -> List:
|
|
153
|
+
return self.global_defs.variables if self.global_defs else []
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def flags(self) -> List:
|
|
157
|
+
return self.global_defs.flags if self.global_defs else []
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def event_queues(self) -> List:
|
|
161
|
+
return self.global_defs.event_queues if self.global_defs else []
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def interrupts(self) -> List:
|
|
165
|
+
return self.global_defs.interrupts if self.global_defs else []
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def custom_types(self) -> List:
|
|
169
|
+
return self.global_defs.custom_types if self.global_defs else []
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def timer_base(self):
|
|
173
|
+
return self.global_defs.timer_base if self.global_defs else None
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def extra_timers(self) -> List:
|
|
177
|
+
return self.global_defs.extra_timers if self.global_defs else []
|