openc3-cosmos-cfdp 1.0.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/README.md +14 -12
- data/microservices/CFDP/Gemfile +3 -3
- data/microservices/CFDP/app/controllers/cfdp_controller.rb +23 -22
- data/microservices/CFDP/app/models/cfdp_mib.rb +31 -6
- data/microservices/CFDP/app/models/cfdp_pdu.rb +3 -0
- data/microservices/CFDP/app/models/cfdp_receive_transaction.rb +8 -7
- data/microservices/CFDP/app/models/cfdp_source_transaction.rb +8 -7
- data/microservices/CFDP/app/models/cfdp_transaction.rb +16 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_ack.rb +3 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_eof.rb +3 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_file_data.rb +2 -1
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_finished.rb +3 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_keep_alive.rb +3 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_metadata.rb +3 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_nak.rb +3 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_prompt.rb +3 -2
- data/microservices/CFDP/spec/models/cfdp_pdu_ack_spec.rb +1 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_eof_spec.rb +1 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_file_data_spec.rb +1 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_finished_spec.rb +1 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_keep_alive_spec.rb +1 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_metadata_spec.rb +1 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_nak_spec.rb +1 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_prompt_spec.rb +1 -0
- data/microservices/CFDP/spec/requests/cfdp_spec.rb +2 -2
- data/targets/CFDPTEST/procedures/cfdp_test_suite.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a1ac89a99cdc040f8e53d176140dacbfd149764c97c16622d8d08dc1523c4b2
|
4
|
+
data.tar.gz: e4fc3910126cf90dc8c046f76949e2f88f3b2f3b2f0256be283176fa55979159
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9279b10d783a7752adff1dbaf10e58b435cd1361f8a67e9c79b23682a8e54110ee4e32a055ae7af08b0d4d5ce1431a8321fdd28ffa4fb568db9ee1815945d07e
|
7
|
+
data.tar.gz: c4082c2b4a770300579aba7bcd4f88b9114a88d7bd4ab291f3dcfb2aaafe0812a3171d3c3bfca33e61a860be11aa4a326b8512df7301c0d91137497e2ca773c3
|
data/README.md
CHANGED
@@ -109,18 +109,19 @@ Minimum required settings:
|
|
109
109
|
|
110
110
|
### Source Entity Configuration
|
111
111
|
|
112
|
-
| Setting Name | Description | Allowed Values
|
113
|
-
| ------------------------------- | ------------------------------------------------------------------------------------------------- |
|
114
|
-
| root_path | The path to send/receive files from | Valid directory
|
115
|
-
| bucket | The bucket to send/receive files from | Valid bucket Name
|
116
|
-
| source_entity_id | The entity id for this CFDP microservice | Any integer
|
117
|
-
| tlm_info | A target_name, packet_name, and item_name to receive PDUs. Multiple tlm_info options can be given | COSMOS packet information
|
118
|
-
| eof_sent_indication | Issue EOF-Sent.indication | true or false
|
119
|
-
| eof_recv_indication | Issue EOF-Recv.indication | true or false
|
120
|
-
| file_segment_recv_indication | Issue File-Segment-Recv.indication | true or false
|
121
|
-
| transaction_finished_indication | Issue Transaction-Finished.indication | true or false
|
122
|
-
| suspended_indication | Issue Suspended.indication | true or false
|
123
|
-
| resume_indication | Issue Resume.indication | true or false
|
112
|
+
| Setting Name | Description | Allowed Values | Default Value |
|
113
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------- |
|
114
|
+
| root_path | The path to send/receive files from | Valid directory | N/A - Must be given |
|
115
|
+
| bucket | The bucket to send/receive files from | Valid bucket Name | nil - Serve from mounted volume |
|
116
|
+
| source_entity_id | The entity id for this CFDP microservice | Any integer | N/A - Must be given |
|
117
|
+
| tlm_info | A target_name, packet_name, and item_name to receive PDUs. Multiple tlm_info options can be given | COSMOS packet information | N/A - Must be given |
|
118
|
+
| eof_sent_indication | Issue EOF-Sent.indication | true or false | true |
|
119
|
+
| eof_recv_indication | Issue EOF-Recv.indication | true or false | true |
|
120
|
+
| file_segment_recv_indication | Issue File-Segment-Recv.indication | true or false | true |
|
121
|
+
| transaction_finished_indication | Issue Transaction-Finished.indication | true or false | true |
|
122
|
+
| suspended_indication | Issue Suspended.indication | true or false | true |
|
123
|
+
| resume_indication | Issue Resume.indication | true or false | true |
|
124
|
+
| transaction_retain_seconds | Time to keep completed transactions in seconds. | Floating point value greater than 0 | 86400 |
|
124
125
|
|
125
126
|
### Remote Entity Configuration
|
126
127
|
|
@@ -150,6 +151,7 @@ Minimum required settings:
|
|
150
151
|
| transaction_closure_requested | Default closure requested setting | CLOSURE_REQUESTED or CLOSURE_NOT_REQUESTED | CLOSURE_REQUESTED |
|
151
152
|
| incomplete_file_disposition | What to do with an incomplete file | DISCARD or RETAIN | DISCARD |
|
152
153
|
| fault_handler | Fault handler setting | (ACK_LIMIT_REACHED, KEEP_ALIVE_LIMIT_REACHED, INVALID_TRANSMISSION_MODE, FILESTORE_REJECTION, FILE_CHECKSUM_FAILURE, FILE_SIZE_ERROR, NAK_LIMIT_REACHED, INACTIVITY_DETECTED, INVALID_FILE_STRUCTURE, CHECK_LIMIT_REACHED, or UNSUPPORTED_CHECKSUM_TYPE) followed by (ISSUE_NOTICE_OF_CANCELLATION, ISSUE_NOTICE_OF_SUSPENSION, IGNORE_ERROR, or ABANDON_TRANSACTION) | See Code |
|
154
|
+
| cmd_delay | Delay after sending each PDU in seconds. Defaults to no delay. | Floating point value greater than 0 | nil |
|
153
155
|
|
154
156
|
## Known Limitations
|
155
157
|
|
data/microservices/CFDP/Gemfile
CHANGED
@@ -2,13 +2,13 @@ source ENV['RUBYGEMS_URL'] || "https://rubygems.org"
|
|
2
2
|
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
3
3
|
|
4
4
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
5
|
-
gem 'rails', '
|
5
|
+
gem 'rails', '~> 7.1.0'
|
6
6
|
|
7
7
|
# Reduces boot times through caching; required in config/boot.rb
|
8
8
|
gem 'bootsnap', '>= 1.9.3', require: false
|
9
9
|
|
10
10
|
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
|
11
|
-
gem 'rack-cors', '
|
11
|
+
gem 'rack-cors', '~> 2.0'
|
12
12
|
|
13
13
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
14
14
|
gem 'tzinfo-data'
|
@@ -21,7 +21,7 @@ group :development, :test do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
group :test do
|
24
|
-
gem 'mock_redis'
|
24
|
+
gem 'mock_redis', '0.39'
|
25
25
|
end
|
26
26
|
|
27
27
|
if ENV['OPENC3_DEVEL']
|
@@ -33,9 +33,9 @@ class CfdpController < ApplicationController
|
|
33
33
|
transaction = $cfdp_user.start_source_transaction(params)
|
34
34
|
render json: transaction.id
|
35
35
|
rescue ActionController::ParameterMissing => error
|
36
|
-
render :json => { :status => 'error', :message => error.message }, :status => 400
|
36
|
+
render :json => { :status => 'error', :message => error.message }.as_json(:allow_nan => true), :status => 400
|
37
37
|
rescue => error
|
38
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
38
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
39
39
|
end
|
40
40
|
|
41
41
|
# Cancel.request (transaction ID)
|
@@ -46,10 +46,10 @@ class CfdpController < ApplicationController
|
|
46
46
|
if transaction
|
47
47
|
render json: transaction.id
|
48
48
|
else
|
49
|
-
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }, :status => 404
|
49
|
+
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }.as_json(:allow_nan => true), :status => 404
|
50
50
|
end
|
51
51
|
rescue => error
|
52
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
52
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
53
53
|
end
|
54
54
|
|
55
55
|
# Suspend.request (transaction ID)
|
@@ -60,10 +60,10 @@ class CfdpController < ApplicationController
|
|
60
60
|
if transaction
|
61
61
|
render json: transaction.id
|
62
62
|
else
|
63
|
-
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }, :status => 404
|
63
|
+
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }.as_json(:allow_nan => true), :status => 404
|
64
64
|
end
|
65
65
|
rescue => error
|
66
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
66
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
67
67
|
end
|
68
68
|
|
69
69
|
# Resume.request (transaction ID)
|
@@ -74,10 +74,10 @@ class CfdpController < ApplicationController
|
|
74
74
|
if transaction
|
75
75
|
render json: transaction.id
|
76
76
|
else
|
77
|
-
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }, :status => 404
|
77
|
+
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }.as_json(:allow_nan => true), :status => 404
|
78
78
|
end
|
79
79
|
rescue => error
|
80
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
80
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
81
81
|
end
|
82
82
|
|
83
83
|
# Report.request (transaction ID)
|
@@ -88,10 +88,10 @@ class CfdpController < ApplicationController
|
|
88
88
|
if transaction
|
89
89
|
render json: transaction.id
|
90
90
|
else
|
91
|
-
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }, :status => 404
|
91
|
+
render :json => { :status => 'error', :message => "Transaction #{params[:transaction_id]} not found" }.as_json(:allow_nan => true), :status => 404
|
92
92
|
end
|
93
93
|
rescue => error
|
94
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
94
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
95
95
|
end
|
96
96
|
|
97
97
|
def directory_listing
|
@@ -100,17 +100,17 @@ class CfdpController < ApplicationController
|
|
100
100
|
transaction = $cfdp_user.start_directory_listing(params)
|
101
101
|
render json: transaction.id
|
102
102
|
rescue ActionController::ParameterMissing => error
|
103
|
-
render :json => { :status => 'error', :message => error.message }, :status => 400
|
103
|
+
render :json => { :status => 'error', :message => error.message }.as_json(:allow_nan => true), :status => 400
|
104
104
|
rescue => error
|
105
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
105
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
106
106
|
end
|
107
107
|
|
108
108
|
def subscribe
|
109
109
|
return unless check_authorization()
|
110
110
|
result = CfdpTopic.subscribe_indications
|
111
|
-
render json: result
|
111
|
+
render json: result.as_json(:allow_nan => true)
|
112
112
|
rescue => error
|
113
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
113
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
114
114
|
end
|
115
115
|
|
116
116
|
# Transaction.indication (transaction ID)
|
@@ -149,9 +149,9 @@ class CfdpController < ApplicationController
|
|
149
149
|
def indications
|
150
150
|
return unless check_authorization()
|
151
151
|
result = CfdpTopic.read_indications(transaction_id: params[:transaction_id], continuation: params[:continuation], limit: params[:limit])
|
152
|
-
render json: result
|
152
|
+
render json: result.as_json(:allow_nan => true)
|
153
153
|
rescue => error
|
154
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
154
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
155
155
|
end
|
156
156
|
|
157
157
|
def transactions
|
@@ -166,9 +166,9 @@ class CfdpController < ApplicationController
|
|
166
166
|
end
|
167
167
|
end
|
168
168
|
result = result.sort {|a, b| a.id <=> b.id}
|
169
|
-
render json: result
|
169
|
+
render json: result.as_json(:allow_nan => true)
|
170
170
|
rescue => error
|
171
|
-
render :json => { :status => 'error', :message => error.message }, :status => 500
|
171
|
+
render :json => { :status => 'error', :message => "#{error.message}\n#{error.backtrace.join("\n")}" }.as_json(:allow_nan => true), :status => 500
|
172
172
|
end
|
173
173
|
|
174
174
|
# private
|
@@ -179,14 +179,14 @@ class CfdpController < ApplicationController
|
|
179
179
|
|
180
180
|
if params[:remote_entity_id]
|
181
181
|
if params[:remote_entity_id].to_i.to_s != params[:remote_entity_id].to_s
|
182
|
-
render :json => { :status => 'error', :message => "remote_entity_id must be numeric" }, :status => 400
|
182
|
+
render :json => { :status => 'error', :message => "remote_entity_id must be numeric" }.as_json(:allow_nan => true), :status => 400
|
183
183
|
return false
|
184
184
|
end
|
185
185
|
cmd_entity_id = Integer(params[:remote_entity_id])
|
186
186
|
cmd_entity = CfdpMib.entity(cmd_entity_id)
|
187
187
|
elsif params[:destination_entity_id]
|
188
188
|
if params[:destination_entity_id].to_i.to_s != params[:destination_entity_id].to_s
|
189
|
-
render :json => { :status => 'error', :message => "destination_entity_id must be numeric" }, :status => 400
|
189
|
+
render :json => { :status => 'error', :message => "destination_entity_id must be numeric" }.as_json(:allow_nan => true), :status => 400
|
190
190
|
return
|
191
191
|
end
|
192
192
|
cmd_entity_id = Integer(params[:destination_entity_id])
|
@@ -207,15 +207,16 @@ class CfdpController < ApplicationController
|
|
207
207
|
# Caller must be able to send this command
|
208
208
|
return false unless authorization('cmd', target_name: target_name, packet_name: packet_name)
|
209
209
|
else
|
210
|
-
render :json => { :status => 'error', :message => "info not configured for entity: #{cmd_entity_id}" }, :status => 400
|
210
|
+
render :json => { :status => 'error', :message => "info not configured for entity: #{cmd_entity_id}" }.as_json(:allow_nan => true), :status => 400
|
211
211
|
return false
|
212
212
|
end
|
213
213
|
else
|
214
|
-
render :json => { :status => 'error', :message => "Unknown entity: #{cmd_entity_id}" }, :status => 400
|
214
|
+
render :json => { :status => 'error', :message => "Unknown entity: #{cmd_entity_id}" }.as_json(:allow_nan => true), :status => 400
|
215
215
|
return false
|
216
216
|
end
|
217
217
|
|
218
218
|
# Authorized
|
219
|
+
CfdpMib.cleanup_old_transactions()
|
219
220
|
return true
|
220
221
|
end
|
221
222
|
|
@@ -101,6 +101,7 @@ class CfdpMib
|
|
101
101
|
'enable_keep_alive',
|
102
102
|
'enable_finished',
|
103
103
|
'enable_eof_nak',
|
104
|
+
'cmd_delay',
|
104
105
|
'tlm_info',
|
105
106
|
'eof_sent_indication',
|
106
107
|
'eof_recv_indication',
|
@@ -108,7 +109,8 @@ class CfdpMib
|
|
108
109
|
'transaction_finished_indication',
|
109
110
|
'suspended_indication',
|
110
111
|
'resume_indication',
|
111
|
-
'fault_handler'
|
112
|
+
'fault_handler',
|
113
|
+
'transaction_retain_seconds'
|
112
114
|
]
|
113
115
|
|
114
116
|
@@source_entity_id = 0
|
@@ -184,6 +186,7 @@ class CfdpMib
|
|
184
186
|
entity['enable_keep_alive'] = true
|
185
187
|
entity['enable_finished'] = true
|
186
188
|
entity['enable_eof_nak'] = false
|
189
|
+
entity['cmd_delay'] = nil
|
187
190
|
|
188
191
|
# Local Entity Settings
|
189
192
|
entity['tlm_info'] = []
|
@@ -205,6 +208,7 @@ class CfdpMib
|
|
205
208
|
entity['fault_handler']["INVALID_FILE_STRUCTURE"] = "IGNORE_ERROR"
|
206
209
|
entity['fault_handler']["CHECK_LIMIT_REACHED"] = "IGNORE_ERROR"
|
207
210
|
entity['fault_handler']["UNSUPPORTED_CHECKSUM_TYPE"] = "IGNORE_ERROR"
|
211
|
+
entity['transaction_retain_seconds'] = 86400.0
|
208
212
|
|
209
213
|
# TODO: Use interface connected? to limit opportunities?
|
210
214
|
@@entities[entity_id] = entity
|
@@ -236,7 +240,7 @@ class CfdpMib
|
|
236
240
|
def self.get_source_file(source_file_name)
|
237
241
|
file_name = File.join(@@root_path, source_file_name)
|
238
242
|
if self.bucket
|
239
|
-
file = Tempfile.new
|
243
|
+
file = Tempfile.new('cfdp', binmode: true)
|
240
244
|
OpenC3::Bucket.getClient().get_object(bucket: self.bucket, key: file_name, path: file.path)
|
241
245
|
else
|
242
246
|
file = File.open(file_name, 'rb')
|
@@ -316,7 +320,7 @@ class CfdpMib
|
|
316
320
|
elsif not client.check_object(bucket: self.bucket, key: first_file_name)
|
317
321
|
status_code = "OLD_FILE_DOES_NOT_EXIST"
|
318
322
|
else
|
319
|
-
temp = Tempfile.new
|
323
|
+
temp = Tempfile.new('cfdp', binmode: true)
|
320
324
|
client.get_object(bucket: self.bucket, key: first_file_name, path: temp.path)
|
321
325
|
client.put_object(bucket: self.bucket, key: second_file_name, body: temp.read)
|
322
326
|
client.delete_object(bucket: self.bucket, key: first_file_name)
|
@@ -342,8 +346,8 @@ class CfdpMib
|
|
342
346
|
elsif not client.check_object(bucket: self.bucket, key: second_file_name)
|
343
347
|
status_code = "FILE_2_DOES_NOT_EXIST"
|
344
348
|
else
|
345
|
-
temp1 = Tempfile.new
|
346
|
-
temp2 = Tempfile.new
|
349
|
+
temp1 = Tempfile.new('cfdp', binmode: true)
|
350
|
+
temp2 = Tempfile.new('cfdp', binmode: true)
|
347
351
|
client.get_object(bucket: self.bucket, key: first_file_name, path: temp1.path)
|
348
352
|
client.get_object(bucket: self.bucket, key: second_file_name, path: temp2.path)
|
349
353
|
client.put_object(bucket: self.bucket, key: first_file_name, body: temp1.read + temp2.read)
|
@@ -372,7 +376,7 @@ class CfdpMib
|
|
372
376
|
elsif not client.check_object(bucket: self.bucket, key: second_file_name)
|
373
377
|
status_code = "FILE_2_DOES_NOT_EXIST"
|
374
378
|
else
|
375
|
-
temp = Tempfile.new
|
379
|
+
temp = Tempfile.new('cfdp', binmode: true)
|
376
380
|
client.get_object(bucket: self.bucket, key: second_file_name, path: temp.path)
|
377
381
|
client.put_object(bucket: self.bucket, key: first_file_name, body: temp.read)
|
378
382
|
temp.unlink
|
@@ -499,6 +503,13 @@ class CfdpMib
|
|
499
503
|
else
|
500
504
|
raise "Value for MIB setting #{field_name} must be a three part array of target_name, packet_name, item_name"
|
501
505
|
end
|
506
|
+
when 'cmd_delay', 'transaction_retain_seconds'
|
507
|
+
value = Float(value)
|
508
|
+
if value >= 0
|
509
|
+
CfdpMib.set_entity_value(current_entity_id, field_name, value)
|
510
|
+
else
|
511
|
+
raise "Value for MIB setting #{field_name} must be greater than or equal to zero"
|
512
|
+
end
|
502
513
|
when 'immediate_nak_mode', 'crcs_required', 'eof_sent_indication', 'eof_recv_indication', 'file_segment_recv_indication', 'transaction_finished_indication', 'suspended_indication', 'resume_indication',
|
503
514
|
'enable_acks', 'enable_keep_alive', 'enable_finished', 'enable_eof_nak'
|
504
515
|
value = OpenC3::ConfigParser.handle_true_false(value)
|
@@ -610,4 +621,18 @@ class CfdpMib
|
|
610
621
|
@@root_path = "/"
|
611
622
|
@@transactions = {}
|
612
623
|
end
|
624
|
+
|
625
|
+
def self.cleanup_old_transactions
|
626
|
+
to_remove = []
|
627
|
+
current_time = Time.now.utc
|
628
|
+
transaction_retain_seconds = @@entities[@@source_entity_id]['transaction_retain_seconds']
|
629
|
+
@@transactions.each do |id, transaction|
|
630
|
+
if transaction.complete_time and (current_time - transaction.complete_time) > transaction_retain_seconds
|
631
|
+
to_remove << id
|
632
|
+
end
|
633
|
+
end
|
634
|
+
to_remove.each do |id|
|
635
|
+
@@transactions.delete(id)
|
636
|
+
end
|
637
|
+
end
|
613
638
|
end
|
@@ -80,7 +80,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
80
80
|
|
81
81
|
if @source_file_name and @destination_file_name
|
82
82
|
if complete_file_received?
|
83
|
-
@tmp_file ||= Tempfile.new('cfdp')
|
83
|
+
@tmp_file ||= Tempfile.new('cfdp', binmode: true)
|
84
84
|
|
85
85
|
# Complete
|
86
86
|
if @checksum.check(@tmp_file, @eof_pdu_hash['FILE_CHECKSUM'], @full_checksum_needed)
|
@@ -174,7 +174,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
174
174
|
fault_location_entity_id: nil)
|
175
175
|
cmd_params = {}
|
176
176
|
cmd_params[item_name] = @finished_pdu
|
177
|
-
|
177
|
+
cfdp_cmd(source_entity, target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
178
178
|
rescue => err
|
179
179
|
abandon() if @state == "CANCELED"
|
180
180
|
raise err
|
@@ -184,6 +184,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
184
184
|
|
185
185
|
@state = "FINISHED" unless @state == "CANCELED" or @state == "ABANDONED"
|
186
186
|
@transaction_status = "TERMINATED"
|
187
|
+
@complete_time = Time.now.utc
|
187
188
|
OpenC3::Logger.info("CFDP Finished Receive Transaction #{@id}, #{@condition_code}", scope: ENV['OPENC3_SCOPE'])
|
188
189
|
|
189
190
|
if CfdpMib.source_entity['transaction_finished_indication']
|
@@ -297,7 +298,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
297
298
|
raise "cmd_info not defined for source entity: #{@metadata_pdu_hash['SOURCE_ENTITY_ID']}" unless target_name and packet_name and item_name
|
298
299
|
cmd_params = {}
|
299
300
|
cmd_params[item_name] = @finished_pdu
|
300
|
-
|
301
|
+
cfdp_cmd(source_entity, target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
301
302
|
@finished_count += 1
|
302
303
|
if @finished_count > CfdpMib.entity(@source_entity_id)['ack_timer_expiration_limit']
|
303
304
|
# Positive ACK Limit Reached Fault
|
@@ -328,7 +329,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
328
329
|
progress: @progress)
|
329
330
|
cmd_params = {}
|
330
331
|
cmd_params[item_name] = keep_alive_pdu
|
331
|
-
|
332
|
+
cfdp_cmd(source_entity, target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
332
333
|
end
|
333
334
|
|
334
335
|
def send_naks(force = false)
|
@@ -421,7 +422,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
421
422
|
start_of_scope = end_of_scope
|
422
423
|
cmd_params = {}
|
423
424
|
cmd_params[item_name] = nak_pdu
|
424
|
-
|
425
|
+
cfdp_cmd(source_entity, target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
425
426
|
end
|
426
427
|
break if segment_requests.length <= 0
|
427
428
|
end
|
@@ -520,7 +521,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
520
521
|
transaction_status: "ACTIVE")
|
521
522
|
cmd_params = {}
|
522
523
|
cmd_params[item_name] = ack_pdu
|
523
|
-
|
524
|
+
cfdp_cmd(source_entity, target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
524
525
|
end
|
525
526
|
|
526
527
|
# Note: This also handles canceled
|
@@ -554,7 +555,7 @@ class CfdpReceiveTransaction < CfdpTransaction
|
|
554
555
|
else # File Data
|
555
556
|
@source_entity_id = @metadata_pdu_hash['SOURCE_ENTITY_ID']
|
556
557
|
|
557
|
-
@tmp_file ||= Tempfile.new('cfdp')
|
558
|
+
@tmp_file ||= Tempfile.new('cfdp', binmode: true)
|
558
559
|
offset = pdu_hash['OFFSET']
|
559
560
|
file_data = pdu_hash['FILE_DATA']
|
560
561
|
progress = offset + file_data.length
|
@@ -96,7 +96,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
96
96
|
# Resend eof pdu
|
97
97
|
cmd_params = {}
|
98
98
|
cmd_params[@item_name] = @eof_pdu
|
99
|
-
|
99
|
+
cfdp_cmd(@destination_entity, @target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
100
100
|
@eof_count += 1
|
101
101
|
if @eof_count > @destination_entity['ack_timer_expiration_limit']
|
102
102
|
# Positive ACK Limit Reached Fault
|
@@ -206,7 +206,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
206
206
|
transmission_mode: @transmission_mode)
|
207
207
|
cmd_params = {}
|
208
208
|
cmd_params[@item_name] = @metadata_pdu
|
209
|
-
|
209
|
+
cfdp_cmd(@destination_entity, @target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
210
210
|
|
211
211
|
if source_file
|
212
212
|
checksum = get_checksum(@destination_entity['default_checksum_type'])
|
@@ -236,7 +236,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
236
236
|
transmission_mode: @transmission_mode)
|
237
237
|
cmd_params = {}
|
238
238
|
cmd_params[@item_name] = file_data_pdu
|
239
|
-
|
239
|
+
cfdp_cmd(@destination_entity, @target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
240
240
|
checksum.add(offset, file_data)
|
241
241
|
offset += file_data.length
|
242
242
|
@progress = offset
|
@@ -271,7 +271,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
271
271
|
canceling_entity_id: @canceling_entity_id)
|
272
272
|
cmd_params = {}
|
273
273
|
cmd_params[@item_name] = @eof_pdu
|
274
|
-
|
274
|
+
cfdp_cmd(@destination_entity, @target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
275
275
|
rescue => err
|
276
276
|
abandon() if @canceling_entity_id
|
277
277
|
raise err
|
@@ -327,6 +327,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
327
327
|
end
|
328
328
|
@state = "FINISHED" unless @state == "CANCELED" or @state == "ABANDONED"
|
329
329
|
@transaction_status = "TERMINATED"
|
330
|
+
@complete_time = Time.now.utc
|
330
331
|
OpenC3::Logger.info("CFDP Finished Source Transaction #{@id}, #{@condition_code}", scope: ENV['OPENC3_SCOPE'])
|
331
332
|
|
332
333
|
if CfdpMib.source_entity['transaction_finished_indication']
|
@@ -369,7 +370,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
369
370
|
transaction_status: @transaction_status)
|
370
371
|
cmd_params = {}
|
371
372
|
cmd_params[@item_name] = ack_pdu
|
372
|
-
|
373
|
+
cfdp_cmd(@destination_entity, @target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
373
374
|
end
|
374
375
|
|
375
376
|
when "ACK"
|
@@ -412,7 +413,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
412
413
|
# Send Metadata PDU
|
413
414
|
cmd_params = {}
|
414
415
|
cmd_params[@item_name] = @metadata_pdu
|
415
|
-
|
416
|
+
cfdp_cmd(@destination_entity, @target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
416
417
|
else
|
417
418
|
# Send File Data PDU(s)
|
418
419
|
offset = start_offset
|
@@ -438,7 +439,7 @@ class CfdpSourceTransaction < CfdpTransaction
|
|
438
439
|
transmission_mode: @transmission_mode)
|
439
440
|
cmd_params = {}
|
440
441
|
cmd_params[@item_name] = file_data_pdu
|
441
|
-
|
442
|
+
cfdp_cmd(@destination_entity, @target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
442
443
|
offset += file_data.length
|
443
444
|
end
|
444
445
|
end
|
@@ -37,6 +37,8 @@ class CfdpTransaction
|
|
37
37
|
attr_reader :file_status
|
38
38
|
attr_reader :metadata_pdu_hash
|
39
39
|
attr_reader :metadata_pdu_count
|
40
|
+
attr_reader :create_time
|
41
|
+
attr_reader :complete_time
|
40
42
|
attr_accessor :proxy_response_info
|
41
43
|
attr_accessor :proxy_response_needed
|
42
44
|
|
@@ -59,10 +61,12 @@ class CfdpTransaction
|
|
59
61
|
@proxy_response_needed = false
|
60
62
|
@source_file_name = nil
|
61
63
|
@destination_file_name = nil
|
64
|
+
@create_time = Time.now.utc
|
65
|
+
@complete_time = nil
|
62
66
|
end
|
63
67
|
|
64
68
|
def as_json(*args)
|
65
|
-
|
69
|
+
result = {
|
66
70
|
"id" => @id,
|
67
71
|
"frozen" => @frozen,
|
68
72
|
"state" => @state,
|
@@ -70,8 +74,11 @@ class CfdpTransaction
|
|
70
74
|
"progress" => @progress,
|
71
75
|
"condition_code" => @condition_code,
|
72
76
|
"source_file_name" => @source_file_name,
|
73
|
-
"destination_file_name" => @destination_file_name
|
77
|
+
"destination_file_name" => @destination_file_name,
|
78
|
+
"create_time" => @create_time.iso8601(6)
|
74
79
|
}
|
80
|
+
result["complete_time"] = @complete_time.iso8601(6) if @complete_time
|
81
|
+
return result
|
75
82
|
end
|
76
83
|
|
77
84
|
def suspend
|
@@ -104,6 +111,7 @@ class CfdpTransaction
|
|
104
111
|
end
|
105
112
|
@state = "CANCELED"
|
106
113
|
@transaction_status = "TERMINATED"
|
114
|
+
@complete_time = Time.now.utc
|
107
115
|
end
|
108
116
|
end
|
109
117
|
|
@@ -113,6 +121,7 @@ class CfdpTransaction
|
|
113
121
|
@state = "ABANDONED"
|
114
122
|
@transaction_status = "TERMINATED"
|
115
123
|
CfdpTopic.write_indication("Abandoned", transaction_id: @id, condition_code: @condition_code, progress: @progress)
|
124
|
+
@complete_time = Time.now.utc
|
116
125
|
end
|
117
126
|
end
|
118
127
|
|
@@ -185,4 +194,9 @@ class CfdpTransaction
|
|
185
194
|
return nil
|
186
195
|
end
|
187
196
|
end
|
197
|
+
|
198
|
+
def cfdp_cmd(entity, target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
199
|
+
cmd(target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
|
200
|
+
sleep(entity['cmd_delay']) if entity['cmd_delay']
|
201
|
+
end
|
188
202
|
end
|
@@ -35,9 +35,10 @@ class CfdpPdu < OpenC3::Packet
|
|
35
35
|
transaction_status:)
|
36
36
|
|
37
37
|
pdu = build_initial_pdu(type: "FILE_DIRECTIVE", destination_entity: destination_entity, transmission_mode: transmission_mode, file_size: 0, segmentation_control: segmentation_control)
|
38
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
38
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
39
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
39
40
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'], directive_code: "ACK")
|
40
|
-
pdu_header_part_2_length = pdu_header.length
|
41
|
+
pdu_header_part_2_length = pdu_header.length - DIRECTIVE_CODE_BYTE_SIZE # Minus 1 = Directive code is part of data per 5.2.1.1
|
41
42
|
if ack_directive_code == "FINISHED" or ack_directive_code == 5
|
42
43
|
pdu.write("DIRECTION", "TOWARD_FILE_RECEIVER")
|
43
44
|
else
|
@@ -39,9 +39,10 @@ class CfdpPdu < OpenC3::Packet
|
|
39
39
|
canceling_entity_id: nil)
|
40
40
|
|
41
41
|
pdu = build_initial_pdu(type: "FILE_DIRECTIVE", destination_entity: destination_entity, transmission_mode: transmission_mode, file_size: file_size, segmentation_control: segmentation_control)
|
42
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
42
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
43
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
43
44
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'], directive_code: "EOF")
|
44
|
-
pdu_header_part_2_length = pdu_header.length
|
45
|
+
pdu_header_part_2_length = pdu_header.length - DIRECTIVE_CODE_BYTE_SIZE # Minus 1 = Directive code is part of data per 5.2.1.1
|
45
46
|
pdu_contents = pdu.build_eof_pdu_contents(condition_code: condition_code, file_checksum: file_checksum, file_size: file_size, canceling_entity_id: canceling_entity_id)
|
46
47
|
pdu.write("VARIABLE_DATA", pdu_header + pdu_contents)
|
47
48
|
pdu.write("PDU_DATA_LENGTH", pdu.length - pdu_header_part_1_length - pdu_header_part_2_length)
|
@@ -43,7 +43,8 @@ class CfdpPdu < OpenC3::Packet
|
|
43
43
|
segment_metadata: nil)
|
44
44
|
|
45
45
|
pdu = build_initial_pdu(type: "FILE_DATA", destination_entity: destination_entity, file_size: file_size, segmentation_control: segmentation_control, transmission_mode: transmission_mode)
|
46
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
46
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
47
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
47
48
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'])
|
48
49
|
pdu_header_part_2_length = pdu_header.length
|
49
50
|
pdu_contents = pdu.build_file_data_pdu_contents(offset: offset, file_data: file_data, record_continuation_state: record_continuation_state, segment_metadata: segment_metadata)
|
@@ -41,9 +41,10 @@ class CfdpPdu < OpenC3::Packet
|
|
41
41
|
|
42
42
|
pdu = build_initial_pdu(type: "FILE_DIRECTIVE", destination_entity: destination_entity, transmission_mode: transmission_mode, file_size: 0, segmentation_control: segmentation_control)
|
43
43
|
pdu.write("DIRECTION", "TOWARD_FILE_SENDER")
|
44
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
44
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
45
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
45
46
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'], directive_code: "FINISHED")
|
46
|
-
pdu_header_part_2_length = pdu_header.length
|
47
|
+
pdu_header_part_2_length = pdu_header.length - DIRECTIVE_CODE_BYTE_SIZE # Minus 1 = Directive code is part of data per 5.2.1.1
|
47
48
|
pdu_contents = pdu.build_finished_pdu_contents(condition_code: condition_code, delivery_code: delivery_code, file_status: file_status, filestore_responses: filestore_responses, fault_location_entity_id: fault_location_entity_id)
|
48
49
|
pdu.write("VARIABLE_DATA", pdu_header + pdu_contents)
|
49
50
|
pdu.write("PDU_DATA_LENGTH", pdu.length - pdu_header_part_1_length - pdu_header_part_2_length)
|
@@ -32,9 +32,10 @@ class CfdpPdu < OpenC3::Packet
|
|
32
32
|
|
33
33
|
pdu = build_initial_pdu(type: "FILE_DIRECTIVE", destination_entity: destination_entity, transmission_mode: transmission_mode, file_size: file_size, segmentation_control: segmentation_control)
|
34
34
|
pdu.write("DIRECTION", "TOWARD_FILE_SENDER")
|
35
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
35
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
36
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
36
37
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'], directive_code: "KEEP_ALIVE")
|
37
|
-
pdu_header_part_2_length = pdu_header.length
|
38
|
+
pdu_header_part_2_length = pdu_header.length - DIRECTIVE_CODE_BYTE_SIZE # Minus 1 = Directive code is part of data per 5.2.1.1
|
38
39
|
pdu_contents = pdu.build_keep_alive_pdu_contents(progress: progress)
|
39
40
|
pdu.write("VARIABLE_DATA", pdu_header + pdu_contents)
|
40
41
|
pdu.write("PDU_DATA_LENGTH", pdu.length - pdu_header_part_1_length - pdu_header_part_2_length)
|
@@ -50,9 +50,10 @@ class CfdpPdu < OpenC3::Packet
|
|
50
50
|
options: [])
|
51
51
|
|
52
52
|
pdu = build_initial_pdu(type: "FILE_DIRECTIVE", destination_entity: destination_entity, transmission_mode: transmission_mode, file_size: file_size, segmentation_control: segmentation_control)
|
53
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
53
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
54
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
54
55
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'], directive_code: "METADATA")
|
55
|
-
pdu_header_part_2_length = pdu_header.length
|
56
|
+
pdu_header_part_2_length = pdu_header.length - DIRECTIVE_CODE_BYTE_SIZE # Minus 1 = Directive code is part of data per 5.2.1.1
|
56
57
|
if checksum_type_implemented(destination_entity['default_checksum_type'])
|
57
58
|
checksum_type = destination_entity['default_checksum_type']
|
58
59
|
else
|
@@ -44,9 +44,10 @@ class CfdpPdu < OpenC3::Packet
|
|
44
44
|
|
45
45
|
pdu = build_initial_pdu(type: "FILE_DIRECTIVE", destination_entity: destination_entity, transmission_mode: transmission_mode, file_size: file_size, segmentation_control: segmentation_control)
|
46
46
|
pdu.write("DIRECTION", "TOWARD_FILE_SENDER")
|
47
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
47
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
48
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
48
49
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'], directive_code: "NAK")
|
49
|
-
pdu_header_part_2_length = pdu_header.length
|
50
|
+
pdu_header_part_2_length = pdu_header.length - DIRECTIVE_CODE_BYTE_SIZE # Minus 1 = Directive code is part of data per 5.2.1.1
|
50
51
|
pdu_contents = pdu.build_nak_pdu_contents(start_of_scope: start_of_scope, end_of_scope: end_of_scope, segment_requests: segment_requests)
|
51
52
|
pdu.write("VARIABLE_DATA", pdu_header + pdu_contents)
|
52
53
|
pdu.write("PDU_DATA_LENGTH", pdu.length - pdu_header_part_1_length - pdu_header_part_2_length)
|
@@ -30,9 +30,10 @@ class CfdpPdu < OpenC3::Packet
|
|
30
30
|
response_required:)
|
31
31
|
|
32
32
|
pdu = build_initial_pdu(type: "FILE_DIRECTIVE", destination_entity: destination_entity, transmission_mode: transmission_mode, file_size: 0, segmentation_control: segmentation_control)
|
33
|
-
pdu_header_part_1_length = pdu.length # Measured here before writing variable data
|
33
|
+
pdu_header_part_1_length = pdu.length # Measured here before writing variable data - Includes CRC if present
|
34
|
+
pdu_header_part_1_length -= CRC_BYTE_SIZE if destination_entity['crcs_required'] # PDU_DATA_LENGTH field should contain CRC length
|
34
35
|
pdu_header = pdu.build_variable_header(source_entity_id: source_entity['id'], transaction_seq_num: transaction_seq_num, destination_entity_id: destination_entity['id'], directive_code: "PROMPT")
|
35
|
-
pdu_header_part_2_length = pdu_header.length
|
36
|
+
pdu_header_part_2_length = pdu_header.length - DIRECTIVE_CODE_BYTE_SIZE # Minus 1 = Directive code is part of data per 5.2.1.1
|
36
37
|
pdu_contents = pdu.build_prompt_pdu_contents(response_required: response_required)
|
37
38
|
pdu.write("VARIABLE_DATA", pdu_header + pdu_contents)
|
38
39
|
pdu.write("PDU_DATA_LENGTH", pdu.length - pdu_header_part_1_length - pdu_header_part_2_length)
|
@@ -53,6 +53,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
53
53
|
|
54
54
|
# By default the first 7 bytes are the header
|
55
55
|
# This assumes 1 byte per entity ID and sequence number
|
56
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 5 # PDU_DATA_LENGTH - Directive Code plus Ack Data plus CRC
|
56
57
|
|
57
58
|
# Directive Code
|
58
59
|
expect(buffer[7].unpack('C')[0]).to eql 6 # ACK per Table 5-4
|
@@ -55,6 +55,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
55
55
|
|
56
56
|
# By default the first 7 bytes are the header
|
57
57
|
# This assumes 1 byte per entity ID and sequence number
|
58
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 12 # PDU_DATA_LENGTH - Directive Code plus Data plus CRC
|
58
59
|
|
59
60
|
# Directive Code
|
60
61
|
expect(buffer[7].unpack('C')[0]).to eql 4 # EOF per Table 5-4
|
@@ -56,6 +56,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
56
56
|
|
57
57
|
# By default the first 7 bytes are the header
|
58
58
|
# This assumes 1 byte per entity ID and sequence number
|
59
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 8 # PDU_DATA_LENGTH - Directive Code plus Data plus CRC
|
59
60
|
|
60
61
|
# PDU Type
|
61
62
|
expect((buffer[0].unpack('C')[0] >> 4) & 0x1).to eql 1 # File Data per Table 5-1
|
@@ -56,6 +56,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
56
56
|
|
57
57
|
# By default the first 7 bytes are the header
|
58
58
|
# This assumes 1 byte per entity ID and sequence number
|
59
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 4 # PDU_DATA_LENGTH - Directive Code plus Data plus CRC
|
59
60
|
|
60
61
|
# Directive Code
|
61
62
|
expect(buffer[7].unpack('C')[0]).to eql 5 # Finished per Table 5-4
|
@@ -53,6 +53,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
53
53
|
|
54
54
|
# By default the first 7 bytes are the header
|
55
55
|
# This assumes 1 byte per entity ID and sequence number
|
56
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 7 # PDU_DATA_LENGTH - Directive Code plus Data plus CRC
|
56
57
|
|
57
58
|
# Directive Code
|
58
59
|
expect(buffer[7].unpack('C')[0]).to eql 0x0C # Keep Alive per Table 5-4
|
@@ -56,6 +56,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
56
56
|
|
57
57
|
# By default the first 7 bytes are the header
|
58
58
|
# This assumes 1 byte per entity ID and sequence number
|
59
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 22 # PDU_DATA_LENGTH - Directive Code plus Data plus CRC
|
59
60
|
|
60
61
|
# Directive Code
|
61
62
|
expect(buffer[7].unpack('C')[0]).to eql 7 # Metadata per Table 5-4
|
@@ -55,6 +55,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
55
55
|
|
56
56
|
# By default the first 7 bytes are the header
|
57
57
|
# This assumes 1 byte per entity ID and sequence number
|
58
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 11 # PDU_DATA_LENGTH - Directive Code plus Data plus CRC
|
58
59
|
|
59
60
|
# Directive Code
|
60
61
|
expect(buffer[7].unpack('C')[0]).to eql 8 # NAK per Table 5-4
|
@@ -52,6 +52,7 @@ RSpec.describe CfdpPdu, type: :model do
|
|
52
52
|
|
53
53
|
# By default the first 7 bytes are the header
|
54
54
|
# This assumes 1 byte per entity ID and sequence number
|
55
|
+
expect(buffer[1..2].unpack('n')[0]).to eql 4 # PDU_DATA_LENGTH - Directive Code plus Data plus CRC
|
55
56
|
|
56
57
|
# Directive Code
|
57
58
|
expect(buffer[7].unpack('C')[0]).to eql 9 # Prompt per Table 5-4
|
@@ -1687,7 +1687,7 @@ module OpenC3
|
|
1687
1687
|
expect(fsr['FIRST_FILE_NAME']).to eql 'another'
|
1688
1688
|
end
|
1689
1689
|
if ENV['MINIO'] && type == 'bucket'
|
1690
|
-
file = Tempfile.new
|
1690
|
+
file = Tempfile.new('cfdp', binmode: true)
|
1691
1691
|
OpenC3::Bucket.getClient().get_object(bucket: @bucket, key: File.join(@root_path, 'first.txt'), path: file.path)
|
1692
1692
|
expect(file.read).to eql 'FIRSTSECOND'
|
1693
1693
|
file.unlink
|
@@ -1764,7 +1764,7 @@ module OpenC3
|
|
1764
1764
|
expect(fsr['FIRST_FILE_NAME']).to eql 'another'
|
1765
1765
|
end
|
1766
1766
|
if ENV['MINIO'] && type == 'bucket'
|
1767
|
-
file = Tempfile.new
|
1767
|
+
file = Tempfile.new('cfdp', binmode: true)
|
1768
1768
|
OpenC3::Bucket.getClient().get_object(bucket: @bucket, key: File.join(@root_path, 'orig.txt'), path: file.path)
|
1769
1769
|
expect(file.read).to eql 'REPLACE'
|
1770
1770
|
file.rewind
|
@@ -99,7 +99,7 @@ class CfdpTestGroup < OpenC3::Group
|
|
99
99
|
data = "\x00" * 1000
|
100
100
|
|
101
101
|
# small.bin
|
102
|
-
file = Tempfile.new('cfdp')
|
102
|
+
file = Tempfile.new('cfdp', binmode: true)
|
103
103
|
1000.times do
|
104
104
|
file.write(data)
|
105
105
|
end
|
@@ -108,7 +108,7 @@ class CfdpTestGroup < OpenC3::Group
|
|
108
108
|
file.unlink
|
109
109
|
|
110
110
|
# medium.bin
|
111
|
-
file = Tempfile.new('cfdp')
|
111
|
+
file = Tempfile.new('cfdp', binmode: true)
|
112
112
|
10000.times do
|
113
113
|
file.write(data)
|
114
114
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openc3-cosmos-cfdp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Melton
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-11-25 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: " This plugin provides COSMOS Support for CFDP\n"
|
15
15
|
email:
|