fprime-gds 3.6.1__py3-none-any.whl → 4.0.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.
Files changed (70) hide show
  1. fprime_gds/common/communication/adapters/ip.py +14 -9
  2. fprime_gds/common/communication/adapters/uart.py +34 -25
  3. fprime_gds/common/communication/ccsds/__init__.py +0 -0
  4. fprime_gds/common/communication/ccsds/apid.py +19 -0
  5. fprime_gds/common/communication/ccsds/chain.py +106 -0
  6. fprime_gds/common/communication/ccsds/space_data_link.py +196 -0
  7. fprime_gds/common/communication/ccsds/space_packet.py +129 -0
  8. fprime_gds/common/communication/framing.py +27 -32
  9. fprime_gds/common/decoders/ch_decoder.py +1 -1
  10. fprime_gds/common/decoders/event_decoder.py +9 -2
  11. fprime_gds/common/decoders/pkt_decoder.py +1 -1
  12. fprime_gds/common/distributor/distributor.py +6 -3
  13. fprime_gds/common/encoders/ch_encoder.py +2 -2
  14. fprime_gds/common/encoders/cmd_encoder.py +2 -2
  15. fprime_gds/common/encoders/event_encoder.py +2 -2
  16. fprime_gds/common/encoders/pkt_encoder.py +2 -2
  17. fprime_gds/common/encoders/seq_writer.py +2 -2
  18. fprime_gds/common/fpy/README.md +56 -0
  19. fprime_gds/common/fpy/SPEC.md +69 -0
  20. fprime_gds/common/fpy/__init__.py +0 -0
  21. fprime_gds/common/fpy/bytecode/__init__.py +0 -0
  22. fprime_gds/common/fpy/bytecode/directives.py +490 -0
  23. fprime_gds/common/fpy/codegen.py +1687 -0
  24. fprime_gds/common/fpy/grammar.lark +88 -0
  25. fprime_gds/common/fpy/main.py +40 -0
  26. fprime_gds/common/fpy/parser.py +239 -0
  27. fprime_gds/common/gds_cli/base_commands.py +1 -1
  28. fprime_gds/common/handlers.py +39 -0
  29. fprime_gds/common/loaders/fw_type_json_loader.py +54 -0
  30. fprime_gds/common/loaders/json_loader.py +15 -0
  31. fprime_gds/common/loaders/pkt_json_loader.py +125 -0
  32. fprime_gds/common/loaders/prm_json_loader.py +85 -0
  33. fprime_gds/common/logger/__init__.py +2 -2
  34. fprime_gds/common/pipeline/dictionaries.py +60 -41
  35. fprime_gds/common/pipeline/encoding.py +19 -0
  36. fprime_gds/common/pipeline/histories.py +4 -0
  37. fprime_gds/common/pipeline/standard.py +16 -2
  38. fprime_gds/common/templates/cmd_template.py +8 -0
  39. fprime_gds/common/templates/prm_template.py +81 -0
  40. fprime_gds/common/testing_fw/api.py +148 -1
  41. fprime_gds/common/testing_fw/pytest_integration.py +37 -3
  42. fprime_gds/common/tools/README.md +34 -0
  43. fprime_gds/common/tools/params.py +246 -0
  44. fprime_gds/common/utils/config_manager.py +6 -6
  45. fprime_gds/common/utils/data_desc_type.py +6 -1
  46. fprime_gds/executables/apps.py +189 -11
  47. fprime_gds/executables/cli.py +468 -127
  48. fprime_gds/executables/comm.py +5 -2
  49. fprime_gds/executables/data_product_writer.py +164 -165
  50. fprime_gds/executables/fprime_cli.py +3 -3
  51. fprime_gds/executables/run_deployment.py +13 -5
  52. fprime_gds/flask/app.py +3 -0
  53. fprime_gds/flask/resource.py +5 -2
  54. fprime_gds/flask/static/addons/chart-display/addon.js +8 -3
  55. fprime_gds/flask/static/js/datastore.js +1 -0
  56. fprime_gds/flask/static/js/vue-support/channel.js +1 -1
  57. fprime_gds/flask/static/js/vue-support/event.js +1 -1
  58. fprime_gds/plugin/definitions.py +86 -8
  59. fprime_gds/plugin/system.py +172 -58
  60. {fprime_gds-3.6.1.dist-info → fprime_gds-4.0.0.dist-info}/METADATA +23 -21
  61. {fprime_gds-3.6.1.dist-info → fprime_gds-4.0.0.dist-info}/RECORD +66 -50
  62. {fprime_gds-3.6.1.dist-info → fprime_gds-4.0.0.dist-info}/WHEEL +1 -1
  63. {fprime_gds-3.6.1.dist-info → fprime_gds-4.0.0.dist-info}/entry_points.txt +2 -0
  64. fprime_gds/common/loaders/ch_py_loader.py +0 -79
  65. fprime_gds/common/loaders/cmd_py_loader.py +0 -66
  66. fprime_gds/common/loaders/event_py_loader.py +0 -75
  67. fprime_gds/common/loaders/python_loader.py +0 -132
  68. {fprime_gds-3.6.1.dist-info → fprime_gds-4.0.0.dist-info/licenses}/LICENSE.txt +0 -0
  69. {fprime_gds-3.6.1.dist-info → fprime_gds-4.0.0.dist-info/licenses}/NOTICE.txt +0 -0
  70. {fprime_gds-3.6.1.dist-info → fprime_gds-4.0.0.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,8 @@ that implement this pattern. The current list of implementation classes are:
11
11
 
12
12
  @author lestarch
13
13
  """
14
+
15
+ from __future__ import annotations
14
16
  import abc
15
17
  import copy
16
18
  import struct
@@ -18,7 +20,10 @@ import sys
18
20
  from typing import Type
19
21
 
20
22
  from .checksum import calculate_checksum, CHECKSUM_MAPPING
21
- from fprime_gds.plugin.definitions import gds_plugin_implementation, gds_plugin_specification
23
+ from fprime_gds.plugin.definitions import (
24
+ gds_plugin_implementation,
25
+ gds_plugin_specification,
26
+ )
22
27
 
23
28
 
24
29
  class FramerDeframer(abc.ABC):
@@ -28,7 +33,7 @@ class FramerDeframer(abc.ABC):
28
33
  """
29
34
 
30
35
  @abc.abstractmethod
31
- def frame(self, data):
36
+ def frame(self, data: bytes) -> bytes:
32
37
  """
33
38
  Frames outgoing data in the specified format. Expects incoming raw bytes to frame, and adds on the needed header
34
39
  and footer bytes. This new array of bytes is returned from the method.
@@ -38,20 +43,27 @@ class FramerDeframer(abc.ABC):
38
43
  """
39
44
 
40
45
  @abc.abstractmethod
41
- def deframe(self, data, no_copy=False):
46
+ def deframe(
47
+ self, data: bytes, no_copy=False
48
+ ) -> tuple[(bytes | None), bytes, bytes]:
42
49
  """
43
- Deframes the incoming data from the specified format. Produces exactly one packet, and leftover bytes. Users
44
- wanting all packets to be deframed should call "deframe_all". If no full packet is available, this method
50
+ Deframes the incoming data from the specified format.
51
+ Produces:
52
+ - One packet, or None if no packet found
53
+ - leftover bytes (not consumed yet)
54
+ - Discarded data (consumed and determined not to be valid)
55
+
56
+ Users wanting all packets to be deframed should call "deframe_all". If no full packet is available, this method
45
57
  returns None. Expects incoming raw bytes to deframe, and returns a deframed packet or None, the leftover
46
58
  bytes that were unused, and any bytes discarded from the existing data stream. Will search and discard data up
47
59
  until a start token is found. Note: data will be consumed up to the first start token found.
48
60
 
49
61
  :param data: framed data bytes
50
62
  :param no_copy: (optional) will prevent extra copy if True, but "data" input will be destroyed.
51
- :return: (packet as array of bytes or None, leftover bytes, any discarded data)
63
+ :return: (packet as bytes or None, leftover bytes, any discarded data)
52
64
  """
53
65
 
54
- def deframe_all(self, data, no_copy):
66
+ def deframe_all(self, data: bytes, no_copy: bool):
55
67
  """
56
68
  Deframes all available packets found in a single set of bytes by calling deframe until a None packet is
57
69
  retrieved. This list of packets, and the remaining bytes are returned
@@ -66,11 +78,11 @@ class FramerDeframer(abc.ABC):
66
78
  discarded_aggregate = b""
67
79
  while True:
68
80
  # Deframe and return only on None
69
- (packet, data, discarded) = self.deframe(data, no_copy=True)
81
+ (deframed, data, discarded) = self.deframe(data, no_copy=True)
70
82
  discarded_aggregate += discarded
71
- if packet is None:
83
+ if deframed is None: # No more packets available, return aggregate
72
84
  return packets, data, discarded_aggregate
73
- packets.append(packet)
85
+ packets.append(deframed)
74
86
 
75
87
  @classmethod
76
88
  @gds_plugin_specification
@@ -117,7 +129,7 @@ class FpFramerDeframer(FramerDeframer):
117
129
  HEADER_FORMAT = None
118
130
  START_TOKEN = None
119
131
 
120
- def __init__(self, checksum_type):
132
+ def __init__(self, checksum_type="crc32"):
121
133
  """Sets constants on construction."""
122
134
  # Setup the constants as soon as possible.
123
135
  FpFramerDeframer.set_constants()
@@ -197,13 +209,12 @@ class FpFramerDeframer(FramerDeframer):
197
209
  )
198
210
  # If the checksum is valid, return the packet. Otherwise continue to rotate
199
211
  if check == calculate_checksum(
200
- data[: data_size + FpFramerDeframer.HEADER_SIZE],
201
- self.checksum
212
+ data[: data_size + FpFramerDeframer.HEADER_SIZE], self.checksum
202
213
  ):
203
214
  data = data[total_size:]
204
215
  return deframed, data, discarded
205
216
  print(
206
- "[WARNING] Checksum validation failed. Have you correctly set '--comm-checksum-type'",
217
+ "[WARNING] Checksum validation failed.",
207
218
  file=sys.stderr,
208
219
  )
209
220
  # Bad checksum, rotate 1 and keep looking for non-garbage
@@ -216,29 +227,13 @@ class FpFramerDeframer(FramerDeframer):
216
227
 
217
228
  @classmethod
218
229
  def get_name(cls):
219
- """ Get the name of this plugin """
230
+ """Get the name of this plugin"""
220
231
  return "fprime"
221
232
 
222
- @classmethod
223
- def get_arguments(cls):
224
- """ Get arguments for the framer/deframer """
225
- return {("--comm-checksum-type",): {
226
- "dest": "checksum_type",
227
- "action": "store",
228
- "type": str,
229
- "help": "Setup the checksum algorithm. [default: %(default)s]",
230
- "choices": [
231
- item
232
- for item in CHECKSUM_MAPPING.keys()
233
- if item != "default"
234
- ],
235
- "default": "crc32",
236
- }}
237
-
238
233
  @classmethod
239
234
  @gds_plugin_implementation
240
235
  def register_framing_plugin(cls):
241
- """ Register a bad plugin """
236
+ """Register a bad plugin"""
242
237
  return cls
243
238
 
244
239
 
@@ -44,7 +44,7 @@ class ChDecoder(Decoder):
44
44
  config = config_manager.ConfigManager().get_instance()
45
45
 
46
46
  self.__dict = ch_dict
47
- self.id_obj = config.get_type("ch_id")
47
+ self.id_obj = config.get_type("FwChanIdType")
48
48
 
49
49
  def decode_api(self, data):
50
50
  """
@@ -19,8 +19,11 @@ from fprime.common.models.serialize.type_exceptions import TypeException
19
19
 
20
20
  from fprime_gds.common.data_types import event_data
21
21
  from fprime_gds.common.decoders import decoder
22
+ from fprime_gds.common.decoders.decoder import DecodingException
22
23
  from fprime_gds.common.utils import config_manager
23
24
 
25
+ import logging
26
+ LOGGER = logging.getLogger("event_decoder")
24
27
 
25
28
  class EventDecoder(decoder.Decoder):
26
29
  """Decoder class for event data"""
@@ -43,7 +46,7 @@ class EventDecoder(decoder.Decoder):
43
46
  config = config_manager.ConfigManager().get_instance()
44
47
 
45
48
  self.__dict = event_dict
46
- self.id_obj = config.get_type("event_id")
49
+ self.id_obj = config.get_type("FwEventIdType")
47
50
 
48
51
  def decode_api(self, data):
49
52
  """
@@ -66,7 +69,11 @@ class EventDecoder(decoder.Decoder):
66
69
  while (ptr < len(data)):
67
70
 
68
71
  # Decode event ID here...
69
- self.id_obj.deserialize(data, ptr)
72
+ if ptr + self.id_obj.getSize() <= len(data):
73
+ self.id_obj.deserialize(data, ptr)
74
+ else:
75
+ LOGGER.warning("Insufficient data for event ID")
76
+ break
70
77
  ptr += self.id_obj.getSize()
71
78
  event_id = self.id_obj.val
72
79
 
@@ -45,7 +45,7 @@ class PktDecoder(ChDecoder):
45
45
  super().__init__(ch_dict, config)
46
46
 
47
47
  self.__dict = pkt_name_dict
48
- self.id_obj = config.get_type("pkt_id")
48
+ self.id_obj = config.get_type("FwTlmPacketizeIdType")
49
49
 
50
50
  def decode_api(self, data):
51
51
  """
@@ -57,7 +57,7 @@ class Distributor(DataHandler):
57
57
  self.key_frame = int(config.get("framing", "key_val"), 16)
58
58
  self.key_obj = config.get_type("key_val")
59
59
  self.len_obj = config.get_type("msg_len")
60
- self.desc_obj = config.get_type("msg_desc")
60
+ self.desc_obj = config.get_type("FwPacketDescriptorType")
61
61
 
62
62
  # NOTE we could use either the type of the object or an enum as the type argument.
63
63
  # It should indicate what the decoder decodes.
@@ -199,9 +199,12 @@ class Distributor(DataHandler):
199
199
  (length, data_desc, msg) = self.parse_raw_msg_api(raw_msg)
200
200
 
201
201
  data_desc_key = data_desc_type.DataDescType(data_desc).name
202
-
203
202
  for d in self.__decoders[data_desc_key]:
204
203
  try:
205
204
  d.data_callback(msg)
206
205
  except DecodingException as dexc:
207
- LOGGER.warning("Decoding error occurred: %s. Skipping.", dexc)
206
+ LOGGER.warning("Decoding error: %s", dexc)
207
+ except Exception as exc:
208
+ LOGGER.warning("Parsing error: %s", exc)
209
+ else:
210
+ LOGGER.warning("No decoder registered for: %s", data_desc_type.DataDescType(data_desc).name)
@@ -57,8 +57,8 @@ class ChEncoder(Encoder):
57
57
  super().__init__(config)
58
58
 
59
59
  self.len_obj = self.config.get_type("msg_len")
60
- self.desc_obj = self.config.get_type("msg_desc")
61
- self.id_obj = self.config.get_type("ch_id")
60
+ self.desc_obj = self.config.get_type("FwPacketDescriptorType")
61
+ self.id_obj = self.config.get_type("FwChanIdType")
62
62
 
63
63
  def encode_api(self, data):
64
64
  """
@@ -72,8 +72,8 @@ class CmdEncoder(encoder.Encoder):
72
72
  super().__init__(config)
73
73
 
74
74
  self.len_obj = self.config.get_type("msg_len")
75
- self.desc_obj = self.config.get_type("msg_desc")
76
- self.opcode_obj = self.config.get_type("op_code")
75
+ self.desc_obj = self.config.get_type("FwPacketDescriptorType")
76
+ self.opcode_obj = self.config.get_type("FwOpcodeType")
77
77
 
78
78
  def encode_api(self, data):
79
79
  """
@@ -61,8 +61,8 @@ class EventEncoder(Encoder):
61
61
  super().__init__(config)
62
62
 
63
63
  self.len_obj = self.config.get_type("msg_len")
64
- self.desc_obj = self.config.get_type("msg_desc")
65
- self.id_obj = self.config.get_type("event_id")
64
+ self.desc_obj = self.config.get_type("FwPacketDescriptorType")
65
+ self.id_obj = self.config.get_type("FwEventIdType")
66
66
 
67
67
  def encode_api(self, data):
68
68
  """
@@ -61,8 +61,8 @@ class PktEncoder(Encoder):
61
61
  super().__init__(config)
62
62
 
63
63
  self.len_obj = self.config.get_type("msg_len")
64
- self.desc_obj = self.config.get_type("msg_desc")
65
- self.id_obj = self.config.get_type("pkt_id")
64
+ self.desc_obj = self.config.get_type("FwPacketDescriptorType")
65
+ self.id_obj = self.config.get_type("FwTlmPacketizeIdType")
66
66
 
67
67
  def encode_api(self, data):
68
68
  """
@@ -28,8 +28,8 @@ class SeqBinaryWriter:
28
28
 
29
29
  self.__fd = None
30
30
  self.__timebase = timebase
31
- self.desc_obj = config.get_type("msg_desc")
32
- self.opcode_obj = config.get_type("op_code")
31
+ self.desc_obj = config.get_type("FwPacketDescriptorType")
32
+ self.opcode_obj = config.get_type("FwOpcodeType")
33
33
  self.len_obj = config.get_type("msg_len")
34
34
 
35
35
  def open(self, filename):
@@ -0,0 +1,56 @@
1
+ # Fpy Advanced Sequencing Language Version 0.1
2
+ The Fpy advanced sequencing language is a combination of a high-level scripting language and a low-level bytecode language for running complex command sequences on spacecraft flight software.
3
+ ## Fpy Syntax
4
+ ### Modules, components, channels, commands and types
5
+ You can imagine the Fpy syntax as Python with the following mappings:
6
+ 1. FPrime modules become Python namespaces
7
+ 2. FPrime components become Python classes
8
+ 3. FPrime types (structs, arrays and enums) become Python classes
9
+ 4. FPrime component instances become Python object instances
10
+ 5. FPrime commands become member functions of Python object instances
11
+ 6. FPrime telemetry channels become member properties of Python object instances
12
+
13
+ FPrime declaration:
14
+ ```
15
+ module Ref {
16
+ passive component ExampleComponent {
17
+ telemetry testChannel: U8
18
+ sync command TEST_COMMAND(arg: string size 40)
19
+ }
20
+
21
+ instance exampleInstance: ExampleComponent base id 0x01
22
+ }
23
+ ```
24
+ Fpy usage:
25
+ ```py
26
+ # reference a telemetry channel
27
+ Ref.exampleInstance.testChannel
28
+ # call a command
29
+ Ref.exampleInstance.TEST_COMMAND("arg value")
30
+ ```
31
+
32
+
33
+ FPrime declaration:
34
+ ```
35
+ struct ExampleStruct {
36
+ member: F32
37
+ }
38
+
39
+ enum TestEnum {
40
+ ONE
41
+ TWO
42
+ THREE
43
+ }
44
+
45
+ array TestArray = [3] U8
46
+ ```
47
+
48
+ Fpy usage:
49
+ ```py
50
+ # construct a struct
51
+ ExampleStruct(0.0)
52
+ # reference an enum const
53
+ TestEnum.THREE
54
+ # construct an array
55
+ TestArray(1, 2, 3)
56
+ ```
@@ -0,0 +1,69 @@
1
+ Nothing type is a type whose set of values is an empty set
2
+ Unit type is a type whose set of values is a set with one element
3
+
4
+ # `if` statement
5
+
6
+ ## Syntactical and semantic checks
7
+
8
+ `"if" condition ":" INDENT stmt* DEDENT ("elif" condition ":" INDENT stmt* DEDENT)* ["else" ":" INDENT stmt* DEDENT]`
9
+
10
+ 1. where `condition` is an expression which evaluates to a boolean
11
+ 2. where `stmt` is a statement
12
+
13
+ ## Code generation
14
+
15
+ 1. `if` generates `IF`, followed by the generated code for the first body, followed by `GOTO` to the end of the if statement
16
+ 2. `elif` generates `IF`, followed by the generated code for its body, followed by `GOTO` to the end of the if statement
17
+ 3. `else` generates the code for its body
18
+
19
+ # `not` boolean operator
20
+
21
+ ## Syntactical and semantic checks
22
+
23
+ `"not" value` evaluates to a boolean at runtime
24
+
25
+ 1. where `value` is an expression which evaluates to a boolean
26
+
27
+ ## Code generation
28
+
29
+ 1. `not` generates `NOT`
30
+
31
+ # `and` and `or` boolean operators
32
+
33
+ ## Syntactical and semantic checks
34
+
35
+ `value (op value)+` evaluates to a boolean at runtime
36
+
37
+ 1. where `op: "and"|"or"`
38
+ 2. where `value` is an expression which evaluates to a boolean
39
+
40
+ ## Code generation
41
+
42
+ 1. Each `and` or `or` between two `value`s generates an `AND` or `OR`, respectively
43
+
44
+ # Infix comparisons
45
+
46
+ ## Syntactical and semantic checks
47
+
48
+ `lhs op rhs` evaluates to a boolean at runtime
49
+
50
+ 1. where `op: ">" | "<" | "<=" | ">=" | "==" | "!="`
51
+ 2. and `lhs`, `rhs` are expressions which evaluate to a number
52
+
53
+ If either `lhs` or `rhs` evaluate to a float:
54
+ 3. both `lhs` and `rhs` must evaluate to floats of the same bit width
55
+
56
+ Otherwise, `lhs` and `rhs` evaluate to integer values. All comparisons between integer values are valid.
57
+
58
+ ## Code generation
59
+
60
+ ### Equality comparisons
61
+
62
+ 1. `==` or `!=` between two floats generates `FEQ` or `FNE`, respectively
63
+ 2. `==` or `!=` between two ints generates `IEQ` or `INE`, respectively
64
+
65
+ ### Inequality comparisons
66
+
67
+ 1. `>`, `<`, `<=`, or `>=` between two floats generates `FGT`, `FLT`, `FLE` or `FGE`, respectively
68
+ 2. `>`, `<`, `<=`, or `>=` between two unsigned ints generates `UGT`, `ULT`, `ULE` or `UGE`, respectively
69
+ 2. `>`, `<`, `<=`, or `>=` between two ints, where at least one is signed, generates `SGT`, `SLT`, `SLE` or `SGE`, respectively
File without changes
File without changes