openc3-cosmos-cfdp 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -12
  3. data/microservices/CFDP/Gemfile +3 -3
  4. data/microservices/CFDP/app/controllers/cfdp_controller.rb +23 -22
  5. data/microservices/CFDP/app/models/cfdp_mib.rb +31 -6
  6. data/microservices/CFDP/app/models/cfdp_pdu.rb +3 -0
  7. data/microservices/CFDP/app/models/cfdp_receive_transaction.rb +8 -7
  8. data/microservices/CFDP/app/models/cfdp_source_transaction.rb +8 -7
  9. data/microservices/CFDP/app/models/cfdp_transaction.rb +16 -2
  10. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_ack.rb +3 -2
  11. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_eof.rb +3 -2
  12. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_file_data.rb +2 -1
  13. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_finished.rb +3 -2
  14. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_keep_alive.rb +3 -2
  15. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_metadata.rb +3 -2
  16. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_nak.rb +3 -2
  17. data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_prompt.rb +3 -2
  18. data/microservices/CFDP/spec/models/cfdp_pdu_ack_spec.rb +1 -0
  19. data/microservices/CFDP/spec/models/cfdp_pdu_eof_spec.rb +1 -0
  20. data/microservices/CFDP/spec/models/cfdp_pdu_file_data_spec.rb +1 -0
  21. data/microservices/CFDP/spec/models/cfdp_pdu_finished_spec.rb +1 -0
  22. data/microservices/CFDP/spec/models/cfdp_pdu_keep_alive_spec.rb +1 -0
  23. data/microservices/CFDP/spec/models/cfdp_pdu_metadata_spec.rb +1 -0
  24. data/microservices/CFDP/spec/models/cfdp_pdu_nak_spec.rb +1 -0
  25. data/microservices/CFDP/spec/models/cfdp_pdu_prompt_spec.rb +1 -0
  26. data/microservices/CFDP/spec/requests/cfdp_spec.rb +2 -2
  27. data/targets/CFDPTEST/procedures/cfdp_test_suite.rb +2 -2
  28. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 254a8c3fa0ae6e2ec535bc5c565ad7e376706858dbd1a87e394a70998bca8a33
4
- data.tar.gz: '08d510acfdb03663b4f3426dff1a6a9dbbeada23e0b1d0b5a3ea2160341b3ac8'
3
+ metadata.gz: 9a1ac89a99cdc040f8e53d176140dacbfd149764c97c16622d8d08dc1523c4b2
4
+ data.tar.gz: e4fc3910126cf90dc8c046f76949e2f88f3b2f3b2f0256be283176fa55979159
5
5
  SHA512:
6
- metadata.gz: 3d93dd03e13bcae8c6fea52df4ad07320ab9097c5c3e9d2241c988682189d03edfcef9f274d7a02cde434d930dc0b9b3f851a82efa7d9b6d8dbd8e34e03c877f
7
- data.tar.gz: 409424118dd69bd8ee1d14fdd88bc3fcc6759f4e4edb694aa94b2af3ffac387504a70a5167b6f6aef1c2d28ea0c932ab6395af9d261a3693fca03957cabde3f1
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 | 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 |
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
 
@@ -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', '>= 7.0.0'
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', '>= 1.1'
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
@@ -30,6 +30,9 @@ require 'cfdp_pdu/cfdp_pdu_file_data'
30
30
  require 'cfdp_pdu/cfdp_pdu_user_ops'
31
31
 
32
32
  class CfdpPdu < OpenC3::Packet
33
+ DIRECTIVE_CODE_BYTE_SIZE = 1
34
+ CRC_BYTE_SIZE = 2
35
+
33
36
  def initialize(crcs_required:)
34
37
  super()
35
38
  append_item("VERSION", 3, :UINT)
@@ -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
- cmd(target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(target_name, packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(@target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(@target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(@target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(@target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(@target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(@target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- cmd(@target_name, @packet_name, cmd_params, scope: ENV['OPENC3_SCOPE'])
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
- return {
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.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-05-15 00:00:00.000000000 Z
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: