openc3 6.8.1 → 6.9.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/bin/openc3cli +5 -5
  4. data/data/config/command_modifiers.yaml +9 -1
  5. data/data/config/screen.yaml +1 -1
  6. data/lib/openc3/accessors/json_accessor.rb +5 -5
  7. data/lib/openc3/api/interface_api.rb +71 -4
  8. data/lib/openc3/api/router_api.rb +98 -8
  9. data/lib/openc3/api/stash_api.rb +3 -3
  10. data/lib/openc3/api/tlm_api.rb +1 -1
  11. data/lib/openc3/bridge/bridge_config.rb +1 -1
  12. data/lib/openc3/interfaces/file_interface.rb +18 -0
  13. data/lib/openc3/interfaces/http_client_interface.rb +11 -0
  14. data/lib/openc3/interfaces/http_server_interface.rb +8 -0
  15. data/lib/openc3/interfaces/interface.rb +90 -21
  16. data/lib/openc3/interfaces/mqtt_interface.rb +19 -0
  17. data/lib/openc3/interfaces/mqtt_stream_interface.rb +20 -0
  18. data/lib/openc3/interfaces/protocols/burst_protocol.rb +16 -0
  19. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +18 -0
  20. data/lib/openc3/interfaces/protocols/crc_protocol.rb +19 -0
  21. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +17 -1
  22. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +14 -0
  23. data/lib/openc3/interfaces/protocols/length_protocol.rb +25 -1
  24. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +16 -3
  25. data/lib/openc3/interfaces/protocols/protocol.rb +79 -1
  26. data/lib/openc3/interfaces/protocols/slip_protocol.rb +23 -0
  27. data/lib/openc3/interfaces/protocols/template_protocol.rb +38 -0
  28. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +14 -1
  29. data/lib/openc3/interfaces/serial_interface.rb +14 -0
  30. data/lib/openc3/interfaces/simulated_target_interface.rb +1 -1
  31. data/lib/openc3/interfaces/tcpip_client_interface.rb +16 -2
  32. data/lib/openc3/interfaces/tcpip_server_interface.rb +11 -1
  33. data/lib/openc3/interfaces/udp_interface.rb +14 -0
  34. data/lib/openc3/io/json_api_object.rb +1 -1
  35. data/lib/openc3/io/json_drb.rb +1 -1
  36. data/lib/openc3/io/json_drb_object.rb +1 -1
  37. data/lib/openc3/io/json_rpc.rb +5 -4
  38. data/lib/openc3/logs/packet_log_reader.rb +1 -1
  39. data/lib/openc3/logs/packet_log_writer.rb +6 -6
  40. data/lib/openc3/microservices/decom_microservice.rb +5 -1
  41. data/lib/openc3/microservices/interface_microservice.rb +103 -38
  42. data/lib/openc3/microservices/microservice.rb +4 -4
  43. data/lib/openc3/microservices/queue_microservice.rb +14 -21
  44. data/lib/openc3/microservices/reducer_microservice.rb +1 -1
  45. data/lib/openc3/microservices/router_microservice.rb +28 -25
  46. data/lib/openc3/models/activity_model.rb +18 -17
  47. data/lib/openc3/models/cvt_model.rb +12 -9
  48. data/lib/openc3/models/interface_model.rb +70 -12
  49. data/lib/openc3/models/metadata_model.rb +2 -2
  50. data/lib/openc3/models/microservice_model.rb +1 -1
  51. data/lib/openc3/models/microservice_status_model.rb +2 -2
  52. data/lib/openc3/models/model.rb +4 -4
  53. data/lib/openc3/models/note_model.rb +2 -2
  54. data/lib/openc3/models/plugin_model.rb +9 -4
  55. data/lib/openc3/models/queue_model.rb +25 -25
  56. data/lib/openc3/models/reaction_model.rb +6 -6
  57. data/lib/openc3/models/script_engine_model.rb +1 -1
  58. data/lib/openc3/models/script_status_model.rb +3 -3
  59. data/lib/openc3/models/sorted_model.rb +5 -5
  60. data/lib/openc3/models/target_model.rb +11 -11
  61. data/lib/openc3/models/timeline_model.rb +2 -2
  62. data/lib/openc3/models/tool_model.rb +1 -1
  63. data/lib/openc3/models/trigger_group_model.rb +3 -3
  64. data/lib/openc3/models/trigger_model.rb +6 -6
  65. data/lib/openc3/models/widget_model.rb +1 -1
  66. data/lib/openc3/operators/operator.rb +2 -2
  67. data/lib/openc3/packets/json_packet.rb +1 -1
  68. data/lib/openc3/packets/packet.rb +1 -1
  69. data/lib/openc3/script/calendar.rb +2 -2
  70. data/lib/openc3/script/metadata.rb +4 -4
  71. data/lib/openc3/script/queue.rb +13 -5
  72. data/lib/openc3/script/script_runner.rb +9 -9
  73. data/lib/openc3/script/storage.rb +1 -1
  74. data/lib/openc3/script/tables.rb +2 -2
  75. data/lib/openc3/script/web_socket_api.rb +7 -7
  76. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +0 -12
  77. data/lib/openc3/tools/table_manager/table_manager_core.rb +1 -1
  78. data/lib/openc3/topics/command_decom_topic.rb +3 -3
  79. data/lib/openc3/topics/command_topic.rb +1 -1
  80. data/lib/openc3/topics/interface_topic.rb +45 -5
  81. data/lib/openc3/topics/limits_event_topic.rb +8 -8
  82. data/lib/openc3/topics/router_topic.rb +42 -3
  83. data/lib/openc3/topics/system_events_topic.rb +1 -1
  84. data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
  85. data/lib/openc3/utilities/authentication.rb +1 -1
  86. data/lib/openc3/utilities/cosmos_rails_formatter.rb +2 -3
  87. data/lib/openc3/utilities/local_mode.rb +8 -8
  88. data/lib/openc3/utilities/logger.rb +3 -3
  89. data/lib/openc3/utilities/running_script.rb +7 -8
  90. data/lib/openc3/version.rb +5 -5
  91. data/templates/plugin/README.md +3 -3
  92. data/templates/plugin/Rakefile +3 -3
  93. data/templates/plugin/plugin.gemspec +1 -0
  94. data/templates/tool_angular/.gitignore +1 -1
  95. data/templates/tool_angular/package.json +2 -48
  96. data/templates/tool_react/.gitignore +1 -2
  97. data/templates/tool_react/package.json +1 -51
  98. data/templates/tool_svelte/.gitignore +1 -2
  99. data/templates/tool_svelte/package.json +1 -49
  100. data/templates/tool_vue/package.json +3 -36
  101. data/templates/widget/Rakefile +1 -1
  102. data/templates/widget/package.json +2 -28
  103. metadata +9 -9
@@ -34,7 +34,7 @@ module OpenC3
34
34
  if response.nil? || response.status != 200
35
35
  _script_response_error(response, "Script list request failed", scope: scope)
36
36
  else
37
- scripts = JSON.parse(response.body, :allow_nan => true, :create_additions => true)
37
+ scripts = JSON.parse(response.body, allow_nan: true, create_additions: true)
38
38
  # Remove the '*' from the script names
39
39
  return scripts.each { |script| script.gsub!(/\*$/, '') }
40
40
  end
@@ -50,7 +50,7 @@ module OpenC3
50
50
  if response.nil? || response.status != 200
51
51
  _script_response_error(response, "Script syntax check request failed", scope: scope)
52
52
  else
53
- result = JSON.parse(response.body, :allow_nan => true, :create_additions => true)
53
+ result = JSON.parse(response.body, allow_nan: true, create_additions: true)
54
54
  if result['title'] == "Syntax Check Successful"
55
55
  result['success'] = true
56
56
  else
@@ -66,7 +66,7 @@ module OpenC3
66
66
  if response.nil? || response.status != 200
67
67
  _script_response_error(response, "Failed to get #{filename}", scope: scope)
68
68
  else
69
- result = JSON.parse(response.body, :allow_nan => true, :create_additions => true)
69
+ result = JSON.parse(response.body, allow_nan: true, create_additions: true)
70
70
  return result['contents']
71
71
  end
72
72
  end
@@ -142,9 +142,9 @@ module OpenC3
142
142
  if response.nil? || response.status != 200
143
143
  _script_response_error(response, "Script instrumented request failed", scope: scope)
144
144
  else
145
- result = JSON.parse(response.body, :allow_nan => true, :create_additions => true)
145
+ result = JSON.parse(response.body, allow_nan: true, create_additions: true)
146
146
  if result['title'] == "Instrumented Script"
147
- parsed = JSON.parse(result['description'], :allow_nan => true, :create_additions => true)
147
+ parsed = JSON.parse(result['description'], allow_nan: true, create_additions: true)
148
148
  return parsed.join("\n")
149
149
  else
150
150
  raise result.inspect
@@ -158,7 +158,7 @@ module OpenC3
158
158
  if response.nil? || response.status != 200
159
159
  _script_response_error(response, "Script create request failed", scope: scope)
160
160
  else
161
- return JSON.parse(response.body, :allow_nan => true, :create_additions => true)
161
+ return JSON.parse(response.body, allow_nan: true, create_additions: true)
162
162
  end
163
163
  end
164
164
 
@@ -178,7 +178,7 @@ module OpenC3
178
178
  if response.nil? || response.status != 200
179
179
  _script_response_error(response, "Running script list request failed", scope: scope)
180
180
  else
181
- return JSON.parse(response.body, :allow_nan => true, :create_additions => true)['items']
181
+ return JSON.parse(response.body, allow_nan: true, create_additions: true)['items']
182
182
  end
183
183
  end
184
184
 
@@ -188,7 +188,7 @@ module OpenC3
188
188
  if response.nil? || response.status != 200
189
189
  _script_response_error(response, "Running script show request failed", scope: scope)
190
190
  else
191
- return JSON.parse(response.body, :allow_nan => true, :create_additions => true)
191
+ return JSON.parse(response.body, allow_nan: true, create_additions: true)
192
192
  end
193
193
  end
194
194
  alias running_script_get script_get # Deprecated alias for compatibility
@@ -275,7 +275,7 @@ module OpenC3
275
275
  if response.nil? || response.status != 200
276
276
  _script_response_error(response, "Completed script list request failed", scope: scope)
277
277
  else
278
- return JSON.parse(response.body, :allow_nan => true, :create_additions => true)['items']
278
+ return JSON.parse(response.body, allow_nan: true, create_additions: true)['items']
279
279
  end
280
280
  end
281
281
  end
@@ -199,7 +199,7 @@ module OpenC3
199
199
  if response.nil? || response.status != 201
200
200
  raise "Failed to get presigned URL for #{endpoint}"
201
201
  end
202
- JSON.parse(response.body, :allow_nan => true, :create_additions => true)
202
+ JSON.parse(response.body, allow_nan: true, create_additions: true)
203
203
  end
204
204
  end
205
205
  end
@@ -40,10 +40,10 @@ module OpenC3
40
40
  def _tables_handle_response(response, error_message)
41
41
  return nil if response.nil?
42
42
  if response.status >= 400
43
- result = JSON.parse(response.body, :allow_nan => true, :create_additions => true)
43
+ result = JSON.parse(response.body, allow_nan: true, create_additions: true)
44
44
  raise "#{error_message} due to #{result['message']}"
45
45
  end
46
- return JSON.parse(response.body, :allow_nan => true, :create_additions => true)
46
+ return JSON.parse(response.body, allow_nan: true, create_additions: true)
47
47
  end
48
48
  end
49
49
  end
@@ -91,8 +91,8 @@ module OpenC3
91
91
  unless @subscribed
92
92
  json_hash = {}
93
93
  json_hash['command'] = 'subscribe'
94
- json_hash['identifier'] = JSON.generate(@identifier)
95
- @stream.write(JSON.generate(json_hash))
94
+ json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
95
+ @stream.write(JSON.generate(json_hash, allow_nan: true))
96
96
  @subscribed = true
97
97
  end
98
98
  end
@@ -102,8 +102,8 @@ module OpenC3
102
102
  if @subscribed
103
103
  json_hash = {}
104
104
  json_hash['command'] = 'unsubscribe'
105
- json_hash['identifier'] = JSON.generate(@identifier)
106
- @stream.write(JSON.generate(json_hash))
105
+ json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
106
+ @stream.write(JSON.generate(json_hash, allow_nan: true))
107
107
  @subscribed = false
108
108
  end
109
109
  end
@@ -112,9 +112,9 @@ module OpenC3
112
112
  def write_action(data_hash)
113
113
  json_hash = {}
114
114
  json_hash['command'] = 'message'
115
- json_hash['identifier'] = JSON.generate(@identifier)
116
- json_hash['data'] = JSON.generate(data_hash)
117
- write(JSON.generate(json_hash))
115
+ json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
116
+ json_hash['data'] = JSON.generate(data_hash, allow_nan: true)
117
+ write(JSON.generate(json_hash, allow_nan: true))
118
118
  end
119
119
 
120
120
  # General write to the websocket
@@ -210,18 +210,6 @@ module OpenC3
210
210
  rescue => e
211
211
  Logger.error "Problem writing to router #{router.name} - #{e.class}:#{e.message}"
212
212
  end
213
-
214
- # Write to packet log writers
215
- if packet.stored and !@interface.stored_packet_log_writer_pairs.empty?
216
- @interface.stored_packet_log_writer_pairs.each do |packet_log_writer_pair|
217
- packet_log_writer_pair.tlm_log_writer.write(packet)
218
- end
219
- else
220
- @interface.packet_log_writer_pairs.each do |packet_log_writer_pair|
221
- # Write errors are handled by the log writer
222
- packet_log_writer_pair.tlm_log_writer.write(packet)
223
- end
224
- end
225
213
  end
226
214
 
227
215
  def handle_connection_failed(connect_error)
@@ -191,7 +191,7 @@ module OpenC3
191
191
  end
192
192
  end
193
193
  end
194
- json.as_json(:allow_nan => true)
194
+ json.as_json()
195
195
  end
196
196
 
197
197
  def self.load_binary(config, data)
@@ -39,8 +39,8 @@ module OpenC3
39
39
  json_hash[item.name + "__F"] = packet.read_item(item, :FORMATTED) if item.format_string
40
40
  json_hash[item.name + "__U"] = packet.read_item(item, :WITH_UNITS) if item.units
41
41
  end
42
- json_hash['extra'] = JSON.generate(packet.extra.as_json(:allow_nan => true))
43
- msg_hash['json_data'] = JSON.generate(json_hash.as_json(:allow_nan => true))
42
+ json_hash['extra'] = JSON.generate(packet.extra.as_json, allow_nan: true)
43
+ msg_hash['json_data'] = JSON.generate(json_hash.as_json, allow_nan: true)
44
44
  EphemeralStoreQueued.write_topic(topic, msg_hash)
45
45
  end
46
46
 
@@ -51,7 +51,7 @@ module OpenC3
51
51
  msg_hash['received_count'].to_i
52
52
  else
53
53
  json = msg_hash['json_data']
54
- hash = JSON.parse(json, :allow_nan => true, :create_additions => true)
54
+ hash = JSON.parse(json, allow_nan: true, create_additions: true)
55
55
  # Start from the most complex down to the basic raw value
56
56
  value = hash["#{param_name}__U"]
57
57
  return value if value && type == :WITH_UNITS
@@ -47,7 +47,7 @@ module OpenC3
47
47
  Topic.update_topic_offsets([ack_topic])
48
48
  # Save the existing cmd_params Hash and JSON generate before writing to the topic
49
49
  cmd_params = command['cmd_params']
50
- command['cmd_params'] = JSON.generate(command['cmd_params'].as_json(:allow_nan => true))
50
+ command['cmd_params'] = JSON.generate(command['cmd_params'].as_json, allow_nan: true)
51
51
  OpenC3.inject_context(command)
52
52
  cmd_id = Topic.write_topic("{#{scope}__CMD}TARGET__#{command['target_name']}", command, '*', 100)
53
53
  command["cmd_params"] = cmd_params # Restore the original cmd_params Hash
@@ -42,10 +42,13 @@ module OpenC3
42
42
  while true
43
43
  Topic.read_topics(InterfaceTopic.topics(interface, scope: scope)) do |topic, msg_id, msg_hash, redis|
44
44
  result = yield topic, msg_id, msg_hash, redis
45
- ack_topic = topic.split("__")
46
- ack_topic[1] = 'ACK' + ack_topic[1]
47
- ack_topic = ack_topic.join("__")
48
- Topic.write_topic(ack_topic, { 'result' => result, 'id' => msg_id }, '*', 100)
45
+ if result
46
+ # Only ack if we intend to - Disabled targets will not ack
47
+ ack_topic = topic.split("__")
48
+ ack_topic[1] = 'ACK' + ack_topic[1]
49
+ ack_topic = ack_topic.join("__")
50
+ Topic.write_topic(ack_topic, { 'result' => result, 'id' => msg_id }, '*', 100)
51
+ end
49
52
  end
50
53
  end
51
54
  end
@@ -75,7 +78,7 @@ module OpenC3
75
78
 
76
79
  def self.connect_interface(interface_name, *interface_params, scope:)
77
80
  if interface_params && !interface_params.empty?
78
- Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'connect' => 'true', 'params' => JSON.generate(interface_params) }, '*', 100)
81
+ Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'connect' => 'true', 'params' => JSON.generate(interface_params, allow_nan: true) }, '*', 100)
79
82
  else
80
83
  Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'connect' => 'true' }, '*', 100)
81
84
  end
@@ -121,5 +124,42 @@ module OpenC3
121
124
  data['type'] = type
122
125
  Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'inject_tlm' => JSON.generate(data, allow_nan: true) }, '*', 100)
123
126
  end
127
+
128
+ def self.interface_target_enable(interface_name, target_name, cmd_only: false, tlm_only: false, scope:)
129
+ data = {}
130
+ data['target_name'] = target_name.to_s.upcase
131
+ data['cmd_only'] = cmd_only
132
+ data['tlm_only'] = tlm_only
133
+ data['action'] = 'enable'
134
+ Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100)
135
+ end
136
+
137
+ def self.interface_target_disable(interface_name, target_name, cmd_only: false, tlm_only: false, scope:)
138
+ data = {}
139
+ data['target_name'] = target_name.to_s.upcase
140
+ data['cmd_only'] = cmd_only
141
+ data['tlm_only'] = tlm_only
142
+ data['action'] = 'disable'
143
+ Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100)
144
+ end
145
+
146
+ def self.interface_details(interface_name, timeout: nil, scope:)
147
+ interface_name = interface_name.upcase
148
+
149
+ timeout = COMMAND_ACK_TIMEOUT_S unless timeout
150
+ ack_topic = "{#{scope}__ACKCMD}INTERFACE__#{interface_name}"
151
+ Topic.update_topic_offsets([ack_topic])
152
+
153
+ cmd_id = Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'interface_details' => 'true' }, '*', 100)
154
+ time = Time.now
155
+ while (Time.now - time) < timeout
156
+ Topic.read_topics([ack_topic]) do |_topic, _msg_id, msg_hash, _redis|
157
+ if msg_hash["id"] == cmd_id
158
+ return JSON.parse(msg_hash["result"], :allow_nan => true, :create_additions => true)
159
+ end
160
+ end
161
+ end
162
+ raise "Timeout of #{timeout}s waiting for cmd ack"
163
+ end
124
164
  end
125
165
  end
@@ -47,7 +47,7 @@ module OpenC3
47
47
  field = "#{event[:target_name]}__#{event[:packet_name]}__#{event[:item_name]}"
48
48
  limits_settings = Store.hget("#{scope}__current_limits_settings", field)
49
49
  if limits_settings
50
- limits_settings = JSON.parse(limits_settings, :allow_nan => true, :create_additions => true)
50
+ limits_settings = JSON.parse(limits_settings, allow_nan: true, create_additions: true)
51
51
  else
52
52
  limits_settings = {}
53
53
  end
@@ -61,18 +61,18 @@ module OpenC3
61
61
  limits_settings[event[:limits_set]] = limits
62
62
  limits_settings['persistence_setting'] = event[:persistence] if event[:persistence]
63
63
  limits_settings['enabled'] = event[:enabled] if not event[:enabled].nil?
64
- Store.hset("#{scope}__current_limits_settings", field, JSON.generate(limits_settings, :allow_nan => true))
64
+ Store.hset("#{scope}__current_limits_settings", field, JSON.generate(limits_settings, allow_nan: true))
65
65
 
66
66
  when :LIMITS_ENABLE_STATE
67
67
  field = "#{event[:target_name]}__#{event[:packet_name]}__#{event[:item_name]}"
68
68
  limits_settings = Store.hget("#{scope}__current_limits_settings", field)
69
69
  if limits_settings
70
- limits_settings = JSON.parse(limits_settings, :allow_nan => true, :create_additions => true)
70
+ limits_settings = JSON.parse(limits_settings, allow_nan: true, create_additions: true)
71
71
  else
72
72
  limits_settings = {}
73
73
  end
74
74
  limits_settings['enabled'] = event[:enabled]
75
- Store.hset("#{scope}__current_limits_settings", field, JSON.generate(limits_settings, :allow_nan => true))
75
+ Store.hset("#{scope}__current_limits_settings", field, JSON.generate(limits_settings, allow_nan: true))
76
76
 
77
77
  when :LIMITS_SET
78
78
  sets = sets(scope: scope)
@@ -86,7 +86,7 @@ module OpenC3
86
86
  raise "Invalid limits event type '#{event[:type]}'"
87
87
  end
88
88
 
89
- Topic.write_topic("#{scope}__openc3_limits_events", {event: JSON.generate(event, :allow_nan => true)}, '*', 1000)
89
+ Topic.write_topic("#{scope}__openc3_limits_events", {event: JSON.generate(event, allow_nan: true)}, '*', 1000)
90
90
  end
91
91
 
92
92
  # Remove the JSON encoding to return hashes directly
@@ -106,7 +106,7 @@ module OpenC3
106
106
  end
107
107
  parsed_result = []
108
108
  final_result.each do |offset, hash|
109
- parsed_result << [offset, JSON.parse(hash['event'], :allow_nan => true, :create_additions => true)]
109
+ parsed_result << [offset, JSON.parse(hash['event'], allow_nan: true, create_additions: true)]
110
110
  end
111
111
  return parsed_result
112
112
  end
@@ -175,7 +175,7 @@ module OpenC3
175
175
  if target
176
176
  packet = target[packet_name]
177
177
  if packet
178
- limits_settings = JSON.parse(limits_settings, :allow_nan => true, :create_additions => true)
178
+ limits_settings = JSON.parse(limits_settings, allow_nan: true, create_additions: true)
179
179
  enabled = limits_settings['enabled']
180
180
  persistence = limits_settings['persistence_setting']
181
181
  limits_settings.each do |limits_set, settings|
@@ -199,7 +199,7 @@ module OpenC3
199
199
  telemetry = System.telemetry.all
200
200
  topics = ["#{scope}__openc3_limits_events"]
201
201
  Topic.read_topics(topics, nil, block_ms) do |_topic, _msg_id, event, _redis|
202
- event = JSON.parse(event['event'], :allow_nan => true, :create_additions => true)
202
+ event = JSON.parse(event['event'], allow_nan: true, create_additions: true)
203
203
  case event['type']
204
204
  when 'LIMITS_CHANGE'
205
205
  # Ignore
@@ -24,6 +24,8 @@ require 'openc3/topics/topic'
24
24
 
25
25
  module OpenC3
26
26
  class RouterTopic < Topic
27
+ COMMAND_ACK_TIMEOUT_S = 30
28
+
27
29
  # Generate a list of topics for this router. This includes the router itself
28
30
  # and all the targets which are assigned to this router.
29
31
  def self.topics(router, scope:)
@@ -41,11 +43,11 @@ module OpenC3
41
43
  while true
42
44
  Topic.read_topics(RouterTopic.topics(router, scope: scope)) do |topic, msg_id, msg_hash, redis|
43
45
  result = yield topic, msg_id, msg_hash, redis
44
- if /CMD}ROUTER/.match?(topic)
46
+ if result and /CMD}ROUTER/.match?(topic)
45
47
  ack_topic = topic.split("__")
46
48
  ack_topic[1] = 'ACK' + ack_topic[1]
47
49
  ack_topic = ack_topic.join("__")
48
- Topic.write_topic(ack_topic, { 'result' => result }, msg_id, 100)
50
+ Topic.write_topic(ack_topic, { 'result' => result, 'id' => msg_id }, msg_id, 100)
49
51
  end
50
52
  end
51
53
  end
@@ -65,7 +67,7 @@ module OpenC3
65
67
 
66
68
  def self.connect_router(router_name, *router_params, scope:)
67
69
  if router_params && !router_params.empty?
68
- Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => 'true', 'params' => JSON.generate(router_params) }, '*', 100)
70
+ Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => 'true', 'params' => JSON.generate(router_params, allow_nan: true) }, '*', 100)
69
71
  else
70
72
  Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => 'true' }, '*', 100)
71
73
  end
@@ -102,5 +104,42 @@ module OpenC3
102
104
  data['index'] = index
103
105
  Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
104
106
  end
107
+
108
+ def self.router_target_enable(router_name, target_name, cmd_only: false, tlm_only: false, scope:)
109
+ data = {}
110
+ data['target_name'] = target_name.to_s.upcase
111
+ data['cmd_only'] = cmd_only
112
+ data['tlm_only'] = tlm_only
113
+ data['action'] = 'enable'
114
+ Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100)
115
+ end
116
+
117
+ def self.router_target_disable(router_name, target_name, cmd_only: false, tlm_only: false, scope:)
118
+ data = {}
119
+ data['target_name'] = target_name.to_s.upcase
120
+ data['cmd_only'] = cmd_only
121
+ data['tlm_only'] = tlm_only
122
+ data['action'] = 'disable'
123
+ Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100)
124
+ end
125
+
126
+ def self.router_details(router_name, timeout: nil, scope:)
127
+ router_name = router_name.upcase
128
+
129
+ timeout = COMMAND_ACK_TIMEOUT_S unless timeout
130
+ ack_topic = "{#{scope}__ACKCMD}ROUTER__#{router_name}"
131
+ Topic.update_topic_offsets([ack_topic])
132
+
133
+ cmd_id = Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_details' => 'true' }, '*', 100)
134
+ time = Time.now
135
+ while (Time.now - time) < timeout
136
+ Topic.read_topics([ack_topic]) do |_topic, _msg_id, msg_hash, _redis|
137
+ if msg_hash["id"] == cmd_id
138
+ return JSON.parse(msg_hash["result"], :allow_nan => true, :create_additions => true)
139
+ end
140
+ end
141
+ end
142
+ raise "Timeout of #{timeout}s waiting for cmd ack"
143
+ end
105
144
  end
106
145
  end
@@ -28,7 +28,7 @@ module OpenC3
28
28
 
29
29
  def self.write(type, event)
30
30
  event['type'] = type
31
- Topic.write_topic(PRIMARY_KEY, {event: JSON.generate(event)}, '*', 1000)
31
+ Topic.write_topic(PRIMARY_KEY, {event: JSON.generate(event, allow_nan: true)}, '*', 1000)
32
32
  end
33
33
 
34
34
  def self.read()
@@ -42,7 +42,7 @@ module OpenC3
42
42
  :target_name => packet.target_name,
43
43
  :packet_name => packet.packet_name,
44
44
  :received_count => packet.received_count,
45
- :json_data => JSON.generate(json_hash.as_json(:allow_nan => true)),
45
+ :json_data => JSON.generate(json_hash.as_json, allow_nan: true),
46
46
  }
47
47
  Topic.write_topic("#{scope}__DECOM__{#{packet.target_name}}__#{packet.packet_name}", msg_hash, id)
48
48
 
@@ -161,7 +161,7 @@ module OpenC3
161
161
  @log[1] = "response status: #{resp.status} header: #{resp.headers} body: #{resp.body}"
162
162
  STDOUT.puts @log[1] if JsonDRb.debug?
163
163
  if resp.status >= 200 && resp.status <= 299
164
- return JSON.parse(resp.body, :allow_nan => true, :create_additions => true)
164
+ return JSON.parse(resp.body, allow_nan: true, create_additions: true)
165
165
  elsif resp.status >= 500 && resp.status <= 599
166
166
  raise OpenC3AuthenticationRetryableError, "authentication request retryable #{@log[0]} ::: #{@log[1]}"
167
167
  else
@@ -50,11 +50,10 @@ module OpenC3
50
50
  other[:exception_backtrace] = payload[:exception_object].backtrace.as_json
51
51
  end
52
52
  end
53
- # This happens for a separate exception log entry which we want to not include the backtrace a second time
54
53
  if log.exception
55
- message = "Exception was raised - #{log.exception.class}:#{log.exception.message}" unless message
54
+ message = "Exception was raised - #{log.exception.class}:#{log.exception.message}:#{log.exception.backtrace.join(":")}" unless message
56
55
  end
57
- return OpenC3::Logger.build_log_data(log.level.to_s.upcase, message, user: username, type: OpenC3::Logger::LOG, url: nil, other: other).as_json(:allow_nan => true).to_json(:allow_nan => true)
56
+ return OpenC3::Logger.build_log_data(log.level.to_s.upcase, message, user: username, type: OpenC3::Logger::LOG, url: nil, other: other).as_json().to_json(allow_nan: true)
58
57
  end
59
58
  end
60
59
  end
@@ -170,7 +170,7 @@ module OpenC3
170
170
  if gem_name == local_gem_name
171
171
  # Gems match - Do the names match?
172
172
  data = File.read(plugin_instance)
173
- json = JSON.parse(data, :allow_nan => true, :create_additions => true)
173
+ json = JSON.parse(data, allow_nan: true, create_additions: true)
174
174
 
175
175
  found = false
176
176
  found_models.each do |name, _model_details|
@@ -221,7 +221,7 @@ module OpenC3
221
221
  if plugin_file_path =~ Regexp.new("^#{OPENC3_LOCAL_MODE_PATH}/#{scope}/")
222
222
  # From local init - Always just update the exact one
223
223
  File.open(File.join(File.dirname(plugin_file_path), 'plugin_instance.json'), 'wb') do |file|
224
- file.write(JSON.pretty_generate(plugin_hash, :allow_nan => true))
224
+ file.write(JSON.pretty_generate(plugin_hash, allow_nan: true))
225
225
  end
226
226
  else
227
227
  # From online install / update
@@ -247,7 +247,7 @@ module OpenC3
247
247
  if old_plugin_name
248
248
  # And we're updating a plugin
249
249
  data = File.read(plugin_instance)
250
- json = JSON.parse(data, :allow_nan => true, :create_additions => true)
250
+ json = JSON.parse(data, allow_nan: true, create_additions: true)
251
251
  if json["name"] == old_plugin_name
252
252
  # Found plugin to update
253
253
  found = true
@@ -301,7 +301,7 @@ module OpenC3
301
301
  file.write(data)
302
302
  end
303
303
  File.open(File.join(full_folder_path, 'plugin_instance.json'), 'wb') do |file|
304
- file.write(JSON.pretty_generate(plugin_hash, :allow_nan => true))
304
+ file.write(JSON.pretty_generate(plugin_hash, allow_nan: true))
305
305
  end
306
306
  ensure
307
307
  FileUtils.remove_entry_secure(temp_dir, true)
@@ -317,7 +317,7 @@ module OpenC3
317
317
  plugin_instance = details[:plugin_instance]
318
318
  if gems.length == 1 and plugin_instance
319
319
  data = File.read(plugin_instance)
320
- json = JSON.parse(data, :allow_nan => true, :create_additions => true)
320
+ json = JSON.parse(data, allow_nan: true, create_additions: true)
321
321
  instance_name = json['name']
322
322
  if plugin_name == instance_name
323
323
  puts "Removing local plugin files: #{full_folder_path}"
@@ -443,7 +443,7 @@ module OpenC3
443
443
  data = File.read(config)
444
444
  begin
445
445
  # Parse just to ensure we have valid JSON
446
- JSON.parse(data, :allow_nan => true, :create_additions => true)
446
+ JSON.parse(data, allow_nan: true, create_additions: true)
447
447
  # Only save if the parse was successful
448
448
  ToolConfigModel.save_config(parts[-2], File.basename(config, '.json'), data, scope: scope, local_mode: false)
449
449
  rescue JSON::ParserError => e
@@ -454,12 +454,12 @@ module OpenC3
454
454
  end
455
455
 
456
456
  def self.save_tool_config(scope, tool, name, data)
457
- json = JSON.parse(data, :allow_nan => true, :create_additions => true)
457
+ json = JSON.parse(data, allow_nan: true, create_additions: true)
458
458
  config_path = "#{OPENC3_LOCAL_MODE_PATH}/#{scope}/tool_config/#{tool}/#{name}.json"
459
459
  return unless File.expand_path(config_path).start_with?(OPENC3_LOCAL_MODE_PATH)
460
460
  FileUtils.mkdir_p(File.dirname(config_path))
461
461
  File.open(config_path, 'w') do |file|
462
- file.write(JSON.pretty_generate(json, :allow_nan => true))
462
+ file.write(JSON.pretty_generate(json, allow_nan: true))
463
463
  end
464
464
  end
465
465
 
@@ -204,14 +204,14 @@ module OpenC3
204
204
  case log_level
205
205
  when WARN_LEVEL, ERROR_LEVEL, FATAL_LEVEL
206
206
  if ENV['OPENC3_LOG_STDERR']
207
- $stderr.puts data.as_json(:allow_nan => true).to_json(:allow_nan => true)
207
+ $stderr.puts data.as_json().to_json(allow_nan: true)
208
208
  $stderr.flush
209
209
  else
210
- $stdout.puts data.as_json(:allow_nan => true).to_json(:allow_nan => true)
210
+ $stdout.puts data.as_json().to_json(allow_nan: true)
211
211
  $stdout.flush
212
212
  end
213
213
  else
214
- $stdout.puts data.as_json(:allow_nan => true).to_json(:allow_nan => true)
214
+ $stdout.puts data.as_json().to_json(allow_nan: true)
215
215
  $stdout.flush
216
216
  end
217
217
  end
@@ -51,13 +51,13 @@ SCRIPT_API = 'script-api'
51
51
 
52
52
  def running_script_publish(channel_name, data)
53
53
  stream_name = [SCRIPT_API, channel_name].compact.join(":")
54
- OpenC3::Store.publish(stream_name, JSON.generate(data))
54
+ OpenC3::Store.publish(stream_name, JSON.generate(data, allow_nan: true))
55
55
  end
56
56
 
57
57
  def running_script_anycable_publish(channel_name, data)
58
58
  stream_name = [SCRIPT_API, channel_name].compact.join(":")
59
- stream_data = {"stream" => stream_name, "data" => JSON.generate(data)}
60
- OpenC3::Store.publish("__anycable__", JSON.generate(stream_data))
59
+ stream_data = {"stream" => stream_name, "data" => JSON.generate(data, allow_nan: true)}
60
+ OpenC3::Store.publish("__anycable__", JSON.generate(stream_data, allow_nan: true))
61
61
  end
62
62
 
63
63
  module OpenC3
@@ -458,8 +458,8 @@ class RunningScript
458
458
  start_time: start_time, # Time the script started ISO format
459
459
  end_time: nil, # Time the script ended ISO format
460
460
  disconnect: disconnect, # Disconnect is set to true if the script is running in a disconnected mode
461
- environment: status_environment.as_json(:allow_nan => true).to_json(:allow_nan => true), # nil or Hash of key/value pairs for environment variables
462
- suite_runner: suite_runner ? suite_runner.as_json(:allow_nan => true).to_json(:allow_nan => true) : nil,
461
+ environment: status_environment, # hash of environment variables set for the script
462
+ suite_runner: suite_runner ? suite_runner : nil, # current suite information if any
463
463
  errors: nil, # array of errors that occurred during the script run
464
464
  pid: nil, # pid of the script process - set by the script itself when it starts
465
465
  script_engine: script_engine, # script engine filename
@@ -770,7 +770,6 @@ class RunningScript
770
770
 
771
771
  def run
772
772
  if @script_status.suite_runner
773
- @script_status.suite_runner = JSON.parse(@script_status.suite_runner, :allow_nan => true, :create_additions => true) # Convert to hash
774
773
  if @script_status.suite_runner['options']
775
774
  parse_options(@script_status.suite_runner['options'])
776
775
  else
@@ -1050,7 +1049,7 @@ class RunningScript
1050
1049
  line_count = 0
1051
1050
  string.each_line(chomp: true) do |out_line|
1052
1051
  begin
1053
- json = JSON.parse(out_line, :allow_nan => true, :create_additions => true)
1052
+ json = JSON.parse(out_line, allow_nan: true, create_additions: true)
1054
1053
  time_formatted = Time.parse(json["@timestamp"]).sys.formatted if json["@timestamp"]
1055
1054
  if json["log"]
1056
1055
  out_line = json["log"]
@@ -1082,7 +1081,7 @@ class RunningScript
1082
1081
  else
1083
1082
  published_lines = lines_to_write
1084
1083
  end
1085
- running_script_anycable_publish("running-script-channel:#{@script_status.id}", { type: :output, line: published_lines.as_json(:allow_nan => true), color: color })
1084
+ running_script_anycable_publish("running-script-channel:#{@script_status.id}", { type: :output, line: published_lines.as_json(), color: color })
1086
1085
  # Add to the message log
1087
1086
  message_log.write(lines_to_write)
1088
1087
  end
@@ -1,14 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- OPENC3_VERSION = '6.8.1'
3
+ OPENC3_VERSION = '6.9.1'
4
4
  module OpenC3
5
5
  module Version
6
6
  MAJOR = '6'
7
- MINOR = '8'
7
+ MINOR = '9'
8
8
  PATCH = '1'
9
9
  OTHER = ''
10
- BUILD = 'cd2a90541ad224d1dd22e28b4f6a9c0630db26ba'
10
+ BUILD = 'e1529f7b88799a9f85f31378db40a48b6c106899'
11
11
  end
12
- VERSION = '6.8.1'
13
- GEM_VERSION = '6.8.1'
12
+ VERSION = '6.9.1'
13
+ GEM_VERSION = '6.9.1'
14
14
  end
@@ -15,9 +15,9 @@ Update this comment with your own description.
15
15
  - VERSION is required
16
16
  - gem file will be built locally
17
17
 
18
- ## Building tool / widget plugins using a local Ruby/Node/Yarn/Rake Environment
18
+ ## Building tool / widget plugins using a local Ruby/Node/pnpm/Rake Environment
19
19
 
20
- 1. yarn
20
+ 1. pnpm install --frozen-lockfile --ignore-scripts
21
21
  1. rake build VERSION=1.0.0
22
22
 
23
23
  ## Building tool / widget plugins using Docker and the openc3-node container
@@ -36,7 +36,7 @@ Windows:
36
36
  docker run -it -v %cd%:/openc3/local -w /openc3/local docker.io/openc3inc/openc3-node sh
37
37
  ```
38
38
 
39
- 1. yarn
39
+ 1. pnpm install --frozen-lockfile --ignore-scripts
40
40
  1. rake build VERSION=1.0.0
41
41
 
42
42
  ## Installing into OpenC3 COSMOS