rccn-gen 1.0.2__py3-none-any.whl → 1.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.
- rccn_gen/__init__.py +0 -1
- rccn_gen/systems.py +80 -35
- rccn_gen/text_modules/cargo_toml/cargo.txt +4 -1
- rccn_gen/text_modules/command/command_module_enum.txt +2 -1
- rccn_gen/text_modules/main/main.txt +1 -0
- rccn_gen/text_modules/main/service_module_import_service.txt +1 -1
- rccn_gen/text_modules/service/service.txt +1 -1
- rccn_gen/utils.py +28 -16
- {rccn_gen-1.0.2.dist-info → rccn_gen-1.1.0.dist-info}/METADATA +38 -7
- rccn_gen-1.1.0.dist-info/RECORD +19 -0
- rccn_gen/application.py +0 -0
- rccn_gen/service.py +0 -0
- rccn_gen/telemetry.py +0 -83
- rccn_gen-1.0.2.dist-info/RECORD +0 -22
- {rccn_gen-1.0.2.dist-info → rccn_gen-1.1.0.dist-info}/WHEEL +0 -0
rccn_gen/__init__.py
CHANGED
rccn_gen/systems.py
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
from yamcs.pymdb import System, Subsystem, Command
|
1
|
+
from yamcs.pymdb import System, Subsystem, Command, Container, AndExpression, EqExpression
|
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 .telemetry import *
|
9
|
-
|
10
8
|
|
11
9
|
class Application(Subsystem):
|
12
10
|
"""
|
@@ -191,7 +189,7 @@ class Service(Subsystem):
|
|
191
189
|
def __init__(
|
192
190
|
self,
|
193
191
|
name: str,
|
194
|
-
service_id: int
|
192
|
+
service_id: int,
|
195
193
|
*args,
|
196
194
|
**kwargs
|
197
195
|
):
|
@@ -258,7 +256,7 @@ class Service(Subsystem):
|
|
258
256
|
return paths
|
259
257
|
|
260
258
|
def rccn_commands(self):
|
261
|
-
return [command for command in self.commands if isinstance(command, RCCNCommand)]
|
259
|
+
return [command for command in self.commands if isinstance(command, RCCNCommand) and command.name != 'base']
|
262
260
|
|
263
261
|
def find_and_replace_keywords(self, text, text_modules_path):
|
264
262
|
# Find and replace service module keywords
|
@@ -274,23 +272,20 @@ class Service(Subsystem):
|
|
274
272
|
replacement_text = (self.find_and_replace_keywords(module_text, text_modules_path) + '\n')
|
275
273
|
text = insert_before_with_indentation(text, service_module_keyword, replacement_text)
|
276
274
|
|
277
|
-
# Call keyword replacement for all associated commands (Later, there needs to be checking to account for user changes to the generated files)
|
278
|
-
if len(self.rccn_commands()) == 0:
|
279
|
-
print('RCCN-Gen: Service '+self.name+' does not have any commands associated with it.')
|
280
275
|
for command in self.rccn_commands():
|
281
276
|
text = command.find_and_replace_keywords(text, text_modules_path)
|
282
277
|
|
283
278
|
# Find and replace service variable keywords
|
284
279
|
var_keywords = get_var_keywords(text)
|
285
280
|
service_var_translation = {
|
286
|
-
'<<VAR_SERVICE_NAME>>': self.name,
|
287
|
-
'<<VAR_SERVICE_ID>>': str(self.service_id),
|
288
|
-
'<<VAR_SERVICE_NAME_UCASE>>': pascalcase(self.name),
|
289
|
-
'<<VAR_SERVICE_TELEMETRY>>': self.generate_rust_telemetry_definition(),
|
281
|
+
'<<VAR_SERVICE_NAME>>': lambda: snakecase(self.name),
|
282
|
+
'<<VAR_SERVICE_ID>>': lambda: str(self.service_id),
|
283
|
+
'<<VAR_SERVICE_NAME_UCASE>>': lambda: pascalcase(self.name),
|
284
|
+
'<<VAR_SERVICE_TELEMETRY>>': lambda: self.generate_rust_telemetry_definition(),
|
290
285
|
}
|
291
286
|
for var_keyword in var_keywords:
|
292
287
|
if var_keyword in service_var_translation.keys():
|
293
|
-
text = replace_with_indentation(text, var_keyword, service_var_translation[var_keyword])
|
288
|
+
text = replace_with_indentation(text, var_keyword, service_var_translation[var_keyword]())
|
294
289
|
|
295
290
|
# Delete all command module keywords
|
296
291
|
text = delete_all_command_module_keywords(text)
|
@@ -327,7 +322,7 @@ class Service(Subsystem):
|
|
327
322
|
self.generate_snapshot('command', 'command_user_snapshot')
|
328
323
|
# Generate command.rs file
|
329
324
|
if len(self.rccn_commands()) == 0:
|
330
|
-
print('RCCN-
|
325
|
+
print('RCCN-Information: Service \''+self.name+'\' has no commands other than base command. Generation of command.rs file will be skipped.')
|
331
326
|
return
|
332
327
|
command_file_path = self.file_paths()['command_template']
|
333
328
|
with open(command_file_path, 'r') as file:
|
@@ -385,7 +380,8 @@ class Service(Subsystem):
|
|
385
380
|
def generate_rust_telemetry_definition(self):
|
386
381
|
telemetry_definition_text = ''
|
387
382
|
for container in self.containers:
|
388
|
-
container
|
383
|
+
if not isinstance(container, RCCNContainer):
|
384
|
+
container.__class__ = RCCNContainer
|
389
385
|
telemetry_definition_text += container.generate_rccn_telemetry()
|
390
386
|
return telemetry_definition_text
|
391
387
|
|
@@ -396,22 +392,28 @@ class RCCNCommand(Command):
|
|
396
392
|
self.init_kwargs = kwargs
|
397
393
|
if 'system' in kwargs and isinstance(kwargs['system'], Service):
|
398
394
|
self.add_to_service(kwargs['system'])
|
395
|
+
elif 'base' in kwargs and (isinstance(kwargs['base'], Command) or isinstance(kwargs['base'], RCCNCommand)):
|
396
|
+
self.add_to_service(kwargs['base'].system)
|
399
397
|
|
400
398
|
def add_to_service(self, service):
|
401
|
-
if not any(command.name == 'base' for command in service.commands):
|
402
|
-
|
399
|
+
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 used with system = \'"+service.name+"\' and type = "+str(service.service_id)+".")
|
401
|
+
self.init_kwargs['base'] = Command(
|
403
402
|
system=service,
|
404
403
|
name='base',
|
405
404
|
abstract=True,
|
406
405
|
base='/PUS/pus-tc',
|
407
406
|
assignments={'type': service.service_id}
|
408
407
|
)
|
409
|
-
|
408
|
+
elif not 'base' in self.init_kwargs and any(command.name == 'base' for command in service.commands):
|
409
|
+
print("RCCN-Information: Command \'"+self.init_kwargs['name']+"\' doesn\'t have a \'base\' argument. Existing base command for service \'"+service.name+"\' will be used.")
|
410
|
+
self.init_kwargs['base'] = next(command for command in service.commands if command.name == 'base')
|
410
411
|
if 'system' in self.init_kwargs and isinstance(self.init_kwargs['system'], Service):
|
411
412
|
super().__init__(*self.init_args, **self.init_kwargs)
|
412
413
|
else:
|
413
414
|
super().__init__(system=service, *self.init_args, **self.init_kwargs)
|
414
415
|
self.assignments['apid'] = self.system.system.apid
|
416
|
+
self.struct_name = self.name + 'Args'
|
415
417
|
|
416
418
|
|
417
419
|
def find_and_replace_keywords(self, text, text_modules_path):
|
@@ -431,14 +433,16 @@ class RCCNCommand(Command):
|
|
431
433
|
# Find and replace command variable keywords
|
432
434
|
command_var_keywords = get_var_keywords(text)
|
433
435
|
command_var_translation = {
|
434
|
-
'<<VAR_COMMAND_NAME_UCASE>>': pascalcase(self.name),
|
435
|
-
'<<VAR_COMMAND_NAME>>': self.name,
|
436
|
-
'<<
|
437
|
-
'<<
|
436
|
+
'<<VAR_COMMAND_NAME_UCASE>>': lambda: pascalcase(self.name),
|
437
|
+
'<<VAR_COMMAND_NAME>>': lambda: self.name,
|
438
|
+
'<<VAR_COMMAND_STRUCT_NAME>>': lambda: self.struct_name,
|
439
|
+
'<<VAR_COMMAND_SUBTYPE>>': lambda: str(self.assignments['subtype']),
|
440
|
+
'<<VAR_COMMAND_STRUCT>>': lambda: self.struct_definition(),
|
441
|
+
'<<VAR_SHORT_DESCRIPTION>>': lambda: "\n/// " + self.short_description if self.short_description is not None else "",
|
438
442
|
}
|
439
443
|
for command_var_keyword in command_var_keywords:
|
440
444
|
if command_var_keyword in command_var_translation.keys():
|
441
|
-
text = replace_with_indentation(text, command_var_keyword, command_var_translation[command_var_keyword])
|
445
|
+
text = replace_with_indentation(text, command_var_keyword, command_var_translation[command_var_keyword]())
|
442
446
|
return text
|
443
447
|
|
444
448
|
def check_user_input(self):
|
@@ -449,37 +453,78 @@ class RCCNCommand(Command):
|
|
449
453
|
return os.path.join(self.snapshot_file_path, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
450
454
|
|
451
455
|
def struct_definition(self):
|
456
|
+
struct_definition_text = ""
|
452
457
|
if len(self.arguments) == 0:
|
453
458
|
return ''
|
454
|
-
|
459
|
+
if hasattr(self, 'long_description') and self.long_description is not None:
|
460
|
+
struct_definition_text += "/// "+str(self.long_description)+"\n"
|
461
|
+
struct_definition_text += "#[derive(BitStruct, Debug, PartialEq)]\npub struct "+pascalcase(self.struct_name)+" {\n"
|
462
|
+
ins = ""
|
463
|
+
append = ""
|
455
464
|
for argument in self.arguments:
|
456
|
-
|
465
|
+
arg_def = rust_type_definition(argument)
|
466
|
+
ins += arg_def[0]
|
467
|
+
append += arg_def[1]
|
468
|
+
struct_definition_text += ins
|
457
469
|
struct_definition_text += "}\n"
|
458
|
-
|
459
|
-
struct_definition_text += rust_type_definition(argument)[1]
|
470
|
+
struct_definition_text += append
|
460
471
|
return struct_definition_text
|
461
472
|
|
462
473
|
class RCCNContainer(Container):
|
463
|
-
def __init__(self, base="/PUS/pus-tm", *args, **kwargs):
|
474
|
+
def __init__(self, base="/PUS/pus-tm", subtype = None, *args, **kwargs):
|
464
475
|
self.base = base
|
476
|
+
self.subtype = subtype
|
465
477
|
self.init_args = args
|
466
478
|
self.init_kwargs = kwargs
|
479
|
+
self.init_kwargs['base'] = base
|
480
|
+
self.name = kwargs['name']
|
467
481
|
if 'system' in kwargs and isinstance(kwargs['system'], Service):
|
468
482
|
self.add_to_service(kwargs['system'])
|
469
483
|
|
470
484
|
def add_to_service(self, service):
|
485
|
+
self.type = service.service_id
|
486
|
+
condition_type = None
|
487
|
+
condition_subtype = None
|
488
|
+
if 'condition' in self.init_kwargs:
|
489
|
+
for eq_expression in self.init_kwargs['condition'].expressions:
|
490
|
+
if eq_expression.ref == self.base+'/type':
|
491
|
+
condition_type = eq_expression.value
|
492
|
+
if eq_expression.ref == self.base+'/subtype':
|
493
|
+
condition_subtype = eq_expression.value
|
494
|
+
if condition_type is not None and condition_type != self.type:
|
495
|
+
print('RCCN-Warning: Container '+self.name+' has a user-defined type of '+str(eq_expression.value)+', which does\'nt match the service ID. User-defined type will be used.')
|
496
|
+
self.type = condition_type
|
497
|
+
if condition_subtype is not None and self.subtype is not None and condition_subtype != self.subtype:
|
498
|
+
print('RCCN-Warning: Container '+self.name+' has an ambiguous user-defined subtype. \'subtype\' argument should match the \'condition\' argument.')
|
499
|
+
elif condition_subtype is not None:
|
500
|
+
self.subtype = condition_subtype
|
501
|
+
elif self.subtype is not None and not 'condition' in self.init_kwargs:
|
502
|
+
self.init_kwargs['condition'] = AndExpression(
|
503
|
+
EqExpression(self.base+'/type', self.type),
|
504
|
+
EqExpression(self.base+'/subtype', self.subtype)
|
505
|
+
)
|
506
|
+
else:
|
507
|
+
raise ValueError('RCCN-Error: Container '+self.name+' has no \'condition\' argument and condition could not be created due to a missing \'subtype\' argument.')
|
508
|
+
|
471
509
|
if 'system' in self.init_kwargs and isinstance(self.init_kwargs['system'], Service):
|
472
510
|
super().__init__(*self.init_args, **self.init_kwargs)
|
473
511
|
else:
|
474
512
|
super().__init__(system=service, *self.init_args, **self.init_kwargs)
|
475
513
|
|
476
514
|
def generate_rccn_telemetry(self):
|
477
|
-
rccn_telemetry_text = "
|
478
|
-
|
479
|
-
rccn_telemetry_text +=
|
480
|
-
|
481
|
-
|
515
|
+
rccn_telemetry_text = ""
|
516
|
+
if hasattr(self, 'short_description') and self.short_description is not None:
|
517
|
+
rccn_telemetry_text += "/// "+str(self.short_description)+"\n"
|
518
|
+
rccn_telemetry_text += "#[derive(ServiceTelemetry, BitStruct, Debug)]\n"
|
519
|
+
if hasattr(self, 'subtype') and self.subtype is not None:
|
520
|
+
rccn_telemetry_text += "#[subtype("+str(self.subtype)+")]\n"
|
521
|
+
rccn_telemetry_text += "pub struct " + self.name + " {\n"
|
522
|
+
insert, append = ["",""]
|
482
523
|
for parameter_entry in self.entries:
|
483
|
-
|
484
|
-
|
524
|
+
par_def = rust_type_definition(parameter_entry.parameter)
|
525
|
+
insert += par_def[0]
|
526
|
+
append += par_def[1]
|
527
|
+
rccn_telemetry_text += insert
|
528
|
+
rccn_telemetry_text += "}\n\n"
|
529
|
+
rccn_telemetry_text += append
|
485
530
|
return rccn_telemetry_text
|
@@ -11,4 +11,7 @@ futures = "0.3.31"
|
|
11
11
|
rccn_usr = { version = "0.1.0", path = "../rccn_usr" }
|
12
12
|
satrs = "0.2.1"
|
13
13
|
spacepackets = "0.12.0"
|
14
|
-
tokio = "1.41.1"
|
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"
|
@@ -1 +1 @@
|
|
1
|
-
use
|
1
|
+
use <<VAR_SERVICE_NAME>>::service::<<VAR_SERVICE_NAME_UCASE>>;
|
@@ -21,7 +21,7 @@ impl <<VAR_SERVICE_NAME_UCASE>> {
|
|
21
21
|
impl PusService for <<VAR_SERVICE_NAME_UCASE>> {
|
22
22
|
type CommandT = command::Command;
|
23
23
|
|
24
|
-
fn handle_tc(&mut self, tc: AcceptedTc, cmd: Self::CommandT) -> AcceptanceResult {
|
24
|
+
fn handle_tc(&mut self, mut tc: AcceptedTc, cmd: Self::CommandT) -> AcceptanceResult {
|
25
25
|
println!("PUS-Service: Command received.");
|
26
26
|
match cmd {
|
27
27
|
<<COMMAND_MODULE_MATCH_CMD>>
|
rccn_gen/utils.py
CHANGED
@@ -133,7 +133,9 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
|
|
133
133
|
pymdb_data_instance.name = singular_name
|
134
134
|
print("RCCN-Gen: Information: An unnamed "+base_type+" has been named \""+pascalcase(pymdb_data_instance.name)+"\" in the generated RCCN code.")
|
135
135
|
sc_instance_name = snakecase(pymdb_data_instance.name)
|
136
|
-
|
136
|
+
definition_text = ["",""]
|
137
|
+
if pymdb_data_instance.short_description is not None:
|
138
|
+
definition_text[0] += ("\n\t/// "+str(pymdb_data_instance.short_description)+"\n")
|
137
139
|
if data_type == 'IntegerDataType':
|
138
140
|
if pymdb_data_instance.encoding is None or pymdb_data_instance.encoding.bits is None:
|
139
141
|
raw_bit_number = 8
|
@@ -143,41 +145,51 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
|
|
143
145
|
raw_bit_number_str = str(raw_bit_number)
|
144
146
|
eng_bit_number = engineering_bit_number(raw_bit_number)
|
145
147
|
eng_bit_number_str = str(eng_bit_number)
|
146
|
-
definition_text
|
148
|
+
definition_text[0] += "\t#[bits("+raw_bit_number_str+")]\n"
|
147
149
|
if pymdb_data_instance.signed:
|
148
150
|
definition_text[0] += ("\tpub "+sc_instance_name+": i"+eng_bit_number_str+",\n")
|
149
151
|
else:
|
150
152
|
definition_text[0] += ("\tpub "+sc_instance_name+": u"+eng_bit_number_str+",\n")
|
151
|
-
definition_text.append("")
|
152
153
|
|
153
154
|
elif data_type == 'BooleanDataType':
|
154
|
-
definition_text
|
155
|
-
definition_text.append("")
|
155
|
+
definition_text[0] += ("\t#[bits(1)]\n\tpub "+sc_instance_name+": bool,\n")
|
156
156
|
|
157
157
|
elif data_type == 'StringDataType':
|
158
|
-
definition_text
|
158
|
+
definition_text[0] += "\t#[null_terminated]\n\tpub "+sc_instance_name+": String,\n"
|
159
159
|
|
160
160
|
elif data_type == 'ArrayDataType':
|
161
161
|
definition_text = rust_type_definition(pymdb_data_instance.data_type, parent_name=pymdb_data_instance.name)
|
162
162
|
definition_text[0] = definition_text[0].replace(': ', ': Vec<').replace(',\n', '>,\n')
|
163
|
+
if pymdb_data_instance.short_description is not None:
|
164
|
+
definition_text[0] = "\n\t/// "+pymdb_data_instance.short_description+"\n"+definition_text[0]
|
165
|
+
if pymdb_data_instance.long_description is not None:
|
166
|
+
definition_text[1] = "/// "+pymdb_data_instance.long_description+"\n" + definition_text[1]
|
163
167
|
|
164
168
|
elif data_type == 'EnumeratedDataType':
|
165
|
-
definition_text
|
166
|
-
definition_text.
|
169
|
+
definition_text[0] += "\t#[bits("+str(pymdb_data_instance.encoding.bits)+")]\n"
|
170
|
+
definition_text[0] += "\tpub "+pymdb_data_instance.name+": "+pascalcase(pymdb_data_instance.name)+",\n"
|
171
|
+
definition_text[1] += ("#[derive(FromPrimative, ToPrimative, Debug)]\npub enum "+pascalcase(pymdb_data_instance.name)+" {\n")
|
167
172
|
for choice in pymdb_data_instance.choices:
|
168
173
|
definition_text[1] += "\t"+str(choice[1])+" = "+str(choice[0])+",\n"
|
169
|
-
definition_text[1] += "}\n"
|
174
|
+
definition_text[1] += "}\n\n"
|
175
|
+
if pymdb_data_instance.long_description is not None:
|
176
|
+
definition_text[1] = "/// "+pymdb_data_instance.long_description+"\n" + definition_text[1]
|
170
177
|
|
171
178
|
elif data_type == 'AggregateDataType':
|
172
179
|
struct_name = pascalcase(pymdb_data_instance.name)
|
173
|
-
definition_text
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
definition_text[1] += "
|
180
|
+
definition_text[0] += "\tpub "+sc_instance_name+": "+struct_name+",\n"
|
181
|
+
if pymdb_data_instance.long_description is not None:
|
182
|
+
definition_text[1] += "\t/// "+pymdb_data_instance.long_description+"\n"
|
183
|
+
definition_text[1] += ("#[derive(FromPrimative, ToPrimative, Debug)]\n")
|
184
|
+
definition_text[1] += ("pub struct "+struct_name+" {\n")
|
185
|
+
insert, append = ["",""]
|
178
186
|
for member in pymdb_data_instance.members:
|
179
|
-
|
180
|
-
|
187
|
+
mem_def = rust_type_definition(member, parent_name=pymdb_data_instance.name)
|
188
|
+
insert += mem_def[0]
|
189
|
+
append += mem_def[1]
|
190
|
+
definition_text[1] += insert
|
191
|
+
definition_text[1] += "}\n\n"
|
192
|
+
definition_text[1] += append
|
181
193
|
else:
|
182
194
|
definition_text = ["\t// Please implement datatype "+data_type+" here.\n", ""]
|
183
195
|
return definition_text
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rccn_gen
|
3
|
-
Version: 1.0
|
3
|
+
Version: 1.1.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
|
@@ -16,19 +16,18 @@ Description-Content-Type: text/markdown
|
|
16
16
|
|
17
17
|
# RCCN Code Generator
|
18
18
|
This generator is used to generate Rust files to deploy on the RACCOON OS satellite based on the pymdb config files used to generate the ground station XTCE files.
|
19
|
+
|
20
|
+
To see whats new, see the [CHANGELOG](CHANGELOG.md).
|
19
21
|
## Setup
|
20
|
-
- Pull the repo
|
21
22
|
- Set up a virtual python environment with `python3` and activate:
|
22
23
|
```zsh
|
23
24
|
python -m venv .venv
|
24
25
|
source .venv/bin/activate
|
25
26
|
```
|
26
|
-
- Install
|
27
|
+
- Install with `pip install rccn-gen`
|
27
28
|
|
28
29
|
## Using the Generator
|
29
30
|
The generator is based on [`pymdb`](https://github.com/yamcs/pymdb) and uses the same structure. Where `pymdb` gives the user freedom in defining systems, subsystems and their respective relations, the definitions for rust code generation is more restricted. For the rust generation, the user is bound to the following classes:
|
30
|
-
- Application,
|
31
|
-
- Service, and
|
32
31
|
- `Application`,
|
33
32
|
- `Service`, and
|
34
33
|
- `RCCNCommand`.
|
@@ -53,7 +52,13 @@ A service may be defined with the following command.
|
|
53
52
|
```python
|
54
53
|
service = Service(name="ExampleService", system=app, service_id = 131)
|
55
54
|
```
|
56
|
-
|
55
|
+
Or like this:
|
56
|
+
```python
|
57
|
+
service = Service(name="ExampleService", service_id = 131)
|
58
|
+
app.add_service(service)
|
59
|
+
```
|
60
|
+
|
61
|
+
It has the obligatory arguments **name** and **system**. The system corresponds to the application where the service is associated to. Note that each service has one associated app. A service cannot be associated to more than one app. You may think of the Application-Service-Command structure as a mathematical tree, where merging branches are not allowed. However, you can create multiple instances of the same service and associate them to different applications, or create multiple identical command instances and associate them to different services.
|
57
62
|
|
58
63
|
### RCCNCommand
|
59
64
|
An RCCNCommand can be defined with the following statement.
|
@@ -65,7 +70,33 @@ RCCNCommand(
|
|
65
70
|
arguments=[StringArgument("name", encoding=StringEncoding())],
|
66
71
|
)
|
67
72
|
```
|
68
|
-
|
73
|
+
|
74
|
+
Or like this:
|
75
|
+
```python
|
76
|
+
my_command = RCCNCommand(
|
77
|
+
assignments={"subtype": 2},
|
78
|
+
name="StopProcess",
|
79
|
+
arguments=[StringArgument("name", encoding=StringEncoding())],
|
80
|
+
)
|
81
|
+
service.add_command(my_command)
|
82
|
+
```
|
83
|
+
|
84
|
+
The only obligatory arguments are **name** and a **subtype assignment**, like shown in the example. The connection to a service can also be achieved with base commands, where every base command must be unique to a service. For example:
|
85
|
+
|
86
|
+
```python
|
87
|
+
base_cmd = RCCNCommand(
|
88
|
+
system=service,
|
89
|
+
assignments={"type": service.service_id},
|
90
|
+
name="base",
|
91
|
+
base="/PUS/pus-tc"
|
92
|
+
)
|
93
|
+
my_command = RCCNCommand(
|
94
|
+
base=base_cmd,
|
95
|
+
assignments={"subtype": 2},
|
96
|
+
name="StopProcess",
|
97
|
+
arguments=[StringArgument("name", encoding=StringEncoding())],
|
98
|
+
)
|
99
|
+
```
|
69
100
|
|
70
101
|
## Output
|
71
102
|
From the python configuration, the `main.rs`, `service.rs`, `command.rs`, `mod.rs`, `Cargo.toml` and `telemetry.rs` files are generated and are structured accordingly:
|
@@ -0,0 +1,19 @@
|
|
1
|
+
rccn_gen/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
2
|
+
rccn_gen/__init__.py,sha256=rBnqIw3uQk-uBbRh9VnungoTRSr2V0Bqos32xFZ44Eo,168
|
3
|
+
rccn_gen/systems.py,sha256=a_ylpOnHQL-RSL16HSjPZgonf4wGXiakNsvZcnq-5P8,30079
|
4
|
+
rccn_gen/utils.py,sha256=VKnTC2hrgMyLdneksAifnqEgXO27zLQJ9x5igaF35rE,8269
|
5
|
+
rccn_gen/text_modules/cargo_toml/cargo.txt,sha256=AYjSo3WJE7lhOcJaiNgXP9Y-DXHDIFIt6p42rDTVNVE,427
|
6
|
+
rccn_gen/text_modules/command/command.txt,sha256=8Y-uJilhFLoinftIbn7uKfia9LLMZno2LkoDJ-4Y-9M,345
|
7
|
+
rccn_gen/text_modules/command/command_module_enum.txt,sha256=35sBlAV_CzQw95Uf2dNynrYOxVD2tT2XWfEvS4Zx_KY,121
|
8
|
+
rccn_gen/text_modules/command/command_module_struct.txt,sha256=FT7Ke0uOVxWYpGC_oj9zafr1ahrH-nf0nSxQje6kToY,22
|
9
|
+
rccn_gen/text_modules/main/main.txt,sha256=t8lAYNCgxY8XHHGt8lVeZC103O1YLEY82UoTZ2U7S2I,446
|
10
|
+
rccn_gen/text_modules/main/service_module_import_service.txt,sha256=BYPvt2VrTV4yjt7SSAPYKzvQOBIqeDQsMzgzTQ8XmZY,62
|
11
|
+
rccn_gen/text_modules/main/service_module_mod_service.txt,sha256=guvXFdV_-YezhTD_PWA-Z0tL8ReSZc0rh3RuWraJnQE,25
|
12
|
+
rccn_gen/text_modules/main/service_module_register_service.txt,sha256=4EIsgaxDLh51u0WjXfy7Xgo-6UFTdb4Nh2O4J7uFjS0,115
|
13
|
+
rccn_gen/text_modules/mod/mod.txt,sha256=BF8LablBE4ddutdl5m0prvpvLdBRejueVOujkyrLe7I,52
|
14
|
+
rccn_gen/text_modules/service/command_module_match_cmd.txt,sha256=eVGo6ltuerG37rVxpXtL-JYuLyLW4c0i6NXb5g1_U-A,89
|
15
|
+
rccn_gen/text_modules/service/service.txt,sha256=qTxoOD5i7wH4yFiDn13rOJW9hIZyACA8W3m6UABe22U,695
|
16
|
+
rccn_gen/text_modules/telemetry/telemetry.txt,sha256=Re1d3BfpyXT_CEe7jJzLF3MARik0-J-K98K85iPOE40,193
|
17
|
+
rccn_gen-1.1.0.dist-info/METADATA,sha256=O6tvxJsBHrtdX0kxFW9j7MU_oSFGW0CAVD_9Mi_WCnk,8924
|
18
|
+
rccn_gen-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
19
|
+
rccn_gen-1.1.0.dist-info/RECORD,,
|
rccn_gen/application.py
DELETED
File without changes
|
rccn_gen/service.py
DELETED
File without changes
|
rccn_gen/telemetry.py
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
from yamcs.pymdb import Container, ParameterEntry, AggregateParameter, ArrayParameter, BinaryParameter, StringParameter, BooleanParameter, FloatParameter, IntegerParameter, StringMember, EnumeratedMember
|
2
|
-
from .utils import *
|
3
|
-
|
4
|
-
|
5
|
-
def generate_rccn_parameter_telemetry(parameter):
|
6
|
-
sc_parameter_name = to_snake_case(parameter.name)
|
7
|
-
|
8
|
-
if isinstance(parameter, ArrayParameter):
|
9
|
-
struct_name = to_upper_camel_case(parameter.name)
|
10
|
-
telemetry = ["\tpub "+sc_parameter_name+": Vec<"+struct_name+">,\n"]
|
11
|
-
telemetry.append("pub struct "+struct_name+" {\n")
|
12
|
-
for member in parameter.data_type.members:
|
13
|
-
telemetry[1] += generate_rccn_member_telementry(member)[0]
|
14
|
-
telemetry[1] += "}\n"
|
15
|
-
for member in parameter.data_type.members:
|
16
|
-
telemetry[1] += generate_rccn_member_telementry(member)[1]
|
17
|
-
|
18
|
-
elif isinstance(parameter, BooleanParameter):
|
19
|
-
telemetry = ["\t#[bits(1)]\n\tpub "+sc_parameter_name+": bool,\n"]
|
20
|
-
telemetry.append("")
|
21
|
-
|
22
|
-
elif isinstance(parameter, IntegerParameter):
|
23
|
-
if parameter.encoding is None or parameter.encoding.bits is None:
|
24
|
-
raw_bit_number_str = '8'
|
25
|
-
print("Warning: No encoding for parameter "+parameter.name+" found. Using 8 as default for raw bit number.")
|
26
|
-
else:
|
27
|
-
raw_bit_number = parameter.encoding.bits
|
28
|
-
raw_bit_number_str = str(raw_bit_number)
|
29
|
-
eng_bit_number = engineering_bit_number(raw_bit_number)
|
30
|
-
eng_bit_number_str = str(eng_bit_number)
|
31
|
-
telemetry = ["\t#[bits("+raw_bit_number_str+")]\n"]
|
32
|
-
if parameter.signed:
|
33
|
-
telemetry[0] += ("\tpub "+sc_parameter_name+": i"+eng_bit_number_str+",\n")
|
34
|
-
else:
|
35
|
-
telemetry += ("\tpub "+sc_parameter_name+": u"+eng_bit_number_str+",\n")
|
36
|
-
telemetry.append("")
|
37
|
-
|
38
|
-
elif isinstance(parameter, StringParameter):
|
39
|
-
telemetry = ["#[null_terminated]\npub "+sc_parameter_name+": String,\n"]
|
40
|
-
telemetry.append("")
|
41
|
-
|
42
|
-
else:
|
43
|
-
telemetry = ["\t// Please implement datatype "+type(parameter).__name__+" here.\n", ""]
|
44
|
-
return telemetry
|
45
|
-
|
46
|
-
def generate_rccn_member_telementry(member):
|
47
|
-
sc_member_name = to_snake_case(member.name)
|
48
|
-
|
49
|
-
if isinstance(member, StringMember):
|
50
|
-
member_telemetry = ["#[null_terminated]\n\tpub "+member.name+": String,\n"]
|
51
|
-
member_telemetry.append("")
|
52
|
-
|
53
|
-
elif isinstance(member, EnumeratedMember):
|
54
|
-
member_telemetry = ["\tpub "+member.name+": "+to_upper_camel_case(member.name)+",\n"]
|
55
|
-
member_telemetry.append("pub enum "+to_upper_camel_case(member.name)+" {\n")
|
56
|
-
for choice in member.choices:
|
57
|
-
member_telemetry[1] += "\t"+str(choice[1])+" = "+str(choice[0])+",\n"
|
58
|
-
member_telemetry[1] += "}\n"
|
59
|
-
|
60
|
-
elif isinstance(member, BooleanParameter):
|
61
|
-
telemetry = ["pub "+sc_member_name+": bool,\n"]
|
62
|
-
telemetry.append("")
|
63
|
-
|
64
|
-
elif isinstance(member, IntegerParameter):
|
65
|
-
if member.encoding is None or member.encoding.bits is None:
|
66
|
-
raw_bit_number = 8
|
67
|
-
print("Warning: No encoding for member "+member.name+" found. Using 8 as default for raw bit number.")
|
68
|
-
else:
|
69
|
-
raw_bit_number = member.encoding.bits
|
70
|
-
raw_bit_number_str = str(raw_bit_number)
|
71
|
-
eng_bit_number = engineering_bit_number(raw_bit_number)
|
72
|
-
eng_bit_number_str = str(eng_bit_number)
|
73
|
-
telemetry = ["\t#[bits("+raw_bit_number_str+")]\n"]
|
74
|
-
if member.signed:
|
75
|
-
telemetry[0] += ("\tpub "+sc_member_name+": i"+eng_bit_number_str+",\n")
|
76
|
-
else:
|
77
|
-
telemetry += ("\tpub "+sc_member_name+": u"+eng_bit_number_str+",\n")
|
78
|
-
telemetry.append("")
|
79
|
-
|
80
|
-
else:
|
81
|
-
member_telemetry[0] += "\t// Please implement datatype "+type(member).__name__+" here.\n"
|
82
|
-
member_telemetry.append("")
|
83
|
-
return member_telemetry
|
rccn_gen-1.0.2.dist-info/RECORD
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
rccn_gen/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
2
|
-
rccn_gen/__init__.py,sha256=XVvKYcWw9K1P3J1BTbzbODY71Nc1FApCc0wf7ezd4Gc,193
|
3
|
-
rccn_gen/application.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
rccn_gen/service.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
rccn_gen/systems.py,sha256=ibpegtBKPF-w6hmiWC_-TgJII6uvWP8wRSDpwEq8rjY,26755
|
6
|
-
rccn_gen/telemetry.py,sha256=huVcqGs95IXlpETWdlIyR9lK4ctZUsq7czgJwvTYtBk,3855
|
7
|
-
rccn_gen/utils.py,sha256=HF79aMpovRdqLdVjL1w0iEPTEvOl7VSPrf_9QifUyoc,7290
|
8
|
-
rccn_gen/text_modules/cargo_toml/cargo.txt,sha256=2e6xKomkml6-onfQ0QHNqvKnhGLYMUl8zYgbykmm83Y,292
|
9
|
-
rccn_gen/text_modules/command/command.txt,sha256=8Y-uJilhFLoinftIbn7uKfia9LLMZno2LkoDJ-4Y-9M,345
|
10
|
-
rccn_gen/text_modules/command/command_module_enum.txt,sha256=ApzRDQIs-BHJHtFA4ysfpYuWElGntXakkzZkgy57b74,94
|
11
|
-
rccn_gen/text_modules/command/command_module_struct.txt,sha256=FT7Ke0uOVxWYpGC_oj9zafr1ahrH-nf0nSxQje6kToY,22
|
12
|
-
rccn_gen/text_modules/main/main.txt,sha256=UQ0oHbzH7H3G4mxfeiTh5UeQ-X4C0Fw7IrCyZNH2clQ,426
|
13
|
-
rccn_gen/text_modules/main/service_module_import_service.txt,sha256=qPrU4fLs7Y65X5KJ868ut-izV5pHq7hU2nVRLxPnFCE,57
|
14
|
-
rccn_gen/text_modules/main/service_module_mod_service.txt,sha256=guvXFdV_-YezhTD_PWA-Z0tL8ReSZc0rh3RuWraJnQE,25
|
15
|
-
rccn_gen/text_modules/main/service_module_register_service.txt,sha256=4EIsgaxDLh51u0WjXfy7Xgo-6UFTdb4Nh2O4J7uFjS0,115
|
16
|
-
rccn_gen/text_modules/mod/mod.txt,sha256=BF8LablBE4ddutdl5m0prvpvLdBRejueVOujkyrLe7I,52
|
17
|
-
rccn_gen/text_modules/service/command_module_match_cmd.txt,sha256=eVGo6ltuerG37rVxpXtL-JYuLyLW4c0i6NXb5g1_U-A,89
|
18
|
-
rccn_gen/text_modules/service/service.txt,sha256=0KwwrfrjXuY4e3VNpCdTTzgW8bmYRQ21i-Lvhkz3hVA,691
|
19
|
-
rccn_gen/text_modules/telemetry/telemetry.txt,sha256=Re1d3BfpyXT_CEe7jJzLF3MARik0-J-K98K85iPOE40,193
|
20
|
-
rccn_gen-1.0.2.dist-info/METADATA,sha256=SmSD9_-46j9ZXvYAhuVPr2hINx0_xHOihSPJ31vfwVc,8260
|
21
|
-
rccn_gen-1.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
-
rccn_gen-1.0.2.dist-info/RECORD,,
|
File without changes
|