rccn-gen 1.2.0__py3-none-any.whl → 1.3.1__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 add_integer_parameter(
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
- int_parameter = IntegerParameter(
706
- system=self,
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(int_parameter)
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))