rccn-gen 1.3.2__tar.gz → 1.3.4__tar.gz
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-1.3.2 → rccn_gen-1.3.4}/.gitignore +2 -1
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/CHANGELOG.md +20 -1
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/PKG-INFO +3 -3
- rccn_gen-1.3.4/examples/generate-user-example.py +263 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/pyproject.toml +3 -2
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/systems.py +89 -36
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/cargo_toml/cargo.txt +6 -3
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/command/command.txt +1 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/command/command_module_enum.txt +1 -1
- rccn_gen-1.3.4/src/rccn_gen/text_modules/service/command_module_match_cmd.txt +5 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/service/service.txt +2 -1
- rccn_gen-1.3.2/rccn_usr_bi_x1_cntrl_app/Cargo.toml +0 -17
- rccn_gen-1.3.2/src/rccn_gen/text_modules/service/command_module_match_cmd.txt +0 -3
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/.gitlab-ci.yml +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/README.md +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/LICENSE +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/__init__.py +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/command/command_module_struct.txt +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/main/main.txt +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/main/service_module_import_service.txt +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/main/service_module_mod_service.txt +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/main/service_module_register_service.txt +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/mod/mod.txt +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/telemetry/telemetry.txt +0 -0
- {rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/utils.py +0 -0
@@ -1,7 +1,26 @@
|
|
1
1
|
# CHANGE LOG
|
2
2
|
|
3
|
+
### [1.3.4] - 2025-07-16
|
4
|
+
- Fixed Rust edition in autogenerated Cargo.toml file
|
5
|
+
- Removed 'rccn_usr'-prefix from generated user workspace
|
6
|
+
- Removed '(args)' from autogenerated commands if they don't take any arguments
|
7
|
+
- Fixed handler function call for commands that request telemetry
|
8
|
+
- RCCNCommand now has a 'specify_target_telemetry' parameter
|
9
|
+
- used if a RCCNCommand should request a RCCNContainer (Telemetry)
|
10
|
+
- specifies name of the target RCCNContainer to request
|
11
|
+
- generated files and directories no longer hold the 'rccn_usr_' prefix
|
12
|
+
- fixed major bug of receiving multiple telemetries of different apps when apid's are the same
|
13
|
+
- Added autogenerated output for missing implementation of commands
|
14
|
+
- Added dedicated example script for users to experiment with
|
15
|
+
- Added generation info texts as logging.info (user has to set 'include-system-site-packages = true' in pyvenv.py)
|
16
|
+
|
17
|
+
### [1.3.3] - 2025-05-26
|
18
|
+
- Fixed toml file error. Added new create_toml_file method for the Application class.
|
19
|
+
- Updated minimum required python version
|
20
|
+
- Updated toml file contents
|
21
|
+
|
3
22
|
### [1.3.2] - 2025-05-26
|
4
|
-
- Fixed base command
|
23
|
+
- Fixed base command inheritance bug
|
5
24
|
|
6
25
|
### [1.3.1] - 2025-05-15
|
7
26
|
- Added documentation for utils.py
|
@@ -1,14 +1,14 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rccn_gen
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.4
|
4
4
|
Summary: A python based generator for RACCOON OS source files in Rust from yamcs-pymdb config files.
|
5
5
|
Project-URL: Homepage, https://gitlab.com/rccn/pymdb_code_generation
|
6
6
|
Project-URL: Issues, https://gitlab.com/rccn/pymdb_code_generation/issues
|
7
|
-
Author-email: Fabian Krech <f.krech@tu-berlin.de>
|
7
|
+
Author-email: Fabian Krech <f.krech@tu-berlin.de>, Alexander Balke <a.balke@tu-berlin.de>
|
8
8
|
License-Expression: GPL-3.0
|
9
9
|
Classifier: Operating System :: OS Independent
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
11
|
-
Requires-Python: >=3.
|
11
|
+
Requires-Python: >=3.12
|
12
12
|
Requires-Dist: case-converter>=1.2.0
|
13
13
|
Requires-Dist: inflect>=7.5.0
|
14
14
|
Requires-Dist: yamcs-pymdb>=0.1.0
|
@@ -0,0 +1,263 @@
|
|
1
|
+
#!/usr/bin/env python3.13
|
2
|
+
import sys
|
3
|
+
import os
|
4
|
+
|
5
|
+
from yamcs.pymdb import *
|
6
|
+
from rccn_gen import *
|
7
|
+
|
8
|
+
root_system = System("RACCOON_GS")
|
9
|
+
app = Application(system = root_system, name = "AutogeneratedUserApp", apid = 67)
|
10
|
+
|
11
|
+
service = Service(name="CommConfigService", system=app, service_id=133)
|
12
|
+
|
13
|
+
virtual_channel_id = IntegerArgument(
|
14
|
+
name="virtual channel id",
|
15
|
+
signed=False,
|
16
|
+
encoding=uint8_t,
|
17
|
+
)
|
18
|
+
|
19
|
+
fop1_set_v_s_command = RCCNCommand(
|
20
|
+
system=service,
|
21
|
+
name="SetVsDirective",
|
22
|
+
short_description="Set Next Expected Frame Sequence Number [V(s)]",
|
23
|
+
arguments=[
|
24
|
+
virtual_channel_id,
|
25
|
+
IntegerArgument(
|
26
|
+
name="NextExpectedSequenceNumber",
|
27
|
+
encoding=uint8_t,
|
28
|
+
signed=False,
|
29
|
+
|
30
|
+
),
|
31
|
+
],
|
32
|
+
)
|
33
|
+
|
34
|
+
fop1_set_sliding_window_command = RCCNCommand(
|
35
|
+
system=service,
|
36
|
+
name="SetSlidingWindowDirective",
|
37
|
+
short_description="Set Sliding Window Width [k]",
|
38
|
+
arguments=[
|
39
|
+
virtual_channel_id,
|
40
|
+
IntegerArgument(
|
41
|
+
name="SlidingWindowWidth",
|
42
|
+
encoding=uint8_t,
|
43
|
+
signed=False,
|
44
|
+
),
|
45
|
+
],
|
46
|
+
)
|
47
|
+
|
48
|
+
fop1_set_t1_initial_command = RCCNCommand(
|
49
|
+
system=service,
|
50
|
+
name="SetT1initialDirective",
|
51
|
+
short_description="Set the initial value [t1] in ms to which the countdown Timer is set",
|
52
|
+
arguments=[
|
53
|
+
virtual_channel_id,
|
54
|
+
IntegerArgument(
|
55
|
+
name="T1Initial",
|
56
|
+
encoding=uint32_t,
|
57
|
+
signed=False,
|
58
|
+
),
|
59
|
+
],
|
60
|
+
)
|
61
|
+
|
62
|
+
fop1_set_transmission_limit_command = RCCNCommand(
|
63
|
+
system=service,
|
64
|
+
name="SetTransmissionLimitDirective",
|
65
|
+
short_description="Set the Transmission Limit",
|
66
|
+
arguments=[
|
67
|
+
virtual_channel_id,
|
68
|
+
IntegerArgument(
|
69
|
+
name="TransmissionLimit",
|
70
|
+
encoding=uint8_t,
|
71
|
+
signed=False,
|
72
|
+
),
|
73
|
+
],
|
74
|
+
)
|
75
|
+
|
76
|
+
fop1_set_timeout_type_command = RCCNCommand(
|
77
|
+
system=service,
|
78
|
+
name="SetTimeoutTypeDirective",
|
79
|
+
short_description="Set the Timeout Type",
|
80
|
+
arguments=[
|
81
|
+
virtual_channel_id,
|
82
|
+
IntegerArgument(
|
83
|
+
name="TimeoutType",
|
84
|
+
encoding=uint8_t,
|
85
|
+
minimum=0,
|
86
|
+
maximum=1,
|
87
|
+
signed=False,
|
88
|
+
),
|
89
|
+
],
|
90
|
+
)
|
91
|
+
|
92
|
+
fop1_initiate_ad_service_command = RCCNCommand(
|
93
|
+
system=service,
|
94
|
+
name="InitADServiceDirective",
|
95
|
+
short_description="Set AD Service",
|
96
|
+
arguments=[
|
97
|
+
virtual_channel_id,
|
98
|
+
BooleanArgument(
|
99
|
+
name="CLCW Check",
|
100
|
+
short_description="Set if CLCW check shall be performed",
|
101
|
+
),
|
102
|
+
],
|
103
|
+
)
|
104
|
+
|
105
|
+
fop1_initiate_ad_service_with_unlock_command = RCCNCommand(
|
106
|
+
system=service,
|
107
|
+
name="InitADServiceWithUnlockDirective",
|
108
|
+
short_description="Set AD Service With Unlock",
|
109
|
+
arguments=[
|
110
|
+
virtual_channel_id
|
111
|
+
]
|
112
|
+
)
|
113
|
+
|
114
|
+
fop1_initiate_ad_service_with_set_v_r_command = RCCNCommand(
|
115
|
+
system=service,
|
116
|
+
name="InitADServiceWithSetVrDirective",
|
117
|
+
short_description="Set AD Service With Set Vr",
|
118
|
+
arguments=[
|
119
|
+
virtual_channel_id,
|
120
|
+
IntegerArgument(
|
121
|
+
name="ReceiverFrameSequenceNumber",
|
122
|
+
short_description="Receiver Frame Sequence Number [V(R)]",
|
123
|
+
encoding=uint8_t,
|
124
|
+
signed=False,
|
125
|
+
),
|
126
|
+
],
|
127
|
+
)
|
128
|
+
|
129
|
+
fop1_terminate_ad_service_command = RCCNCommand(
|
130
|
+
system=service,
|
131
|
+
name="TerminateADServiceDirective",
|
132
|
+
short_description="Terminate AD Service",
|
133
|
+
arguments=[
|
134
|
+
virtual_channel_id
|
135
|
+
]
|
136
|
+
)
|
137
|
+
|
138
|
+
request_my_first_telemetry = RCCNCommand(
|
139
|
+
system=service,
|
140
|
+
name="RQ_my_first_telemetry",
|
141
|
+
short_description="Request-command for reading MyFirstTelemetry",
|
142
|
+
specify_target_telemetry='MyFirstTelemetry'
|
143
|
+
)
|
144
|
+
|
145
|
+
request_my_second_telemetry = RCCNCommand(
|
146
|
+
system=service,
|
147
|
+
name="RQ_my_second_telemetry",
|
148
|
+
short_description="Request-command for reading MyFirstTelemetry",
|
149
|
+
specify_target_telemetry = "MySecondTelemetry"
|
150
|
+
)
|
151
|
+
|
152
|
+
|
153
|
+
my_first_telemetry = RCCNContainer(
|
154
|
+
system=service,
|
155
|
+
name="MyFirstTelemetry",
|
156
|
+
entries=[
|
157
|
+
ParameterEntry(
|
158
|
+
IntegerParameter(
|
159
|
+
system=service,
|
160
|
+
name="State",
|
161
|
+
encoding=uint8_t,
|
162
|
+
|
163
|
+
)
|
164
|
+
),
|
165
|
+
ParameterEntry(
|
166
|
+
IntegerParameter(
|
167
|
+
system=service,
|
168
|
+
name="VS",
|
169
|
+
encoding=uint8_t
|
170
|
+
)
|
171
|
+
),
|
172
|
+
ParameterEntry(
|
173
|
+
BooleanParameter(
|
174
|
+
system=service,
|
175
|
+
name="ad_out_flag",
|
176
|
+
encoding=uint1_t
|
177
|
+
)
|
178
|
+
),
|
179
|
+
ParameterEntry(
|
180
|
+
BooleanParameter(
|
181
|
+
system=service,
|
182
|
+
name="bd_out_flag",
|
183
|
+
encoding=uint1_t
|
184
|
+
)
|
185
|
+
),
|
186
|
+
ParameterEntry(
|
187
|
+
BooleanParameter(
|
188
|
+
system=service,
|
189
|
+
name="bc_out_flag",
|
190
|
+
encoding=uint1_t
|
191
|
+
)
|
192
|
+
),
|
193
|
+
ParameterEntry(
|
194
|
+
IntegerParameter(
|
195
|
+
system=service,
|
196
|
+
name="ExpectedAcknowledgementFrameSequenceNumber",
|
197
|
+
encoding=uint8_t
|
198
|
+
)
|
199
|
+
),
|
200
|
+
ParameterEntry(
|
201
|
+
IntegerParameter(
|
202
|
+
system=service,
|
203
|
+
name="T1_Initial",
|
204
|
+
encoding=uint32_t
|
205
|
+
)
|
206
|
+
),
|
207
|
+
ParameterEntry(
|
208
|
+
IntegerParameter(
|
209
|
+
system=service,
|
210
|
+
name="TransmissionLimitTM",
|
211
|
+
encoding=uint8_t
|
212
|
+
)
|
213
|
+
),
|
214
|
+
ParameterEntry(
|
215
|
+
IntegerParameter(
|
216
|
+
system=service,
|
217
|
+
name="TransmissionCount",
|
218
|
+
encoding=uint8_t
|
219
|
+
)
|
220
|
+
),
|
221
|
+
ParameterEntry(
|
222
|
+
IntegerParameter(
|
223
|
+
system=service,
|
224
|
+
name="FopSlidingWindwWidth",
|
225
|
+
encoding=uint8_t
|
226
|
+
)
|
227
|
+
),
|
228
|
+
]
|
229
|
+
)
|
230
|
+
|
231
|
+
my_second_telemetry = RCCNContainer(
|
232
|
+
system=service,
|
233
|
+
name="MySecondTelemetry",
|
234
|
+
entries=[
|
235
|
+
ParameterEntry(
|
236
|
+
IntegerParameter(
|
237
|
+
system=service,
|
238
|
+
name="MyOtherState",
|
239
|
+
encoding=uint8_t
|
240
|
+
)
|
241
|
+
),
|
242
|
+
]
|
243
|
+
)
|
244
|
+
|
245
|
+
if __name__ == '__main__':
|
246
|
+
## change target userspace directory to put generated rust code into
|
247
|
+
## "../../rccn-userspace-ws/src/"
|
248
|
+
target_dir_userspace = "./"
|
249
|
+
|
250
|
+
## change target mdb directory to put generated .xml into
|
251
|
+
## "../../yamcs-instance/src/main/yamcs/mdb/"
|
252
|
+
target_dir_mdb = "./"
|
253
|
+
|
254
|
+
## Specify name of autogenerated *.xml
|
255
|
+
target_file_xml = "AUTOGENERATED_USER_GS.xml"
|
256
|
+
|
257
|
+
app.generate_rccn_code(export_directory=target_dir_userspace)
|
258
|
+
with open(target_dir_mdb + target_file_xml, "wt") as f:
|
259
|
+
app.dump(f)
|
260
|
+
|
261
|
+
print(f"----- Autogeneration done -----")
|
262
|
+
print(f"[ TODO 1/2 ] Add the path to your generated src/ directory to '[packages]' in the '<project>/Cargo.toml'")
|
263
|
+
print(f"[ TODO 2/2 ] Add the path to your generated '{target_file_xml}' to the 'mdb'-section of your *.yaml file")
|
@@ -4,13 +4,14 @@ build-backend = "hatchling.build"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "rccn_gen"
|
7
|
-
version = "1.3.
|
7
|
+
version = "1.3.4"
|
8
8
|
authors = [
|
9
9
|
{ name="Fabian Krech", email="f.krech@tu-berlin.de" },
|
10
|
+
{ name="Alexander Balke", email="a.balke@tu-berlin.de" },
|
10
11
|
]
|
11
12
|
description = "A python based generator for RACCOON OS source files in Rust from yamcs-pymdb config files."
|
12
13
|
readme = "README.md"
|
13
|
-
requires-python = ">=3.
|
14
|
+
requires-python = ">=3.12"
|
14
15
|
dependencies = [
|
15
16
|
"yamcs-pymdb>=0.1.0",
|
16
17
|
"inflect>=7.5.0",
|
@@ -19,6 +19,8 @@ from yamcs.pymdb.parameters import (
|
|
19
19
|
StringParameter
|
20
20
|
)
|
21
21
|
from yamcs.pymdb.commands import Command, CommandLevel, CommandEntry, Argument, TransmissionConstraint
|
22
|
+
import logging
|
23
|
+
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
|
22
24
|
|
23
25
|
class Application(Subsystem):
|
24
26
|
"""
|
@@ -162,13 +164,16 @@ class Application(Subsystem):
|
|
162
164
|
A dictionary mapping file types to their absolute paths.
|
163
165
|
"""
|
164
166
|
paths = {
|
165
|
-
'main': os.path.join(self.export_directory,
|
166
|
-
'main_generated_snapshot': os.path.join(self.snapshot_directory, 'generated',
|
167
|
-
'main_user_snapshot': os.path.join(self.user_snapshot_path(),
|
168
|
-
'main_diff': os.path.join(self.diff_directory,
|
167
|
+
'main': os.path.join(self.export_directory, snakecase(self.name), 'src', 'main.rs'),
|
168
|
+
'main_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', snakecase(self.name), 'src', 'main.rs'),
|
169
|
+
'main_user_snapshot': os.path.join(self.user_snapshot_path(), snakecase(self.name), 'src', 'main.rs'),
|
170
|
+
'main_diff': os.path.join(self.diff_directory, snakecase(self.name), 'src', 'main.diff'),
|
169
171
|
'main_template': os.path.join(self.text_modules_main_path, 'main.txt'),
|
170
|
-
'cargo_toml': os.path.join(self.export_directory,
|
172
|
+
'cargo_toml': os.path.join(self.export_directory, snakecase(self.name), 'Cargo.toml'),
|
171
173
|
'cargo_toml_template': os.path.join(self.text_modules_path, 'cargo_toml', 'cargo.txt'),
|
174
|
+
'cargo_toml_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', snakecase(self.name), 'cargo.toml'),
|
175
|
+
'cargo_toml_user_snapshot': os.path.join(self.user_snapshot_path(), snakecase(self.name), 'cargo.toml'),
|
176
|
+
'cargo_toml_diff': os.path.join(self.diff_directory, snakecase(self.name), 'cargo.diff'),
|
172
177
|
}
|
173
178
|
return paths
|
174
179
|
|
@@ -201,7 +206,7 @@ class Application(Subsystem):
|
|
201
206
|
Creates the necessary directory structure for the application and its services
|
202
207
|
in the export directory.
|
203
208
|
"""
|
204
|
-
app_src_dir = os.path.join(self.export_directory,
|
209
|
+
app_src_dir = os.path.join(self.export_directory, snakecase(self.name), 'src')
|
205
210
|
if not os.path.exists(app_src_dir):
|
206
211
|
os.makedirs(app_src_dir)
|
207
212
|
for service in self.services():
|
@@ -230,9 +235,11 @@ class Application(Subsystem):
|
|
230
235
|
# Generate main.rs file
|
231
236
|
with open(self.file_paths()['main_template'], 'r') as file:
|
232
237
|
main_template_text = file.read()
|
238
|
+
logging.debug(f"[{__class__.__name__}] {self.file_paths()['main_template']} read")
|
233
239
|
with open(self.file_paths()['main'], 'w') as file:
|
234
240
|
new_main_text = self.find_and_replace_keywords(main_template_text)
|
235
241
|
file.write("".join(new_main_text))
|
242
|
+
logging.info(f"[{__class__.__name__}] {self.file_paths()['main']} written")
|
236
243
|
# Create snapshot of newly generated main.rs if instructed
|
237
244
|
if self.snapshots:
|
238
245
|
self.generate_snapshot('main', 'main_generated_snapshot')
|
@@ -321,7 +328,7 @@ class Application(Subsystem):
|
|
321
328
|
if not os.path.exists(service.file_paths()['mod']):
|
322
329
|
service.generate_mod_file()
|
323
330
|
service.generate_telemetry_file()
|
324
|
-
service.generate_rccn_command_file(os.path.join(self.export_directory,
|
331
|
+
service.generate_rccn_command_file(os.path.join(self.export_directory, snakecase(self.name), 'src'), os.path.join(self.text_modules_path, 'command'))
|
325
332
|
self.delete_old_snapshots()
|
326
333
|
|
327
334
|
def generate_snapshot(self, current_file_reference, snapshot_file_reference):
|
@@ -407,12 +414,34 @@ class Application(Subsystem):
|
|
407
414
|
|
408
415
|
def generate_cargo_toml_file(self):
|
409
416
|
"""
|
410
|
-
Generate the
|
417
|
+
Generate the cargo.toml file for the application.
|
418
|
+
|
419
|
+
Performs several tasks:
|
420
|
+
1. Creates diff file if both current and snapshot files exist
|
421
|
+
2. Creates snapshot of current main with user changes if snapshots are enabled
|
422
|
+
3. Generates new cargo.toml file from template
|
423
|
+
4. Creates snapshot of newly generated main if snapshots are enabled
|
424
|
+
5. Applies user changes from diff file if rebase_changes is enabled
|
411
425
|
"""
|
426
|
+
# Create cargo_toml.diff file
|
427
|
+
if os.path.exists(self.file_paths()['cargo_toml']) and os.path.exists(self.file_paths()['cargo_toml_generated_snapshot']):
|
428
|
+
os.maked(os.path.dirname(self.file_paths()['cargo_toml_diff']), exist_ok=True)
|
429
|
+
self.generate_diff_file('cargo_toml', 'cargo_toml_generated_snapshot', 'cargo_toml_diff')
|
430
|
+
# Create snapshot of cargo_toml with user changes if instructed
|
431
|
+
if self.snapshots and os.path.exists(self.file_paths()['cargo_toml']):
|
432
|
+
self.generate_snapshot('cargo_toml', 'cargo_toml_user_snapshot')
|
433
|
+
# Generate cargo_toml file
|
412
434
|
with open(self.file_paths()['cargo_toml_template'], 'r') as file:
|
413
435
|
cargo_toml_template_text = file.read()
|
414
436
|
with open(self.file_paths()['cargo_toml'], 'w') as file:
|
415
|
-
|
437
|
+
new_cargo_toml_text = self.find_and_replace_keywords(cargo_toml_template_text)
|
438
|
+
file.write("".join(new_cargo_toml_text))
|
439
|
+
# Create snapshot of newly generated cargo_toml if instructed
|
440
|
+
if self.snapshots:
|
441
|
+
self.generate_snapshot('cargo_toml', 'cargo_toml_generated_snapshot')
|
442
|
+
# Rebase cargo_toml.diff on cargo_toml if instructed
|
443
|
+
if self.rebase_changes and os.path.exists(self.file_paths()['cargo_toml_diff']):
|
444
|
+
os.system('patch '+self.file_paths()['cargo_toml']+' < '+self.file_paths()['cargo_toml_diff'])
|
416
445
|
|
417
446
|
|
418
447
|
class Service(Subsystem):
|
@@ -532,22 +561,22 @@ class Service(Subsystem):
|
|
532
561
|
source files, generated snapshots, user snapshots, diff files, and templates.
|
533
562
|
"""
|
534
563
|
paths = {
|
535
|
-
'service': os.path.join(self.export_directory,
|
536
|
-
'service_generated_snapshot': os.path.join(self.snapshot_directory, 'generated',
|
537
|
-
'service_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"),
|
538
|
-
'service_diff': os.path.join(self.diff_directory,
|
564
|
+
'service': os.path.join(self.export_directory, snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
565
|
+
'service_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
566
|
+
'service_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), snakecase(self.system.name), 'src', snakecase(self.name), 'service.rs'),
|
567
|
+
'service_diff': os.path.join(self.diff_directory, snakecase(self.system.name), 'src', snakecase(self.name), 'service.diff'),
|
539
568
|
'service_template': os.path.join(self.text_modules_service_path, 'service.txt'),
|
540
|
-
'command': os.path.join(self.export_directory,
|
541
|
-
'command_generated_snapshot': os.path.join(self.snapshot_directory, 'generated',
|
542
|
-
'command_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"),
|
543
|
-
'command_diff': os.path.join(self.diff_directory,
|
569
|
+
'command': os.path.join(self.export_directory, snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
570
|
+
'command_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
571
|
+
'command_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
572
|
+
'command_diff': os.path.join(self.diff_directory, snakecase(self.system.name), 'src', snakecase(self.name), 'command.diff'),
|
544
573
|
'command_template': os.path.join(self.text_modules_command_path, 'command.txt'),
|
545
|
-
'mod': os.path.join(self.export_directory,
|
574
|
+
'mod': os.path.join(self.export_directory, snakecase(self.system.name), 'src', snakecase(self.name), 'mod.rs'),
|
546
575
|
'mod_template': os.path.join(self.text_modules_path, 'mod', 'mod.txt'),
|
547
|
-
'telemetry': os.path.join(self.export_directory,
|
548
|
-
'telemetry_generated_snapshot': os.path.join(self.snapshot_directory, 'generated',
|
549
|
-
'telemetry_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"),
|
550
|
-
'telemetry_diff': os.path.join(self.diff_directory,
|
576
|
+
'telemetry': os.path.join(self.export_directory, snakecase(self.system.name), 'src', snakecase(self.name), 'telemetry.rs'),
|
577
|
+
'telemetry_generated_snapshot': os.path.join(self.snapshot_directory, 'generated', snakecase(self.system.name), 'src', snakecase(self.name), 'telemetry.rs'),
|
578
|
+
'telemetry_user_snapshot': os.path.join(self.snapshot_directory, 'user', datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), snakecase(self.system.name), 'src', snakecase(self.name), 'command.rs'),
|
579
|
+
'telemetry_diff': os.path.join(self.diff_directory, snakecase(self.system.name), 'src', snakecase(self.name), 'telemetry.diff'),
|
551
580
|
'telemetry_template': os.path.join(self.text_modules_telemetry_path, 'telemetry.txt'),
|
552
581
|
}
|
553
582
|
return paths
|
@@ -605,6 +634,10 @@ class Service(Subsystem):
|
|
605
634
|
for command in self.rccn_commands():
|
606
635
|
text = command.find_and_replace_keywords(text, text_modules_path)
|
607
636
|
|
637
|
+
containers = self.rccn_container()
|
638
|
+
cnames = [c.name for c in containers]
|
639
|
+
telemetries = ", ".join(cnames)
|
640
|
+
|
608
641
|
# Find and replace service variable keywords
|
609
642
|
var_keywords = get_var_keywords(text)
|
610
643
|
service_var_translation = {
|
@@ -612,6 +645,7 @@ class Service(Subsystem):
|
|
612
645
|
'<<VAR_SERVICE_ID>>': lambda: str(self.service_id),
|
613
646
|
'<<VAR_SERVICE_NAME_UCASE>>': lambda: pascalcase(self.name),
|
614
647
|
'<<VAR_SERVICE_TELEMETRY>>': lambda: self.generate_rust_telemetry_definition(),
|
648
|
+
'<<VAR_IMPORTS_TELEMETRIES>>' : lambda: "use super::{command" + ("};" if len(containers) == 0 else ", telemetry};"),
|
615
649
|
}
|
616
650
|
for var_keyword in var_keywords:
|
617
651
|
if var_keyword in service_var_translation.keys():
|
@@ -644,6 +678,7 @@ class Service(Subsystem):
|
|
644
678
|
service_template_file_text = file.read()
|
645
679
|
with open(self.file_paths()['service'], 'w') as file:
|
646
680
|
file.write(self.find_and_replace_keywords(service_template_file_text, self.text_modules_service_path))
|
681
|
+
logging.info(f"[{__class__.__name__}] {self.file_paths()['service']} written")
|
647
682
|
# Create snapshot of service.rs if instructed
|
648
683
|
if self.snapshots:
|
649
684
|
self.generate_snapshot('service', 'service_generated_snapshot')
|
@@ -678,7 +713,7 @@ class Service(Subsystem):
|
|
678
713
|
self.generate_snapshot('command', 'command_user_snapshot')
|
679
714
|
# Generate command.rs file
|
680
715
|
if len(self.rccn_commands()) == 0:
|
681
|
-
|
716
|
+
logging.debug(f'[{__class__.__name__}] RCCN-Information: Service \''+self.name+'\' has no commands other than base command. Generation of command.rs file will be skipped.')
|
682
717
|
return
|
683
718
|
command_file_path = self.file_paths()['command_template']
|
684
719
|
with open(command_file_path, 'r') as file:
|
@@ -686,6 +721,7 @@ class Service(Subsystem):
|
|
686
721
|
command_export_directory = os.path.join(export_file_dir, snakecase(self.name), 'command.rs')
|
687
722
|
with open(command_export_directory, 'w') as file:
|
688
723
|
file.write(self.find_and_replace_keywords(command_file_text, text_modules_path))
|
724
|
+
logging.info(f"[{__class__.__name__}] {command_export_directory} written")
|
689
725
|
# Create snapshot of command.rs if instructed
|
690
726
|
if self.snapshots:
|
691
727
|
self.generate_snapshot('command', 'command_generated_snapshot')
|
@@ -762,6 +798,7 @@ class Service(Subsystem):
|
|
762
798
|
telemetry_template_file_text = file.read()
|
763
799
|
with open(self.file_paths()['telemetry'], 'w') as file:
|
764
800
|
file.write(self.find_and_replace_keywords(telemetry_template_file_text, self.text_modules_telemetry_path))
|
801
|
+
logging.info(f"[{__class__.__name__}] {self.file_paths()['telemetry']} written")
|
765
802
|
# Create snapshot of telemetry.rs if instructed
|
766
803
|
if self.snapshots:
|
767
804
|
self.generate_snapshot('telemetry', 'telemetry_generated_snapshot')
|
@@ -886,6 +923,7 @@ class RCCNCommand(Command):
|
|
886
923
|
long_description: str | None = None,
|
887
924
|
extra: Mapping[str, str] | None = None,
|
888
925
|
abstract: bool = False,
|
926
|
+
specify_target_telemetry: str = None,
|
889
927
|
base: Command | str | None = None,
|
890
928
|
assignments: Mapping[str, Any] | None = None,
|
891
929
|
arguments: Sequence[Argument] | None = None,
|
@@ -914,6 +952,8 @@ class RCCNCommand(Command):
|
|
914
952
|
Arbitrary information about the command, keyed by name.
|
915
953
|
abstract : bool, optional
|
916
954
|
Whether this command is abstract. Default is False.
|
955
|
+
specify_target_telemetry : str, optional
|
956
|
+
Specify target telemetry that this command is supposed to request.
|
917
957
|
base : Command | str, optional
|
918
958
|
The base command or reference to a base command.
|
919
959
|
assignments : Mapping[str, Any], optional
|
@@ -949,6 +989,7 @@ class RCCNCommand(Command):
|
|
949
989
|
'constraint': constraint,
|
950
990
|
**kwargs
|
951
991
|
}
|
992
|
+
self.target_telemetry = specify_target_telemetry
|
952
993
|
if 'system' in kwargs and isinstance(kwargs['system'], Service):
|
953
994
|
self.add_to_service(kwargs['system'])
|
954
995
|
elif base and (isinstance(base, Command) or isinstance(base, RCCNCommand)):
|
@@ -975,7 +1016,7 @@ class RCCNCommand(Command):
|
|
975
1016
|
- The command's APID is automatically set to match the service's application APID
|
976
1017
|
"""
|
977
1018
|
if self.init_kwargs['base'] is None and not any(command.name == 'base' for command in service.commands):
|
978
|
-
|
1019
|
+
logging.debug(f"[{__class__.__name__}] 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)+".")
|
979
1020
|
self.init_kwargs['base'] = Command(
|
980
1021
|
system=service,
|
981
1022
|
name='base',
|
@@ -984,7 +1025,7 @@ class RCCNCommand(Command):
|
|
984
1025
|
assignments={'type': service.service_id}
|
985
1026
|
)
|
986
1027
|
elif self.init_kwargs['base'] is None and any(command.name == 'base' for command in service.commands):
|
987
|
-
|
1028
|
+
logging.debug(f"[{__class__.__name__}] RCCN-Information: Command \'"+self.init_kwargs['name']+"\' doesn\'t have a \'base\' argument. Existing base command for service \'"+service.name+"\' will be used.")
|
988
1029
|
self.init_kwargs['base'] = next(command for command in service.commands if command.name == 'base')
|
989
1030
|
if 'system' in self.init_kwargs and isinstance(self.init_kwargs['system'], Service):
|
990
1031
|
super().__init__(*self.init_args, **self.init_kwargs)
|
@@ -996,12 +1037,13 @@ class RCCNCommand(Command):
|
|
996
1037
|
new_subtype = 1
|
997
1038
|
while new_subtype in used_subtypes:
|
998
1039
|
new_subtype = new_subtype + 1
|
999
|
-
|
1040
|
+
logging.debug(f'[{__class__.__name__}] RCCN-Information: Command \''+self.name+'\' has no subtype specified. Subtype will be set to '+str(new_subtype)+'.')
|
1000
1041
|
self.assignments['subtype'] = new_subtype
|
1001
|
-
|
1042
|
+
if len(self.arguments) != 0:
|
1043
|
+
self.struct_name = self.name + 'Args'
|
1002
1044
|
|
1003
1045
|
|
1004
|
-
def find_and_replace_keywords(self, text, text_modules_path):
|
1046
|
+
def find_and_replace_keywords(self, text, text_modules_path): # , service : Service):
|
1005
1047
|
"""
|
1006
1048
|
Replace template keywords with actual command values.
|
1007
1049
|
|
@@ -1040,15 +1082,24 @@ class RCCNCommand(Command):
|
|
1040
1082
|
replacement_text = (self.find_and_replace_keywords(module_text, text_modules_path) + '\n')
|
1041
1083
|
text = insert_before_with_indentation(text, command_module_keyword, replacement_text)
|
1042
1084
|
|
1085
|
+
cmd_requests_tm = (self.target_telemetry is not None)
|
1086
|
+
|
1043
1087
|
# Find and replace command variable keywords
|
1044
1088
|
command_var_keywords = get_var_keywords(text)
|
1045
1089
|
command_var_translation = {
|
1046
1090
|
'<<VAR_COMMAND_NAME_UCASE>>': lambda: pascalcase(self.name),
|
1091
|
+
'<<VAR_ARGS>>' : lambda: "(args)" if len(self.arguments) != 0 else "",
|
1092
|
+
'<<VAR_HANDLER_FUNCTION>>' : lambda: "tc.handle_with_tm" if cmd_requests_tm else "tc.handle",
|
1047
1093
|
'<<VAR_COMMAND_NAME>>': lambda: self.name,
|
1048
|
-
'<<VAR_COMMAND_STRUCT_NAME>>': lambda: self.struct_name,
|
1094
|
+
'<<VAR_COMMAND_STRUCT_NAME>>': lambda: f"({self.struct_name})" if len(self.arguments) != 0 else '',
|
1049
1095
|
'<<VAR_COMMAND_SUBTYPE>>': lambda: str(self.assignments['subtype']),
|
1050
1096
|
'<<VAR_COMMAND_STRUCT>>': lambda: self.struct_definition(),
|
1051
|
-
'<<VAR_SHORT_DESCRIPTION>>': lambda: "
|
1097
|
+
'<<VAR_SHORT_DESCRIPTION>>': lambda: "// " + self.short_description if self.short_description is not None else "No short description provided",
|
1098
|
+
'<<VAR_INFO_TEXT>>' : lambda: "User functionality missing in" if not cmd_requests_tm else "Returning default telemetry from",
|
1099
|
+
'<<VAR_RETURN_TYPE>>' : lambda : "if false { return Err(()); }" if cmd_requests_tm else "return false;",
|
1100
|
+
'<<VAR_RETURN_TARGET_TM_TEMPLATE>>' : lambda : "" if not cmd_requests_tm else \
|
1101
|
+
f"\nlet mut tm = telemetry::{self.target_telemetry}::default();\n" \
|
1102
|
+
f"\nOk(tm)",
|
1052
1103
|
}
|
1053
1104
|
for command_var_keyword in command_var_keywords:
|
1054
1105
|
if command_var_keyword in command_var_translation.keys():
|
@@ -1223,16 +1274,17 @@ class RCCNContainer(Container):
|
|
1223
1274
|
if eq_expression.ref == self.base+'/subtype':
|
1224
1275
|
condition_subtype = eq_expression.value
|
1225
1276
|
if condition_type is not None and condition_type != self.type:
|
1226
|
-
|
1277
|
+
logging.debug(f'[{__class__.__name__}] RCCN-Warning: Container '+self.name+' has a user-defined type of '+str(eq_expression.value)+', which doesn\'t match the service ID. User-defined type will be used.')
|
1227
1278
|
self.type = condition_type
|
1228
1279
|
if condition_subtype is not None and self.subtype is not None and condition_subtype != self.subtype:
|
1229
|
-
|
1280
|
+
logging.debug(f'[{__class__.__name__}] RCCN-Warning: Container '+self.name+' has an ambiguous user-defined subtype. \'subtype\' argument should match the \'condition\' argument.')
|
1230
1281
|
elif condition_subtype is not None:
|
1231
1282
|
self.subtype = condition_subtype
|
1232
1283
|
elif self.subtype is not None and self.init_kwargs['condition'] is not None:
|
1233
1284
|
self.init_kwargs['condition'] = AndExpression(
|
1234
1285
|
EqExpression(self.base+'/type', self.type),
|
1235
|
-
EqExpression(self.base+'/subtype', self.subtype)
|
1286
|
+
EqExpression(self.base+'/subtype', self.subtype),
|
1287
|
+
EqExpression('/PUS/ccsds/apid', service.system.apid)
|
1236
1288
|
)
|
1237
1289
|
else:
|
1238
1290
|
used_subtypes = [container.subtype for container in service.rccn_container()]
|
@@ -1242,9 +1294,10 @@ class RCCNContainer(Container):
|
|
1242
1294
|
self.subtype = new_subtype
|
1243
1295
|
self.init_kwargs['condition'] = AndExpression(
|
1244
1296
|
EqExpression(self.base+'/type', self.type),
|
1245
|
-
EqExpression(self.base+'/subtype', self.subtype)
|
1297
|
+
EqExpression(self.base+'/subtype', self.subtype),
|
1298
|
+
EqExpression('/PUS/ccsds/apid', service.system.apid)
|
1246
1299
|
)
|
1247
|
-
|
1300
|
+
logging.debug(f'[{__class__.__name__}] RCCN-Information: Subtype for Container '+self.name+' is not specified through \'subtype\' or \'condition\' arguments. Subtype will be set to '+str(self.subtype)+'.')
|
1248
1301
|
|
1249
1302
|
if 'system' in self.init_kwargs and isinstance(self.init_kwargs['system'], Service):
|
1250
1303
|
super().__init__(**self.init_kwargs)
|
@@ -1269,7 +1322,7 @@ class RCCNContainer(Container):
|
|
1269
1322
|
rccn_telemetry_text = ""
|
1270
1323
|
if hasattr(self, 'short_description') and self.short_description is not None:
|
1271
1324
|
rccn_telemetry_text += "/// "+str(self.short_description)+"\n"
|
1272
|
-
rccn_telemetry_text += "#[derive(ServiceTelemetry, BitStruct, Debug)]\n"
|
1325
|
+
rccn_telemetry_text += "#[derive(ServiceTelemetry, BitStruct, Debug, Default)]\n"
|
1273
1326
|
if hasattr(self, 'subtype') and self.subtype is not None:
|
1274
1327
|
rccn_telemetry_text += "#[subtype("+str(self.subtype)+")]\n"
|
1275
1328
|
rccn_telemetry_text += "pub struct " + self.name + " {\n"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
[package]
|
2
|
-
name = "
|
2
|
+
name = "<<VAR_APP_NAME_SCASE>>"
|
3
3
|
version = "0.1.0"
|
4
|
-
edition = "
|
4
|
+
edition = "2024"
|
5
5
|
|
6
6
|
[dependencies]
|
7
7
|
anyhow = "1.0.91"
|
@@ -14,4 +14,7 @@ spacepackets = "0.12.0"
|
|
14
14
|
tokio = "1.41.1"
|
15
15
|
env_logger = { version = "0.11.7", default-features = false, features = ["color", "humantime"] }
|
16
16
|
num-derive = "0.4"
|
17
|
-
num-traits = "0.2"
|
17
|
+
num-traits = "0.2"
|
18
|
+
rccn_usr_bitstruct_derive = { version = "0.1.0", path = "../rccn_usr_bitstruct_derive" }
|
19
|
+
rccn_usr_pus_macros = { version = "0.1.0", path = "../rccn_usr_pus_macros" }
|
20
|
+
chrono = "0.4.40"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
use std::process::Command;
|
2
2
|
use rccn_usr::service::{AcceptanceResult, AcceptedTc, PusService};
|
3
|
-
|
3
|
+
<<VAR_IMPORTS_TELEMETRIES>>
|
4
4
|
use anyhow::Result;
|
5
5
|
use std::error::Error;
|
6
6
|
use chrono::Local;
|
@@ -8,6 +8,7 @@ use std::fs;
|
|
8
8
|
|
9
9
|
|
10
10
|
pub struct <<VAR_SERVICE_NAME_UCASE>> {
|
11
|
+
// tm_parameter
|
11
12
|
}
|
12
13
|
|
13
14
|
|
@@ -1,17 +0,0 @@
|
|
1
|
-
[package]
|
2
|
-
name = "rccn_usr_example_app"
|
3
|
-
version = "0.1.0"
|
4
|
-
edition = "2021"
|
5
|
-
|
6
|
-
[dependencies]
|
7
|
-
anyhow = "1.0.91"
|
8
|
-
binary_serde = "1.0.24"
|
9
|
-
crossbeam-channel = "0.5.13"
|
10
|
-
futures = "0.3.31"
|
11
|
-
rccn_usr = { version = "0.1.0", path = "../rccn_usr" }
|
12
|
-
satrs = "0.2.1"
|
13
|
-
spacepackets = "0.12.0"
|
14
|
-
tokio = "1.41.1"
|
15
|
-
env_logger = { version = "0.11.7", default-features = false, features = ["color", "humantime"] }
|
16
|
-
num-derive = "0.4"
|
17
|
-
num-traits = "0.2"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/command/command_module_struct.txt
RENAMED
File without changes
|
File without changes
|
{rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/main/service_module_import_service.txt
RENAMED
File without changes
|
{rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/main/service_module_mod_service.txt
RENAMED
File without changes
|
{rccn_gen-1.3.2 → rccn_gen-1.3.4}/src/rccn_gen/text_modules/main/service_module_register_service.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|