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.
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: