fprime-gds 4.0.0a8__py3-none-any.whl → 4.0.0a10__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/fpy/README.md +56 -0
- fprime_gds/common/fpy/SPEC.md +69 -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/templates/cmd_template.py +8 -0
- fprime_gds/executables/cli.py +31 -16
- fprime_gds/executables/data_product_writer.py +66 -30
- {fprime_gds-4.0.0a8.dist-info → fprime_gds-4.0.0a10.dist-info}/METADATA +2 -1
- {fprime_gds-4.0.0a8.dist-info → fprime_gds-4.0.0a10.dist-info}/RECORD +18 -12
- {fprime_gds-4.0.0a8.dist-info → fprime_gds-4.0.0a10.dist-info}/entry_points.txt +1 -1
- fprime_gds/common/fpy/serialize_bytecode.py +0 -229
- fprime_gds/common/fpy/types.py +0 -203
- {fprime_gds-4.0.0a8.dist-info → fprime_gds-4.0.0a10.dist-info}/WHEEL +0 -0
- {fprime_gds-4.0.0a8.dist-info → fprime_gds-4.0.0a10.dist-info}/licenses/LICENSE.txt +0 -0
- {fprime_gds-4.0.0a8.dist-info → fprime_gds-4.0.0a10.dist-info}/licenses/NOTICE.txt +0 -0
- {fprime_gds-4.0.0a8.dist-info → fprime_gds-4.0.0a10.dist-info}/top_level.txt +0 -0
@@ -194,6 +194,7 @@ class QualifiedType(BaseModel):
|
|
194
194
|
class StructMember(BaseModel):
|
195
195
|
type: Union[IntegerType, FloatType, BoolType, StringType, QualifiedType]
|
196
196
|
size: int = 1
|
197
|
+
index: int
|
197
198
|
|
198
199
|
class StructType(BaseModel):
|
199
200
|
kind: str
|
@@ -209,7 +210,7 @@ class StructType(BaseModel):
|
|
209
210
|
class AliasType(BaseModel):
|
210
211
|
kind: str
|
211
212
|
qualifiedName: str
|
212
|
-
type: Union[AliasType, StructType, ArrayType, IntegerType, FloatType,
|
213
|
+
type: Union[AliasType, StructType, ArrayType, IntegerType, BoolType, FloatType, StringType, QualifiedType]
|
213
214
|
underlyingType: Union[StructType, ArrayType, IntegerType, BoolType, FloatType, StringType, QualifiedType]
|
214
215
|
|
215
216
|
@field_validator('kind')
|
@@ -222,7 +223,7 @@ class ArrayType(BaseModel):
|
|
222
223
|
kind: str
|
223
224
|
qualifiedName: str
|
224
225
|
size: int
|
225
|
-
elementType: Union[AliasType, StructType, ArrayType, IntegerType, FloatType,
|
226
|
+
elementType: Union[AliasType, StructType, ArrayType, IntegerType, BoolType, FloatType, StringType, QualifiedType]
|
226
227
|
|
227
228
|
@field_validator('kind')
|
228
229
|
def kind_qualifiedIdentifier(cls, v):
|
@@ -297,29 +298,33 @@ class DPHeader(BaseModel):
|
|
297
298
|
@computed_field
|
298
299
|
@property
|
299
300
|
def header(self) -> Dict[str, Union[Type, ArrayType, EnumType]]:
|
300
|
-
#
|
301
|
-
|
302
|
-
"FwPacketDescriptorType":
|
303
|
-
"FwDpIdType":
|
304
|
-
"FwDpPriorityType" :
|
305
|
-
"Seconds":
|
306
|
-
"USeconds":
|
307
|
-
"FwTimeBaseStoreType":
|
308
|
-
"FwTimeContextStoreType":
|
309
|
-
"Fw.DpCfg.ProcType":
|
310
|
-
"Fw.DpCfg.CONTAINER_USER_DATA_SIZE":
|
311
|
-
"Fw.DpState":
|
312
|
-
"FwSizeStoreType":
|
301
|
+
# Mapping from type/constant names to header field names
|
302
|
+
header_field_names = {
|
303
|
+
"FwPacketDescriptorType": "PacketDescriptor",
|
304
|
+
"FwDpIdType": "Id",
|
305
|
+
"FwDpPriorityType" : "Priority",
|
306
|
+
"Seconds": "Seconds",
|
307
|
+
"USeconds": "USeconds",
|
308
|
+
"FwTimeBaseStoreType": "TimeBase",
|
309
|
+
"FwTimeContextStoreType": "Context",
|
310
|
+
"Fw.DpCfg.ProcType": "ProcTypes",
|
311
|
+
"Fw.DpCfg.CONTAINER_USER_DATA_SIZE": "UserData",
|
312
|
+
"Fw.DpState": "DpState",
|
313
|
+
"FwSizeStoreType": "DataSize"
|
313
314
|
}
|
314
|
-
|
315
|
+
# All types/constants that make up the DP header
|
316
|
+
# Key: Values of above header_field_names
|
317
|
+
# Values: Initialized to None, populated below based on type names/constants in the JSON dictionary
|
318
|
+
header_dict = {v: None for v in header_field_names.values()}
|
319
|
+
for k, v in header_field_names.items():
|
315
320
|
# Seconds and USeconds are not in the dictionary, but are both always U32
|
316
321
|
if k == "Seconds" or k == "USeconds":
|
317
|
-
header_dict[
|
322
|
+
header_dict[v] = Type(type=IntegerType(name="U32", kind="integer", size=32, signed=False))
|
318
323
|
# According to Fw.Dp SDD, Header::UserData is an array of U8 of size Fw::DpCfg::CONTAINER_USER_DATA_SIZE.
|
319
324
|
elif k == "Fw.DpCfg.CONTAINER_USER_DATA_SIZE":
|
320
325
|
for t in self.constants:
|
321
326
|
if t.qualifiedName == k:
|
322
|
-
header_dict[
|
327
|
+
header_dict[v] = ArrayType(
|
323
328
|
kind="array",
|
324
329
|
qualifiedName="UserData",
|
325
330
|
size=t.value,
|
@@ -329,7 +334,7 @@ class DPHeader(BaseModel):
|
|
329
334
|
else:
|
330
335
|
for t in self.typeDefinitions:
|
331
336
|
if t.qualifiedName == k:
|
332
|
-
header_dict[
|
337
|
+
header_dict[v] = t
|
333
338
|
break
|
334
339
|
|
335
340
|
return header_dict
|
@@ -337,12 +342,15 @@ class DPHeader(BaseModel):
|
|
337
342
|
@computed_field
|
338
343
|
@property
|
339
344
|
def dataId(self) -> AliasType:
|
340
|
-
return self.header.get("
|
345
|
+
return self.header.get("Id")
|
341
346
|
|
342
347
|
@computed_field
|
343
348
|
@property
|
344
349
|
def dataSize(self) -> AliasType:
|
345
|
-
return self.header.get("
|
350
|
+
return self.header.get("DataSize")
|
351
|
+
|
352
|
+
def get_size_store_bytes(self) -> int:
|
353
|
+
return self.header.get("DataSize").underlyingType.size // 8
|
346
354
|
|
347
355
|
@model_validator(mode='after')
|
348
356
|
def validate_header(self) -> 'DPHeader':
|
@@ -371,7 +379,8 @@ type_mapping = {
|
|
371
379
|
'U64': 'Q', # Unsigned 64-bit integer
|
372
380
|
'F32': 'f', # 32-bit float
|
373
381
|
'F64': 'd', # 64-bit float
|
374
|
-
'bool': '?' # An 8 bit boolean
|
382
|
+
'bool': '?', # An 8 bit boolean
|
383
|
+
'string': 's'
|
375
384
|
# Add more mappings as needed
|
376
385
|
}
|
377
386
|
|
@@ -485,6 +494,7 @@ class DataProductWriter:
|
|
485
494
|
self.binaryFileName = binaryFileName
|
486
495
|
self.totalBytesRead = 0
|
487
496
|
self.calculatedCRC = 0
|
497
|
+
self.headerJSON = None
|
488
498
|
|
489
499
|
|
490
500
|
# ----------------------------------------------------------------------------------------------
|
@@ -520,8 +530,29 @@ class DataProductWriter:
|
|
520
530
|
except KeyError:
|
521
531
|
raise KeyError(f"Unrecognized JSON Dictionary Type: {intType}")
|
522
532
|
data = struct.unpack(format_str, bytes_read)[0]
|
533
|
+
return data
|
534
|
+
|
535
|
+
def read_and_deserialize_string(self) -> str:
|
536
|
+
size_store_type_bytes = self.headerJSON.get_size_store_bytes()
|
537
|
+
bytes_read_store = self.binaryFile.read(size_store_type_bytes)
|
538
|
+
if len(bytes_read_store) != size_store_type_bytes:
|
539
|
+
raise IOError(f"Tried to read {size_store_type_bytes} bytes from the binary file, but failed.")
|
540
|
+
|
541
|
+
self.totalBytesRead += size_store_type_bytes
|
542
|
+
|
543
|
+
format_str = f'{BIG_ENDIAN}H'
|
544
|
+
string_size_data = struct.unpack(format_str, bytes_read_store)[0]
|
545
|
+
|
546
|
+
bytes_read = self.binaryFile.read(string_size_data)
|
547
|
+
if len(bytes_read) != string_size_data:
|
548
|
+
raise IOError(f"Tried to read {string_size_data} bytes from the binary file, but failed.")
|
523
549
|
|
550
|
+
self.calculatedCRC = crc32(bytes_read_store + bytes_read, self.calculatedCRC) & 0xffffffff
|
551
|
+
self.totalBytesRead += string_size_data
|
524
552
|
|
553
|
+
format_str = f'{BIG_ENDIAN}{string_size_data}s'
|
554
|
+
data = struct.unpack(format_str, bytes_read)[0]
|
555
|
+
data = data.decode()
|
525
556
|
return data
|
526
557
|
|
527
558
|
# -----------------------------------------------------------------------------------------------------------------------
|
@@ -568,7 +599,7 @@ class DataProductWriter:
|
|
568
599
|
# AssertionError: If the field_config is not an IntegerType, FloatType, or BoolType.
|
569
600
|
# -----------------------------------------------------------------------------------------------------------------------
|
570
601
|
|
571
|
-
def read_field(self, field_config: Union[IntegerType, FloatType, BoolType]) -> Union[int, float, bool]:
|
602
|
+
def read_field(self, field_config: Union[IntegerType, FloatType, BoolType, StringType]) -> Union[int, float, bool]:
|
572
603
|
|
573
604
|
if type(field_config) is IntegerType:
|
574
605
|
sizeBytes = field_config.size // 8
|
@@ -579,6 +610,9 @@ class DataProductWriter:
|
|
579
610
|
elif type(field_config) is BoolType:
|
580
611
|
sizeBytes = field_config.size // 8
|
581
612
|
|
613
|
+
elif type(field_config) is StringType:
|
614
|
+
return self.read_and_deserialize_string()
|
615
|
+
|
582
616
|
else:
|
583
617
|
assert False, "Unsupported typeKind encountered"
|
584
618
|
|
@@ -626,6 +660,8 @@ class DataProductWriter:
|
|
626
660
|
elif isinstance(typeKind, BoolType):
|
627
661
|
parent_dict[field_name] = self.read_field(typeKind)
|
628
662
|
|
663
|
+
elif isinstance(typeKind, StringType):
|
664
|
+
parent_dict[field_name] = self.read_field(typeKind)
|
629
665
|
|
630
666
|
elif isinstance(typeKind, EnumType):
|
631
667
|
value = self.read_field(typeKind.representationType)
|
@@ -646,7 +682,8 @@ class DataProductWriter:
|
|
646
682
|
|
647
683
|
elif isinstance(typeKind, StructType):
|
648
684
|
array_list = []
|
649
|
-
|
685
|
+
sorted_members = dict(sorted(typeKind.members.items(), key=lambda member: member[1].index))
|
686
|
+
for key, member in sorted_members.items():
|
650
687
|
for i in range(member.size):
|
651
688
|
element_dict = {}
|
652
689
|
self.get_struct_item(key, member.type, typeList, element_dict)
|
@@ -830,7 +867,7 @@ class DataProductWriter:
|
|
830
867
|
header_json["typeDefinitions"] = dict_json["typeDefinitions"]
|
831
868
|
if "constants" in dict_json:
|
832
869
|
header_json["constants"] = dict_json["constants"]
|
833
|
-
headerJSON = DPHeader(**header_json)
|
870
|
+
self.headerJSON = DPHeader(**header_json)
|
834
871
|
|
835
872
|
except json.JSONDecodeError as e:
|
836
873
|
raise DictionaryError(self.jsonDict, e.lineno)
|
@@ -840,10 +877,10 @@ class DataProductWriter:
|
|
840
877
|
with open(self.binaryFileName, 'rb') as self.binaryFile:
|
841
878
|
|
842
879
|
# Read the header data up until the Records
|
843
|
-
headerData = self.get_header_info(headerJSON)
|
880
|
+
headerData = self.get_header_info(self.headerJSON)
|
844
881
|
|
845
882
|
# Read the total data size
|
846
|
-
dataSize = headerData['
|
883
|
+
dataSize = headerData['DataSize']
|
847
884
|
|
848
885
|
# Restart the count of bytes read
|
849
886
|
self.totalBytesRead = 0
|
@@ -851,13 +888,12 @@ class DataProductWriter:
|
|
851
888
|
recordList = [headerData]
|
852
889
|
|
853
890
|
while self.totalBytesRead < dataSize:
|
854
|
-
|
855
|
-
recordData = self.get_record_data(headerJSON, dictJSON)
|
891
|
+
recordData = self.get_record_data(self.headerJSON, dictJSON)
|
856
892
|
recordList.append(recordData)
|
857
893
|
|
858
894
|
computedCRC = self.calculatedCRC
|
859
895
|
# Read the data checksum
|
860
|
-
headerData['dataHash'] = self.read_field(headerJSON.dataHash.type)
|
896
|
+
headerData['dataHash'] = self.read_field(self.headerJSON.dataHash.type)
|
861
897
|
|
862
898
|
if computedCRC != headerData['dataHash']:
|
863
899
|
raise CRCError("Data", headerData['dataHash'], computedCRC)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fprime-gds
|
3
|
-
Version: 4.0.
|
3
|
+
Version: 4.0.0a10
|
4
4
|
Summary: F Prime Flight Software Ground Data System layer
|
5
5
|
Author-email: Michael Starch <Michael.D.Starch@jpl.nasa.gov>, Thomas Boyer-Chammard <Thomas.Boyer.Chammard@jpl.nasa.gov>
|
6
6
|
License:
|
@@ -240,6 +240,7 @@ Requires-Dist: Jinja2>=2.11.3
|
|
240
240
|
Requires-Dist: openpyxl>=3.0.10
|
241
241
|
Requires-Dist: pyserial>=3.5
|
242
242
|
Requires-Dist: pydantic>=2.6
|
243
|
+
Requires-Dist: lark>=1.2.2
|
243
244
|
Requires-Dist: PyYAML>=6.0.2
|
244
245
|
Requires-Dist: spacepackets>=0.30.0
|
245
246
|
Requires-Dist: crc>=7.0.0
|
@@ -49,9 +49,15 @@ fprime_gds/common/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
49
49
|
fprime_gds/common/files/downlinker.py,sha256=CZPfhH0J9-LNqW5Cv_ryicLTuoedLSWK8OPQmmQDZZY,7498
|
50
50
|
fprime_gds/common/files/helpers.py,sha256=sGaxcczXmZ5_soawT7x_eJ_cC2PZ6KOGBfusAV4QC_g,7219
|
51
51
|
fprime_gds/common/files/uplinker.py,sha256=lgqhlgeipBt3Arx-ohzK8vCdS54fKpv9Rg7SUTocUX8,13244
|
52
|
+
fprime_gds/common/fpy/README.md,sha256=AJE9KJDarTTW9nsTJBiZ6cKyPYQbWKbKJbFiNz6M8Z4,1414
|
53
|
+
fprime_gds/common/fpy/SPEC.md,sha256=XTMgr96BIVJheOtjAp6Qgu5l5RE16DxxIkPl0WwXfEs,2248
|
52
54
|
fprime_gds/common/fpy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
53
|
-
fprime_gds/common/fpy/
|
54
|
-
fprime_gds/common/fpy/
|
55
|
+
fprime_gds/common/fpy/codegen.py,sha256=5VdV-f6CdU1LPJx4swB_GV5pvPe3Cqh9E3bkS0dBAo4,59828
|
56
|
+
fprime_gds/common/fpy/grammar.lark,sha256=QIQgmj58pW9ou2u7DzE39c0fz55eGNj3X7ridzAHRFk,2128
|
57
|
+
fprime_gds/common/fpy/main.py,sha256=U50sQCrwhZgxGzkjKgaJywoQM_cEH78hN9Vy3pDKKMA,1073
|
58
|
+
fprime_gds/common/fpy/parser.py,sha256=g-l67oZN_vB1JAdZ5S3u9m8APf6PdJ88GZyz0iX66qo,4418
|
59
|
+
fprime_gds/common/fpy/bytecode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
|
+
fprime_gds/common/fpy/bytecode/directives.py,sha256=tBpPQ2ZnQXA7juB85HhacDdil1vEHUKW8m_AsMf1KdQ,13360
|
55
61
|
fprime_gds/common/gds_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
62
|
fprime_gds/common/gds_cli/base_commands.py,sha256=nHnzHRMXs9haRjiUmq-Ciu1DSo1BXtcVscByk1Ypsxo,9432
|
57
63
|
fprime_gds/common/gds_cli/channels.py,sha256=S8y0Mo2JbBFPvMzeW22HFVJ8p-UC-tzpYM7rXqIGWi4,2136
|
@@ -97,7 +103,7 @@ fprime_gds/common/pipeline/router.py,sha256=-P1wI0KXEh_snOzDaq8CjEoWuM_zRm8vUMR1
|
|
97
103
|
fprime_gds/common/pipeline/standard.py,sha256=fDSPfyhYPMNhev5IQG2j51sCtQxXZ5PrqmulKH8TNjE,9778
|
98
104
|
fprime_gds/common/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
99
105
|
fprime_gds/common/templates/ch_template.py,sha256=1MoDZsia0dI_CvnIttwyKLhbQhum35OcJnFc50Xohuo,3893
|
100
|
-
fprime_gds/common/templates/cmd_template.py,sha256=
|
106
|
+
fprime_gds/common/templates/cmd_template.py,sha256=n91z4WhFgHwTu6_fQqy7JqpkEObAkllIeEy0AR0DvrQ,5455
|
101
107
|
fprime_gds/common/templates/data_template.py,sha256=U87d8oC-BDTDuBRZbNnPkXy6rI_Pr-XnChHWZunw5jo,735
|
102
108
|
fprime_gds/common/templates/event_template.py,sha256=L0hkWB_kEMhTNodPUqBAev76SMmWT9EWdcqxaaQX9ZE,4062
|
103
109
|
fprime_gds/common/templates/pkt_template.py,sha256=5Wi6389m5j8w7JITBGfeUnw6CYE1-hjcVJ42NJmLDcE,1794
|
@@ -117,9 +123,9 @@ fprime_gds/common/utils/event_severity.py,sha256=7qPXHrDaM_REJ7sKBUEJTZIE0D4qVnV
|
|
117
123
|
fprime_gds/common/utils/string_util.py,sha256=u_2iahRG3ROu3lAAt_KVcK226gEByElXqrA8mH8eDpI,3584
|
118
124
|
fprime_gds/executables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
119
125
|
fprime_gds/executables/apps.py,sha256=u79T_PlgMmNmA4YwWjs7LvPMCJnrjnURr05NMthOYP0,13350
|
120
|
-
fprime_gds/executables/cli.py,sha256=
|
126
|
+
fprime_gds/executables/cli.py,sha256=Qqq3JQOqTsfDRsbTALw25xnwN7fCEVlpcKDV4jvGopQ,50961
|
121
127
|
fprime_gds/executables/comm.py,sha256=08rO0o0MJgTRngB7Ygu2IL_gEAWKF7WFvFyro1CqReE,5214
|
122
|
-
fprime_gds/executables/data_product_writer.py,sha256=
|
128
|
+
fprime_gds/executables/data_product_writer.py,sha256=e1Rp2LT_Cpg08f0Ki8GhirC7Wn6LtYiAef7KLAkZHUY,37773
|
123
129
|
fprime_gds/executables/fprime_cli.py,sha256=CMoT7zWNwM8h2mSZW03AR96wl_XnZXoLNiOZN_sDi38,12431
|
124
130
|
fprime_gds/executables/run_deployment.py,sha256=Zl0Y9-6i6c8tZhcS7XkAeVQtzn0d9fV-3UJQZ0bnBrc,7237
|
125
131
|
fprime_gds/executables/tcpserver.py,sha256=KspVpu5YIuiWKOk5E6UDMKvqXYrRB1j9aX8CkMxysfw,17555
|
@@ -238,10 +244,10 @@ fprime_gds/flask/static/third-party/webfonts/fa-solid-900.woff2,sha256=mDS4KtJuK
|
|
238
244
|
fprime_gds/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
239
245
|
fprime_gds/plugin/definitions.py,sha256=QlxW1gNvoiqGMslSJjh3dTFZuv0igFHawN__3XJ0Wns,5355
|
240
246
|
fprime_gds/plugin/system.py,sha256=M9xb-8jBhCUUx3X1z2uAP8Wx_v6NkL8JeaFgGcMnQqY,13432
|
241
|
-
fprime_gds-4.0.
|
242
|
-
fprime_gds-4.0.
|
243
|
-
fprime_gds-4.0.
|
244
|
-
fprime_gds-4.0.
|
245
|
-
fprime_gds-4.0.
|
246
|
-
fprime_gds-4.0.
|
247
|
-
fprime_gds-4.0.
|
247
|
+
fprime_gds-4.0.0a10.dist-info/licenses/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
248
|
+
fprime_gds-4.0.0a10.dist-info/licenses/NOTICE.txt,sha256=vXjA_xRcQhd83Vfk5D_vXg5kOjnnXvLuMi5vFKDEVmg,1612
|
249
|
+
fprime_gds-4.0.0a10.dist-info/METADATA,sha256=zD-VViHWpGeATD0SzBD1-yKMFOR25JIEIM3uBqMlY5Q,24577
|
250
|
+
fprime_gds-4.0.0a10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
251
|
+
fprime_gds-4.0.0a10.dist-info/entry_points.txt,sha256=V2XMHMUJUGTVx5s3_kK1jLmoxSKE1vvj2XWHH9y49WQ,423
|
252
|
+
fprime_gds-4.0.0a10.dist-info/top_level.txt,sha256=6vzFLIX6ANfavKaXFHDMSLFtS94a6FaAsIWhjgYuSNE,27
|
253
|
+
fprime_gds-4.0.0a10.dist-info/RECORD,,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
[console_scripts]
|
2
2
|
fprime-cli = fprime_gds.executables.fprime_cli:main
|
3
3
|
fprime-dp-write = fprime_gds.executables.data_product_writer:main
|
4
|
-
fprime-
|
4
|
+
fprime-fpyc = fprime_gds.common.fpy.main:main
|
5
5
|
fprime-gds = fprime_gds.executables.run_deployment:main
|
6
6
|
fprime-prm-write = fprime_gds.common.tools.params:main
|
7
7
|
fprime-seqgen = fprime_gds.common.tools.seqgen:main
|
@@ -1,229 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from dataclasses import astuple
|
3
|
-
import inspect
|
4
|
-
import json
|
5
|
-
from pathlib import Path
|
6
|
-
from argparse import ArgumentParser
|
7
|
-
import struct
|
8
|
-
import zlib
|
9
|
-
from fprime_gds.common.fpy.types import (
|
10
|
-
StatementTemplate,
|
11
|
-
StatementData,
|
12
|
-
Header,
|
13
|
-
Footer,
|
14
|
-
HEADER_FORMAT,
|
15
|
-
FOOTER_FORMAT,
|
16
|
-
StatementType,
|
17
|
-
FPY_DIRECTIVES,
|
18
|
-
BytecodeParseContext,
|
19
|
-
get_type_obj_for,
|
20
|
-
)
|
21
|
-
from fprime_gds.common.loaders.ch_json_loader import ChJsonLoader
|
22
|
-
from fprime_gds.common.loaders.cmd_json_loader import CmdJsonLoader
|
23
|
-
from fprime.common.models.serialize.numerical_types import (
|
24
|
-
U8Type,
|
25
|
-
)
|
26
|
-
|
27
|
-
from fprime_gds.common.loaders.prm_json_loader import PrmJsonLoader
|
28
|
-
|
29
|
-
|
30
|
-
def serialize_statement(stmt: StatementData) -> bytes:
|
31
|
-
"""converts a StatementData object into bytes that the FpySequencer can read"""
|
32
|
-
# see https://github.com/nasa/fprime/issues/3023#issuecomment-2693051677
|
33
|
-
# TODO replace this with actual documentation
|
34
|
-
|
35
|
-
# type: U8 (0 if directive, 1 if cmd)
|
36
|
-
# opcode: FwOpcodeType (default U32)
|
37
|
-
# argBufSize: FwSizeStoreType (default U16)
|
38
|
-
# argBuf: X bytes
|
39
|
-
|
40
|
-
output = bytes()
|
41
|
-
output += U8Type(stmt.template.statement_type.value).serialize()
|
42
|
-
output += get_type_obj_for("FwOpcodeType")(stmt.template.opcode).serialize()
|
43
|
-
|
44
|
-
arg_bytes = bytes()
|
45
|
-
for arg in stmt.arg_values:
|
46
|
-
arg_bytes += arg.serialize()
|
47
|
-
|
48
|
-
output += get_type_obj_for("FwSizeStoreType")(len(arg_bytes)).serialize()
|
49
|
-
output += arg_bytes
|
50
|
-
|
51
|
-
return output
|
52
|
-
|
53
|
-
|
54
|
-
def parse_str_as_statement(
|
55
|
-
stmt: str, templates: list[StatementTemplate], context: BytecodeParseContext
|
56
|
-
) -> StatementData:
|
57
|
-
"""Converts a human-readable line of bytecode into a StatementData instance, given a list of
|
58
|
-
possible statement templates"""
|
59
|
-
name = stmt.split()[0]
|
60
|
-
args = stmt[len(name) :]
|
61
|
-
|
62
|
-
args = json.loads("[" + args + "]")
|
63
|
-
|
64
|
-
matching_template = [t for t in templates if t.name == name]
|
65
|
-
if len(matching_template) != 1:
|
66
|
-
# no unique match
|
67
|
-
if len(matching_template) == 0:
|
68
|
-
raise RuntimeError("Could not find command or directive " + str(name))
|
69
|
-
raise RuntimeError(
|
70
|
-
"Found multiple commands or directives with name " + str(name)
|
71
|
-
)
|
72
|
-
matching_template = matching_template[0]
|
73
|
-
|
74
|
-
arg_values = []
|
75
|
-
if len(args) < len(matching_template.args):
|
76
|
-
raise RuntimeError(
|
77
|
-
"Missing arguments for statement "
|
78
|
-
+ str(matching_template.name)
|
79
|
-
+ ": "
|
80
|
-
+ str(matching_template.args[len(args) :])
|
81
|
-
)
|
82
|
-
if len(args) > len(matching_template.args):
|
83
|
-
raise RuntimeError(
|
84
|
-
"Extra arguments for"
|
85
|
-
+ str(matching_template.name)
|
86
|
-
+ ": "
|
87
|
-
+ str(args[len(matching_template.args) :])
|
88
|
-
)
|
89
|
-
for index, arg_json in enumerate(args):
|
90
|
-
arg_type = matching_template.args[index]
|
91
|
-
if inspect.isclass(arg_type):
|
92
|
-
# it's a type. instantiate it with the json
|
93
|
-
arg_value = arg_type(arg_json)
|
94
|
-
else:
|
95
|
-
# it's a function. give it the json and the ctx
|
96
|
-
arg_value = arg_type(arg_json, context)
|
97
|
-
arg_values.append(arg_value)
|
98
|
-
|
99
|
-
return StatementData(matching_template, arg_values)
|
100
|
-
|
101
|
-
|
102
|
-
def main():
|
103
|
-
arg_parser = ArgumentParser()
|
104
|
-
arg_parser.add_argument(
|
105
|
-
"input", type=Path, help="The path to the input .fpybc file"
|
106
|
-
)
|
107
|
-
|
108
|
-
arg_parser.add_argument(
|
109
|
-
"-d",
|
110
|
-
"--dictionary",
|
111
|
-
type=Path,
|
112
|
-
help="The JSON topology dictionary to compile against",
|
113
|
-
required=True,
|
114
|
-
)
|
115
|
-
|
116
|
-
arg_parser.add_argument(
|
117
|
-
"-o",
|
118
|
-
"--output",
|
119
|
-
type=Path,
|
120
|
-
help="The output .bin file path. Defaults to the input file path with a .bin extension",
|
121
|
-
default=None,
|
122
|
-
)
|
123
|
-
|
124
|
-
args = arg_parser.parse_args()
|
125
|
-
|
126
|
-
if not args.input.exists():
|
127
|
-
print("Input file", args.input, "does not exist")
|
128
|
-
exit(1)
|
129
|
-
|
130
|
-
if not args.dictionary.exists():
|
131
|
-
print("Dictionary file", args.dictionary, "does not exist")
|
132
|
-
exit(1)
|
133
|
-
|
134
|
-
serialize_bytecode(args.input, args.dictionary, args.output)
|
135
|
-
|
136
|
-
|
137
|
-
def serialize_bytecode(input: Path, dictionary: Path, output: Path = None):
|
138
|
-
"""Given an input .fpybc file, and a dictionary .json file, converts the
|
139
|
-
bytecode file into binary and writes it to the output file. If the output file
|
140
|
-
is None, writes it to the input file with a .bin extension"""
|
141
|
-
cmd_json_dict_loader = CmdJsonLoader(str(dictionary))
|
142
|
-
(_, cmd_name_dict, _) = cmd_json_dict_loader.construct_dicts(
|
143
|
-
str(dictionary)
|
144
|
-
)
|
145
|
-
|
146
|
-
stmt_templates = []
|
147
|
-
stmt_templates.extend(FPY_DIRECTIVES)
|
148
|
-
for cmd_template in cmd_name_dict.values():
|
149
|
-
stmt_template = StatementTemplate(
|
150
|
-
StatementType.CMD,
|
151
|
-
cmd_template.opcode,
|
152
|
-
cmd_template.get_full_name(),
|
153
|
-
[arg[2] for arg in cmd_template.arguments],
|
154
|
-
)
|
155
|
-
stmt_templates.append(stmt_template)
|
156
|
-
|
157
|
-
tlm_json_loader = ChJsonLoader(str(dictionary))
|
158
|
-
(_, tlm_name_dict, _) = tlm_json_loader.construct_dicts(
|
159
|
-
str(dictionary)
|
160
|
-
)
|
161
|
-
|
162
|
-
prm_json_loader = PrmJsonLoader(str(dictionary))
|
163
|
-
(_, prm_name_dict, _) = prm_json_loader.construct_dicts(
|
164
|
-
str(dictionary)
|
165
|
-
)
|
166
|
-
|
167
|
-
context = BytecodeParseContext()
|
168
|
-
context.types = cmd_json_dict_loader.parsed_types
|
169
|
-
context.channels = tlm_name_dict
|
170
|
-
context.params = prm_name_dict
|
171
|
-
|
172
|
-
input_lines = input.read_text().splitlines()
|
173
|
-
input_lines = [line.strip() for line in input_lines]
|
174
|
-
# remove comments and empty lines
|
175
|
-
input_lines = [
|
176
|
-
line for line in input_lines if not line.startswith(";") and len(line) > 0
|
177
|
-
]
|
178
|
-
|
179
|
-
goto_tags = {}
|
180
|
-
stmt_idx = 0
|
181
|
-
statement_strs: list[str] = []
|
182
|
-
for stmt in input_lines:
|
183
|
-
if stmt.endswith(":"):
|
184
|
-
# it's a goto tag
|
185
|
-
goto_tags[stmt[:-1]] = stmt_idx
|
186
|
-
else:
|
187
|
-
statement_strs.append(stmt)
|
188
|
-
stmt_idx += 1
|
189
|
-
|
190
|
-
context.goto_tags = goto_tags
|
191
|
-
|
192
|
-
statements: list[StatementData] = []
|
193
|
-
for stmt_idx, stmt in enumerate(statement_strs):
|
194
|
-
try:
|
195
|
-
stmt_data = parse_str_as_statement(stmt, stmt_templates, context)
|
196
|
-
statements.append(stmt_data)
|
197
|
-
except BaseException as e:
|
198
|
-
raise RuntimeError(
|
199
|
-
"Exception while parsing statement index " + str(stmt_idx) + ": " + stmt
|
200
|
-
) from e
|
201
|
-
|
202
|
-
# perform some checks for things we know will fail
|
203
|
-
for stmt in statements:
|
204
|
-
if stmt.template.name == "GOTO":
|
205
|
-
if stmt.arg_values[0].val > len(statements):
|
206
|
-
raise RuntimeError(
|
207
|
-
f"GOTO index is outside the valid range for this sequence (was {stmt.arg_values[0].val}, should be <{len(statements)})"
|
208
|
-
)
|
209
|
-
|
210
|
-
output_bytes = bytes()
|
211
|
-
|
212
|
-
for stmt in statements:
|
213
|
-
output_bytes += serialize_statement(stmt)
|
214
|
-
|
215
|
-
header = Header(0, 0, 0, 1, 0, len(statements), len(output_bytes))
|
216
|
-
output_bytes = struct.pack(HEADER_FORMAT, *astuple(header)) + output_bytes
|
217
|
-
|
218
|
-
crc = zlib.crc32(output_bytes) % (1 << 32)
|
219
|
-
footer = Footer(crc)
|
220
|
-
output_bytes += struct.pack(FOOTER_FORMAT, *astuple(footer))
|
221
|
-
|
222
|
-
if output is None:
|
223
|
-
output = input.with_suffix(".bin")
|
224
|
-
|
225
|
-
output.write_bytes(output_bytes)
|
226
|
-
|
227
|
-
|
228
|
-
if __name__ == "__main__":
|
229
|
-
main()
|