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 @@
1
+ # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
@@ -0,0 +1,114 @@
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-8: ACK PDU Contents
41
+ describe "build_ack_pdu" do
42
+ it "builds a ACK PDU with Finished" do
43
+ buffer = CfdpPdu.build_ack_pdu(
44
+ source_entity: CfdpMib.entity(@source_entity_id),
45
+ transaction_seq_num: 1,
46
+ destination_entity: CfdpMib.entity(@destination_entity_id),
47
+ condition_code: "NO_ERROR", # 0
48
+ segmentation_control: "NOT_PRESERVED",
49
+ ack_directive_code: "FINISHED", # 5
50
+ transaction_status: "TERMINATED") # 2
51
+ # puts buffer.formatted
52
+ expect(buffer.length).to eql 12
53
+
54
+ # By default the first 7 bytes are the header
55
+ # This assumes 1 byte per entity ID and sequence number
56
+
57
+ # Directive Code
58
+ expect(buffer[7].unpack('C')[0]).to eql 6 # ACK per Table 5-4
59
+ # Directive Code of ACK PDU
60
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 5 # Finished
61
+ # Directive Subtype Code
62
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 1
63
+ # Condition Code
64
+ expect(buffer[9].unpack('C')[0] >> 4).to eql 0
65
+ # Transaction Status
66
+ expect(buffer[9].unpack('C')[0] & 0x3).to eql 2
67
+
68
+ hash = {}
69
+ # decom takes just the ACK specific part of the buffer
70
+ # so start at offset 8 and ignore the 2 checksum bytes
71
+ CfdpPdu.decom_ack_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
72
+ expect(hash['ACK_DIRECTIVE_CODE']).to eql 'FINISHED'
73
+ expect(hash['ACK_DIRECTIVE_SUBTYPE']).to eql 1
74
+ expect(hash['CONDITION_CODE']).to eql 'NO_ERROR'
75
+ expect(hash['TRANSACTION_STATUS']).to eql 'TERMINATED'
76
+ end
77
+
78
+ it "builds a ACK PDU with EOF" do
79
+ buffer = CfdpPdu.build_ack_pdu(
80
+ source_entity: CfdpMib.entity(@source_entity_id),
81
+ transaction_seq_num: 1,
82
+ destination_entity: CfdpMib.entity(@destination_entity_id),
83
+ condition_code: "ACK_LIMIT_REACHED", # 1
84
+ segmentation_control: "NOT_PRESERVED",
85
+ ack_directive_code: "EOF", # 4
86
+ transaction_status: "UNRECOGNIZED") # 3
87
+ # puts buffer.formatted
88
+ expect(buffer.length).to eql 12
89
+
90
+ # By default the first 7 bytes are the header
91
+ # This assumes 1 byte per entity ID and sequence number
92
+
93
+ # Directive Code
94
+ expect(buffer[7].unpack('C')[0]).to eql 6 # ACK per Table 5-4
95
+ # Directive Code
96
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 4
97
+ # Directive Subtype Code
98
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0
99
+ # Condition Code
100
+ expect(buffer[9].unpack('C')[0] >> 4).to eql 1
101
+ # Transaction Status
102
+ expect(buffer[9].unpack('C')[0] & 0x3).to eql 3
103
+
104
+ hash = {}
105
+ # decom takes just the ACK specific part of the buffer
106
+ # so start at offset 8 and ignore the 2 checksum bytes
107
+ CfdpPdu.decom_ack_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
108
+ expect(hash['ACK_DIRECTIVE_CODE']).to eql 'EOF'
109
+ expect(hash['ACK_DIRECTIVE_SUBTYPE']).to eql 0
110
+ expect(hash['CONDITION_CODE']).to eql 'ACK_LIMIT_REACHED'
111
+ expect(hash['TRANSACTION_STATUS']).to eql 'UNRECOGNIZED'
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,159 @@
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-6: End-of-File PDU Contents
41
+ describe "build_eof_pdu" do
42
+ it "builds a EOF PDU with no error" do
43
+ buffer = CfdpPdu.build_eof_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: 0xBA5EBA11,
48
+ file_checksum: 0xDEADBEEF,
49
+ condition_code: "NO_ERROR", # 0
50
+ segmentation_control: "NOT_PRESERVED",
51
+ transmission_mode: nil,
52
+ canceling_entity_id: nil)
53
+ # puts buffer.formatted
54
+ expect(buffer.length).to eql 19
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 4 # EOF per Table 5-4
61
+ # Condition Code
62
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 0
63
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # Spare
64
+ # File Checksum
65
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
66
+ # File Size
67
+ expect(buffer[13..16].unpack('N')[0]).to eql 0xBA5EBA11
68
+
69
+ hash = {}
70
+ # decom takes just the EOF specific part of the buffer
71
+ # so start at offset 8 and ignore the 2 checksum bytes
72
+ CfdpPdu.decom_eof_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
73
+ expect(hash['CONDITION_CODE']).to eql "NO_ERROR"
74
+ expect(hash['FILE_CHECKSUM']).to eql 0xDEADBEEF
75
+ expect(hash['FILE_SIZE']).to eql 0xBA5EBA11
76
+ end
77
+
78
+ it "builds a EOF PDU with large file size" do
79
+ buffer = CfdpPdu.build_eof_pdu(
80
+ source_entity: CfdpMib.entity(@source_entity_id),
81
+ transaction_seq_num: 1,
82
+ destination_entity: CfdpMib.entity(@destination_entity_id),
83
+ file_size: 0x100000000,
84
+ file_checksum: 0xDEADBEEF,
85
+ condition_code: "NO_ERROR", # 0
86
+ segmentation_control: "NOT_PRESERVED",
87
+ transmission_mode: nil,
88
+ canceling_entity_id: nil)
89
+ # puts buffer.formatted
90
+ expect(buffer.length).to eql 23
91
+
92
+ # By default the first 7 bytes are the header
93
+ # This assumes 1 byte per entity ID and sequence number
94
+
95
+ # Directive Code
96
+ expect(buffer[7].unpack('C')[0]).to eql 4 # EOF per Table 5-4
97
+ # Condition Code
98
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 0
99
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # Spare
100
+ # File Checksum
101
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
102
+ # File Size
103
+ expect(buffer[13..16].unpack('N')[0]).to eql 1
104
+
105
+ hash = {}
106
+ # decom takes just the EOF specific part of the buffer
107
+ # so start at offset 8 and ignore the 2 checksum bytes
108
+ pdu = CfdpPdu.new(crcs_required: false)
109
+ # Set the LARGE_FILE flag so we properly decom
110
+ pdu.write("LARGE_FILE_FLAG", "LARGE_FILE")
111
+ CfdpPdu.decom_eof_pdu_contents(pdu, hash, buffer[8..-3])
112
+ expect(hash['CONDITION_CODE']).to eql "NO_ERROR"
113
+ expect(hash['FILE_CHECKSUM']).to eql 0xDEADBEEF
114
+ expect(hash['FILE_SIZE']).to eql 0x100000000
115
+ end
116
+
117
+ it "builds a EOF PDU with cancellation status" do
118
+ buffer = CfdpPdu.build_eof_pdu(
119
+ source_entity: CfdpMib.entity(@source_entity_id),
120
+ transaction_seq_num: 1,
121
+ destination_entity: CfdpMib.entity(@destination_entity_id),
122
+ file_size: 0xBA5EBA11,
123
+ file_checksum: 0xDEADBEEF,
124
+ condition_code: "CANCEL_REQUEST_RECEIVED", # 15
125
+ segmentation_control: "NOT_PRESERVED",
126
+ transmission_mode: nil,
127
+ canceling_entity_id: 0x5)
128
+ # puts buffer.formatted
129
+ expect(buffer.length).to eql 22
130
+
131
+ # By default the first 7 bytes are the header
132
+ # This assumes 1 byte per entity ID and sequence number
133
+
134
+ # Directive Code
135
+ expect(buffer[7].unpack('C')[0]).to eql 4 # EOF per Table 5-4
136
+ # Condition Code
137
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 15
138
+ expect(buffer[8].unpack('C')[0] & 0xF).to eql 0 # Spare
139
+ # File Checksum
140
+ expect(buffer[9..12].unpack('N')[0]).to eql 0xDEADBEEF
141
+ # File Size
142
+ expect(buffer[13..16].unpack('N')[0]).to eql 0xBA5EBA11
143
+ # Fault Location
144
+ expect(buffer[17].unpack('C')[0]).to eql 0x06
145
+ expect(buffer[18].unpack('C')[0]).to eql 1
146
+ expect(buffer[19].unpack('C')[0]).to eql 0x5
147
+
148
+ hash = {}
149
+ # decom takes just the EOF specific part of the buffer
150
+ # so start at offset 8 and ignore the 2 checksum bytes
151
+ CfdpPdu.decom_eof_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
152
+ expect(hash['CONDITION_CODE']).to eql 'CANCEL_REQUEST_RECEIVED'
153
+ expect(hash['FILE_CHECKSUM']).to eql 0xDEADBEEF
154
+ expect(hash['FILE_SIZE']).to eql 0xBA5EBA11
155
+ expect(hash['TLVS'][0]['TYPE']).to eql 'ENTITY_ID'
156
+ expect(hash['TLVS'][0]['ENTITY_ID']).to eql 5
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,76 @@
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-14: File Data PDU Contents
41
+ describe "build_file_data_pdu" do
42
+ it "builds a Prompt PDU with nak response" do
43
+ buffer = CfdpPdu.build_file_data_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: 0,
48
+ segmentation_control: "NOT_PRESERVED",
49
+ transmission_mode: nil,
50
+ offset: 0xDEADBEEF,
51
+ file_data: [0xAA, 0x55].pack("C*"), # raw file data
52
+ record_continuation_state: 'START_AND_END',
53
+ segment_metadata: nil)
54
+ # puts buffer.formatted
55
+ expect(buffer.length).to eql 15
56
+
57
+ # By default the first 7 bytes are the header
58
+ # This assumes 1 byte per entity ID and sequence number
59
+
60
+ # PDU Type
61
+ expect((buffer[0].unpack('C')[0] >> 4) & 0x1).to eql 1 # File Data per Table 5-1
62
+ # Offset
63
+ expect(buffer[7..10].unpack('N')[0]).to eql 0xDEADBEEF
64
+ expect(buffer[11].unpack('C')[0]).to eql 0xAA
65
+ expect(buffer[12].unpack('C')[0]).to eql 0x55
66
+
67
+ hash = {}
68
+ # decom takes just the Prompt specific part of the buffer
69
+ # so start at offset 8 and ignore the 2 checksum bytes
70
+ CfdpPdu.decom_file_data_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[7..-3])
71
+ expect(hash['OFFSET']).to eql 0xDEADBEEF
72
+ # TODO: FILE_DATA doesn't round trip
73
+ # expect(hash['FILE_DATA']).to eql [0xAA, 0x55].pack("C*")
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,192 @@
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-7: Finished PDU Contents
41
+ describe "build_finished_pdu" do
42
+ it "builds a Finished PDU with no responses" do
43
+ buffer = CfdpPdu.build_finished_pdu(
44
+ source_entity: CfdpMib.entity(@source_entity_id),
45
+ transaction_seq_num: 1,
46
+ destination_entity: CfdpMib.entity(@destination_entity_id),
47
+ condition_code: "NO_ERROR", # 0
48
+ segmentation_control: "NOT_PRESERVED",
49
+ transmission_mode: nil,
50
+ delivery_code: "DATA_INCOMPLETE",
51
+ file_status: "UNREPORTED",
52
+ filestore_responses: [],
53
+ fault_location_entity_id: nil)
54
+ # puts buffer.formatted
55
+ expect(buffer.length).to eql 11
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 5 # Finished per Table 5-4
62
+ # Condition Code
63
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 0
64
+ # Delivery Code
65
+ expect((buffer[8].unpack('C')[0] & 0x4) >> 2).to eql 1
66
+ # File Status
67
+ expect(buffer[8].unpack('C')[0] & 0x3).to eql 3
68
+
69
+ hash = {}
70
+ # decom takes just the EOF specific part of the buffer
71
+ # so start at offset 8 and ignore the 2 checksum bytes
72
+ CfdpPdu.decom_finished_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
73
+ expect(hash['CONDITION_CODE']).to eql 'NO_ERROR'
74
+ expect(hash['DELIVERY_CODE']).to eql 'DATA_INCOMPLETE'
75
+ expect(hash['FILE_STATUS']).to eql 'UNREPORTED'
76
+ end
77
+
78
+ it "builds a Finished PDU with a filestore response" do
79
+ buffer = CfdpPdu.build_finished_pdu(
80
+ source_entity: CfdpMib.entity(@source_entity_id),
81
+ transaction_seq_num: 1,
82
+ destination_entity: CfdpMib.entity(@destination_entity_id),
83
+ condition_code: "NO_ERROR", # 0
84
+ segmentation_control: "NOT_PRESERVED",
85
+ transmission_mode: nil,
86
+ delivery_code: "DATA_COMPLETE",
87
+ file_status: "FILESTORE_SUCCESS",
88
+ filestore_responses: [{ # Table 5-17, Table 5-18
89
+ 'ACTION_CODE' => 'DELETE_FILE', # 1
90
+ 'STATUS_CODE' => 'NOT_PERFORMED', # 0xF
91
+ 'FIRST_FILE_NAME' => 'filename', # Length + value
92
+ 'SECOND_FILE_NAME' => 'test', # Length + value
93
+ 'FILESTORE_MESSAGE' => 'Message', # Length + value
94
+ }],
95
+ fault_location_entity_id: nil)
96
+ # puts buffer.formatted
97
+ expect(buffer.length).to eql 36
98
+
99
+ # By default the first 7 bytes are the header
100
+ # This assumes 1 byte per entity ID and sequence number
101
+
102
+ # Directive Code
103
+ expect(buffer[7].unpack('C')[0]).to eql 5 # Finished per Table 5-4
104
+ # Condition Code
105
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 0
106
+ # Delivery Code
107
+ expect((buffer[8].unpack('C')[0] & 0x4) >> 2).to eql 0
108
+ # File Status
109
+ expect(buffer[8].unpack('C')[0] & 0x3).to eql 2
110
+ # Filestore Response TLV Type (5.4.2.1)
111
+ expect(buffer[9].unpack('C')[0]).to eql 1
112
+ # Filestore Response TLV Length
113
+ expect(buffer[10].unpack('C')[0]).to eql 23
114
+ # Table 5-17: Filestore Response TLV Contents
115
+ # Filestore Response Action Code
116
+ expect(buffer[11].unpack('C')[0] >> 4).to eql 1
117
+ # Filestore Response Status Code
118
+ expect(buffer[11].unpack('C')[0] & 0xF).to eql 0xF
119
+ # Filestore Response First File Name
120
+ expect(buffer[12].unpack('C')[0]).to eql 8
121
+ expect(buffer[13..20].unpack('A*')[0]).to eql 'filename'
122
+ # Filestore Response Second File Name
123
+ expect(buffer[21].unpack('C')[0]).to eql 4
124
+ expect(buffer[22..25].unpack('A*')[0]).to eql 'test'
125
+ # Filestore Response Message
126
+ expect(buffer[26].unpack('C')[0]).to eql 7
127
+ expect(buffer[27..33].unpack('A*')[0]).to eql 'Message'
128
+
129
+ hash = {}
130
+ # decom takes just the EOF specific part of the buffer
131
+ # so start at offset 8 and ignore the 2 checksum bytes
132
+ CfdpPdu.decom_finished_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
133
+ expect(hash['CONDITION_CODE']).to eql 'NO_ERROR'
134
+ expect(hash['DELIVERY_CODE']).to eql 'DATA_COMPLETE'
135
+ expect(hash['FILE_STATUS']).to eql 'FILESTORE_SUCCESS'
136
+ tlv = hash['TLVS'][0]
137
+ expect(tlv['TYPE']).to eql 'FILESTORE_RESPONSE'
138
+ expect(tlv['ACTION_CODE']).to eql 'DELETE_FILE'
139
+ expect(tlv['STATUS_CODE']).to eql 'NOT_PERFORMED'
140
+ expect(tlv['FIRST_FILE_NAME']).to eql 'filename'
141
+ expect(tlv['SECOND_FILE_NAME']).to eql 'test'
142
+ expect(tlv['FILESTORE_MESSAGE']).to eql 'Message'
143
+ end
144
+
145
+ it "builds a Finished PDU with a fault location" do
146
+ buffer = CfdpPdu.build_finished_pdu(
147
+ source_entity: CfdpMib.entity(@source_entity_id),
148
+ transaction_seq_num: 1,
149
+ destination_entity: CfdpMib.entity(@destination_entity_id),
150
+ condition_code: "INACTIVITY_DETECTED", # 8
151
+ segmentation_control: "NOT_PRESERVED",
152
+ transmission_mode: nil,
153
+ delivery_code: "DATA_INCOMPLETE",
154
+ file_status: "FILESTORE_REJECTION",
155
+ filestore_responses: [],
156
+ fault_location_entity_id: 1)
157
+ # puts buffer.formatted
158
+ expect(buffer.length).to eql 14
159
+
160
+ # By default the first 7 bytes are the header
161
+ # This assumes 1 byte per entity ID and sequence number
162
+
163
+ # Directive Code
164
+ expect(buffer[7].unpack('C')[0]).to eql 5 # Finished per Table 5-4
165
+ # Condition Code
166
+ expect(buffer[8].unpack('C')[0] >> 4).to eql 8
167
+ # Delivery Code
168
+ expect((buffer[8].unpack('C')[0] & 0x4) >> 2).to eql 1
169
+ # File Status
170
+ expect(buffer[8].unpack('C')[0] & 0x3).to eql 1
171
+ # File Status
172
+ expect(buffer[8].unpack('C')[0] & 0x3).to eql 1
173
+ # TLV Entity Type (5.4.6)
174
+ expect(buffer[9].unpack('C')[0]).to eql 6
175
+ # TLV Length
176
+ expect(buffer[10].unpack('C')[0]).to eql 1
177
+ # Entity
178
+ expect(buffer[11].unpack('C')[0]).to eql 1
179
+
180
+ hash = {}
181
+ # decom takes just the EOF specific part of the buffer
182
+ # so start at offset 8 and ignore the 2 checksum bytes
183
+ CfdpPdu.decom_finished_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
184
+ expect(hash['CONDITION_CODE']).to eql 'INACTIVITY_DETECTED'
185
+ expect(hash['DELIVERY_CODE']).to eql 'DATA_INCOMPLETE'
186
+ expect(hash['FILE_STATUS']).to eql 'FILESTORE_REJECTION'
187
+ tlv = hash['TLVS'][0]
188
+ expect(tlv['TYPE']).to eql 'ENTITY_ID'
189
+ expect(tlv['ENTITY_ID']).to eql 1
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,69 @@
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-13: Keep Alive PDU Contents
41
+ describe "build_prompt_pdu" do
42
+ it "builds a Prompt PDU with nak response" do
43
+ buffer = CfdpPdu.build_keep_alive_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: 0,
48
+ segmentation_control: "NOT_PRESERVED",
49
+ transmission_mode: nil,
50
+ progress: 0xDEADBEEF)
51
+ # puts buffer.formatted
52
+ expect(buffer.length).to eql 14
53
+
54
+ # By default the first 7 bytes are the header
55
+ # This assumes 1 byte per entity ID and sequence number
56
+
57
+ # Directive Code
58
+ expect(buffer[7].unpack('C')[0]).to eql 0x0C # Keep Alive per Table 5-4
59
+ # Progress
60
+ expect(buffer[8..11].unpack('N')[0]).to eql 0xDEADBEEF
61
+
62
+ hash = {}
63
+ # decom takes just the Prompt specific part of the buffer
64
+ # so start at offset 8 and ignore the 2 checksum bytes
65
+ CfdpPdu.decom_keep_alive_pdu_contents(CfdpPdu.new(crcs_required: false), hash, buffer[8..-3])
66
+ expect(hash['PROGRESS']).to eql 0xDEADBEEF
67
+ end
68
+ end
69
+ end