rccn-gen 1.2.0__py3-none-any.whl → 1.3.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/systems.py
CHANGED
@@ -6,10 +6,32 @@ from caseconverter import *
|
|
6
6
|
from importlib.resources import files
|
7
7
|
from .utils import *
|
8
8
|
from collections.abc import Mapping, Sequence
|
9
|
+
from typing import Union, Any, Literal
|
10
|
+
from yamcs.pymdb.alarms import EnumerationAlarm, ThresholdAlarm
|
11
|
+
from yamcs.pymdb.alarms import EnumerationContextAlarm, ThresholdContextAlarm
|
12
|
+
from yamcs.pymdb.datatypes import Epoch, Member, DataType, DynamicInteger
|
13
|
+
from yamcs.pymdb.encodings import Encoding, TimeEncoding
|
14
|
+
from yamcs.pymdb.calibrators import Calibrator
|
15
|
+
from yamcs.pymdb.parameters import (
|
16
|
+
AbsoluteTimeParameter, AggregateParameter, ArrayParameter,
|
17
|
+
BinaryParameter, BooleanParameter, DataSource,
|
18
|
+
EnumeratedParameter, FloatParameter, IntegerParameter, Parameter,
|
19
|
+
StringParameter
|
20
|
+
)
|
21
|
+
from yamcs.pymdb.commands import Command, CommandLevel, CommandEntry, Argument, TransmissionConstraint
|
9
22
|
|
10
23
|
class Application(Subsystem):
|
11
24
|
"""
|
12
25
|
A PUS application.
|
26
|
+
|
27
|
+
This class represents a Protocol Utilization Standard (PUS) application in the context
|
28
|
+
of space systems. It extends the Subsystem class of pymdb and provides functionality for managing
|
29
|
+
services, generating Rust code for the application, and handling snapshots and diffs of
|
30
|
+
the generated code.
|
31
|
+
|
32
|
+
The Application class is responsible for creating and organizing the directory structure
|
33
|
+
for Rust code generation, managing services and their associated commands and telemetry,
|
34
|
+
and facilitating the generation of Rust code files for the entire application.
|
13
35
|
"""
|
14
36
|
def __init__(
|
15
37
|
self,
|
@@ -24,6 +46,30 @@ class Application(Subsystem):
|
|
24
46
|
*args,
|
25
47
|
**kwargs
|
26
48
|
):
|
49
|
+
"""
|
50
|
+
Initialize a new PUS application.
|
51
|
+
|
52
|
+
Parameters:
|
53
|
+
-----------
|
54
|
+
system : System
|
55
|
+
The parent system that this application belongs to.
|
56
|
+
name : str
|
57
|
+
The name of the application.
|
58
|
+
apid : int
|
59
|
+
The Application Process ID (APID) for this application.
|
60
|
+
vcid : int, optional
|
61
|
+
The Virtual Channel ID (VCID) for this application. Default is 0.
|
62
|
+
export_directory : str, optional
|
63
|
+
The directory where generated Rust code will be exported. Default is current directory.
|
64
|
+
snapshot_directory : str, optional
|
65
|
+
The directory where snapshots of generated code will be stored. Default is './.rccn_snapshots'.
|
66
|
+
diff_directory : str, optional
|
67
|
+
The directory where diffs between generated code versions will be stored. Default is './.rccn_diffs'.
|
68
|
+
snapshots : bool, optional
|
69
|
+
Whether to create snapshots of generated code. Default is True.
|
70
|
+
*args, **kwargs
|
71
|
+
Additional arguments and keyword arguments passed to the parent class constructor.
|
72
|
+
"""
|
27
73
|
super().__init__(system=system, name=name, *args, **kwargs)
|
28
74
|
|
29
75
|
self.apid = apid
|
@@ -39,6 +85,19 @@ class Application(Subsystem):
|
|
39
85
|
self.keep_snapshots = 10
|
40
86
|
|
41
87
|
def add_service(self, service):
|
88
|
+
"""
|
89
|
+
Add a service to this application.
|
90
|
+
|
91
|
+
Parameters:
|
92
|
+
-----------
|
93
|
+
service : Service
|
94
|
+
The service to add to this application.
|
95
|
+
|
96
|
+
Raises:
|
97
|
+
-------
|
98
|
+
TypeError
|
99
|
+
If the provided service is not an instance of Service.
|
100
|
+
"""
|
42
101
|
if not isinstance(service, Service):
|
43
102
|
raise TypeError('Service '+service.name+' is not a RCCNCommand.')
|
44
103
|
service.add_to_application(self)
|
@@ -54,6 +113,31 @@ class Application(Subsystem):
|
|
54
113
|
*args,
|
55
114
|
**kwargs
|
56
115
|
):
|
116
|
+
"""
|
117
|
+
Create a new service and add it to this application.
|
118
|
+
|
119
|
+
Parameters:
|
120
|
+
-----------
|
121
|
+
name : str
|
122
|
+
The name of the service.
|
123
|
+
service_id : int
|
124
|
+
The service ID.
|
125
|
+
aliases : Mapping[str, str], optional
|
126
|
+
Alternative names for the service, keyed by namespace.
|
127
|
+
short_description : str, optional
|
128
|
+
A short description of the service.
|
129
|
+
long_description : str, optional
|
130
|
+
A longer description of the service.
|
131
|
+
extra : Mapping[str, str], optional
|
132
|
+
Arbitrary information about the service, keyed by name.
|
133
|
+
*args, **kwargs
|
134
|
+
Additional arguments and keyword arguments passed to the Service constructor.
|
135
|
+
|
136
|
+
Raises:
|
137
|
+
-------
|
138
|
+
ValueError
|
139
|
+
If 'system' is provided in kwargs.
|
140
|
+
"""
|
57
141
|
if 'system' not in kwargs:
|
58
142
|
kwargs['system'] = self
|
59
143
|
else:
|
@@ -69,6 +153,14 @@ class Application(Subsystem):
|
|
69
153
|
)
|
70
154
|
|
71
155
|
def file_paths(self):
|
156
|
+
"""
|
157
|
+
Get the file paths for various files used by this application.
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
--------
|
161
|
+
dict
|
162
|
+
A dictionary mapping file types to their absolute paths.
|
163
|
+
"""
|
72
164
|
paths = {
|
73
165
|
'main': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.name), 'src', 'main.rs'),
|
74
166
|
'main_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', 'rccn_usr_'+snakecase(self.name), 'src', 'main.rs'),
|
@@ -81,12 +173,34 @@ class Application(Subsystem):
|
|
81
173
|
return paths
|
82
174
|
|
83
175
|
def user_snapshot_path(self):
|
176
|
+
"""
|
177
|
+
Get the path for user snapshots with current timestamp.
|
178
|
+
|
179
|
+
Returns:
|
180
|
+
--------
|
181
|
+
str
|
182
|
+
The path where user snapshots are stored.
|
183
|
+
"""
|
84
184
|
return os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
85
185
|
|
86
186
|
def services(self):
|
187
|
+
"""
|
188
|
+
Get all services belonging to this application.
|
189
|
+
|
190
|
+
Returns:
|
191
|
+
--------
|
192
|
+
list[Service]
|
193
|
+
A list of Service objects that are subsystems of this application.
|
194
|
+
"""
|
87
195
|
return [subsystem for subsystem in self.subsystems if isinstance(subsystem, Service)]
|
88
196
|
|
89
197
|
def create_rccn_directories(self):
|
198
|
+
"""
|
199
|
+
Create directory structure for Rust code generation.
|
200
|
+
|
201
|
+
Creates the necessary directory structure for the application and its services
|
202
|
+
in the export directory.
|
203
|
+
"""
|
90
204
|
app_src_dir = os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.name), 'src')
|
91
205
|
if not os.path.exists(app_src_dir):
|
92
206
|
os.makedirs(app_src_dir)
|
@@ -96,6 +210,16 @@ class Application(Subsystem):
|
|
96
210
|
os.makedirs(service_dir)
|
97
211
|
|
98
212
|
def generate_rccn_main_file(self):
|
213
|
+
"""
|
214
|
+
Generate the main.rs file for the application.
|
215
|
+
|
216
|
+
Performs several tasks:
|
217
|
+
1. Creates diff file if both current and snapshot files exist
|
218
|
+
2. Creates snapshot of current main.rs with user changes if snapshots are enabled
|
219
|
+
3. Generates new main.rs file from template
|
220
|
+
4. Creates snapshot of newly generated main.rs if snapshots are enabled
|
221
|
+
5. Applies user changes from diff file if rebase_changes is enabled
|
222
|
+
"""
|
99
223
|
# Create main.diff file
|
100
224
|
if os.path.exists(self.file_paths()['main']) and os.path.exists(self.file_paths()['main_generated_snapshot']):
|
101
225
|
os.makedirs(os.path.dirname(self.file_paths()['main_diff']), exist_ok=True)
|
@@ -117,6 +241,24 @@ class Application(Subsystem):
|
|
117
241
|
os.system('patch '+self.file_paths()['main']+' < '+self.file_paths()['main_diff'])
|
118
242
|
|
119
243
|
def find_and_replace_keywords(self, text):
|
244
|
+
"""
|
245
|
+
Replace template keywords with actual values.
|
246
|
+
|
247
|
+
Parameters:
|
248
|
+
-----------
|
249
|
+
text : str
|
250
|
+
Template text containing keywords to be replaced.
|
251
|
+
|
252
|
+
Returns:
|
253
|
+
--------
|
254
|
+
str
|
255
|
+
Text with all keywords replaced with actual values.
|
256
|
+
|
257
|
+
Raises:
|
258
|
+
-------
|
259
|
+
KeyError
|
260
|
+
If a keyword in the template text is not found in the translation dictionary.
|
261
|
+
"""
|
120
262
|
# Call keyword replacement for all associated services (Later, there needs to be checking to account for user changes to the generated files)
|
121
263
|
for service in self.services():
|
122
264
|
text = service.find_and_replace_keywords(text, self.text_modules_main_path)
|
@@ -136,6 +278,22 @@ class Application(Subsystem):
|
|
136
278
|
return text
|
137
279
|
|
138
280
|
def generate_rccn_code(self, export_directory:str, snapshot_directory='', diff_directory='', rebase_changes=True, check=True):
|
281
|
+
"""
|
282
|
+
Generate Rust code for the application and its services.
|
283
|
+
|
284
|
+
Parameters:
|
285
|
+
-----------
|
286
|
+
export_directory : str
|
287
|
+
Directory where the generated code will be exported.
|
288
|
+
snapshot_directory : str, optional
|
289
|
+
Directory where snapshots of generated code will be stored. Default is a subdirectory of export_directory.
|
290
|
+
diff_directory : str, optional
|
291
|
+
Directory where diffs between generated code versions will be stored. Default is a subdirectory of export_directory.
|
292
|
+
rebase_changes : bool, optional
|
293
|
+
Whether to apply user changes from diff files. Default is True.
|
294
|
+
check : bool, optional
|
295
|
+
Whether to perform checks on user input. Default is True.
|
296
|
+
"""
|
139
297
|
# Update export, snapshot and diff directory for the Application and all Services
|
140
298
|
self.export_directory = export_directory
|
141
299
|
if snapshot_directory == '':
|
@@ -167,10 +325,32 @@ class Application(Subsystem):
|
|
167
325
|
self.delete_old_snapshots()
|
168
326
|
|
169
327
|
def generate_snapshot(self, current_file_reference, snapshot_file_reference):
|
328
|
+
"""
|
329
|
+
Create a snapshot of a file.
|
330
|
+
|
331
|
+
Parameters:
|
332
|
+
-----------
|
333
|
+
current_file_reference : str
|
334
|
+
Reference to the current file in the file_paths dictionary.
|
335
|
+
snapshot_file_reference : str
|
336
|
+
Reference to the snapshot file in the file_paths dictionary.
|
337
|
+
"""
|
170
338
|
os.makedirs(os.path.dirname(self.file_paths()[snapshot_file_reference]), exist_ok=True)
|
171
339
|
shutil.copyfile(self.file_paths()[current_file_reference], self.file_paths()[snapshot_file_reference])
|
172
340
|
|
173
341
|
def generate_diff_file(self, current_file_reference, snapshot_file_reference, diff_file_reference):
|
342
|
+
"""
|
343
|
+
Generate a diff file between current and snapshot files.
|
344
|
+
|
345
|
+
Parameters:
|
346
|
+
-----------
|
347
|
+
current_file_reference : str
|
348
|
+
Reference to the current file in the file_paths dictionary.
|
349
|
+
snapshot_file_reference : str
|
350
|
+
Reference to the snapshot file in the file_paths dictionary.
|
351
|
+
diff_file_reference : str
|
352
|
+
Reference to the diff file in the file_paths dictionary.
|
353
|
+
"""
|
174
354
|
with open(self.file_paths()[current_file_reference], 'r') as current_file:
|
175
355
|
current_text = current_file.readlines()
|
176
356
|
with open(self.file_paths()[snapshot_file_reference], 'r') as snapshot_file:
|
@@ -182,6 +362,9 @@ class Application(Subsystem):
|
|
182
362
|
diff_file.write(diff_text)
|
183
363
|
|
184
364
|
def delete_old_snapshots(self):
|
365
|
+
"""
|
366
|
+
Delete old user snapshots exceeding the keep_snapshots limit.
|
367
|
+
"""
|
185
368
|
if os.path.exists(os.path.join(self.snapshot_directory, 'user')):
|
186
369
|
user_snapshots_path = os.path.join(self.snapshot_directory, 'user')
|
187
370
|
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))]
|
@@ -190,6 +373,14 @@ class Application(Subsystem):
|
|
190
373
|
shutil.rmtree(snapshots.pop(0))
|
191
374
|
|
192
375
|
def check_user_input(self):
|
376
|
+
"""
|
377
|
+
Perform checks on user input to ensure consistency and uniqueness.
|
378
|
+
|
379
|
+
Raises:
|
380
|
+
-------
|
381
|
+
ValueError
|
382
|
+
If there are duplicate service names, service IDs, command names, or command subtypes.
|
383
|
+
"""
|
193
384
|
# Check if all services in the application have unique names
|
194
385
|
service_names = [service.name for service in self.services()]
|
195
386
|
if len(service_names) != len(set(service_names)):
|
@@ -215,6 +406,9 @@ class Application(Subsystem):
|
|
215
406
|
raise ValueError('RCCN-Error: Service \''+service.name+'\' has multiple commands with the same subtype.')
|
216
407
|
|
217
408
|
def generate_cargo_toml_file(self):
|
409
|
+
"""
|
410
|
+
Generate the Cargo.toml file for the application.
|
411
|
+
"""
|
218
412
|
with open(self.file_paths()['cargo_toml_template'], 'r') as file:
|
219
413
|
cargo_toml_template_text = file.read()
|
220
414
|
with open(self.file_paths()['cargo_toml'], 'w') as file:
|
@@ -222,6 +416,17 @@ class Application(Subsystem):
|
|
222
416
|
|
223
417
|
|
224
418
|
class Service(Subsystem):
|
419
|
+
"""
|
420
|
+
A PUS service that belongs to an Application.
|
421
|
+
|
422
|
+
This class represents a Protocol Utilization Standard (PUS) service, which is a collection of
|
423
|
+
related functionality within a space application. It extends the Subsystem class of pymdb and provides
|
424
|
+
functionality for managing commands, telemetry containers, and generating Rust code.
|
425
|
+
|
426
|
+
Each service has a unique service ID within its parent application and is responsible for
|
427
|
+
organizing related commands and telemetry into logical groups. The class facilitates the
|
428
|
+
generation of Rust code files for the service: service.rs, command.rs, and telemetry.rs.
|
429
|
+
"""
|
225
430
|
def __init__(
|
226
431
|
self,
|
227
432
|
name: str,
|
@@ -229,6 +434,19 @@ class Service(Subsystem):
|
|
229
434
|
*args,
|
230
435
|
**kwargs
|
231
436
|
):
|
437
|
+
"""
|
438
|
+
Initialize a new PUS service.
|
439
|
+
|
440
|
+
Parameters:
|
441
|
+
-----------
|
442
|
+
name : str
|
443
|
+
The name of the service.
|
444
|
+
service_id : int
|
445
|
+
The unique service ID for this service within its parent application.
|
446
|
+
*args, **kwargs
|
447
|
+
Additional arguments and keyword arguments passed to the parent class constructor.
|
448
|
+
A 'system' parameter can be provided if the service is being created directly.
|
449
|
+
"""
|
232
450
|
self.init_args = args
|
233
451
|
self.init_kwargs = kwargs
|
234
452
|
self.init_args = args
|
@@ -243,6 +461,17 @@ class Service(Subsystem):
|
|
243
461
|
self.add_to_application(kwargs['system'])
|
244
462
|
|
245
463
|
def add_to_application(self, application):
|
464
|
+
"""
|
465
|
+
Add this service to an Application.
|
466
|
+
|
467
|
+
This method initializes the Service as a Subsystem and sets it as a child
|
468
|
+
of the provided application.
|
469
|
+
|
470
|
+
Parameters:
|
471
|
+
-----------
|
472
|
+
application : Application
|
473
|
+
The parent application that this service will belong to.
|
474
|
+
"""
|
246
475
|
if 'system' in self.init_kwargs and isinstance(self.init_kwargs['system'], Application):
|
247
476
|
super().__init__(
|
248
477
|
name=self.name,
|
@@ -257,16 +486,51 @@ class Service(Subsystem):
|
|
257
486
|
self.snapshots = self.system.snapshots
|
258
487
|
|
259
488
|
def add_container(self, container):
|
489
|
+
"""
|
490
|
+
Add a container to this service.
|
491
|
+
|
492
|
+
Parameters:
|
493
|
+
-----------
|
494
|
+
container : RCCNContainer
|
495
|
+
The container to add to this service.
|
496
|
+
|
497
|
+
Raises:
|
498
|
+
-------
|
499
|
+
TypeError
|
500
|
+
If the provided container is not an instance of RCCNContainer.
|
501
|
+
"""
|
260
502
|
if not isinstance(container, RCCNContainer):
|
261
503
|
raise TypeError('Container '+container.name+' is not a RCCNContainer.')
|
262
504
|
container.add_to_service(self)
|
263
505
|
|
264
506
|
def add_command(self, command):
|
507
|
+
"""
|
508
|
+
Add a command to this service.
|
509
|
+
|
510
|
+
Parameters:
|
511
|
+
-----------
|
512
|
+
command : RCCNCommand
|
513
|
+
The command to add to this service.
|
514
|
+
|
515
|
+
Raises:
|
516
|
+
-------
|
517
|
+
TypeError
|
518
|
+
If the provided command is not an instance of RCCNCommand.
|
519
|
+
"""
|
265
520
|
if not isinstance(command, RCCNCommand):
|
266
521
|
raise TypeError('Command '+command.name+' is not a RCCNCommand.')
|
267
522
|
command.add_to_service(self)
|
268
523
|
|
269
524
|
def file_paths(self):
|
525
|
+
"""
|
526
|
+
Get the file paths for various files used by this service.
|
527
|
+
|
528
|
+
Returns:
|
529
|
+
--------
|
530
|
+
dict
|
531
|
+
A dictionary mapping file types to their absolute paths, including
|
532
|
+
source files, generated snapshots, user snapshots, diff files, and templates.
|
533
|
+
"""
|
270
534
|
paths = {
|
271
535
|
'service': os.path.join(self.export_directory, 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
272
536
|
'service_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', 'rccn_usr_'+snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
@@ -289,9 +553,42 @@ class Service(Subsystem):
|
|
289
553
|
return paths
|
290
554
|
|
291
555
|
def rccn_commands(self):
|
556
|
+
"""
|
557
|
+
Get all RCCNCommand objects belonging to this service, excluding base commands.
|
558
|
+
|
559
|
+
Returns:
|
560
|
+
--------
|
561
|
+
list
|
562
|
+
A list of RCCNCommand objects that belong to this service but are not
|
563
|
+
named 'base'.
|
564
|
+
"""
|
292
565
|
return [command for command in self.commands if isinstance(command, RCCNCommand) and command.name != 'base']
|
293
566
|
|
294
567
|
def find_and_replace_keywords(self, text, text_modules_path):
|
568
|
+
"""
|
569
|
+
Replace template keywords with actual values specific to this service.
|
570
|
+
|
571
|
+
This method processes the template text, replacing service-specific keywords,
|
572
|
+
including module keywords, variable keywords, and command keywords. It delegates
|
573
|
+
command-specific keyword replacement to each command's find_and_replace_keywords method.
|
574
|
+
|
575
|
+
Parameters:
|
576
|
+
-----------
|
577
|
+
text : str
|
578
|
+
The template text containing keywords to be replaced.
|
579
|
+
text_modules_path : str
|
580
|
+
The path to the directory containing text module templates.
|
581
|
+
|
582
|
+
Returns:
|
583
|
+
--------
|
584
|
+
str
|
585
|
+
The processed text with all keywords replaced with their actual values.
|
586
|
+
|
587
|
+
Raises:
|
588
|
+
-------
|
589
|
+
FileExistsError
|
590
|
+
If a referenced text module file does not exist.
|
591
|
+
"""
|
295
592
|
# Find and replace service module keywords
|
296
593
|
service_module_keywords = get_service_module_keywords(text)
|
297
594
|
for service_module_keyword in service_module_keywords:
|
@@ -326,6 +623,15 @@ class Service(Subsystem):
|
|
326
623
|
return text
|
327
624
|
|
328
625
|
def generate_rccn_service_file(self):
|
626
|
+
"""
|
627
|
+
Generate the service.rs file for this service.
|
628
|
+
|
629
|
+
This method creates a diff file (if both current and snapshot files exist),
|
630
|
+
takes a snapshot of user-modified service.rs if snapshots are enabled,
|
631
|
+
generates the new service.rs from the template, takes a snapshot of the
|
632
|
+
newly generated file, and applies user changes from the diff if rebase_changes
|
633
|
+
is enabled.
|
634
|
+
"""
|
329
635
|
# Create service.diff file
|
330
636
|
if os.path.exists(self.file_paths()['service']) and os.path.exists(self.file_paths()['service_generated_snapshot']):
|
331
637
|
os.makedirs(os.path.dirname(self.file_paths()['service_diff']), exist_ok=True)
|
@@ -346,6 +652,23 @@ class Service(Subsystem):
|
|
346
652
|
os.system('patch '+self.file_paths()['service']+' < '+self.file_paths()['service_diff'])
|
347
653
|
|
348
654
|
def generate_rccn_command_file(self, export_file_dir='.', text_modules_path='./text_modules/command'):
|
655
|
+
"""
|
656
|
+
Generate the command.rs file for this service.
|
657
|
+
|
658
|
+
This method creates a diff file (if both current and snapshot files exist),
|
659
|
+
takes a snapshot of user-modified command.rs if snapshots are enabled,
|
660
|
+
generates the new command.rs from the template only if there are commands
|
661
|
+
in the service, takes a snapshot of the newly generated file, and applies
|
662
|
+
user changes from the diff if rebase_changes is enabled.
|
663
|
+
|
664
|
+
Parameters:
|
665
|
+
-----------
|
666
|
+
export_file_dir : str, optional
|
667
|
+
The directory where the command.rs file will be exported. Default is current directory.
|
668
|
+
text_modules_path : str, optional
|
669
|
+
The path to the directory containing command text module templates.
|
670
|
+
Default is './text_modules/command'.
|
671
|
+
"""
|
349
672
|
# Create command.diff file
|
350
673
|
if os.path.exists(self.file_paths()['command']) and os.path.exists(self.file_paths()['command_generated_snapshot']):
|
351
674
|
os.makedirs(os.path.dirname(self.file_paths()['command_diff']), exist_ok=True)
|
@@ -371,10 +694,32 @@ class Service(Subsystem):
|
|
371
694
|
os.system('patch '+self.file_paths()['command']+' < '+self.file_paths()['command_diff'])
|
372
695
|
|
373
696
|
def generate_snapshot(self, current_file_reference, snapshot_file_reference):
|
697
|
+
"""
|
698
|
+
Create a snapshot of a file.
|
699
|
+
|
700
|
+
Parameters:
|
701
|
+
-----------
|
702
|
+
current_file_reference : str
|
703
|
+
Reference to the current file in the file_paths dictionary.
|
704
|
+
snapshot_file_reference : str
|
705
|
+
Reference to the snapshot file in the file_paths dictionary.
|
706
|
+
"""
|
374
707
|
os.makedirs(os.path.dirname(self.file_paths()[snapshot_file_reference]), exist_ok=True)
|
375
708
|
shutil.copyfile(self.file_paths()[current_file_reference], self.file_paths()[snapshot_file_reference])
|
376
709
|
|
377
710
|
def generate_diff_file(self, current_file_reference, snapshot_file_reference, diff_file_reference):
|
711
|
+
"""
|
712
|
+
Generate a diff file between current and snapshot files.
|
713
|
+
|
714
|
+
Parameters:
|
715
|
+
-----------
|
716
|
+
current_file_reference : str
|
717
|
+
Reference to the current file in the file_paths dictionary.
|
718
|
+
snapshot_file_reference : str
|
719
|
+
Reference to the snapshot file in the file_paths dictionary.
|
720
|
+
diff_file_reference : str
|
721
|
+
Reference to the diff file in the file_paths dictionary.
|
722
|
+
"""
|
378
723
|
with open(self.file_paths()[current_file_reference], 'r') as current_file:
|
379
724
|
current_text = current_file.readlines()
|
380
725
|
with open(self.file_paths()[snapshot_file_reference], 'r') as snapshot_file:
|
@@ -385,12 +730,26 @@ class Service(Subsystem):
|
|
385
730
|
diff_file.write(diff_text)
|
386
731
|
|
387
732
|
def generate_mod_file(self):
|
733
|
+
"""
|
734
|
+
Generate the mod.rs file for this service.
|
735
|
+
|
736
|
+
This file serves as the module definition file for the service in Rust,
|
737
|
+
defining what's exported from the service module.
|
738
|
+
"""
|
388
739
|
with open(self.file_paths()['mod_template'], 'r') as file:
|
389
740
|
mod_template_text = file.read()
|
390
741
|
with open(self.file_paths()['mod'], 'w') as file:
|
391
742
|
file.write(mod_template_text)
|
392
743
|
|
393
744
|
def generate_telemetry_file(self):
|
745
|
+
"""
|
746
|
+
Generate the telemetry.rs file for this service.
|
747
|
+
|
748
|
+
This method creates a diff file (if both current and snapshot files exist),
|
749
|
+
takes a snapshot of user-modified telemetry.rs if snapshots are enabled,
|
750
|
+
generates the new telemetry.rs from the template, takes a snapshot of the newly
|
751
|
+
generated file, and applies user changes from the diff if rebase_changes is enabled.
|
752
|
+
"""
|
394
753
|
# Create telemetry.diff file
|
395
754
|
if os.path.exists(self.file_paths()['telemetry']) and os.path.exists(self.file_paths()['telemetry_generated_snapshot']):
|
396
755
|
os.makedirs(os.path.dirname(self.file_paths()['telemetry_diff']), exist_ok=True)
|
@@ -411,6 +770,14 @@ class Service(Subsystem):
|
|
411
770
|
os.system('patch '+self.file_paths()['telemetry']+' < '+self.file_paths()['telemetry_diff'])
|
412
771
|
|
413
772
|
def generate_rust_telemetry_definition(self):
|
773
|
+
"""
|
774
|
+
Generate Rust code definitions for all telemetry containers in this service.
|
775
|
+
|
776
|
+
Returns:
|
777
|
+
--------
|
778
|
+
str
|
779
|
+
A string containing Rust struct definitions for all telemetry containers.
|
780
|
+
"""
|
414
781
|
telemetry_definition_text = ''
|
415
782
|
for container in self.containers:
|
416
783
|
if not isinstance(container, RCCNContainer):
|
@@ -437,6 +804,38 @@ class Service(Subsystem):
|
|
437
804
|
Union[TransmissionConstraint, Sequence[TransmissionConstraint]] | None
|
438
805
|
) = None,
|
439
806
|
):
|
807
|
+
"""
|
808
|
+
Create a new command and add it to this service.
|
809
|
+
|
810
|
+
Parameters:
|
811
|
+
-----------
|
812
|
+
name : str
|
813
|
+
The name of the command.
|
814
|
+
aliases : Mapping[str, str], optional
|
815
|
+
Alternative names for the command, keyed by namespace.
|
816
|
+
short_description : str, optional
|
817
|
+
A short description of the command.
|
818
|
+
long_description : str, optional
|
819
|
+
A longer description of the command.
|
820
|
+
extra : Mapping[str, str], optional
|
821
|
+
Arbitrary information about the command, keyed by name.
|
822
|
+
abstract : bool, optional
|
823
|
+
Whether this command is abstract. Default is False.
|
824
|
+
base : Command | str, optional
|
825
|
+
The base command or reference to a base command.
|
826
|
+
assignments : Mapping[str, Any], optional
|
827
|
+
Command assignments.
|
828
|
+
arguments : Sequence[Argument], optional
|
829
|
+
The arguments for this command.
|
830
|
+
entries : Sequence[CommandEntry], optional
|
831
|
+
Command entries.
|
832
|
+
level : CommandLevel, optional
|
833
|
+
The command level. Default is CommandLevel.NORMAL.
|
834
|
+
warning_message : str, optional
|
835
|
+
Warning message to display when executing this command.
|
836
|
+
constraint : Union[TransmissionConstraint, Sequence[TransmissionConstraint]], optional
|
837
|
+
Transmission constraints for this command.
|
838
|
+
"""
|
440
839
|
Command(
|
441
840
|
name=name,
|
442
841
|
system=self,
|
@@ -455,10 +854,29 @@ class Service(Subsystem):
|
|
455
854
|
)
|
456
855
|
|
457
856
|
def rccn_container(self):
|
857
|
+
"""
|
858
|
+
Get all RCCNContainer objects belonging to this service.
|
859
|
+
|
860
|
+
Returns:
|
861
|
+
--------
|
862
|
+
list
|
863
|
+
A list of RCCNContainer objects that belong to this service.
|
864
|
+
"""
|
458
865
|
return [container for container in self.containers if isinstance(container, RCCNContainer)]
|
459
866
|
|
460
867
|
|
461
868
|
class RCCNCommand(Command):
|
869
|
+
"""
|
870
|
+
A Protocol Utilization Standard (PUS) command that belongs to a Service.
|
871
|
+
|
872
|
+
This class extends the Command class and provides specialized functionality for
|
873
|
+
generating Rust code for commands within a PUS service. RCCNCommand manages command
|
874
|
+
subtype assignment, struct name generation for arguments, and handling of base commands.
|
875
|
+
|
876
|
+
Each RCCNCommand automatically gets assigned to its parent Service's APID (Application
|
877
|
+
Process ID) and a unique subtype within that service. It can generate Rust code for
|
878
|
+
command structs and implements keyword replacement for templates.
|
879
|
+
"""
|
462
880
|
def __init__(
|
463
881
|
self,
|
464
882
|
name: str,
|
@@ -479,6 +897,41 @@ class RCCNCommand(Command):
|
|
479
897
|
) = None,
|
480
898
|
**kwargs
|
481
899
|
):
|
900
|
+
"""
|
901
|
+
Initialize a new PUS command.
|
902
|
+
|
903
|
+
Parameters:
|
904
|
+
-----------
|
905
|
+
name : str
|
906
|
+
The name of the command.
|
907
|
+
aliases : Mapping[str, str], optional
|
908
|
+
Alternative names for the command, keyed by namespace.
|
909
|
+
short_description : str, optional
|
910
|
+
A short description of the command.
|
911
|
+
long_description : str, optional
|
912
|
+
A longer description of the command.
|
913
|
+
extra : Mapping[str, str], optional
|
914
|
+
Arbitrary information about the command, keyed by name.
|
915
|
+
abstract : bool, optional
|
916
|
+
Whether this command is abstract. Default is False.
|
917
|
+
base : Command | str, optional
|
918
|
+
The base command or reference to a base command.
|
919
|
+
assignments : Mapping[str, Any], optional
|
920
|
+
Command assignments, including the subtype.
|
921
|
+
arguments : Sequence[Argument], optional
|
922
|
+
The arguments for this command.
|
923
|
+
entries : Sequence[CommandEntry], optional
|
924
|
+
Command entries.
|
925
|
+
level : CommandLevel, optional
|
926
|
+
The command level. Default is CommandLevel.NORMAL.
|
927
|
+
warning_message : str, optional
|
928
|
+
Warning message to display when executing this command.
|
929
|
+
constraint : Union[TransmissionConstraint, Sequence[TransmissionConstraint]], optional
|
930
|
+
Transmission constraints for this command.
|
931
|
+
**kwargs
|
932
|
+
Additional keyword arguments that may include 'system' to specify
|
933
|
+
which service this command belongs to.
|
934
|
+
"""
|
482
935
|
self.init_args = ()
|
483
936
|
self.init_kwargs = {
|
484
937
|
'name': name,
|
@@ -502,6 +955,25 @@ class RCCNCommand(Command):
|
|
502
955
|
self.add_to_service(base.system)
|
503
956
|
|
504
957
|
def add_to_service(self, service):
|
958
|
+
"""
|
959
|
+
Add this command to a Service.
|
960
|
+
|
961
|
+
This method handles the initialization of the command as part of a service,
|
962
|
+
setting up base commands if needed, and assigning a subtype if one isn't
|
963
|
+
explicitly provided.
|
964
|
+
|
965
|
+
Parameters:
|
966
|
+
-----------
|
967
|
+
service : Service
|
968
|
+
The service this command will belong to.
|
969
|
+
|
970
|
+
Notes:
|
971
|
+
------
|
972
|
+
- If no base command is specified and the service has no base command,
|
973
|
+
a standard base command will be created automatically
|
974
|
+
- If no subtype is provided, a unique one will be assigned automatically
|
975
|
+
- The command's APID is automatically set to match the service's application APID
|
976
|
+
"""
|
505
977
|
if not 'base' in self.init_kwargs and not any(command.name == 'base' for command in service.commands):
|
506
978
|
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)+".")
|
507
979
|
self.init_kwargs['base'] = Command(
|
@@ -530,6 +1002,31 @@ class RCCNCommand(Command):
|
|
530
1002
|
|
531
1003
|
|
532
1004
|
def find_and_replace_keywords(self, text, text_modules_path):
|
1005
|
+
"""
|
1006
|
+
Replace template keywords with actual command values.
|
1007
|
+
|
1008
|
+
This method processes template text, replacing command-specific keywords with
|
1009
|
+
the actual values from this command. It handles:
|
1010
|
+
1. Command module keywords - references to external template files
|
1011
|
+
2. Command variable keywords - specific properties of this command
|
1012
|
+
|
1013
|
+
Parameters:
|
1014
|
+
-----------
|
1015
|
+
text : str
|
1016
|
+
Template text containing keywords to be replaced.
|
1017
|
+
text_modules_path : str
|
1018
|
+
Path to the directory containing text module templates.
|
1019
|
+
|
1020
|
+
Returns:
|
1021
|
+
--------
|
1022
|
+
str
|
1023
|
+
The processed text with all command keywords replaced with their actual values.
|
1024
|
+
|
1025
|
+
Raises:
|
1026
|
+
-------
|
1027
|
+
FileExistsError
|
1028
|
+
If a referenced command module file does not exist.
|
1029
|
+
"""
|
533
1030
|
# Find and replace command module keywords
|
534
1031
|
command_module_keywords = get_command_module_keywords(text)
|
535
1032
|
for command_module_keyword in command_module_keywords:
|
@@ -559,9 +1056,30 @@ class RCCNCommand(Command):
|
|
559
1056
|
return text
|
560
1057
|
|
561
1058
|
def user_snapshot_path(self):
|
1059
|
+
"""
|
1060
|
+
Get the path for user snapshots with current timestamp.
|
1061
|
+
|
1062
|
+
Returns:
|
1063
|
+
--------
|
1064
|
+
str
|
1065
|
+
The path where user snapshots are stored.
|
1066
|
+
"""
|
562
1067
|
return os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
563
1068
|
|
564
1069
|
def struct_definition(self):
|
1070
|
+
"""
|
1071
|
+
Generate a Rust struct definition for this command's arguments.
|
1072
|
+
|
1073
|
+
This method creates a BitStruct definition in Rust for the command's arguments,
|
1074
|
+
with appropriate documentation comments. If the command has no arguments,
|
1075
|
+
an empty string is returned.
|
1076
|
+
|
1077
|
+
Returns:
|
1078
|
+
--------
|
1079
|
+
str
|
1080
|
+
A string containing the Rust code for the struct definition, or an empty
|
1081
|
+
string if the command has no arguments.
|
1082
|
+
"""
|
565
1083
|
struct_definition_text = ""
|
566
1084
|
if len(self.arguments) == 0:
|
567
1085
|
return ''
|
@@ -582,6 +1100,18 @@ class RCCNCommand(Command):
|
|
582
1100
|
|
583
1101
|
|
584
1102
|
class RCCNContainer(Container):
|
1103
|
+
"""
|
1104
|
+
A Protocol Utilization Standard (PUS) telemetry container that belongs to a Service.
|
1105
|
+
|
1106
|
+
This class extends the Container class and provides specialized functionality for
|
1107
|
+
generating Rust code for telemetry containers within a PUS service. RCCNContainer
|
1108
|
+
manages container type and subtype assignment, condition expressions, and handles
|
1109
|
+
the generation of Rust struct definitions for telemetry data.
|
1110
|
+
|
1111
|
+
Each RCCNContainer is automatically assigned to its parent Service's service ID (type)
|
1112
|
+
and receives a unique subtype within that service. It supports various parameter types
|
1113
|
+
for telemetry data collection and provides methods to add these parameters to the container.
|
1114
|
+
"""
|
585
1115
|
def __init__(
|
586
1116
|
self,
|
587
1117
|
base="/PUS/pus-tm",
|
@@ -600,6 +1130,45 @@ class RCCNContainer(Container):
|
|
600
1130
|
rate: float | None = None,
|
601
1131
|
hint_partition: bool = False,
|
602
1132
|
):
|
1133
|
+
"""
|
1134
|
+
Initialize a new PUS telemetry container.
|
1135
|
+
|
1136
|
+
Parameters:
|
1137
|
+
-----------
|
1138
|
+
base : str, optional
|
1139
|
+
Base path for the telemetry packet. Default is "/PUS/pus-tm".
|
1140
|
+
subtype : int, optional
|
1141
|
+
Subtype for the container. If not provided, a unique value will be assigned.
|
1142
|
+
system : System, optional
|
1143
|
+
The system (usually a Service) this container belongs to.
|
1144
|
+
name : str
|
1145
|
+
The name of the container. Required.
|
1146
|
+
entries : Sequence[ParameterEntry | ContainerEntry], optional
|
1147
|
+
Parameter or container entries for this container.
|
1148
|
+
abstract : bool, optional
|
1149
|
+
Whether this container is abstract. Default is False.
|
1150
|
+
condition : Expression, optional
|
1151
|
+
Condition expression for when this container should be used.
|
1152
|
+
aliases : Mapping[str, str], optional
|
1153
|
+
Alternative names for the container, keyed by namespace.
|
1154
|
+
short_description : str, optional
|
1155
|
+
A short description of the container.
|
1156
|
+
long_description : str, optional
|
1157
|
+
A longer description of the container.
|
1158
|
+
extra : Mapping[str, str], optional
|
1159
|
+
Arbitrary information about the container, keyed by name.
|
1160
|
+
bits : int, optional
|
1161
|
+
Size in bits.
|
1162
|
+
rate : float, optional
|
1163
|
+
Expected rate of reception in Hz.
|
1164
|
+
hint_partition : bool, optional
|
1165
|
+
Hint to partition this container. Default is False.
|
1166
|
+
|
1167
|
+
Raises:
|
1168
|
+
-------
|
1169
|
+
ValueError
|
1170
|
+
If name is not provided.
|
1171
|
+
"""
|
603
1172
|
self.base = base
|
604
1173
|
self.subtype = subtype
|
605
1174
|
self.init_kwargs = {
|
@@ -626,6 +1195,24 @@ class RCCNContainer(Container):
|
|
626
1195
|
self.add_to_service(system)
|
627
1196
|
|
628
1197
|
def add_to_service(self, service):
|
1198
|
+
"""
|
1199
|
+
Add this container to a Service.
|
1200
|
+
|
1201
|
+
This method handles the initialization of the container as part of a service,
|
1202
|
+
setting up condition expressions for type and subtype, and managing the container's
|
1203
|
+
integration with the service.
|
1204
|
+
|
1205
|
+
The method performs several key operations:
|
1206
|
+
1. Sets the container's type to match the service ID
|
1207
|
+
2. Extracts type/subtype from existing condition expressions if present
|
1208
|
+
3. Creates appropriate condition expressions for the container
|
1209
|
+
4. Assigns a unique subtype if one isn't explicitly provided
|
1210
|
+
|
1211
|
+
Parameters:
|
1212
|
+
-----------
|
1213
|
+
service : Service
|
1214
|
+
The service this container will belong to.
|
1215
|
+
"""
|
629
1216
|
self.type = service.service_id
|
630
1217
|
condition_type = None
|
631
1218
|
condition_subtype = None
|
@@ -665,6 +1252,20 @@ class RCCNContainer(Container):
|
|
665
1252
|
super().__init__(system=service, **self.init_kwargs)
|
666
1253
|
|
667
1254
|
def generate_rccn_telemetry(self):
|
1255
|
+
"""
|
1256
|
+
Generate Rust code for this telemetry container.
|
1257
|
+
|
1258
|
+
This method creates a Rust struct definition for the container, including:
|
1259
|
+
1. Documentation comments from the container's short_description
|
1260
|
+
2. ServiceTelemetry and BitStruct derive attributes
|
1261
|
+
3. Subtype attribute if a subtype is defined
|
1262
|
+
4. All parameter entries from the container
|
1263
|
+
|
1264
|
+
Returns:
|
1265
|
+
--------
|
1266
|
+
str
|
1267
|
+
A string containing the complete Rust struct definition for this container.
|
1268
|
+
"""
|
668
1269
|
rccn_telemetry_text = ""
|
669
1270
|
if hasattr(self, 'short_description') and self.short_description is not None:
|
670
1271
|
rccn_telemetry_text += "/// "+str(self.short_description)+"\n"
|
@@ -682,7 +1283,7 @@ class RCCNContainer(Container):
|
|
682
1283
|
rccn_telemetry_text += append
|
683
1284
|
return rccn_telemetry_text
|
684
1285
|
|
685
|
-
def
|
1286
|
+
def add_integer_parameter_entry(
|
686
1287
|
self,
|
687
1288
|
name: str,
|
688
1289
|
signed: bool = True,
|
@@ -702,8 +1303,50 @@ class RCCNContainer(Container):
|
|
702
1303
|
alarm: ThresholdAlarm | None = None,
|
703
1304
|
context_alarms: Sequence[ThresholdContextAlarm] | None = None,
|
704
1305
|
):
|
705
|
-
|
706
|
-
|
1306
|
+
"""
|
1307
|
+
Add an integer parameter to this container.
|
1308
|
+
|
1309
|
+
This method creates an integer parameter and adds it as an entry to this container.
|
1310
|
+
|
1311
|
+
Parameters:
|
1312
|
+
-----------
|
1313
|
+
name : str
|
1314
|
+
The name of the parameter.
|
1315
|
+
signed : bool, optional
|
1316
|
+
Whether the integer is signed. Default is True.
|
1317
|
+
bits : int, optional
|
1318
|
+
Number of bits. Default is 32.
|
1319
|
+
minimum : int, optional
|
1320
|
+
Minimum valid value.
|
1321
|
+
maximum : int, optional
|
1322
|
+
Maximum valid value.
|
1323
|
+
aliases : Mapping[str, str], optional
|
1324
|
+
Alternative names for the parameter, keyed by namespace.
|
1325
|
+
data_source : DataSource, optional
|
1326
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1327
|
+
initial_value : Any, optional
|
1328
|
+
Initial value for this parameter.
|
1329
|
+
persistent : bool, optional
|
1330
|
+
Whether this parameter's value should be persisted. Default is True.
|
1331
|
+
short_description : str, optional
|
1332
|
+
A short description of the parameter.
|
1333
|
+
long_description : str, optional
|
1334
|
+
A longer description of the parameter.
|
1335
|
+
extra : Mapping[str, str], optional
|
1336
|
+
Arbitrary information about the parameter, keyed by name.
|
1337
|
+
units : str, optional
|
1338
|
+
Units of this parameter.
|
1339
|
+
encoding : Encoding, optional
|
1340
|
+
Encoding information for this parameter.
|
1341
|
+
calibrator : Calibrator, optional
|
1342
|
+
Calibration information for this parameter.
|
1343
|
+
alarm : ThresholdAlarm, optional
|
1344
|
+
Alarm conditions for this parameter.
|
1345
|
+
context_alarms : Sequence[ThresholdContextAlarm], optional
|
1346
|
+
Context-dependent alarm conditions for this parameter.
|
1347
|
+
"""
|
1348
|
+
parameter = IntegerParameter(
|
1349
|
+
system=self.system,
|
707
1350
|
name=name,
|
708
1351
|
signed=signed,
|
709
1352
|
bits=bits,
|
@@ -722,4 +1365,531 @@ class RCCNContainer(Container):
|
|
722
1365
|
alarm=alarm,
|
723
1366
|
context_alarms=context_alarms
|
724
1367
|
)
|
725
|
-
self.entries.append(
|
1368
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1369
|
+
|
1370
|
+
def add_enumerated_parameter_entry(
|
1371
|
+
self,
|
1372
|
+
name: str,
|
1373
|
+
choices: Choices,
|
1374
|
+
alarm: EnumerationAlarm | None = None,
|
1375
|
+
context_alarms: Sequence[EnumerationContextAlarm] | None = None,
|
1376
|
+
aliases: Mapping[str, str] | None = None,
|
1377
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1378
|
+
initial_value: Any = None,
|
1379
|
+
persistent: bool = True,
|
1380
|
+
short_description: str | None = None,
|
1381
|
+
long_description: str | None = None,
|
1382
|
+
extra: Mapping[str, str] | None = None,
|
1383
|
+
units: str | None = None,
|
1384
|
+
encoding: Encoding | None = None,
|
1385
|
+
):
|
1386
|
+
"""
|
1387
|
+
Add an enumerated parameter to this container.
|
1388
|
+
|
1389
|
+
This method creates an enumerated parameter with predefined choices and adds it
|
1390
|
+
as an entry to this container.
|
1391
|
+
|
1392
|
+
Parameters:
|
1393
|
+
-----------
|
1394
|
+
name : str
|
1395
|
+
The name of the parameter.
|
1396
|
+
choices : Choices
|
1397
|
+
The enumeration choices for this parameter.
|
1398
|
+
alarm : EnumerationAlarm, optional
|
1399
|
+
Alarm conditions for this parameter.
|
1400
|
+
context_alarms : Sequence[EnumerationContextAlarm], optional
|
1401
|
+
Context-dependent alarm conditions for this parameter.
|
1402
|
+
aliases : Mapping[str, str], optional
|
1403
|
+
Alternative names for the parameter, keyed by namespace.
|
1404
|
+
data_source : DataSource, optional
|
1405
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1406
|
+
initial_value : Any, optional
|
1407
|
+
Initial value for this parameter.
|
1408
|
+
persistent : bool, optional
|
1409
|
+
Whether this parameter's value should be persisted. Default is True.
|
1410
|
+
short_description : str, optional
|
1411
|
+
A short description of the parameter.
|
1412
|
+
long_description : str, optional
|
1413
|
+
A longer description of the parameter.
|
1414
|
+
extra : Mapping[str, str], optional
|
1415
|
+
Arbitrary information about the parameter, keyed by name.
|
1416
|
+
units : str, optional
|
1417
|
+
Units of this parameter.
|
1418
|
+
encoding : Encoding, optional
|
1419
|
+
Encoding information for this parameter.
|
1420
|
+
"""
|
1421
|
+
parameter = EnumeratedParameter(
|
1422
|
+
system = self.system,
|
1423
|
+
name=name,
|
1424
|
+
choices=choices,
|
1425
|
+
alarm=alarm,
|
1426
|
+
context_alarms=context_alarms,
|
1427
|
+
aliases=aliases,
|
1428
|
+
data_source=data_source,
|
1429
|
+
initial_value=initial_value,
|
1430
|
+
persistent=persistent,
|
1431
|
+
short_description=short_description,
|
1432
|
+
long_description=long_description,
|
1433
|
+
extra=extra,
|
1434
|
+
units=units,
|
1435
|
+
encoding=encoding
|
1436
|
+
)
|
1437
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1438
|
+
|
1439
|
+
def add_boolean_parameter_entry(
|
1440
|
+
self,
|
1441
|
+
name: str,
|
1442
|
+
zero_string_value: str = "False",
|
1443
|
+
one_string_value: str = "True",
|
1444
|
+
aliases: Mapping[str, str] | None = None,
|
1445
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1446
|
+
initial_value: Any = None,
|
1447
|
+
persistent: bool = True,
|
1448
|
+
short_description: str | None = None,
|
1449
|
+
long_description: str | None = None,
|
1450
|
+
extra: Mapping[str, str] | None = None,
|
1451
|
+
units: str | None = None,
|
1452
|
+
encoding: Encoding | None = None,
|
1453
|
+
):
|
1454
|
+
"""
|
1455
|
+
Add a boolean parameter to this container.
|
1456
|
+
|
1457
|
+
This method creates a boolean parameter and adds it as an entry to this container.
|
1458
|
+
|
1459
|
+
Parameters:
|
1460
|
+
-----------
|
1461
|
+
name : str
|
1462
|
+
The name of the parameter.
|
1463
|
+
zero_string_value : str, optional
|
1464
|
+
String representation of the boolean value 'false'. Default is "False".
|
1465
|
+
one_string_value : str, optional
|
1466
|
+
String representation of the boolean value 'true'. Default is "True".
|
1467
|
+
aliases : Mapping[str, str], optional
|
1468
|
+
Alternative names for the parameter, keyed by namespace.
|
1469
|
+
data_source : DataSource, optional
|
1470
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1471
|
+
initial_value : Any, optional
|
1472
|
+
Initial value for this parameter.
|
1473
|
+
persistent : bool, optional
|
1474
|
+
Whether this parameter's value should be persisted. Default is True.
|
1475
|
+
short_description : str, optional
|
1476
|
+
A short description of the parameter.
|
1477
|
+
long_description : str, optional
|
1478
|
+
A longer description of the parameter.
|
1479
|
+
extra : Mapping[str, str], optional
|
1480
|
+
Arbitrary information about the parameter, keyed by name.
|
1481
|
+
units : str, optional
|
1482
|
+
Units of this parameter.
|
1483
|
+
encoding : Encoding, optional
|
1484
|
+
Encoding information for this parameter.
|
1485
|
+
"""
|
1486
|
+
parameter = BooleanParameter(
|
1487
|
+
system=self.system,
|
1488
|
+
name=name,
|
1489
|
+
zero_string_value=zero_string_value,
|
1490
|
+
one_string_value=one_string_value,
|
1491
|
+
aliases=aliases,
|
1492
|
+
data_source=data_source,
|
1493
|
+
initial_value=initial_value,
|
1494
|
+
persistent=persistent,
|
1495
|
+
short_description=short_description,
|
1496
|
+
long_description=long_description,
|
1497
|
+
extra=extra,
|
1498
|
+
units=units,
|
1499
|
+
encoding=encoding
|
1500
|
+
)
|
1501
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1502
|
+
|
1503
|
+
def add_float_parameter_entry(
|
1504
|
+
self,
|
1505
|
+
name: str,
|
1506
|
+
bits: Literal[32, 64] = 32,
|
1507
|
+
minimum: float | None = None,
|
1508
|
+
minimum_inclusive: bool = True,
|
1509
|
+
maximum: float | None = None,
|
1510
|
+
maximum_inclusive: bool = True,
|
1511
|
+
aliases: Mapping[str, str] | None = None,
|
1512
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1513
|
+
initial_value: Any = None,
|
1514
|
+
persistent: bool = True,
|
1515
|
+
short_description: str | None = None,
|
1516
|
+
long_description: str | None = None,
|
1517
|
+
extra: Mapping[str, str] | None = None,
|
1518
|
+
units: str | None = None,
|
1519
|
+
encoding: Encoding | None = None,
|
1520
|
+
calibrator: Calibrator | None = None,
|
1521
|
+
alarm: ThresholdAlarm | None = None,
|
1522
|
+
context_alarms: Sequence[ThresholdContextAlarm] | None = None,
|
1523
|
+
):
|
1524
|
+
"""
|
1525
|
+
Add a floating-point parameter to this container.
|
1526
|
+
|
1527
|
+
This method creates a float parameter and adds it as an entry to this container.
|
1528
|
+
|
1529
|
+
Parameters:
|
1530
|
+
-----------
|
1531
|
+
name : str
|
1532
|
+
The name of the parameter.
|
1533
|
+
bits : Literal[32, 64], optional
|
1534
|
+
Number of bits, either 32 (float) or 64 (double). Default is 32.
|
1535
|
+
minimum : float, optional
|
1536
|
+
Minimum valid value.
|
1537
|
+
minimum_inclusive : bool, optional
|
1538
|
+
Whether the minimum value is inclusive. Default is True.
|
1539
|
+
maximum : float, optional
|
1540
|
+
Maximum valid value.
|
1541
|
+
maximum_inclusive : bool, optional
|
1542
|
+
Whether the maximum value is inclusive. Default is True.
|
1543
|
+
aliases : Mapping[str, str], optional
|
1544
|
+
Alternative names for the parameter, keyed by namespace.
|
1545
|
+
data_source : DataSource, optional
|
1546
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1547
|
+
initial_value : Any, optional
|
1548
|
+
Initial value for this parameter.
|
1549
|
+
persistent : bool, optional
|
1550
|
+
Whether this parameter's value should be persisted. Default is True.
|
1551
|
+
short_description : str, optional
|
1552
|
+
A short description of the parameter.
|
1553
|
+
long_description : str, optional
|
1554
|
+
A longer description of the parameter.
|
1555
|
+
extra : Mapping[str, str], optional
|
1556
|
+
Arbitrary information about the parameter, keyed by name.
|
1557
|
+
units : str, optional
|
1558
|
+
Units of this parameter.
|
1559
|
+
encoding : Encoding, optional
|
1560
|
+
Encoding information for this parameter.
|
1561
|
+
calibrator : Calibrator, optional
|
1562
|
+
Calibration information for this parameter.
|
1563
|
+
alarm : ThresholdAlarm, optional
|
1564
|
+
Alarm conditions for this parameter.
|
1565
|
+
context_alarms : Sequence[ThresholdContextAlarm], optional
|
1566
|
+
Context-dependent alarm conditions for this parameter.
|
1567
|
+
"""
|
1568
|
+
parameter = FloatParameter(
|
1569
|
+
system=self.system,
|
1570
|
+
name=name,
|
1571
|
+
bits=bits,
|
1572
|
+
minimum=minimum,
|
1573
|
+
minimum_inclusive=minimum_inclusive,
|
1574
|
+
maximum=maximum,
|
1575
|
+
maximum_inclusive=maximum_inclusive,
|
1576
|
+
aliases=aliases,
|
1577
|
+
data_source=data_source,
|
1578
|
+
initial_value=initial_value,
|
1579
|
+
persistent=persistent,
|
1580
|
+
short_description=short_description,
|
1581
|
+
long_description=long_description,
|
1582
|
+
extra=extra,
|
1583
|
+
units=units,
|
1584
|
+
encoding=encoding,
|
1585
|
+
calibrator=calibrator,
|
1586
|
+
alarm=alarm,
|
1587
|
+
context_alarms=context_alarms
|
1588
|
+
)
|
1589
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1590
|
+
|
1591
|
+
def add_string_parameter_entry(
|
1592
|
+
self,
|
1593
|
+
name: str,
|
1594
|
+
min_length: int | None = None,
|
1595
|
+
max_length: int | None = None,
|
1596
|
+
aliases: Mapping[str, str] | None = None,
|
1597
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1598
|
+
initial_value: Any = None,
|
1599
|
+
persistent: bool = True,
|
1600
|
+
short_description: str | None = None,
|
1601
|
+
long_description: str | None = None,
|
1602
|
+
extra: Mapping[str, str] | None = None,
|
1603
|
+
units: str | None = None,
|
1604
|
+
encoding: Encoding | None = None,
|
1605
|
+
):
|
1606
|
+
"""
|
1607
|
+
Add a string parameter to this container.
|
1608
|
+
|
1609
|
+
This method creates a string parameter and adds it as an entry to this container.
|
1610
|
+
|
1611
|
+
Parameters:
|
1612
|
+
-----------
|
1613
|
+
name : str
|
1614
|
+
The name of the parameter.
|
1615
|
+
min_length : int, optional
|
1616
|
+
Minimum valid length of the string.
|
1617
|
+
max_length : int, optional
|
1618
|
+
Maximum valid length of the string.
|
1619
|
+
aliases : Mapping[str, str], optional
|
1620
|
+
Alternative names for the parameter, keyed by namespace.
|
1621
|
+
data_source : DataSource, optional
|
1622
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1623
|
+
initial_value : Any, optional
|
1624
|
+
Initial value for this parameter.
|
1625
|
+
persistent : bool, optional
|
1626
|
+
Whether this parameter's value should be persisted. Default is True.
|
1627
|
+
short_description : str, optional
|
1628
|
+
A short description of the parameter.
|
1629
|
+
long_description : str, optional
|
1630
|
+
A longer description of the parameter.
|
1631
|
+
extra : Mapping[str, str], optional
|
1632
|
+
Arbitrary information about the parameter, keyed by name.
|
1633
|
+
units : str, optional
|
1634
|
+
Units of this parameter.
|
1635
|
+
encoding : Encoding, optional
|
1636
|
+
Encoding information for this parameter.
|
1637
|
+
"""
|
1638
|
+
parameter = StringParameter(
|
1639
|
+
system=self.system,
|
1640
|
+
name=name,
|
1641
|
+
min_length=min_length,
|
1642
|
+
max_length=max_length,
|
1643
|
+
aliases=aliases,
|
1644
|
+
data_source=data_source,
|
1645
|
+
initial_value=initial_value,
|
1646
|
+
persistent=persistent,
|
1647
|
+
short_description=short_description,
|
1648
|
+
long_description=long_description,
|
1649
|
+
extra=extra,
|
1650
|
+
units=units,
|
1651
|
+
encoding=encoding
|
1652
|
+
)
|
1653
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1654
|
+
|
1655
|
+
def add_binary_parameter_entry(
|
1656
|
+
self,
|
1657
|
+
name: str,
|
1658
|
+
min_length: int | None = None,
|
1659
|
+
max_length: int | None = None,
|
1660
|
+
aliases: Mapping[str, str] | None = None,
|
1661
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1662
|
+
initial_value: Any = None,
|
1663
|
+
persistent: bool = True,
|
1664
|
+
short_description: str | None = None,
|
1665
|
+
long_description: str | None = None,
|
1666
|
+
extra: Mapping[str, str] | None = None,
|
1667
|
+
units: str | None = None,
|
1668
|
+
encoding: Encoding | None = None,
|
1669
|
+
):
|
1670
|
+
"""
|
1671
|
+
Add a binary parameter to this container.
|
1672
|
+
|
1673
|
+
This method creates a binary parameter and adds it as an entry to this container.
|
1674
|
+
|
1675
|
+
Parameters:
|
1676
|
+
-----------
|
1677
|
+
name : str
|
1678
|
+
The name of the parameter.
|
1679
|
+
min_length : int, optional
|
1680
|
+
Minimum valid length of the binary data in bytes.
|
1681
|
+
max_length : int, optional
|
1682
|
+
Maximum valid length of the binary data in bytes.
|
1683
|
+
aliases : Mapping[str, str], optional
|
1684
|
+
Alternative names for the parameter, keyed by namespace.
|
1685
|
+
data_source : DataSource, optional
|
1686
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1687
|
+
initial_value : Any, optional
|
1688
|
+
Initial value for this parameter.
|
1689
|
+
persistent : bool, optional
|
1690
|
+
Whether this parameter's value should be persisted. Default is True.
|
1691
|
+
short_description : str, optional
|
1692
|
+
A short description of the parameter.
|
1693
|
+
long_description : str, optional
|
1694
|
+
A longer description of the parameter.
|
1695
|
+
extra : Mapping[str, str], optional
|
1696
|
+
Arbitrary information about the parameter, keyed by name.
|
1697
|
+
units : str, optional
|
1698
|
+
Units of this parameter.
|
1699
|
+
encoding : Encoding, optional
|
1700
|
+
Encoding information for this parameter.
|
1701
|
+
"""
|
1702
|
+
parameter = BinaryParameter(
|
1703
|
+
system=self.system,
|
1704
|
+
name=name,
|
1705
|
+
min_length=min_length,
|
1706
|
+
max_length=max_length,
|
1707
|
+
aliases=aliases,
|
1708
|
+
data_source=data_source,
|
1709
|
+
initial_value=initial_value,
|
1710
|
+
persistent=persistent,
|
1711
|
+
short_description=short_description,
|
1712
|
+
long_description=long_description,
|
1713
|
+
extra=extra,
|
1714
|
+
units=units,
|
1715
|
+
encoding=encoding
|
1716
|
+
)
|
1717
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1718
|
+
|
1719
|
+
def add_absolute_time_parameter_entry(
|
1720
|
+
self,
|
1721
|
+
name: str,
|
1722
|
+
reference: Union[Epoch, datetime.datetime, AbsoluteTimeParameter],
|
1723
|
+
aliases: Mapping[str, str] | None = None,
|
1724
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1725
|
+
initial_value: Any = None,
|
1726
|
+
persistent: bool = True,
|
1727
|
+
short_description: str | None = None,
|
1728
|
+
long_description: str | None = None,
|
1729
|
+
extra: Mapping[str, str] | None = None,
|
1730
|
+
units: str | None = None,
|
1731
|
+
encoding: TimeEncoding | None = None,
|
1732
|
+
):
|
1733
|
+
"""
|
1734
|
+
Add an absolute time parameter to this container.
|
1735
|
+
|
1736
|
+
This method creates an absolute time parameter and adds it as an entry to this container.
|
1737
|
+
|
1738
|
+
Parameters:
|
1739
|
+
-----------
|
1740
|
+
name : str
|
1741
|
+
The name of the parameter.
|
1742
|
+
reference : Union[Epoch, datetime.datetime, AbsoluteTimeParameter]
|
1743
|
+
Reference time (epoch) for this parameter.
|
1744
|
+
aliases : Mapping[str, str], optional
|
1745
|
+
Alternative names for the parameter, keyed by namespace.
|
1746
|
+
data_source : DataSource, optional
|
1747
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1748
|
+
initial_value : Any, optional
|
1749
|
+
Initial value for this parameter.
|
1750
|
+
persistent : bool, optional
|
1751
|
+
Whether this parameter's value should be persisted. Default is True.
|
1752
|
+
short_description : str, optional
|
1753
|
+
A short description of the parameter.
|
1754
|
+
long_description : str, optional
|
1755
|
+
A longer description of the parameter.
|
1756
|
+
extra : Mapping[str, str], optional
|
1757
|
+
Arbitrary information about the parameter, keyed by name.
|
1758
|
+
units : str, optional
|
1759
|
+
Units of this parameter.
|
1760
|
+
encoding : TimeEncoding, optional
|
1761
|
+
Encoding information for this time parameter.
|
1762
|
+
"""
|
1763
|
+
parameter = AbsoluteTimeParameter(
|
1764
|
+
system=self,
|
1765
|
+
name=name,
|
1766
|
+
reference=reference,
|
1767
|
+
aliases=aliases,
|
1768
|
+
data_source=data_source,
|
1769
|
+
initial_value=initial_value,
|
1770
|
+
persistent=persistent,
|
1771
|
+
short_description=short_description,
|
1772
|
+
long_description=long_description,
|
1773
|
+
extra=extra,
|
1774
|
+
units=units,
|
1775
|
+
encoding=encoding
|
1776
|
+
)
|
1777
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1778
|
+
|
1779
|
+
def add_aggregate_parameter_entry(
|
1780
|
+
self,
|
1781
|
+
name: str,
|
1782
|
+
members: Sequence[Member],
|
1783
|
+
aliases: Mapping[str, str] | None = None,
|
1784
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1785
|
+
initial_value: Any = None,
|
1786
|
+
persistent: bool = True,
|
1787
|
+
short_description: str | None = None,
|
1788
|
+
long_description: str | None = None,
|
1789
|
+
extra: Mapping[str, str] | None = None,
|
1790
|
+
encoding: Encoding | None = None,
|
1791
|
+
):
|
1792
|
+
"""
|
1793
|
+
Add an aggregate parameter to this container.
|
1794
|
+
|
1795
|
+
This method creates an aggregate parameter (a parameter composed of multiple members)
|
1796
|
+
and adds it as an entry to this container.
|
1797
|
+
|
1798
|
+
Parameters:
|
1799
|
+
-----------
|
1800
|
+
name : str
|
1801
|
+
The name of the parameter.
|
1802
|
+
members : Sequence[Member]
|
1803
|
+
The members that make up this aggregate parameter.
|
1804
|
+
aliases : Mapping[str, str], optional
|
1805
|
+
Alternative names for the parameter, keyed by namespace.
|
1806
|
+
data_source : DataSource, optional
|
1807
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1808
|
+
initial_value : Any, optional
|
1809
|
+
Initial value for this parameter.
|
1810
|
+
persistent : bool, optional
|
1811
|
+
Whether this parameter's value should be persisted. Default is True.
|
1812
|
+
short_description : str, optional
|
1813
|
+
A short description of the parameter.
|
1814
|
+
long_description : str, optional
|
1815
|
+
A longer description of the parameter.
|
1816
|
+
extra : Mapping[str, str], optional
|
1817
|
+
Arbitrary information about the parameter, keyed by name.
|
1818
|
+
encoding : Encoding, optional
|
1819
|
+
Encoding information for this parameter.
|
1820
|
+
"""
|
1821
|
+
parameter = AggregateParameter(
|
1822
|
+
system=self.system,
|
1823
|
+
name=name,
|
1824
|
+
members=members,
|
1825
|
+
aliases=aliases,
|
1826
|
+
data_source=data_source,
|
1827
|
+
initial_value=initial_value,
|
1828
|
+
persistent=persistent,
|
1829
|
+
short_description=short_description,
|
1830
|
+
long_description=long_description,
|
1831
|
+
extra=extra,
|
1832
|
+
encoding=encoding
|
1833
|
+
)
|
1834
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|
1835
|
+
|
1836
|
+
def add_array_parameter_entry(
|
1837
|
+
self,
|
1838
|
+
name: str,
|
1839
|
+
data_type: DataType,
|
1840
|
+
length: int | DynamicInteger,
|
1841
|
+
aliases: Mapping[str, str] | None = None,
|
1842
|
+
data_source: DataSource = DataSource.TELEMETERED,
|
1843
|
+
initial_value: Any = None,
|
1844
|
+
persistent: bool = True,
|
1845
|
+
short_description: str | None = None,
|
1846
|
+
long_description: str | None = None,
|
1847
|
+
extra: Mapping[str, str] | None = None,
|
1848
|
+
encoding: Encoding | None = None,
|
1849
|
+
):
|
1850
|
+
"""
|
1851
|
+
Add an array parameter to this container.
|
1852
|
+
|
1853
|
+
This method creates an array parameter (a parameter containing multiple elements
|
1854
|
+
of the same type) and adds it as an entry to this container.
|
1855
|
+
|
1856
|
+
Parameters:
|
1857
|
+
-----------
|
1858
|
+
name : str
|
1859
|
+
The name of the parameter.
|
1860
|
+
data_type : DataType
|
1861
|
+
The data type of the array elements.
|
1862
|
+
length : int | DynamicInteger
|
1863
|
+
The length of the array, either as a fixed integer or a dynamic reference.
|
1864
|
+
aliases : Mapping[str, str], optional
|
1865
|
+
Alternative names for the parameter, keyed by namespace.
|
1866
|
+
data_source : DataSource, optional
|
1867
|
+
Source of the parameter value. Default is DataSource.TELEMETERED.
|
1868
|
+
initial_value : Any, optional
|
1869
|
+
Initial value for this parameter.
|
1870
|
+
persistent : bool, optional
|
1871
|
+
Whether this parameter's value should be persisted. Default is True.
|
1872
|
+
short_description : str, optional
|
1873
|
+
A short description of the parameter.
|
1874
|
+
long_description : str, optional
|
1875
|
+
A longer description of the parameter.
|
1876
|
+
extra : Mapping[str, str], optional
|
1877
|
+
Arbitrary information about the parameter, keyed by name.
|
1878
|
+
encoding : Encoding, optional
|
1879
|
+
Encoding information for this parameter.
|
1880
|
+
"""
|
1881
|
+
parameter = ArrayParameter(
|
1882
|
+
system=self.system,
|
1883
|
+
name=name,
|
1884
|
+
data_type=data_type,
|
1885
|
+
length=length,
|
1886
|
+
aliases=aliases,
|
1887
|
+
data_source=data_source,
|
1888
|
+
initial_value=initial_value,
|
1889
|
+
persistent=persistent,
|
1890
|
+
short_description=short_description,
|
1891
|
+
long_description=long_description,
|
1892
|
+
extra=extra,
|
1893
|
+
encoding=encoding
|
1894
|
+
)
|
1895
|
+
self.entries.append(ParameterEntry(parameter=parameter))
|