openc3-cosmos-cfdp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +18 -0
  3. data/README.md +181 -0
  4. data/Rakefile +40 -0
  5. data/lib/cfdp.rb +283 -0
  6. data/lib/cfdp_api.rb +204 -0
  7. data/microservices/CFDP/Gemfile +37 -0
  8. data/microservices/CFDP/Rakefile +6 -0
  9. data/microservices/CFDP/app/controllers/application_controller.rb +46 -0
  10. data/microservices/CFDP/app/controllers/cfdp_controller.rb +222 -0
  11. data/microservices/CFDP/app/models/cfdp_checksum.rb +52 -0
  12. data/microservices/CFDP/app/models/cfdp_crc_checksum.rb +41 -0
  13. data/microservices/CFDP/app/models/cfdp_mib.rb +613 -0
  14. data/microservices/CFDP/app/models/cfdp_model.rb +25 -0
  15. data/microservices/CFDP/app/models/cfdp_null_checksum.rb +29 -0
  16. data/microservices/CFDP/app/models/cfdp_pdu.rb +202 -0
  17. data/microservices/CFDP/app/models/cfdp_receive_transaction.rb +590 -0
  18. data/microservices/CFDP/app/models/cfdp_source_transaction.rb +449 -0
  19. data/microservices/CFDP/app/models/cfdp_topic.rb +58 -0
  20. data/microservices/CFDP/app/models/cfdp_transaction.rb +188 -0
  21. data/microservices/CFDP/app/models/cfdp_user.rb +601 -0
  22. data/microservices/CFDP/bin/rails +4 -0
  23. data/microservices/CFDP/bin/rake +4 -0
  24. data/microservices/CFDP/bin/setup +25 -0
  25. data/microservices/CFDP/config/application.rb +55 -0
  26. data/microservices/CFDP/config/boot.rb +4 -0
  27. data/microservices/CFDP/config/credentials.yml.enc +1 -0
  28. data/microservices/CFDP/config/environment.rb +5 -0
  29. data/microservices/CFDP/config/environments/development.rb +53 -0
  30. data/microservices/CFDP/config/environments/production.rb +75 -0
  31. data/microservices/CFDP/config/environments/test.rb +50 -0
  32. data/microservices/CFDP/config/initializers/application_controller_renderer.rb +8 -0
  33. data/microservices/CFDP/config/initializers/backtrace_silencers.rb +7 -0
  34. data/microservices/CFDP/config/initializers/cfdp_initializer.rb +26 -0
  35. data/microservices/CFDP/config/initializers/cors.rb +16 -0
  36. data/microservices/CFDP/config/initializers/filter_parameter_logging.rb +8 -0
  37. data/microservices/CFDP/config/initializers/inflections.rb +16 -0
  38. data/microservices/CFDP/config/initializers/mime_types.rb +4 -0
  39. data/microservices/CFDP/config/initializers/wrap_parameters.rb +9 -0
  40. data/microservices/CFDP/config/locales/en.yml +29 -0
  41. data/microservices/CFDP/config/puma.rb +38 -0
  42. data/microservices/CFDP/config/routes.rb +16 -0
  43. data/microservices/CFDP/config.ru +5 -0
  44. data/microservices/CFDP/init.sh +9 -0
  45. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_ack.rb +82 -0
  46. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_enum.rb +237 -0
  47. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_eof.rb +87 -0
  48. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_file_data.rb +98 -0
  49. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_finished.rb +114 -0
  50. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_keep_alive.rb +65 -0
  51. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_metadata.rb +116 -0
  52. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_nak.rb +91 -0
  53. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_prompt.rb +60 -0
  54. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_tlv.rb +291 -0
  55. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_user_ops.rb +749 -0
  56. data/microservices/CFDP/public/robots.txt +1 -0
  57. data/microservices/CFDP/spec/models/cfdp_pdu_ack_spec.rb +114 -0
  58. data/microservices/CFDP/spec/models/cfdp_pdu_eof_spec.rb +159 -0
  59. data/microservices/CFDP/spec/models/cfdp_pdu_file_data_spec.rb +76 -0
  60. data/microservices/CFDP/spec/models/cfdp_pdu_finished_spec.rb +192 -0
  61. data/microservices/CFDP/spec/models/cfdp_pdu_keep_alive_spec.rb +69 -0
  62. data/microservices/CFDP/spec/models/cfdp_pdu_metadata_spec.rb +346 -0
  63. data/microservices/CFDP/spec/models/cfdp_pdu_nak_spec.rb +126 -0
  64. data/microservices/CFDP/spec/models/cfdp_pdu_prompt_spec.rb +94 -0
  65. data/microservices/CFDP/spec/models/cfdp_pdu_spec.rb +111 -0
  66. data/microservices/CFDP/spec/rails_helper.rb +71 -0
  67. data/microservices/CFDP/spec/requests/cfdp_spec.rb +1965 -0
  68. data/microservices/CFDP/spec/spec_helper.rb +200 -0
  69. data/plugin.txt +67 -0
  70. data/targets/CFDPTEST/cmd_tlm/cmd.txt +5 -0
  71. data/targets/CFDPTEST/cmd_tlm/tlm.txt +4 -0
  72. data/targets/CFDPTEST/procedures/cfdp_test_suite.rb +130 -0
  73. data/targets/CFDPTEST/target.txt +4 -0
  74. metadata +118 -0
@@ -0,0 +1,346 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2023 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # Licensed for Evaluation and Educational Use
7
+ #
8
+ # This file may only be used commercially under the terms of a commercial license
9
+ # purchased from OpenC3, Inc.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+ #
15
+ # The development of this software was funded in-whole or in-part by MethaneSAT LLC.
16
+
17
+ require 'rails_helper'
18
+ require 'cfdp_pdu'
19
+ require 'openc3/models/microservice_model'
20
+ require 'openc3/utilities/store_autoload'
21
+
22
+ RSpec.describe CfdpPdu, type: :model do
23
+ before(:each) do
24
+ mock_redis()
25
+ @source_entity_id = 1
26
+ @destination_entity_id = 2
27
+ ENV['OPENC3_MICROSERVICE_NAME'] = 'DEFAULT__API__CFDP'
28
+ # Create the model that is consumed by CfdpMib.setup
29
+ model = OpenC3::MicroserviceModel.new(name: ENV['OPENC3_MICROSERVICE_NAME'], scope: "DEFAULT",
30
+ options: [
31
+ ["source_entity_id", @source_entity_id],
32
+ ["destination_entity_id", @destination_entity_id],
33
+ ["root_path", SPEC_DIR],
34
+ ],
35
+ )
36
+ model.create
37
+ CfdpMib.setup
38
+ end
39
+
40
+ # Validate Table 5-9: Metadata PDU Contents
41
+ describe "build_metadata_pdu" do
42
+ it "builds a Metadata PDU with no options" do
43
+ buffer = CfdpPdu.build_metadata_pdu(
44
+ source_entity: CfdpMib.entity(@source_entity_id),
45
+ transaction_seq_num: 1,
46
+ destination_entity: CfdpMib.entity(@destination_entity_id),
47
+ file_size: 0xDEADBEEF,
48
+ segmentation_control: "NOT_PRESERVED",
49
+ transmission_mode: nil,
50
+ source_file_name: "filename",
51
+ destination_file_name: "test",
52
+ closure_requested: 1,
53
+ options: [])
54
+ # puts buffer.formatted
55
+ expect(buffer.length).to eql 29
56
+
57
+ # By default the first 7 bytes are the header
58
+ # This assumes 1 byte per entity ID and sequence number
59
+
60
+ # Directive Code
61
+ expect(buffer[7].unpack('C')[0]).to eql 7 # Metadata per Table 5-4
62
+ # Closure requested
63
+ expect(buffer[8].unpack('C')[0] >> 6).to eql 1
64
+ # Checksum type
65
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # legacy modular checksum
66
+ # File Size
67
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
68
+ # Source File Name
69
+ expect(buffer[13].unpack('C')[0]).to eql 8
70
+ expect(buffer[14..21].unpack('A*')[0]).to eql 'filename'
71
+ # Destination File Name
72
+ expect(buffer[22].unpack('C')[0]).to eql 4
73
+ expect(buffer[23..26].unpack('A*')[0]).to eql 'test'
74
+
75
+ hash = {}
76
+ # decom takes just the Metadata specific part of the buffer
77
+ # so start at offset 8 and ignore the 2 checksum bytes
78
+ CfdpPdu.decom_metadata_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
79
+ expect(hash['CLOSURE_REQUESTED']).to eql 'CLOSURE_REQUESTED'
80
+ expect(hash['CHECKSUM_TYPE']).to eql 0
81
+ expect(hash['FILE_SIZE']).to eql 0xDEADBEEF
82
+ expect(hash['SOURCE_FILE_NAME']).to eql 'filename'
83
+ expect(hash['DESTINATION_FILE_NAME']).to eql 'test'
84
+ end
85
+
86
+ it "builds a Metadata PDU with option: filestore request" do
87
+ buffer = CfdpPdu.build_metadata_pdu(
88
+ source_entity: CfdpMib.entity(@source_entity_id),
89
+ transaction_seq_num: 1,
90
+ destination_entity: CfdpMib.entity(@destination_entity_id),
91
+ file_size: 0xDEADBEEF,
92
+ segmentation_control: "NOT_PRESERVED",
93
+ transmission_mode: nil,
94
+ source_file_name: "filename",
95
+ destination_file_name: "test",
96
+ closure_requested: 0,
97
+ options: [{
98
+ "TYPE" => "FILESTORE_REQUEST",
99
+ "ACTION_CODE" => "DELETE_FILE", # 1
100
+ "FIRST_FILE_NAME" => "first",
101
+ },
102
+ {
103
+ "TYPE" => "FILESTORE_REQUEST",
104
+ "ACTION_CODE" => "RENAME_FILE", # 2
105
+ "FIRST_FILE_NAME" => "begin",
106
+ "SECOND_FILE_NAME" => "end",
107
+ }])
108
+ # puts buffer.formatted
109
+ expect(buffer.length).to eql 51
110
+
111
+ # By default the first 7 bytes are the header
112
+ # This assumes 1 byte per entity ID and sequence number
113
+
114
+ # Directive Code
115
+ expect(buffer[7].unpack('C')[0]).to eql 7 # Metadata per Table 5-4
116
+ # Closure requested
117
+ expect(buffer[8].unpack('C')[0] >> 6).to eql 0
118
+ # Checksum type
119
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # legacy modular checksum
120
+ # File Size
121
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
122
+ # Source File Name
123
+ expect(buffer[13].unpack('C')[0]).to eql 8
124
+ expect(buffer[14..21].unpack('A*')[0]).to eql 'filename'
125
+ # Destination File Name
126
+ expect(buffer[22].unpack('C')[0]).to eql 4
127
+ expect(buffer[23..26].unpack('A*')[0]).to eql 'test'
128
+ # Option TLV Type
129
+ expect(buffer[27].unpack('C')[0]).to eql 0 # 5.4.1.1 Filestore Request
130
+ # Option TLV Length
131
+ expect(buffer[28].unpack('C')[0]).to eql 7 # Action code + length field + length
132
+ # Option TLV Value
133
+ # Action Code
134
+ expect(buffer[29].unpack('C')[0] >> 4).to eql 1
135
+ # First filename length
136
+ expect(buffer[30].unpack('C')[0]).to eql 5
137
+ # First filename
138
+ expect(buffer[31..35].unpack('A*')[0]).to eql 'first'
139
+ # Option TLV Type
140
+ expect(buffer[36].unpack('C')[0]).to eql 0 # 5.4.1.1 Filestore Request
141
+ # Option TLV Length
142
+ expect(buffer[37].unpack('C')[0]).to eql 11
143
+ # Option TLV Value
144
+ # Action Code
145
+ expect(buffer[38].unpack('C')[0] >> 4).to eql 2
146
+ # First filename length
147
+ expect(buffer[39].unpack('C')[0]).to eql 5
148
+ # First filename
149
+ expect(buffer[40..44].unpack('A*')[0]).to eql 'begin'
150
+ # Second filename length
151
+ expect(buffer[45].unpack('C')[0]).to eql 3
152
+ # Second filename
153
+ expect(buffer[46..48].unpack('A*')[0]).to eql 'end'
154
+
155
+ hash = {}
156
+ # decom takes just the Metadata specific part of the buffer
157
+ # so start at offset 8 and ignore the 2 checksum bytes
158
+ CfdpPdu.decom_metadata_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
159
+ expect(hash['CLOSURE_REQUESTED']).to eql 'CLOSURE_NOT_REQUESTED'
160
+ expect(hash['CHECKSUM_TYPE']).to eql 0
161
+ expect(hash['FILE_SIZE']).to eql 0xDEADBEEF
162
+ expect(hash['SOURCE_FILE_NAME']).to eql 'filename'
163
+ expect(hash['DESTINATION_FILE_NAME']).to eql 'test'
164
+ tlv = hash['TLVS'][0]
165
+ expect(tlv['TYPE']).to eql 'FILESTORE_REQUEST'
166
+ expect(tlv['ACTION_CODE']).to eql 'DELETE_FILE'
167
+ expect(tlv['FIRST_FILE_NAME']).to eql 'first'
168
+ tlv = hash['TLVS'][1]
169
+ expect(tlv['TYPE']).to eql 'FILESTORE_REQUEST'
170
+ expect(tlv['ACTION_CODE']).to eql 'RENAME_FILE'
171
+ expect(tlv['FIRST_FILE_NAME']).to eql 'begin'
172
+ expect(tlv['SECOND_FILE_NAME']).to eql 'end'
173
+ end
174
+
175
+ it "builds a Metadata PDU with option: message to user" do
176
+ buffer = CfdpPdu.build_metadata_pdu(
177
+ source_entity: CfdpMib.entity(@source_entity_id),
178
+ transaction_seq_num: 1,
179
+ destination_entity: CfdpMib.entity(@destination_entity_id),
180
+ file_size: 0xDEADBEEF,
181
+ segmentation_control: "NOT_PRESERVED",
182
+ transmission_mode: nil,
183
+ source_file_name: "filename",
184
+ destination_file_name: "test",
185
+ closure_requested: 0,
186
+ options: [{
187
+ "TYPE" => "MESSAGE_TO_USER",
188
+ "MESSAGE_TO_USER" => "Hello"
189
+ }])
190
+ # puts buffer.formatted
191
+ expect(buffer.length).to eql 36
192
+
193
+ # By default the first 7 bytes are the header
194
+ # This assumes 1 byte per entity ID and sequence number
195
+
196
+ # Directive Code
197
+ expect(buffer[7].unpack('C')[0]).to eql 7 # Metadata per Table 5-4
198
+ # Closure requested
199
+ expect(buffer[8].unpack('C')[0] >> 6).to eql 0
200
+ # Checksum type
201
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # legacy modular checksum
202
+ # File Size
203
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
204
+ # Source File Name
205
+ expect(buffer[13].unpack('C')[0]).to eql 8
206
+ expect(buffer[14..21].unpack('A*')[0]).to eql 'filename'
207
+ # Destination File Name
208
+ expect(buffer[22].unpack('C')[0]).to eql 4
209
+ expect(buffer[23..26].unpack('A*')[0]).to eql 'test'
210
+ # Option TLV Type
211
+ expect(buffer[27].unpack('C')[0]).to eql 2 # 5.4.3 Message to User
212
+ # Option TLV Length
213
+ expect(buffer[28].unpack('C')[0]).to eql 5
214
+ # Option TLV Value
215
+ expect(buffer[29..33].unpack('A*')[0]).to eql 'Hello'
216
+
217
+ hash = {}
218
+ # decom takes just the Metadata specific part of the buffer
219
+ # so start at offset 8 and ignore the 2 checksum bytes
220
+ CfdpPdu.decom_metadata_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
221
+ expect(hash['CLOSURE_REQUESTED']).to eql 'CLOSURE_NOT_REQUESTED'
222
+ expect(hash['CHECKSUM_TYPE']).to eql 0
223
+ expect(hash['FILE_SIZE']).to eql 0xDEADBEEF
224
+ expect(hash['SOURCE_FILE_NAME']).to eql 'filename'
225
+ expect(hash['DESTINATION_FILE_NAME']).to eql 'test'
226
+ tlv = hash['TLVS'][0]
227
+ expect(tlv['TYPE']).to eql 'MESSAGE_TO_USER'
228
+ expect(tlv['MESSAGE_TO_USER']).to eql 'Hello'
229
+ end
230
+
231
+ it "builds a Metadata PDU with option: fault handler override" do
232
+ buffer = CfdpPdu.build_metadata_pdu(
233
+ source_entity: CfdpMib.entity(@source_entity_id),
234
+ transaction_seq_num: 1,
235
+ destination_entity: CfdpMib.entity(@destination_entity_id),
236
+ file_size: 0xDEADBEEF,
237
+ segmentation_control: "NOT_PRESERVED",
238
+ transmission_mode: nil,
239
+ source_file_name: "filename",
240
+ destination_file_name: "test",
241
+ closure_requested: 0,
242
+ options: [{
243
+ "TYPE" => "FAULT_HANDLER_OVERRIDE",
244
+ "CONDITION_CODE" => "INVALID_TRANSMISSION_MODE", # 3
245
+ "HANDLER_CODE" => "ABONDON_TRANSACTION", # 4
246
+ }])
247
+ # puts buffer.formatted
248
+ expect(buffer.length).to eql 32
249
+
250
+ # By default the first 7 bytes are the header
251
+ # This assumes 1 byte per entity ID and sequence number
252
+
253
+ # Directive Code
254
+ expect(buffer[7].unpack('C')[0]).to eql 7 # Metadata per Table 5-4
255
+ # Closure requested
256
+ expect(buffer[8].unpack('C')[0] >> 6).to eql 0
257
+ # Checksum type
258
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # legacy modular checksum
259
+ # File Size
260
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
261
+ # Source File Name
262
+ expect(buffer[13].unpack('C')[0]).to eql 8
263
+ expect(buffer[14..21].unpack('A*')[0]).to eql 'filename'
264
+ # Destination File Name
265
+ expect(buffer[22].unpack('C')[0]).to eql 4
266
+ expect(buffer[23..26].unpack('A*')[0]).to eql 'test'
267
+ # Option TLV Type
268
+ expect(buffer[27].unpack('C')[0]).to eql 4 # 5.4.4 Fault Handler Override
269
+ # Option TLV Length
270
+ expect(buffer[28].unpack('C')[0]).to eql 1
271
+ # Option TLV Value
272
+ expect(buffer[29].unpack('C')[0] >> 4).to eql 3
273
+ expect(buffer[29].unpack('C')[0] & 0xF).to eql 4
274
+
275
+ hash = {}
276
+ # decom takes just the Metadata specific part of the buffer
277
+ # so start at offset 8 and ignore the 2 checksum bytes
278
+ CfdpPdu.decom_metadata_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
279
+ expect(hash['CLOSURE_REQUESTED']).to eql 'CLOSURE_NOT_REQUESTED'
280
+ expect(hash['CHECKSUM_TYPE']).to eql 0
281
+ expect(hash['FILE_SIZE']).to eql 0xDEADBEEF
282
+ expect(hash['SOURCE_FILE_NAME']).to eql 'filename'
283
+ expect(hash['DESTINATION_FILE_NAME']).to eql 'test'
284
+ tlv = hash['TLVS'][0]
285
+ expect(tlv['TYPE']).to eql 'FAULT_HANDLER_OVERRIDE'
286
+ expect(tlv['CONDITION_CODE']).to eql 'INVALID_TRANSMISSION_MODE'
287
+ expect(tlv['HANDLER_CODE']).to eql 'ABONDON_TRANSACTION'
288
+ end
289
+
290
+ it "builds a Metadata PDU with option: flow label" do
291
+ buffer = CfdpPdu.build_metadata_pdu(
292
+ source_entity: CfdpMib.entity(@source_entity_id),
293
+ transaction_seq_num: 1,
294
+ destination_entity: CfdpMib.entity(@destination_entity_id),
295
+ file_size: 0xDEADBEEF,
296
+ segmentation_control: "NOT_PRESERVED",
297
+ transmission_mode: nil,
298
+ source_file_name: "filename",
299
+ destination_file_name: "test",
300
+ closure_requested: 0,
301
+ options: [{
302
+ "TYPE" => "FLOW_LABEL",
303
+ "FLOW_LABEL" => "flow"
304
+ }])
305
+ # puts buffer.formatted
306
+ expect(buffer.length).to eql 35
307
+
308
+ # By default the first 7 bytes are the header
309
+ # This assumes 1 byte per entity ID and sequence number
310
+
311
+ # Directive Code
312
+ expect(buffer[7].unpack('C')[0]).to eql 7 # Metadata per Table 5-4
313
+ # Closure requested
314
+ expect(buffer[8].unpack('C')[0] >> 6).to eql 0
315
+ # Checksum type
316
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # legacy modular checksum
317
+ # File Size
318
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
319
+ # Source File Name
320
+ expect(buffer[13].unpack('C')[0]).to eql 8
321
+ expect(buffer[14..21].unpack('A*')[0]).to eql 'filename'
322
+ # Destination File Name
323
+ expect(buffer[22].unpack('C')[0]).to eql 4
324
+ expect(buffer[23..26].unpack('A*')[0]).to eql 'test'
325
+ # Option TLV Type
326
+ expect(buffer[27].unpack('C')[0]).to eql 5 # 5.4.5 Flow Label
327
+ # Option TLV Length
328
+ expect(buffer[28].unpack('C')[0]).to eql 4
329
+ # Option TLV Value
330
+ expect(buffer[29..32].unpack('A*')[0]).to eql 'flow'
331
+
332
+ hash = {}
333
+ # decom takes just the Metadata specific part of the buffer
334
+ # so start at offset 8 and ignore the 2 checksum bytes
335
+ CfdpPdu.decom_metadata_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
336
+ expect(hash['CLOSURE_REQUESTED']).to eql 'CLOSURE_NOT_REQUESTED'
337
+ expect(hash['CHECKSUM_TYPE']).to eql 0
338
+ expect(hash['FILE_SIZE']).to eql 0xDEADBEEF
339
+ expect(hash['SOURCE_FILE_NAME']).to eql 'filename'
340
+ expect(hash['DESTINATION_FILE_NAME']).to eql 'test'
341
+ tlv = hash['TLVS'][0]
342
+ expect(tlv['TYPE']).to eql 'FLOW_LABEL'
343
+ expect(tlv['FLOW_LABEL']).to eql 'flow'
344
+ end
345
+ end
346
+ end
@@ -0,0 +1,126 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2023 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # Licensed for Evaluation and Educational Use
7
+ #
8
+ # This file may only be used commercially under the terms of a commercial license
9
+ # purchased from OpenC3, Inc.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+ #
15
+ # The development of this software was funded in-whole or in-part by MethaneSAT LLC.
16
+
17
+ require 'rails_helper'
18
+ require 'cfdp_pdu'
19
+ require 'openc3/models/microservice_model'
20
+ require 'openc3/utilities/store_autoload'
21
+
22
+ RSpec.describe CfdpPdu, type: :model do
23
+ before(:each) do
24
+ mock_redis()
25
+ @source_entity_id = 1
26
+ @destination_entity_id = 2
27
+ ENV['OPENC3_MICROSERVICE_NAME'] = 'DEFAULT__API__CFDP'
28
+ # Create the model that is consumed by CfdpMib.setup
29
+ model = OpenC3::MicroserviceModel.new(name: ENV['OPENC3_MICROSERVICE_NAME'], scope: "DEFAULT",
30
+ options: [
31
+ ["source_entity_id", @source_entity_id],
32
+ ["destination_entity_id", @destination_entity_id],
33
+ ["root_path", SPEC_DIR],
34
+ ],
35
+ )
36
+ model.create
37
+ CfdpMib.setup
38
+ end
39
+
40
+ # Validate Table 5-10: NAK PDU Contents
41
+ describe "build_nak_pdu" do
42
+ it "builds a NAK PDU with no segement requests" do
43
+ buffer = CfdpPdu.build_nak_pdu(
44
+ source_entity: CfdpMib.entity(@source_entity_id),
45
+ transaction_seq_num: 1,
46
+ destination_entity: CfdpMib.entity(@destination_entity_id),
47
+ file_size: 0xDEADBEEF,
48
+ segmentation_control: "NOT_PRESERVED",
49
+ transmission_mode: nil,
50
+ start_of_scope: 0x12345678,
51
+ end_of_scope: 0xABCDEFFF,
52
+ segment_requests: [])
53
+ # puts buffer.formatted
54
+ expect(buffer.length).to eql 18
55
+
56
+ # By default the first 7 bytes are the header
57
+ # This assumes 1 byte per entity ID and sequence number
58
+
59
+ # Directive Code
60
+ expect(buffer[7].unpack('C')[0]).to eql 8 # NAK per Table 5-4
61
+ # Start of scope
62
+ expect(buffer[8..11].unpack('N')[0]).to eql 0x12345678
63
+ # End of scope
64
+ expect(buffer[12..15].unpack('N')[0]).to eql 0xABCDEFFF
65
+
66
+ hash = {}
67
+ # decom takes just the NAK specific part of the buffer
68
+ # so start at offset 8 and ignore the 2 checksum bytes
69
+ CfdpPdu.decom_nak_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
70
+ expect(hash['START_OF_SCOPE']).to eql 0x12345678
71
+ expect(hash['END_OF_SCOPE']).to eql 0xABCDEFFF
72
+ expect(hash['SEGMENT_REQUESTS']).to eql []
73
+ end
74
+
75
+ # Validate Table 5-11: Segment Request Form
76
+ it "builds a NAK PDU with segement requests" do
77
+ buffer = CfdpPdu.build_nak_pdu(
78
+ source_entity: CfdpMib.entity(@source_entity_id),
79
+ transaction_seq_num: 1,
80
+ destination_entity: CfdpMib.entity(@destination_entity_id),
81
+ file_size: 0xDEADBEEF,
82
+ segmentation_control: "NOT_PRESERVED",
83
+ transmission_mode: nil,
84
+ start_of_scope: 0,
85
+ end_of_scope: 0x1F,
86
+ segment_requests: [
87
+ [0, 0xF],
88
+ [0x10, 0x1F],
89
+ ])
90
+ # puts buffer.formatted
91
+ expect(buffer.length).to eql 34
92
+
93
+ # By default the first 7 bytes are the header
94
+ # This assumes 1 byte per entity ID and sequence number
95
+
96
+ # Directive Code
97
+ expect(buffer[7].unpack('C')[0]).to eql 8 # NAK per Table 5-4
98
+ # Start of scope
99
+ expect(buffer[8..11].unpack('N')[0]).to eql 0
100
+ # End of scope
101
+ expect(buffer[12..15].unpack('N')[0]).to eql 0x1F
102
+ # Segment requests
103
+ expect(buffer[16..19].unpack('N')[0]).to eql 0
104
+ expect(buffer[20..23].unpack('N')[0]).to eql 0xF
105
+ expect(buffer[24..27].unpack('N')[0]).to eql 0x10
106
+ expect(buffer[28..31].unpack('N')[0]).to eql 0x1F
107
+
108
+ hash = {}
109
+ # decom takes just the NAK specific part of the buffer
110
+ # so start at offset 8 and ignore the 2 checksum bytes
111
+ CfdpPdu.decom_nak_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
112
+ expect(hash['START_OF_SCOPE']).to eql 0
113
+ expect(hash['END_OF_SCOPE']).to eql 0x1F
114
+ expect(hash['SEGMENT_REQUESTS']).to eql [
115
+ {
116
+ "START_OFFSET" => 0,
117
+ "END_OFFSET" => 0xF,
118
+ },
119
+ {
120
+ "START_OFFSET" => 0x10,
121
+ "END_OFFSET" => 0x1F,
122
+ }
123
+ ]
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2023 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # Licensed for Evaluation and Educational Use
7
+ #
8
+ # This file may only be used commercially under the terms of a commercial license
9
+ # purchased from OpenC3, Inc.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+ #
15
+ # The development of this software was funded in-whole or in-part by MethaneSAT LLC.
16
+
17
+ require 'rails_helper'
18
+ require 'cfdp_pdu'
19
+ require 'openc3/models/microservice_model'
20
+ require 'openc3/utilities/store_autoload'
21
+
22
+ RSpec.describe CfdpPdu, type: :model do
23
+ before(:each) do
24
+ mock_redis()
25
+ @source_entity_id = 1
26
+ @destination_entity_id = 2
27
+ ENV['OPENC3_MICROSERVICE_NAME'] = 'DEFAULT__API__CFDP'
28
+ # Create the model that is consumed by CfdpMib.setup
29
+ model = OpenC3::MicroserviceModel.new(name: ENV['OPENC3_MICROSERVICE_NAME'], scope: "DEFAULT",
30
+ options: [
31
+ ["source_entity_id", @source_entity_id],
32
+ ["destination_entity_id", @destination_entity_id],
33
+ ["root_path", SPEC_DIR],
34
+ ],
35
+ )
36
+ model.create
37
+ CfdpMib.setup
38
+ end
39
+
40
+ # Validate Table 5-12: Prompt PDU Contents
41
+ describe "build_prompt_pdu" do
42
+ it "builds a Prompt PDU with nak response" do
43
+ buffer = CfdpPdu.build_prompt_pdu(
44
+ source_entity: CfdpMib.entity(@source_entity_id),
45
+ transaction_seq_num: 1,
46
+ destination_entity: CfdpMib.entity(@destination_entity_id),
47
+ segmentation_control: "NOT_PRESERVED",
48
+ transmission_mode: nil,
49
+ response_required: 'NAK')
50
+ # puts buffer.formatted
51
+ expect(buffer.length).to eql 11
52
+
53
+ # By default the first 7 bytes are the header
54
+ # This assumes 1 byte per entity ID and sequence number
55
+
56
+ # Directive Code
57
+ expect(buffer[7].unpack('C')[0]).to eql 9 # Prompt per Table 5-4
58
+ # Response required
59
+ expect(buffer[8].unpack('C')[0] >> 7).to eql 0
60
+
61
+ hash = {}
62
+ # decom takes just the Prompt specific part of the buffer
63
+ # so start at offset 8 and ignore the 2 checksum bytes
64
+ CfdpPdu.decom_prompt_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
65
+ expect(hash['RESPONSE_REQUIRED']).to eql 'NAK'
66
+ end
67
+
68
+ it "builds a Prompt PDU with keep alive response" do
69
+ buffer = CfdpPdu.build_prompt_pdu(
70
+ source_entity: CfdpMib.entity(@source_entity_id),
71
+ transaction_seq_num: 1,
72
+ destination_entity: CfdpMib.entity(@destination_entity_id),
73
+ segmentation_control: "NOT_PRESERVED",
74
+ transmission_mode: nil,
75
+ response_required: 'KEEP_ALIVE')
76
+ # puts buffer.formatted
77
+ expect(buffer.length).to eql 11
78
+
79
+ # By default the first 7 bytes are the header
80
+ # This assumes 1 byte per entity ID and sequence number
81
+
82
+ # Directive Code
83
+ expect(buffer[7].unpack('C')[0]).to eql 9 # Prompt per Table 5-4
84
+ # Response required
85
+ expect(buffer[8].unpack('C')[0] >> 7).to eql 1
86
+
87
+ hash = {}
88
+ # decom takes just the Prompt specific part of the buffer
89
+ # so start at offset 8 and ignore the 2 checksum bytes
90
+ CfdpPdu.decom_prompt_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
91
+ expect(hash['RESPONSE_REQUIRED']).to eql 'KEEP_ALIVE'
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,111 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2023 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # Licensed for Evaluation and Educational Use
7
+ #
8
+ # This file may only be used commercially under the terms of a commercial license
9
+ # purchased from OpenC3, Inc.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+ #
15
+ # The development of this software was funded in-whole or in-part by MethaneSAT LLC.
16
+
17
+ require 'rails_helper'
18
+
19
+ RSpec.describe CfdpPdu, type: :model do
20
+ # Validate Table 5-1: Fixed PDU Header Fields
21
+ describe "initialize" do
22
+ it "builds a PDU with crcs" do
23
+ pdu = CfdpPdu.new(crcs_required: true)
24
+ expect(pdu.items.keys).to include("CRC")
25
+ expect(pdu.buffer.length).to eql 6
26
+ end
27
+
28
+ it "builds a PDU without crcs" do
29
+ pdu = CfdpPdu.new(crcs_required: false)
30
+ expect(pdu.items.keys).to_not include("CRC")
31
+ expect(pdu.buffer.length).to eql 4
32
+ end
33
+
34
+ it "sets the version field" do
35
+ pdu = CfdpPdu.new(crcs_required: false)
36
+ pdu.enable_method_missing
37
+ pdu.version = 1
38
+ expect(pdu.buffer[0].unpack('C')[0] >> 5).to eql 1
39
+ end
40
+
41
+ it "sets the PDU type" do
42
+ pdu = CfdpPdu.new(crcs_required: false)
43
+ pdu.enable_method_missing
44
+ pdu.type = 1
45
+ expect(pdu.buffer[0].unpack('C')[0] >> 4).to eql 1
46
+ end
47
+
48
+ it "sets the direction" do
49
+ pdu = CfdpPdu.new(crcs_required: false)
50
+ pdu.enable_method_missing
51
+ pdu.direction = 1
52
+ expect(pdu.buffer[0].unpack('C')[0] >> 3).to eql 1
53
+ end
54
+
55
+ it "sets the transmission mode" do
56
+ pdu = CfdpPdu.new(crcs_required: false)
57
+ pdu.enable_method_missing
58
+ pdu.transmission_mode = 1
59
+ expect(pdu.buffer[0].unpack('C')[0] >> 2).to eql 1
60
+ end
61
+
62
+ it "sets the crc flag" do
63
+ pdu = CfdpPdu.new(crcs_required: false)
64
+ pdu.enable_method_missing
65
+ pdu.crc_flag = 1
66
+ expect(pdu.buffer[0].unpack('C')[0] >> 1).to eql 1
67
+ end
68
+
69
+ it "sets the large file flag" do
70
+ pdu = CfdpPdu.new(crcs_required: false)
71
+ pdu.enable_method_missing
72
+ pdu.large_file_flag = 1
73
+ expect(pdu.buffer[0].unpack('C')[0]).to eql 1
74
+ end
75
+
76
+ it "sets the length" do
77
+ pdu = CfdpPdu.new(crcs_required: false)
78
+ pdu.enable_method_missing
79
+ pdu.pdu_data_length = 0x1234
80
+ expect(pdu.buffer[1..2].unpack('n')[0]).to eql 0x1234
81
+ end
82
+
83
+ it "sets the segmentation control" do
84
+ pdu = CfdpPdu.new(crcs_required: false)
85
+ pdu.enable_method_missing
86
+ pdu.segmentation_control = 1
87
+ expect(pdu.buffer[3].unpack('C')[0] >> 7).to eql 1
88
+ end
89
+
90
+ it "sets the entity id length" do
91
+ pdu = CfdpPdu.new(crcs_required: false)
92
+ pdu.enable_method_missing
93
+ pdu.entity_id_length = 7
94
+ expect(pdu.buffer[3].unpack('C')[0] >> 4).to eql 7
95
+ end
96
+
97
+ it "sets the segment metadata flag" do
98
+ pdu = CfdpPdu.new(crcs_required: false)
99
+ pdu.enable_method_missing
100
+ pdu.segment_metadata_flag = 1
101
+ expect(pdu.buffer[3].unpack('C')[0] >> 3).to eql 1
102
+ end
103
+
104
+ it "sets the sequence number length" do
105
+ pdu = CfdpPdu.new(crcs_required: false)
106
+ pdu.enable_method_missing
107
+ pdu.sequence_number_length = 7
108
+ expect(pdu.buffer[3].unpack('C')[0]).to eql 7
109
+ end
110
+ end
111
+ end