fprime-gds 3.6.2a1__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.
- fprime_gds/common/communication/adapters/ip.py +14 -9
- fprime_gds/common/communication/adapters/uart.py +34 -25
- fprime_gds/common/communication/ccsds/__init__.py +0 -0
- fprime_gds/common/communication/ccsds/apid.py +19 -0
- fprime_gds/common/communication/ccsds/chain.py +106 -0
- fprime_gds/common/communication/ccsds/space_data_link.py +196 -0
- fprime_gds/common/communication/ccsds/space_packet.py +129 -0
- fprime_gds/common/communication/framing.py +27 -32
- fprime_gds/common/decoders/ch_decoder.py +1 -1
- fprime_gds/common/decoders/event_decoder.py +9 -2
- fprime_gds/common/decoders/pkt_decoder.py +1 -1
- fprime_gds/common/distributor/distributor.py +6 -3
- fprime_gds/common/encoders/ch_encoder.py +2 -2
- fprime_gds/common/encoders/cmd_encoder.py +2 -2
- fprime_gds/common/encoders/event_encoder.py +2 -2
- fprime_gds/common/encoders/pkt_encoder.py +2 -2
- fprime_gds/common/encoders/seq_writer.py +2 -2
- fprime_gds/common/fpy/README.md +56 -0
- fprime_gds/common/fpy/SPEC.md +69 -0
- fprime_gds/common/fpy/__init__.py +0 -0
- fprime_gds/common/fpy/bytecode/__init__.py +0 -0
- fprime_gds/common/fpy/bytecode/directives.py +490 -0
- fprime_gds/common/fpy/codegen.py +1687 -0
- fprime_gds/common/fpy/grammar.lark +88 -0
- fprime_gds/common/fpy/main.py +40 -0
- fprime_gds/common/fpy/parser.py +239 -0
- fprime_gds/common/gds_cli/base_commands.py +1 -1
- fprime_gds/common/handlers.py +39 -0
- fprime_gds/common/loaders/fw_type_json_loader.py +54 -0
- fprime_gds/common/loaders/pkt_json_loader.py +125 -0
- fprime_gds/common/loaders/prm_json_loader.py +85 -0
- fprime_gds/common/logger/__init__.py +2 -2
- fprime_gds/common/pipeline/dictionaries.py +28 -2
- fprime_gds/common/pipeline/encoding.py +19 -0
- fprime_gds/common/pipeline/histories.py +4 -0
- fprime_gds/common/pipeline/standard.py +16 -2
- fprime_gds/common/templates/cmd_template.py +8 -0
- fprime_gds/common/templates/prm_template.py +81 -0
- fprime_gds/common/testing_fw/api.py +148 -1
- fprime_gds/common/testing_fw/pytest_integration.py +37 -3
- fprime_gds/common/tools/README.md +34 -0
- fprime_gds/common/tools/params.py +246 -0
- fprime_gds/common/utils/config_manager.py +6 -6
- fprime_gds/common/utils/data_desc_type.py +6 -1
- fprime_gds/executables/apps.py +189 -11
- fprime_gds/executables/cli.py +468 -127
- fprime_gds/executables/comm.py +5 -2
- fprime_gds/executables/data_product_writer.py +164 -165
- fprime_gds/executables/fprime_cli.py +3 -3
- fprime_gds/executables/run_deployment.py +13 -5
- fprime_gds/flask/static/js/vue-support/channel.js +1 -1
- fprime_gds/flask/static/js/vue-support/event.js +1 -1
- fprime_gds/plugin/definitions.py +86 -8
- fprime_gds/plugin/system.py +172 -58
- {fprime_gds-3.6.2a1.dist-info → fprime_gds-4.0.0.dist-info}/METADATA +23 -21
- {fprime_gds-3.6.2a1.dist-info → fprime_gds-4.0.0.dist-info}/RECORD +61 -41
- {fprime_gds-3.6.2a1.dist-info → fprime_gds-4.0.0.dist-info}/WHEEL +1 -1
- {fprime_gds-3.6.2a1.dist-info → fprime_gds-4.0.0.dist-info}/entry_points.txt +2 -0
- {fprime_gds-3.6.2a1.dist-info → fprime_gds-4.0.0.dist-info/licenses}/LICENSE.txt +0 -0
- {fprime_gds-3.6.2a1.dist-info → fprime_gds-4.0.0.dist-info/licenses}/NOTICE.txt +0 -0
- {fprime_gds-3.6.2a1.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
|
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(
|
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.
|
44
|
-
|
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
|
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
|
-
(
|
81
|
+
(deframed, data, discarded) = self.deframe(data, no_copy=True)
|
70
82
|
discarded_aggregate += discarded
|
71
|
-
if
|
83
|
+
if deframed is None: # No more packets available, return aggregate
|
72
84
|
return packets, data, discarded_aggregate
|
73
|
-
packets.append(
|
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.
|
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
|
-
"""
|
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
|
-
"""
|
236
|
+
"""Register a bad plugin"""
|
242
237
|
return cls
|
243
238
|
|
244
239
|
|
@@ -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("
|
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.
|
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
|
|
@@ -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("
|
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
|
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("
|
61
|
-
self.id_obj = self.config.get_type("
|
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("
|
76
|
-
self.opcode_obj = self.config.get_type("
|
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("
|
65
|
-
self.id_obj = self.config.get_type("
|
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("
|
65
|
-
self.id_obj = self.config.get_type("
|
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("
|
32
|
-
self.opcode_obj = config.get_type("
|
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
|