openc3-cosmos-cfdp 1.0.0 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -12
- data/lib/cfdp_api.rb +7 -6
- 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 +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13e88f3556a631c2f165f5960429dea0b8ebe9d5f6984fc9de89fb1e1f4d3a74
|
4
|
+
data.tar.gz: e8a0e0b293c80d55508b3e1be3435eecb1d9de0e17127df7a5077c1fe7a53e2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c115f598a06588d99ef63ab97d6d85277472de273f8212af2f93b6972769cfc1faaa23303e1695d3ab77fadb206154578cd1410d5bf264b9c3e962c65e68ea2e
|
7
|
+
data.tar.gz: ed1b6971ceb621525768fe60f3a0c326bcd2e1ed59a310cd615d71e4788056bc7d23df0ee6b99000ebe1f19eddc974a5e8fc02413ec1e41f6b171f4855ada68a
|
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/lib/cfdp_api.rb
CHANGED
@@ -23,25 +23,26 @@ require 'openc3/io/json_api'
|
|
23
23
|
#
|
24
24
|
# In ScriptRunner:
|
25
25
|
# require 'cfdp_api'
|
26
|
-
# api = CfdpApi.new
|
26
|
+
# api = CfdpApi.new()
|
27
27
|
# api.put(...)
|
28
28
|
#
|
29
29
|
# Outside cluster - Open Source:
|
30
|
-
# require 'cfdp_api'
|
31
30
|
# $openc3_scope = 'DEFAULT'
|
32
31
|
# ENV['OPENC3_API_PASSWORD'] = 'password'
|
33
|
-
#
|
32
|
+
# require 'cfdp_api'
|
33
|
+
# api = CfdpApi.new(url: "http://127.0.0.1:2900/cfdp")
|
34
34
|
# api.put(...)
|
35
35
|
#
|
36
36
|
# Outside cluster - Enterprise
|
37
|
-
# require 'cfdp_api'
|
38
37
|
# $openc3_scope = 'DEFAULT'
|
39
|
-
# ENV['OPENC3_KEYCLOAK_URL'] = '127.0.0.1:2900'
|
38
|
+
# ENV['OPENC3_KEYCLOAK_URL'] = 'http://127.0.0.1:2900/auth'
|
40
39
|
# ENV['OPENC3_API_USER'] = 'operator'
|
41
40
|
# ENV['OPENC3_API_PASSWORD'] = 'operator'
|
42
|
-
#
|
41
|
+
# require 'cfdp_api'
|
42
|
+
# api = CfdpApi.new(url: "http://127.0.0.1:2900/cfdp")
|
43
43
|
# api.put(...)
|
44
44
|
#
|
45
|
+
|
45
46
|
class CfdpApi < OpenC3::JsonApi
|
46
47
|
def put(
|
47
48
|
destination_entity_id:,
|
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.2
|
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:
|
12
|
+
date: 2024-06-26 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: " This plugin provides COSMOS Support for CFDP\n"
|
15
15
|
email:
|
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: '0'
|
113
113
|
requirements: []
|
114
|
-
rubygems_version: 3.3.
|
114
|
+
rubygems_version: 3.3.27
|
115
115
|
signing_key:
|
116
116
|
specification_version: 4
|
117
117
|
summary: OpenC3 COSMOS CFDP
|