rccn-gen 1.1.0__tar.gz → 1.2.0__tar.gz
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.
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/.gitignore +2 -2
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/CHANGELOG.md +17 -1
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/PKG-INFO +3 -3
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/README.md +2 -2
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/pyproject.toml +1 -1
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/systems.py +272 -77
- rccn_gen-1.2.0/src/rccn_gen/text_modules/cargo_toml/cargo.txt +17 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/.gitlab-ci.yml +0 -0
- /rccn_gen-1.1.0/src/rccn_gen/text_modules/cargo_toml/cargo.txt → /rccn_gen-1.2.0/rccn_usr_bi_x1_cntrl_app/Cargo.toml +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/LICENSE +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/__init__.py +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/command/command.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/command/command_module_enum.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/command/command_module_struct.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/main/main.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/main/service_module_import_service.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/main/service_module_mod_service.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/main/service_module_register_service.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/mod/mod.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/service/command_module_match_cmd.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/service/service.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/telemetry/telemetry.txt +0 -0
- {rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/utils.py +0 -0
@@ -1,5 +1,21 @@
|
|
1
1
|
# CHANGE LOG
|
2
2
|
|
3
|
+
## [1.2.0] - 2025-05-05
|
4
|
+
- Added argument hints for RCCNCommand, Service and RCCNContainer
|
5
|
+
- New create_and_add_service() method for the Application
|
6
|
+
- New create_and_add_command() method for the Service
|
7
|
+
- Better error messages
|
8
|
+
- Subtypes of RCCNCommands and RCCNContainers are defined automatically, if not specified by the user
|
9
|
+
- General refactoring for more readable code
|
10
|
+
- Added add_integer_parameter method to RCCNContainer
|
11
|
+
- Bug fixes
|
12
|
+
|
13
|
+
### [1.1.2] - 2025-04-23
|
14
|
+
- The `export_directory` argument for the `Application` is now mandatory.
|
15
|
+
|
16
|
+
### [1.1.1] - 2025-04-22
|
17
|
+
- Changed the user input checking to allow for multiple command with the same name and subtype, as long as they are associated to different services.
|
18
|
+
|
3
19
|
## [1.1.0] - 2025-04-14
|
4
20
|
- Added derive statements to the rust container structs in the telemetry.rs file.
|
5
21
|
- Inherit subtype of a container from the `condition` argument, if given, or from the new (optional) `subtype` argument. If only the latter is given, condition is created from that. Subtype is included in the derive statements.
|
@@ -10,4 +26,4 @@
|
|
10
26
|
- The `tc` parameter in the service.rs file is now mutable.
|
11
27
|
- `System` of a command is obtained from the base command, if no system argument is given.
|
12
28
|
- Added support for long and short descriptions of commands, arguments and supported telemetry parameters.
|
13
|
-
- Bug fixes.
|
29
|
+
- Bug fixes.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rccn_gen
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.2.0
|
4
4
|
Summary: A python based generator for RACCOON OS source files in Rust from yamcs-pymdb config files.
|
5
5
|
Project-URL: Homepage, https://gitlab.com/rccn/pymdb_code_generation
|
6
6
|
Project-URL: Issues, https://gitlab.com/rccn/pymdb_code_generation/issues
|
@@ -44,7 +44,7 @@ An application can be defined with the following statement.
|
|
44
44
|
```python
|
45
45
|
app = Application(system=root_system, name="ExampleApp", apid=42)
|
46
46
|
```
|
47
|
-
It has the obligatory arguments **system**, **name** and **
|
47
|
+
It has the obligatory arguments **system**, **name**, **apid** and **export_directory**. After all Applications, Services and RCCNCommands are defined, the rust code generator can be called on the application with `app.generate_rccn_code(export_directory='.')`.
|
48
48
|
|
49
49
|
### Service
|
50
50
|
|
@@ -155,4 +155,4 @@ Snapshots of the .rs files in the export directory are stored in `/user/`. These
|
|
155
155
|
app.keep_snapshots = 15 # Whatever value you want
|
156
156
|
```
|
157
157
|
|
158
|
-
With the sequence from above, it becomes apperant that changes to the .rs file in the export directory always trump changes to the pymdb config. If for example, a main.rs file is generated for an application with and APID of 42, and this apid is changed in the main.rs file to 45, this change will persist after regenerating from the python config. Even if changes to the pymdb config where made after the changes to the main.rs file.
|
158
|
+
With the sequence from above, it becomes apperant that changes to the .rs file in the export directory always trump changes to the pymdb config. If for example, a main.rs file is generated for an application with and APID of 42, and this apid is changed in the main.rs file to 45, this change will persist after regenerating from the python config. Even if changes to the pymdb config where made after the changes to the main.rs file.
|
@@ -28,7 +28,7 @@ An application can be defined with the following statement.
|
|
28
28
|
```python
|
29
29
|
app = Application(system=root_system, name="ExampleApp", apid=42)
|
30
30
|
```
|
31
|
-
It has the obligatory arguments **system**, **name** and **
|
31
|
+
It has the obligatory arguments **system**, **name**, **apid** and **export_directory**. After all Applications, Services and RCCNCommands are defined, the rust code generator can be called on the application with `app.generate_rccn_code(export_directory='.')`.
|
32
32
|
|
33
33
|
### Service
|
34
34
|
|
@@ -139,4 +139,4 @@ Snapshots of the .rs files in the export directory are stored in `/user/`. These
|
|
139
139
|
app.keep_snapshots = 15 # Whatever value you want
|
140
140
|
```
|
141
141
|
|
142
|
-
With the sequence from above, it becomes apperant that changes to the .rs file in the export directory always trump changes to the pymdb config. If for example, a main.rs file is generated for an application with and APID of 42, and this apid is changed in the main.rs file to 45, this change will persist after regenerating from the python config. Even if changes to the pymdb config where made after the changes to the main.rs file.
|
142
|
+
With the sequence from above, it becomes apperant that changes to the .rs file in the export directory always trump changes to the pymdb config. If for example, a main.rs file is generated for an application with and APID of 42, and this apid is changed in the main.rs file to 45, this change will persist after regenerating from the python config. Even if changes to the pymdb config where made after the changes to the main.rs file.
|
@@ -1,10 +1,11 @@
|
|
1
|
-
from yamcs.pymdb import
|
1
|
+
from yamcs.pymdb import *
|
2
2
|
import shutil
|
3
3
|
import difflib
|
4
4
|
import datetime
|
5
5
|
from caseconverter import *
|
6
6
|
from importlib.resources import files
|
7
7
|
from .utils import *
|
8
|
+
from collections.abc import Mapping, Sequence
|
8
9
|
|
9
10
|
class Application(Subsystem):
|
10
11
|
"""
|
@@ -16,9 +17,9 @@ class Application(Subsystem):
|
|
16
17
|
name: str,
|
17
18
|
apid: int,
|
18
19
|
vcid: int = 0,
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
export_directory = '.',
|
21
|
+
snapshot_directory = './.rccn_snapshots',
|
22
|
+
diff_directory = './.rccn_diffs',
|
22
23
|
snapshots = True,
|
23
24
|
*args,
|
24
25
|
**kwargs
|
@@ -27,15 +28,13 @@ class Application(Subsystem):
|
|
27
28
|
|
28
29
|
self.apid = apid
|
29
30
|
self.vcid = vcid
|
31
|
+
self.export_directory = export_directory
|
30
32
|
system._subsystems_by_name[name] = self
|
31
|
-
self.
|
32
|
-
self.
|
33
|
-
self.
|
34
|
-
self.diff_file_path = diff_file_path
|
33
|
+
self.snapshot_directory = snapshot_directory
|
34
|
+
self.snapshot_generated_file_path = os.path.join(snapshot_directory, 'auto_generated')
|
35
|
+
self.diff_directory = diff_directory
|
35
36
|
self.text_modules_path = files('rccn_gen').joinpath('text_modules')
|
36
|
-
print(self.text_modules_path)
|
37
37
|
self.text_modules_main_path = os.path.join(self.text_modules_path, 'main')
|
38
|
-
print(self.text_modules_main_path)
|
39
38
|
self.snapshots = snapshots
|
40
39
|
self.keep_snapshots = 10
|
41
40
|
|
@@ -43,27 +42,52 @@ class Application(Subsystem):
|
|
43
42
|
if not isinstance(service, Service):
|
44
43
|
raise TypeError('Service '+service.name+' is not a RCCNCommand.')
|
45
44
|
service.add_to_application(self)
|
46
|
-
|
45
|
+
|
46
|
+
def create_and_add_service(
|
47
|
+
self,
|
48
|
+
name: str,
|
49
|
+
service_id: int,
|
50
|
+
aliases: Mapping[str, str] | None = None,
|
51
|
+
short_description: str | None = None,
|
52
|
+
long_description: str | None = None,
|
53
|
+
extra: Mapping[str, str] | None = None,
|
54
|
+
*args,
|
55
|
+
**kwargs
|
56
|
+
):
|
57
|
+
if 'system' not in kwargs:
|
58
|
+
kwargs['system'] = self
|
59
|
+
else:
|
60
|
+
raise ValueError('RCCN-Error: \'create_and_add_service\' function can not be called with a \'system\' argument.')
|
61
|
+
Service(
|
62
|
+
name=name,
|
63
|
+
service_id=service_id,
|
64
|
+
aliases=aliases,
|
65
|
+
short_description=short_description,
|
66
|
+
long_description=long_description,
|
67
|
+
*args,
|
68
|
+
**kwargs
|
69
|
+
)
|
70
|
+
|
47
71
|
def file_paths(self):
|
48
72
|
paths = {
|
49
|
-
'main': os.path.join(self.
|
50
|
-
'main_generated_snapshot': os.path.join(self.
|
73
|
+
'main': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.name), 'src', 'main.rs'),
|
74
|
+
'main_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', 'rccn_usr_'+snakecase(self.name), 'src', 'main.rs'),
|
51
75
|
'main_user_snapshot': os.path.join(self.user_snapshot_path(), 'rccn_usr_'+snakecase(self.name), 'src', 'main.rs'),
|
52
|
-
'main_diff': os.path.join(self.
|
76
|
+
'main_diff': os.path.join(self.diff_directory, 'rccn_usr_'+snakecase(self.name), 'src', 'main.diff'),
|
53
77
|
'main_template': os.path.join(self.text_modules_main_path, 'main.txt'),
|
54
|
-
'cargo_toml': os.path.join(self.
|
78
|
+
'cargo_toml': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.name), 'Cargo.toml'),
|
55
79
|
'cargo_toml_template': os.path.join(self.text_modules_path, 'cargo_toml', 'cargo.txt'),
|
56
80
|
}
|
57
81
|
return paths
|
58
82
|
|
59
83
|
def user_snapshot_path(self):
|
60
|
-
return os.path.join(self.
|
84
|
+
return os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
61
85
|
|
62
86
|
def services(self):
|
63
87
|
return [subsystem for subsystem in self.subsystems if isinstance(subsystem, Service)]
|
64
88
|
|
65
89
|
def create_rccn_directories(self):
|
66
|
-
app_src_dir = os.path.join(self.
|
90
|
+
app_src_dir = os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.name), 'src')
|
67
91
|
if not os.path.exists(app_src_dir):
|
68
92
|
os.makedirs(app_src_dir)
|
69
93
|
for service in self.services():
|
@@ -111,8 +135,20 @@ class Application(Subsystem):
|
|
111
135
|
text = delete_all_keywords(text)
|
112
136
|
return text
|
113
137
|
|
114
|
-
def generate_rccn_code(self, rebase_changes=True, check=True
|
115
|
-
|
138
|
+
def generate_rccn_code(self, export_directory:str, snapshot_directory='', diff_directory='', rebase_changes=True, check=True):
|
139
|
+
# Update export, snapshot and diff directory for the Application and all Services
|
140
|
+
self.export_directory = export_directory
|
141
|
+
if snapshot_directory == '':
|
142
|
+
snapshot_directory = os.path.join(self.export_directory, '.rccn-snapshots')
|
143
|
+
if diff_directory == '':
|
144
|
+
diff_directory = os.path.join(self.export_directory, '.rccn-diffs')
|
145
|
+
self.snapshot_directory = snapshot_directory
|
146
|
+
self.diff_directory = diff_directory
|
147
|
+
for service in self.services():
|
148
|
+
service.export_directory = self.export_directory
|
149
|
+
service.diff_directory = self.diff_directory
|
150
|
+
service.snapshot_directory = self.snapshot_directory
|
151
|
+
|
116
152
|
if check:
|
117
153
|
self.check_user_input()
|
118
154
|
self.rebase_changes = rebase_changes
|
@@ -122,12 +158,12 @@ class Application(Subsystem):
|
|
122
158
|
if not os.path.exists(self.file_paths()['cargo_toml']):
|
123
159
|
self.generate_cargo_toml_file()
|
124
160
|
for service in self.services():
|
125
|
-
service.
|
161
|
+
service.export_directory = self.export_directory
|
126
162
|
service.generate_rccn_service_file()
|
127
163
|
if not os.path.exists(service.file_paths()['mod']):
|
128
164
|
service.generate_mod_file()
|
129
165
|
service.generate_telemetry_file()
|
130
|
-
service.generate_rccn_command_file(os.path.join(self.
|
166
|
+
service.generate_rccn_command_file(os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.name), 'src'), os.path.join(self.text_modules_path, 'command'))
|
131
167
|
self.delete_old_snapshots()
|
132
168
|
|
133
169
|
def generate_snapshot(self, current_file_reference, snapshot_file_reference):
|
@@ -146,37 +182,37 @@ class Application(Subsystem):
|
|
146
182
|
diff_file.write(diff_text)
|
147
183
|
|
148
184
|
def delete_old_snapshots(self):
|
149
|
-
if os.path.exists(os.path.join(self.
|
150
|
-
user_snapshots_path = os.path.join(self.
|
185
|
+
if os.path.exists(os.path.join(self.snapshot_directory, 'user')):
|
186
|
+
user_snapshots_path = os.path.join(self.snapshot_directory, 'user')
|
151
187
|
snapshots = [os.path.join(user_snapshots_path, d) for d in os.listdir(user_snapshots_path) if os.path.isdir(os.path.join(user_snapshots_path, d))]
|
152
188
|
snapshots.sort(key=os.path.getctime)
|
153
189
|
while len(snapshots) > self.keep_snapshots:
|
154
190
|
shutil.rmtree(snapshots.pop(0))
|
155
191
|
|
156
192
|
def check_user_input(self):
|
157
|
-
# Check if all services have unique names
|
193
|
+
# Check if all services in the application have unique names
|
158
194
|
service_names = [service.name for service in self.services()]
|
159
195
|
if len(service_names) != len(set(service_names)):
|
160
|
-
raise ValueError('
|
196
|
+
raise ValueError('RCCN-Error: App \''+self.name+'\' has multiple services with the same name.')
|
161
197
|
|
162
|
-
# Check if all services have unique service_ids
|
198
|
+
# Check if all services in the application have unique service_ids
|
163
199
|
service_ids = [service.service_id for service in self.services()]
|
164
200
|
if len(service_ids) != len(set(service_ids)):
|
165
|
-
raise ValueError('
|
201
|
+
raise ValueError('RCCN-Error: App \''+self.name+'\' has multiple services with the same ID.')
|
166
202
|
|
167
|
-
# Check if all commands have unique names
|
168
|
-
command_names = []
|
203
|
+
# Check if all commands in each service have unique names
|
169
204
|
for service in self.services():
|
205
|
+
command_names = []
|
170
206
|
command_names += [command.name for command in service.rccn_commands()]
|
171
|
-
|
172
|
-
|
207
|
+
if len(command_names) != len(set(command_names)):
|
208
|
+
raise ValueError('RCCN-Error: Service \''+service.name+'\' has multiple commands with the same name.')
|
173
209
|
|
174
|
-
# Check if all commands have unique subtypes
|
175
|
-
command_subtypes = []
|
210
|
+
# Check if all commands in each service have unique subtypes
|
176
211
|
for service in self.services():
|
212
|
+
command_subtypes = []
|
177
213
|
command_subtypes += [command.assignments['subtype'] for command in service.rccn_commands()]
|
178
|
-
|
179
|
-
|
214
|
+
if len(command_subtypes) != len(set(command_subtypes)):
|
215
|
+
raise ValueError('RCCN-Error: Service \''+service.name+'\' has multiple commands with the same subtype.')
|
180
216
|
|
181
217
|
def generate_cargo_toml_file(self):
|
182
218
|
with open(self.file_paths()['cargo_toml_template'], 'r') as file:
|
@@ -218,10 +254,7 @@ class Service(Subsystem):
|
|
218
254
|
name=self.name,
|
219
255
|
*self.init_args, **self.init_kwargs
|
220
256
|
)
|
221
|
-
self.export_file_path = self.system.export_file_path
|
222
|
-
self.snapshot_file_path = self.system.snapshot_file_path
|
223
257
|
self.snapshots = self.system.snapshots
|
224
|
-
self.diff_file_path = self.system.diff_file_path
|
225
258
|
|
226
259
|
def add_container(self, container):
|
227
260
|
if not isinstance(container, RCCNContainer):
|
@@ -235,22 +268,22 @@ class Service(Subsystem):
|
|
235
268
|
|
236
269
|
def file_paths(self):
|
237
270
|
paths = {
|
238
|
-
'service': os.path.join(self.
|
239
|
-
'service_generated_snapshot': os.path.join(self.
|
240
|
-
'service_user_snapshot': os.path.join(self.
|
241
|
-
'service_diff': os.path.join(self.
|
271
|
+
'service': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
272
|
+
'service_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
273
|
+
'service_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
274
|
+
'service_diff': os.path.join(self.diff_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'service.diff'),
|
242
275
|
'service_template': os.path.join(self.text_modules_service_path, 'service.txt'),
|
243
|
-
'command': os.path.join(self.
|
244
|
-
'command_generated_snapshot': os.path.join(self.
|
245
|
-
'command_user_snapshot': os.path.join(self.
|
246
|
-
'command_diff': os.path.join(self.
|
276
|
+
'command': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
277
|
+
'command_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
278
|
+
'command_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
279
|
+
'command_diff': os.path.join(self.diff_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'command.diff'),
|
247
280
|
'command_template': os.path.join(self.text_modules_command_path, 'command.txt'),
|
248
|
-
'mod': os.path.join(self.
|
281
|
+
'mod': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'mod.rs'),
|
249
282
|
'mod_template': os.path.join(self.text_modules_path, 'mod', 'mod.txt'),
|
250
|
-
'telemetry': os.path.join(self.
|
251
|
-
'telemetry_generated_snapshot': os.path.join(self.
|
252
|
-
'telemetry_user_snapshot': os.path.join(self.
|
253
|
-
'telemetry_diff': os.path.join(self.
|
283
|
+
'telemetry': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'telemetry.rs'),
|
284
|
+
'telemetry_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'telemetry.rs'),
|
285
|
+
'telemetry_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
286
|
+
'telemetry_diff': os.path.join(self.diff_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'telemetry.diff'),
|
254
287
|
'telemetry_template': os.path.join(self.text_modules_telemetry_path, 'telemetry.txt'),
|
255
288
|
}
|
256
289
|
return paths
|
@@ -327,8 +360,8 @@ class Service(Subsystem):
|
|
327
360
|
command_file_path = self.file_paths()['command_template']
|
328
361
|
with open(command_file_path, 'r') as file:
|
329
362
|
command_file_text = file.read()
|
330
|
-
|
331
|
-
with open(
|
363
|
+
command_export_directory = os.path.join(export_file_dir, snakecase(self.name), 'command.rs')
|
364
|
+
with open(command_export_directory, 'w') as file:
|
332
365
|
file.write(self.find_and_replace_keywords(command_file_text, text_modules_path))
|
333
366
|
# Create snapshot of command.rs if instructed
|
334
367
|
if self.snapshots:
|
@@ -384,20 +417,93 @@ class Service(Subsystem):
|
|
384
417
|
container.__class__ = RCCNContainer
|
385
418
|
telemetry_definition_text += container.generate_rccn_telemetry()
|
386
419
|
return telemetry_definition_text
|
420
|
+
|
421
|
+
def create_and_add_command(
|
422
|
+
self,
|
423
|
+
name: str,
|
424
|
+
*,
|
425
|
+
aliases: Mapping[str, str] | None = None,
|
426
|
+
short_description: str | None = None,
|
427
|
+
long_description: str | None = None,
|
428
|
+
extra: Mapping[str, str] | None = None,
|
429
|
+
abstract: bool = False,
|
430
|
+
base: Command | str | None = None,
|
431
|
+
assignments: Mapping[str, Any] | None = None,
|
432
|
+
arguments: Sequence[Argument] | None = None,
|
433
|
+
entries: Sequence[CommandEntry] | None = None,
|
434
|
+
level: CommandLevel = CommandLevel.NORMAL,
|
435
|
+
warning_message: str | None = None,
|
436
|
+
constraint: (
|
437
|
+
Union[TransmissionConstraint, Sequence[TransmissionConstraint]] | None
|
438
|
+
) = None,
|
439
|
+
):
|
440
|
+
Command(
|
441
|
+
name=name,
|
442
|
+
system=self,
|
443
|
+
aliases=aliases,
|
444
|
+
short_description=short_description,
|
445
|
+
long_description=long_description,
|
446
|
+
extra=extra,
|
447
|
+
abstract=abstract,
|
448
|
+
base=base,
|
449
|
+
assignments=assignments,
|
450
|
+
arguments=arguments,
|
451
|
+
entries=entries,
|
452
|
+
level=level,
|
453
|
+
warning_message=warning_message,
|
454
|
+
constraint=constraint
|
455
|
+
)
|
456
|
+
|
457
|
+
def rccn_container(self):
|
458
|
+
return [container for container in self.containers if isinstance(container, RCCNContainer)]
|
387
459
|
|
388
460
|
|
389
461
|
class RCCNCommand(Command):
|
390
|
-
def __init__(
|
391
|
-
self
|
392
|
-
|
462
|
+
def __init__(
|
463
|
+
self,
|
464
|
+
name: str,
|
465
|
+
*,
|
466
|
+
aliases: Mapping[str, str] | None = None,
|
467
|
+
short_description: str | None = None,
|
468
|
+
long_description: str | None = None,
|
469
|
+
extra: Mapping[str, str] | None = None,
|
470
|
+
abstract: bool = False,
|
471
|
+
base: Command | str | None = None,
|
472
|
+
assignments: Mapping[str, Any] | None = None,
|
473
|
+
arguments: Sequence[Argument] | None = None,
|
474
|
+
entries: Sequence[CommandEntry] | None = None,
|
475
|
+
level: CommandLevel = CommandLevel.NORMAL,
|
476
|
+
warning_message: str | None = None,
|
477
|
+
constraint: (
|
478
|
+
Union[TransmissionConstraint, Sequence[TransmissionConstraint]] | None
|
479
|
+
) = None,
|
480
|
+
**kwargs
|
481
|
+
):
|
482
|
+
self.init_args = ()
|
483
|
+
self.init_kwargs = {
|
484
|
+
'name': name,
|
485
|
+
'aliases': aliases,
|
486
|
+
'short_description': short_description,
|
487
|
+
'long_description': long_description,
|
488
|
+
'extra': extra,
|
489
|
+
'abstract': abstract,
|
490
|
+
'base': base,
|
491
|
+
'assignments': assignments,
|
492
|
+
'arguments': arguments,
|
493
|
+
'entries': entries,
|
494
|
+
'level': level,
|
495
|
+
'warning_message': warning_message,
|
496
|
+
'constraint': constraint,
|
497
|
+
**kwargs
|
498
|
+
}
|
393
499
|
if 'system' in kwargs and isinstance(kwargs['system'], Service):
|
394
500
|
self.add_to_service(kwargs['system'])
|
395
|
-
elif
|
396
|
-
self.add_to_service(
|
501
|
+
elif base and (isinstance(base, Command) or isinstance(base, RCCNCommand)):
|
502
|
+
self.add_to_service(base.system)
|
397
503
|
|
398
504
|
def add_to_service(self, service):
|
399
505
|
if not 'base' in self.init_kwargs and not any(command.name == 'base' for command in service.commands):
|
400
|
-
print("RCCN-Information: Command \'"+self.init_kwargs['name']+"\' doesn\'t have a base argument and no base command was found in service \'"+service.name+"\'.\nStandard base command will be
|
506
|
+
print("RCCN-Information: Command \'"+self.init_kwargs['name']+"\' doesn\'t have a base argument and no base command was found in service \'"+service.name+"\'.\nStandard base command will be created with system = \'"+service.name+"\' and type = "+str(service.service_id)+".")
|
401
507
|
self.init_kwargs['base'] = Command(
|
402
508
|
system=service,
|
403
509
|
name='base',
|
@@ -413,6 +519,13 @@ class RCCNCommand(Command):
|
|
413
519
|
else:
|
414
520
|
super().__init__(system=service, *self.init_args, **self.init_kwargs)
|
415
521
|
self.assignments['apid'] = self.system.system.apid
|
522
|
+
if not 'subtype' in self.assignments and self.name is not 'base':
|
523
|
+
used_subtypes = [command.assignments['subtype'] if 'subtype' in command.assignments else None for command in self.system.rccn_commands()]
|
524
|
+
new_subtype = 1
|
525
|
+
while new_subtype in used_subtypes:
|
526
|
+
new_subtype = new_subtype + 1
|
527
|
+
print('RCCN-Information: Command \''+self.name+'\' has no subtype specified. Subtype will be set to '+str(new_subtype)+'.')
|
528
|
+
self.assignments['subtype'] = new_subtype
|
416
529
|
self.struct_name = self.name + 'Args'
|
417
530
|
|
418
531
|
|
@@ -445,12 +558,8 @@ class RCCNCommand(Command):
|
|
445
558
|
text = replace_with_indentation(text, command_var_keyword, command_var_translation[command_var_keyword]())
|
446
559
|
return text
|
447
560
|
|
448
|
-
def check_user_input(self):
|
449
|
-
if self.assignments['subtype'] == None and self.name != 'base':
|
450
|
-
raise ValueError('Command '+self.name+' does not have a subtype assigned to it.')
|
451
|
-
|
452
561
|
def user_snapshot_path(self):
|
453
|
-
return os.path.join(self.
|
562
|
+
return os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
454
563
|
|
455
564
|
def struct_definition(self):
|
456
565
|
struct_definition_text = ""
|
@@ -470,22 +579,57 @@ class RCCNCommand(Command):
|
|
470
579
|
struct_definition_text += append
|
471
580
|
return struct_definition_text
|
472
581
|
|
582
|
+
|
583
|
+
|
473
584
|
class RCCNContainer(Container):
|
474
|
-
def __init__(
|
585
|
+
def __init__(
|
586
|
+
self,
|
587
|
+
base="/PUS/pus-tm",
|
588
|
+
subtype = None,
|
589
|
+
*,
|
590
|
+
system: System = None,
|
591
|
+
name: str = None,
|
592
|
+
entries: Sequence[ParameterEntry | ContainerEntry] | None = None,
|
593
|
+
abstract: bool = False,
|
594
|
+
condition: Expression | None = None,
|
595
|
+
aliases: Mapping[str, str] | None = None,
|
596
|
+
short_description: str | None = None,
|
597
|
+
long_description: str | None = None,
|
598
|
+
extra: Mapping[str, str] | None = None,
|
599
|
+
bits: int | None = None,
|
600
|
+
rate: float | None = None,
|
601
|
+
hint_partition: bool = False,
|
602
|
+
):
|
475
603
|
self.base = base
|
476
604
|
self.subtype = subtype
|
477
|
-
self.
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
605
|
+
self.init_kwargs = {
|
606
|
+
'system': system,
|
607
|
+
'name': name,
|
608
|
+
'entries': entries,
|
609
|
+
'base': base,
|
610
|
+
'abstract': abstract,
|
611
|
+
'condition': condition,
|
612
|
+
'aliases': aliases,
|
613
|
+
'short_description': short_description,
|
614
|
+
'long_description': long_description,
|
615
|
+
'extra': extra,
|
616
|
+
'bits': bits,
|
617
|
+
'rate': rate,
|
618
|
+
'hint_partition': hint_partition,
|
619
|
+
}
|
620
|
+
|
621
|
+
if name is None:
|
622
|
+
raise ValueError('RCCN-Error: Container must have a name.')
|
623
|
+
|
624
|
+
self.name = name
|
625
|
+
if system is not None and isinstance(system, Service):
|
626
|
+
self.add_to_service(system)
|
483
627
|
|
484
628
|
def add_to_service(self, service):
|
485
629
|
self.type = service.service_id
|
486
630
|
condition_type = None
|
487
631
|
condition_subtype = None
|
488
|
-
if 'condition'
|
632
|
+
if self.init_kwargs['condition'] is not None:
|
489
633
|
for eq_expression in self.init_kwargs['condition'].expressions:
|
490
634
|
if eq_expression.ref == self.base+'/type':
|
491
635
|
condition_type = eq_expression.value
|
@@ -498,18 +642,27 @@ class RCCNContainer(Container):
|
|
498
642
|
print('RCCN-Warning: Container '+self.name+' has an ambiguous user-defined subtype. \'subtype\' argument should match the \'condition\' argument.')
|
499
643
|
elif condition_subtype is not None:
|
500
644
|
self.subtype = condition_subtype
|
501
|
-
elif self.subtype is not None and
|
645
|
+
elif self.subtype is not None and self.init_kwargs['condition'] is not None:
|
502
646
|
self.init_kwargs['condition'] = AndExpression(
|
503
647
|
EqExpression(self.base+'/type', self.type),
|
504
648
|
EqExpression(self.base+'/subtype', self.subtype)
|
505
649
|
)
|
506
650
|
else:
|
507
|
-
|
651
|
+
used_subtypes = [container.subtype for container in service.rccn_container()]
|
652
|
+
new_subtype = 1
|
653
|
+
while new_subtype in used_subtypes:
|
654
|
+
new_subtype = new_subtype + 1
|
655
|
+
self.subtype = new_subtype
|
656
|
+
self.init_kwargs['condition'] = AndExpression(
|
657
|
+
EqExpression(self.base+'/type', self.type),
|
658
|
+
EqExpression(self.base+'/subtype', self.subtype)
|
659
|
+
)
|
660
|
+
print('RCCN-Information: Subtype for Container '+self.name+' is not specified through \'subtype\' or \'condition\' arguments. Subtype will be set to '+str(self.subtype)+'.')
|
508
661
|
|
509
662
|
if 'system' in self.init_kwargs and isinstance(self.init_kwargs['system'], Service):
|
510
|
-
super().__init__(
|
663
|
+
super().__init__(**self.init_kwargs)
|
511
664
|
else:
|
512
|
-
super().__init__(system=service,
|
665
|
+
super().__init__(system=service, **self.init_kwargs)
|
513
666
|
|
514
667
|
def generate_rccn_telemetry(self):
|
515
668
|
rccn_telemetry_text = ""
|
@@ -527,4 +680,46 @@ class RCCNContainer(Container):
|
|
527
680
|
rccn_telemetry_text += insert
|
528
681
|
rccn_telemetry_text += "}\n\n"
|
529
682
|
rccn_telemetry_text += append
|
530
|
-
return rccn_telemetry_text
|
683
|
+
return rccn_telemetry_text
|
684
|
+
|
685
|
+
def add_integer_parameter(
|
686
|
+
self,
|
687
|
+
name: str,
|
688
|
+
signed: bool = True,
|
689
|
+
bits: int = 32,
|
690
|
+
minimum: int | None = None,
|
691
|
+
maximum: int | None = None,
|
692
|
+
aliases: Mapping[str, str] | None = None,
|
693
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
694
|
+
initial_value: Any = None,
|
695
|
+
persistent: bool = True,
|
696
|
+
short_description: str | None = None,
|
697
|
+
long_description: str | None = None,
|
698
|
+
extra: Mapping[str, str] | None = None,
|
699
|
+
units: str | None = None,
|
700
|
+
encoding: Encoding | None = None,
|
701
|
+
calibrator: Calibrator | None = None,
|
702
|
+
alarm: ThresholdAlarm | None = None,
|
703
|
+
context_alarms: Sequence[ThresholdContextAlarm] | None = None,
|
704
|
+
):
|
705
|
+
int_parameter = IntegerParameter(
|
706
|
+
system=self,
|
707
|
+
name=name,
|
708
|
+
signed=signed,
|
709
|
+
bits=bits,
|
710
|
+
minimum=minimum,
|
711
|
+
maximum=maximum,
|
712
|
+
aliases=aliases,
|
713
|
+
data_source=data_source,
|
714
|
+
initial_value=initial_value,
|
715
|
+
persistent=persistent,
|
716
|
+
short_description=short_description,
|
717
|
+
long_description=long_description,
|
718
|
+
extra=extra,
|
719
|
+
units=units,
|
720
|
+
encoding=encoding,
|
721
|
+
calibrator=calibrator,
|
722
|
+
alarm=alarm,
|
723
|
+
context_alarms=context_alarms
|
724
|
+
)
|
725
|
+
self.entries.append(int_parameter)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
[package]
|
2
|
+
name = "rccn_usr_example_app"
|
3
|
+
version = "0.1.0"
|
4
|
+
edition = "2021"
|
5
|
+
|
6
|
+
[dependencies]
|
7
|
+
anyhow = "1.0.91"
|
8
|
+
binary_serde = "1.0.24"
|
9
|
+
crossbeam-channel = "0.5.13"
|
10
|
+
futures = "0.3.31"
|
11
|
+
rccn_usr = { version = "0.1.0", path = "../rccn_usr" }
|
12
|
+
satrs = "0.2.1"
|
13
|
+
spacepackets = "0.12.0"
|
14
|
+
tokio = "1.41.1"
|
15
|
+
env_logger = { version = "0.11.7", default-features = false, features = ["color", "humantime"] }
|
16
|
+
num-derive = "0.4"
|
17
|
+
num-traits = "0.2"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/command/command_module_struct.txt
RENAMED
File without changes
|
File without changes
|
{rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/main/service_module_import_service.txt
RENAMED
File without changes
|
{rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/main/service_module_mod_service.txt
RENAMED
File without changes
|
{rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/main/service_module_register_service.txt
RENAMED
File without changes
|
File without changes
|
{rccn_gen-1.1.0 → rccn_gen-1.2.0}/src/rccn_gen/text_modules/service/command_module_match_cmd.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|