fprime-gds 3.4.3__py3-none-any.whl → 3.4.4a2__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.
Files changed (37) hide show
  1. fprime_gds/common/communication/adapters/base.py +30 -58
  2. fprime_gds/common/communication/adapters/ip.py +23 -5
  3. fprime_gds/common/communication/adapters/uart.py +20 -7
  4. fprime_gds/common/communication/checksum.py +1 -3
  5. fprime_gds/common/communication/framing.py +53 -4
  6. fprime_gds/common/data_types/event_data.py +6 -1
  7. fprime_gds/common/data_types/exceptions.py +16 -11
  8. fprime_gds/common/loaders/ch_json_loader.py +107 -0
  9. fprime_gds/common/loaders/ch_xml_loader.py +5 -5
  10. fprime_gds/common/loaders/cmd_json_loader.py +85 -0
  11. fprime_gds/common/loaders/dict_loader.py +1 -1
  12. fprime_gds/common/loaders/event_json_loader.py +108 -0
  13. fprime_gds/common/loaders/event_xml_loader.py +10 -6
  14. fprime_gds/common/loaders/json_loader.py +222 -0
  15. fprime_gds/common/loaders/xml_loader.py +31 -9
  16. fprime_gds/common/pipeline/dictionaries.py +38 -3
  17. fprime_gds/common/tools/seqgen.py +4 -4
  18. fprime_gds/common/utils/string_util.py +57 -65
  19. fprime_gds/common/zmq_transport.py +37 -20
  20. fprime_gds/executables/apps.py +150 -0
  21. fprime_gds/executables/cli.py +239 -103
  22. fprime_gds/executables/comm.py +17 -27
  23. fprime_gds/executables/data_product_writer.py +935 -0
  24. fprime_gds/executables/run_deployment.py +55 -14
  25. fprime_gds/executables/utils.py +24 -12
  26. fprime_gds/flask/sequence.py +1 -1
  27. fprime_gds/flask/static/addons/commanding/command-input.js +3 -2
  28. fprime_gds/plugin/__init__.py +0 -0
  29. fprime_gds/plugin/definitions.py +71 -0
  30. fprime_gds/plugin/system.py +225 -0
  31. {fprime_gds-3.4.3.dist-info → fprime_gds-3.4.4a2.dist-info}/METADATA +3 -2
  32. {fprime_gds-3.4.3.dist-info → fprime_gds-3.4.4a2.dist-info}/RECORD +37 -28
  33. {fprime_gds-3.4.3.dist-info → fprime_gds-3.4.4a2.dist-info}/WHEEL +1 -1
  34. {fprime_gds-3.4.3.dist-info → fprime_gds-3.4.4a2.dist-info}/entry_points.txt +2 -3
  35. {fprime_gds-3.4.3.dist-info → fprime_gds-3.4.4a2.dist-info}/LICENSE.txt +0 -0
  36. {fprime_gds-3.4.3.dist-info → fprime_gds-3.4.4a2.dist-info}/NOTICE.txt +0 -0
  37. {fprime_gds-3.4.3.dist-info → fprime_gds-3.4.4a2.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,7 @@ code that they are importing.
7
7
 
8
8
  @author mstarch
9
9
  """
10
+
10
11
  import argparse
11
12
  import datetime
12
13
  import errno
@@ -23,27 +24,14 @@ from typing import Any, Dict, List, Tuple
23
24
  # Required to set the checksum as a module variable
24
25
  import fprime_gds.common.communication.checksum
25
26
  import fprime_gds.common.logger
26
- from fprime_gds.common.communication.adapters.base import BaseAdapter
27
27
  from fprime_gds.common.communication.adapters.ip import check_port
28
28
  from fprime_gds.common.pipeline.standard import StandardPipeline
29
29
  from fprime_gds.common.transport import ThreadedTCPSocketClient
30
30
  from fprime_gds.common.utils.config_manager import ConfigManager
31
31
  from fprime_gds.executables.utils import find_app, find_dict, get_artifacts_root
32
-
33
- # Optional import: ZeroMQ. Requires package: pyzmq
34
- try:
35
- import zmq
36
-
37
- from fprime_gds.common.zmq_transport import ZmqClient
38
- except ImportError:
39
- zmq = None
40
- ZmqClient = None
41
-
42
- # Optional import: Serial Adapter. Requires package: SerialAdapter
43
- try:
44
- from fprime_gds.common.communication.adapters.uart import SerialAdapter
45
- except ImportError:
46
- SerialAdapter = None
32
+ from fprime_gds.plugin.definitions import PluginType
33
+ from fprime_gds.plugin.system import Plugins
34
+ from fprime_gds.common.zmq_transport import ZmqClient
47
35
 
48
36
 
49
37
  GUIS = ["none", "html"]
@@ -85,11 +73,31 @@ class ParserBase(ABC):
85
73
  Return:
86
74
  argparse parser for supplied arguments
87
75
  """
88
- parser = argparse.ArgumentParser(description=self.description, add_help=True)
89
- for flags, keywords in self.get_arguments().items():
90
- parser.add_argument(*flags, **keywords)
76
+ parser = argparse.ArgumentParser(
77
+ description=self.description,
78
+ add_help=True,
79
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
80
+ )
81
+ self.fill_parser(parser)
91
82
  return parser
92
83
 
84
+ def fill_parser(self, parser):
85
+ """ Fill supplied parser with arguments
86
+
87
+ Fills the supplied parser with the arguments returned via the `get_arguments` method invocation. This
88
+ implementation add the arguments directly to the parser.
89
+
90
+ Args:
91
+ parser: parser to fill with arguments
92
+
93
+ """
94
+ for flags, keywords in self.get_arguments().items():
95
+ try:
96
+ parser.add_argument(*flags, **keywords)
97
+ except argparse.ArgumentError:
98
+ # flag has already been added, pass
99
+ pass
100
+
93
101
  def reproduce_cli_args(self, args_ns):
94
102
  """Reproduce the list of arguments needed on the command line"""
95
103
 
@@ -245,6 +253,162 @@ class DetectionParser(ParserBase):
245
253
  return args
246
254
 
247
255
 
256
+ class PluginArgumentParser(ParserBase):
257
+ """Parser for arguments coming from plugins"""
258
+
259
+ DESCRIPTION = "Plugin options"
260
+ FPRIME_CHOICES = {
261
+ "framing": "fprime",
262
+ "communication": "ip",
263
+ }
264
+
265
+ def __init__(self):
266
+ """Initialize the plugin information for this parser"""
267
+ self._plugin_map = {
268
+ category: Plugins.system().get_plugins(category)
269
+ for category in Plugins.system().get_categories()
270
+ }
271
+
272
+ @staticmethod
273
+ def safe_add_argument(parser, *flags, **keywords):
274
+ """ Add an argument allowing duplicates
275
+
276
+ Add arguments to the parser (passes through *flags and **keywords) to the supplied parser. This method traps
277
+ errors to prevent duplicates.
278
+
279
+ Args:
280
+ parser: parser or argument group to add arguments to
281
+ *flags: positional arguments passed to `add_argument`
282
+ **keywords: key word arguments passed to `add_argument`
283
+ """
284
+ try:
285
+ parser.add_argument(*flags, **keywords)
286
+ except argparse.ArgumentError:
287
+ # flag has already been added, pass
288
+ pass
289
+
290
+ def fill_parser(self, parser):
291
+ """ File supplied parser with grouped arguments
292
+
293
+ Fill the supplied parser with arguments from the `get_arguments` method invocation. This implementation groups
294
+ arguments based on the constituent that sources the argument.
295
+
296
+ Args:
297
+ parser: parser to fill
298
+ """
299
+ for category, plugins in self._plugin_map.items():
300
+ argument_group = parser.add_argument_group(title=f"{category.title()} Plugin Options")
301
+ for flags, keywords in self.get_category_arguments(category).items():
302
+ self.safe_add_argument(argument_group, *flags, **keywords)
303
+
304
+ for plugin in plugins:
305
+ argument_group = parser.add_argument_group(title=f"{category.title()} Plugin '{plugin.get_name()}' Options")
306
+ if plugin.type == PluginType.FEATURE:
307
+ self.safe_add_argument(argument_group,
308
+ f"--disable-{plugin.get_name()}",
309
+ action="store_true",
310
+ default=False,
311
+ help=f"Disable the {category} plugin '{plugin.get_name()}'")
312
+ for flags, keywords in plugin.get_arguments().items():
313
+ self.safe_add_argument(argument_group, *flags, **keywords)
314
+
315
+ def get_category_arguments(self, category):
316
+ """ Get arguments for a plugin category """
317
+ arguments: Dict[Tuple[str, ...], Dict[str, Any]] = {}
318
+ plugins = self._plugin_map[category]
319
+ # Add category options: SELECTION plugins add a selection flag
320
+ plugin_type = Plugins.get_category_plugin_type(category)
321
+ if plugin_type == PluginType.SELECTION:
322
+ arguments.update(
323
+ {
324
+ (f"--{category}-selection",): {
325
+ "choices": [choice.get_name() for choice in plugins],
326
+ "help": f"Select {category} implementer.",
327
+ "default": self.FPRIME_CHOICES.get(
328
+ category, list(plugins)[0].get_name()
329
+ ),
330
+ }
331
+ }
332
+ )
333
+ return arguments
334
+
335
+ def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
336
+ """Return arguments to used in plugins"""
337
+ arguments: Dict[Tuple[str, ...], Dict[str, Any]] = {}
338
+ for category, plugins in self._plugin_map.items():
339
+ arguments.update(self.get_category_arguments(category))
340
+ for plugin in plugins:
341
+ # Add disable flags for feature type plugins
342
+ if plugin.type == PluginType.FEATURE:
343
+ arguments.update({
344
+ (f"--disable-{plugin.get_name()}", ): {
345
+ "action": "store_true",
346
+ "default": False,
347
+ "help": f"Disable the {category} plugin '{plugin.get_name()}'"
348
+ }
349
+ })
350
+ arguments.update(plugin.get_arguments())
351
+ return arguments
352
+
353
+ def handle_arguments(self, args, **kwargs):
354
+ """Handles the arguments"""
355
+ for category, plugins in self._plugin_map.items():
356
+ plugin_type = Plugins.get_category_plugin_type(category)
357
+
358
+ # Selection plugins choose one plugin and instantiate it
359
+ if plugin_type == PluginType.SELECTION:
360
+ selection_string = getattr(args, f"{category}_selection")
361
+ matching_plugins = [plugin for plugin in plugins if plugin.get_name() == selection_string]
362
+ assert len(matching_plugins) == 1, "Plugin selection system failed"
363
+ selection_class = matching_plugins[0].plugin_class
364
+ filled_arguments = self.extract_plugin_arguments(args, selection_class)
365
+ selection_instance = selection_class(**filled_arguments)
366
+ setattr(args, f"{category}_selection_instance", selection_instance)
367
+ # Feature plugins instantiate all enabled plugins
368
+ elif plugin_type == PluginType.FEATURE:
369
+ enabled_plugins = [
370
+ plugin for plugin in plugins
371
+ if not getattr(args, f"disable_{plugin.get_name().replace('-', '_')}", False)
372
+ ]
373
+ plugin_instantiations = [
374
+ plugin.plugin_class(**self.extract_plugin_arguments(args, plugin))
375
+ for plugin in enabled_plugins
376
+ ]
377
+ setattr(args, f"{category}_enabled_instances", plugin_instantiations)
378
+ return args
379
+
380
+ @staticmethod
381
+ def extract_plugin_arguments(args, plugin) -> Dict[str, Any]:
382
+ """Extract plugin argument values from the args namespace into a map
383
+
384
+ Plugin arguments will be supplied to the `__init__` function of the plugin via a keyword argument dictionary.
385
+ This function maps from the argument namespace from parsing back into that dictionary.
386
+
387
+ Args:
388
+ args: argument namespace from argparse
389
+ plugin: plugin to extract arguments for
390
+ Return:
391
+ filled arguments dictionary
392
+ """
393
+ expected_args = plugin.get_arguments()
394
+ argument_destinations = [
395
+ (
396
+ value["dest"]
397
+ if "dest" in value
398
+ else key[0].replace("--", "").replace("-", "_")
399
+ )
400
+ for key, value in expected_args.items()
401
+ ]
402
+ filled_arguments = {
403
+ destination: getattr(args, destination)
404
+ for destination in argument_destinations
405
+ }
406
+ # Check arguments or yield a Value error
407
+ if hasattr(plugin, "check_arguments"):
408
+ plugin.check_arguments(**filled_arguments)
409
+ return filled_arguments
410
+
411
+
248
412
  class CompositeParser(ParserBase):
249
413
  """Composite parser handles parsing as a composition of multiple other parsers"""
250
414
 
@@ -258,6 +422,22 @@ class CompositeParser(ParserBase):
258
422
  ]
259
423
  self.constituent_parsers = {*itertools.chain.from_iterable(flattened)}
260
424
 
425
+ def fill_parser(self, parser):
426
+ """ File supplied parser with grouped arguments
427
+
428
+ Fill the supplied parser with arguments from the `get_arguments` method invocation. This implementation groups
429
+ arguments based on the constituent that sources the argument.
430
+
431
+ Args:
432
+ parser: parser to fill
433
+ """
434
+ for constituent in sorted(self.constituents, key=lambda x: x.description):
435
+ if isinstance(constituent, (PluginArgumentParser, CompositeParser)):
436
+ constituent.fill_parser(parser)
437
+ else:
438
+ argument_group = parser.add_argument_group(title=constituent.description)
439
+ constituent.fill_parser(argument_group)
440
+
261
441
  @property
262
442
  def constituents(self):
263
443
  """Get constituent"""
@@ -286,49 +466,14 @@ class CompositeParser(ParserBase):
286
466
  return args
287
467
 
288
468
 
289
- class CommAdapterParser(ParserBase):
290
- """
291
- Handles parsing of all of the comm-layer arguments. This means selecting a comm adapter, and passing the arguments
292
- required to setup that comm adapter. In addition, this parser uses the import parser to import modules such that a
293
- user may import other adapter implementation files.
294
- """
469
+ class CommExtraParser(ParserBase):
470
+ """Parses extra communication arguments"""
295
471
 
296
- DESCRIPTION = "Process arguments needed to specify a comm-adapter"
472
+ DESCRIPTION = "Communications options"
297
473
 
298
474
  def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
299
475
  """Get arguments for the comm-layer parser"""
300
- adapter_definition_dictionaries = BaseAdapter.get_adapters()
301
- adapter_arguments = {}
302
- for name, adapter in adapter_definition_dictionaries.items():
303
- adapter_arguments_callable = getattr(adapter, "get_arguments", None)
304
- if not callable(adapter_arguments_callable):
305
- print(
306
- f"[WARNING] '{name}' does not have 'get_arguments' method, skipping.",
307
- file=sys.stderr,
308
- )
309
- continue
310
- adapter_arguments.update(adapter.get_arguments())
311
476
  com_arguments = {
312
- ("--comm-adapter",): {
313
- "dest": "adapter",
314
- "action": "store",
315
- "type": str,
316
- "help": "Adapter for communicating to flight deployment. [default: %(default)s]",
317
- "choices": ["none"] + list(adapter_definition_dictionaries),
318
- "default": "ip",
319
- },
320
- ("--comm-checksum-type",): {
321
- "dest": "checksum_type",
322
- "action": "store",
323
- "type": str,
324
- "help": "Setup the checksum algorithm. [default: %(default)s]",
325
- "choices": [
326
- item
327
- for item in fprime_gds.common.communication.checksum.CHECKSUM_MAPPING.keys()
328
- if item != "default"
329
- ],
330
- "default": fprime_gds.common.communication.checksum.CHECKSUM_SELECTION,
331
- },
332
477
  ("--output-unframed-data",): {
333
478
  "dest": "output_unframed_data",
334
479
  "action": "store",
@@ -339,17 +484,9 @@ class CommAdapterParser(ParserBase):
339
484
  "required": False,
340
485
  },
341
486
  }
342
- return {**adapter_arguments, **com_arguments}
487
+ return com_arguments
343
488
 
344
489
  def handle_arguments(self, args, **kwargs):
345
- """
346
- Handle the input arguments for the parser. This will help setup the adapter with its expected arguments.
347
-
348
- :param args: parsed arguments in namespace format
349
- :return: namespace with "comm_adapter" value added
350
- """
351
- args.comm_adapter = BaseAdapter.construct_adapter(args.adapter, args)
352
- fprime_gds.common.communication.checksum.CHECKSUM_SELECTION = args.checksum_type
353
490
  return args
354
491
 
355
492
 
@@ -360,7 +497,7 @@ class LogDeployParser(ParserBase):
360
497
  to end up in the proper place.
361
498
  """
362
499
 
363
- DESCRIPTION = "Process arguments needed to specify a logging"
500
+ DESCRIPTION = "Logging options"
364
501
 
365
502
  def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
366
503
  """Return arguments to parse logging options"""
@@ -423,50 +560,41 @@ class MiddleWareParser(ParserBase):
423
560
  however; it should be close enough.
424
561
  """
425
562
 
426
- DESCRIPTION = "Process arguments needed to specify a tool using the middleware"
563
+ DESCRIPTION = "Middleware options"
427
564
 
428
565
  def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
429
566
  """Return arguments necessary to run a and connect to the GDS middleware"""
430
- # May use ZMQ transportation layer if zmq package is available
431
- zmq_arguments = {}
432
- if zmq is not None and ZmqClient is not None:
433
- zmq_arguments = {
434
- ("--zmq",): {
435
- "dest": "zmq",
436
- "action": "store_true",
437
- "help": "Switch to using the ZMQ transportation layer",
438
- "default": False,
439
- },
440
- ("--zmq-server",): {
441
- "dest": "zmq_server",
442
- "action": "store_true",
443
- "help": "Sets the ZMQ connection to be a server. Default: false (client)",
444
- "default": False,
445
- },
446
- ("--zmq-transport",): {
447
- "dest": "zmq_transport",
448
- "nargs": 2,
449
- "help": "Pair of URls used with --zmq to setup ZeroMQ transportation [default: %(default)s]",
450
- "default": [
451
- "ipc:///tmp/fprime-server-in",
452
- "ipc:///tmp/fprime-server-out",
453
- ],
454
- "metavar": ("serverInUrl", "serverOutUrl"),
455
- },
456
- }
567
+ zmq_arguments = {
568
+ ("--no-zmq",): {
569
+ "dest": "zmq",
570
+ "action": "store_false",
571
+ "help": "Disable ZMQ transportation layer, falling back to TCP socket server.",
572
+ "default": True,
573
+ },
574
+ ("--zmq-transport",): {
575
+ "dest": "zmq_transport",
576
+ "nargs": 2,
577
+ "help": "Pair of URls used with --zmq to setup ZeroMQ transportation [default: %(default)s]",
578
+ "default": [
579
+ "ipc:///tmp/fprime-server-in",
580
+ "ipc:///tmp/fprime-server-out",
581
+ ],
582
+ "metavar": ("serverInUrl", "serverOutUrl"),
583
+ },
584
+ }
457
585
  tts_arguments = {
458
586
  ("--tts-port",): {
459
587
  "dest": "tts_port",
460
588
  "action": "store",
461
589
  "type": int,
462
- "help": "Set the threaded TCP socket server port [default: %(default)s]",
590
+ "help": "Set the threaded TCP socket server port when ZMQ is not used [default: %(default)s]",
463
591
  "default": 50050,
464
592
  },
465
593
  ("--tts-addr",): {
466
594
  "dest": "tts_addr",
467
595
  "action": "store",
468
596
  "type": str,
469
- "help": "Set the threaded TCP socket server address [default: %(default)s]",
597
+ "help": "Set the threaded TCP socket server address when ZMQ is not used [default: %(default)s]",
470
598
  "default": "0.0.0.0",
471
599
  },
472
600
  }
@@ -481,7 +609,6 @@ class MiddleWareParser(ParserBase):
481
609
  :return: args namespace
482
610
  """
483
611
  is_client = kwargs.get("client", False)
484
- args.zmq = getattr(args, "zmq", False)
485
612
  tts_connection_address = (
486
613
  args.tts_addr.replace("0.0.0.0", "127.0.0.1")
487
614
  if is_client
@@ -501,6 +628,8 @@ class MiddleWareParser(ParserBase):
501
628
  class DictionaryParser(DetectionParser):
502
629
  """Parser for deployments"""
503
630
 
631
+ DESCRIPTION = "Dictionary options"
632
+
504
633
  def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
505
634
  """Arguments to handle deployments"""
506
635
  return {
@@ -540,6 +669,8 @@ class DictionaryParser(DetectionParser):
540
669
  class FileHandlingParser(ParserBase):
541
670
  """Parser for deployments"""
542
671
 
672
+ DESCRIPTION = "File handling options"
673
+
543
674
  def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
544
675
  """Arguments to handle deployments"""
545
676
 
@@ -618,7 +749,12 @@ class StandardPipelineParser(CompositeParser):
618
749
  class CommParser(CompositeParser):
619
750
  """Comm Executable Parser"""
620
751
 
621
- CONSTITUENTS = [CommAdapterParser, MiddleWareParser, LogDeployParser]
752
+ CONSTITUENTS = [
753
+ CommExtraParser,
754
+ MiddleWareParser,
755
+ LogDeployParser,
756
+ PluginArgumentParser,
757
+ ]
622
758
 
623
759
  def __init__(self):
624
760
  """Initialization"""
@@ -639,7 +775,7 @@ class GdsParser(ParserBase):
639
775
  Note: deployment can help in setting both dictionary and logs, but isn't strictly required.
640
776
  """
641
777
 
642
- DESCRIPTION = "Process arguments needed to specify a tool using the GDS"
778
+ DESCRIPTION = "GUI options"
643
779
 
644
780
  def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
645
781
  """Return arguments necessary to run a binary deployment via the GDS"""
@@ -686,7 +822,7 @@ class BinaryDeployment(DetectionParser):
686
822
  and represents the flight-side of the equation.
687
823
  """
688
824
 
689
- DESCRIPTION = "Process arguments needed for running F prime binary"
825
+ DESCRIPTION = "FPrime binary options"
690
826
 
691
827
  def get_arguments(self) -> Dict[Tuple[str, ...], Dict[str, Any]]:
692
828
  """Return arguments necessary to run a binary deployment via the GDS"""
@@ -732,7 +868,7 @@ class SearchArgumentsParser(ParserBase):
732
868
  """Parser for search arguments"""
733
869
 
734
870
  DESCRIPTION = (
735
- "Process arguments relevant to searching/filtering Channels/Events/Commands"
871
+ "Searching and filtering options"
736
872
  )
737
873
 
738
874
  def __init__(self, command_name: str) -> None:
@@ -778,7 +914,7 @@ class SearchArgumentsParser(ParserBase):
778
914
  class RetrievalArgumentsParser(ParserBase):
779
915
  """Parser for retrieval arguments"""
780
916
 
781
- DESCRIPTION = "Process arguments relevant to retrieving Channels/Events"
917
+ DESCRIPTION = "Data retrieval options"
782
918
 
783
919
  def __init__(self, command_name: str) -> None:
784
920
  self.command_name = command_name
@@ -16,26 +16,19 @@ Note: assuming the module containing the ground adapter has been imported, then
16
16
  @author lestarch
17
17
  """
18
18
 
19
-
20
19
  import logging
21
20
  import signal
22
21
  import sys
23
-
24
22
  from pathlib import Path
25
23
 
26
24
  # Required adapters built on standard tools
27
- try:
28
- from fprime_gds.common.zmq_transport import ZmqGround
29
- except ImportError:
30
- ZmqGround = None
31
25
  import fprime_gds.common.communication.adapters.base
32
26
  import fprime_gds.common.communication.adapters.ip
33
- import fprime_gds.common.communication.checksum
34
27
  import fprime_gds.common.communication.ground
35
28
  import fprime_gds.common.logger
36
29
  import fprime_gds.executables.cli
37
- from fprime_gds.common.communication.framing import FpFramerDeframer
38
30
  from fprime_gds.common.communication.updown import Downlinker, Uplinker
31
+ from fprime_gds.common.zmq_transport import ZmqGround
39
32
 
40
33
  # Uses non-standard PIP package pyserial, so test the waters before getting a hard-import crash
41
34
  try:
@@ -58,38 +51,35 @@ def main():
58
51
  fprime_gds.executables.cli.LogDeployParser,
59
52
  fprime_gds.executables.cli.MiddleWareParser,
60
53
  fprime_gds.executables.cli.CommParser,
54
+ fprime_gds.executables.cli.PluginArgumentParser,
61
55
  ],
62
56
  description="F prime communications layer.",
63
57
  client=True,
64
58
  )
65
- fprime_gds.common.communication.checksum = args.checksum_type
66
- if args.comm_adapter == "none":
67
- print("[ERROR] Comm adapter set to 'none'. Nothing to do but exit.", file=sys.stderr)
59
+ if args.communication_selection == "none":
60
+ print(
61
+ "[ERROR] Comm adapter set to 'none'. Nothing to do but exit.",
62
+ file=sys.stderr,
63
+ )
68
64
  sys.exit(-1)
69
65
 
70
66
  # Create the handling components for either side of this script, adapter for hardware, and ground for the GDS side
71
- if args.zmq and ZmqGround is None:
72
- print("[ERROR] ZeroMQ is not available. Install pyzmq.", file=sys.stderr)
73
- sys.exit(-1)
74
- elif args.zmq:
75
- ground = fprime_gds.common.zmq_transport.ZmqGround(args.zmq_transport)
76
- # Check for need to make this a server
77
- if args.zmq_server:
78
- ground.make_server()
67
+ if args.zmq:
68
+ ground = ZmqGround(args.zmq_transport)
79
69
  else:
80
70
  ground = fprime_gds.common.communication.ground.TCPGround(
81
71
  args.tts_addr, args.tts_port
82
72
  )
83
73
 
84
- adapter = args.comm_adapter
74
+ adapter = args.communication_selection_instance
85
75
 
86
76
  # Set the framing class used and pass it to the uplink and downlink component constructions giving each a separate
87
77
  # instantiation
88
- framer_class = FpFramerDeframer
78
+ framer_instance = args.framing_selection_instance
89
79
  LOGGER.info(
90
80
  "Starting uplinker/downlinker connecting to FSW using %s with %s",
91
- adapter,
92
- framer_class.__name__,
81
+ args.communication_selection,
82
+ args.framing_selection,
93
83
  )
94
84
  discarded_file_handle = None
95
85
  try:
@@ -108,9 +98,9 @@ def main():
108
98
  discarded_file_handle_path,
109
99
  )
110
100
  downlinker = Downlinker(
111
- adapter, ground, framer_class(), discarded=discarded_file_handle
101
+ adapter, ground, framer_instance, discarded=discarded_file_handle
112
102
  )
113
- uplinker = Uplinker(adapter, ground, framer_class(), downlinker)
103
+ uplinker = Uplinker(adapter, ground, framer_instance, downlinker)
114
104
 
115
105
  # Open resources for the handlers on either side, this prepares the resources needed for reading/writing data
116
106
  ground.open()
@@ -121,8 +111,8 @@ def main():
121
111
  uplinker.start()
122
112
  LOGGER.debug("Uplinker and downlinker running")
123
113
 
124
- # Wait for shutdown event in the form of a KeyboardInterrupt then stop the processing, close resources, and wait for
125
- # everything to terminate as expected.
114
+ # Wait for shutdown event in the form of a KeyboardInterrupt then stop the processing, close resources,
115
+ # and wait for everything to terminate as expected.
126
116
  def shutdown(*_):
127
117
  """Shutdown function for signals"""
128
118
  uplinker.stop()