openc3 5.20.0 → 6.0.0

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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +12 -120
  3. data/data/config/command_modifiers.yaml +13 -1
  4. data/data/config/interface_modifiers.yaml +21 -4
  5. data/data/config/item_modifiers.yaml +1 -1
  6. data/data/config/microservice.yaml +15 -2
  7. data/data/config/param_item_modifiers.yaml +1 -1
  8. data/data/config/parameter_modifiers.yaml +1 -1
  9. data/data/config/table_manager.yaml +2 -2
  10. data/data/config/target.yaml +11 -0
  11. data/data/config/telemetry_modifiers.yaml +17 -1
  12. data/data/config/tool.yaml +12 -0
  13. data/data/config/widgets.yaml +13 -17
  14. data/lib/openc3/accessors/form_accessor.rb +4 -3
  15. data/lib/openc3/accessors/html_accessor.rb +3 -3
  16. data/lib/openc3/accessors/http_accessor.rb +13 -13
  17. data/lib/openc3/accessors/xml_accessor.rb +16 -4
  18. data/lib/openc3/api/target_api.rb +0 -30
  19. data/lib/openc3/config/config_parser.rb +6 -3
  20. data/lib/openc3/core_ext/array.rb +0 -16
  21. data/lib/openc3/core_ext.rb +0 -1
  22. data/lib/openc3/interfaces/file_interface.rb +198 -0
  23. data/lib/openc3/interfaces/http_client_interface.rb +71 -39
  24. data/lib/openc3/interfaces/http_server_interface.rb +0 -7
  25. data/lib/openc3/interfaces/interface.rb +2 -0
  26. data/lib/openc3/interfaces/mqtt_interface.rb +32 -15
  27. data/lib/openc3/interfaces/mqtt_stream_interface.rb +19 -4
  28. data/lib/openc3/interfaces/protocols/crc_protocol.rb +7 -0
  29. data/lib/openc3/interfaces/serial_interface.rb +1 -0
  30. data/lib/openc3/interfaces.rb +2 -4
  31. data/lib/openc3/microservices/multi_microservice.rb +3 -3
  32. data/lib/openc3/migrations/20241208080000_no_critical_cmd.rb +31 -0
  33. data/lib/openc3/migrations/20241208080001_no_trigger_group.rb +46 -0
  34. data/lib/openc3/models/interface_model.rb +9 -3
  35. data/lib/openc3/models/microservice_model.rb +8 -1
  36. data/lib/openc3/models/plugin_model.rb +6 -1
  37. data/lib/openc3/models/python_package_model.rb +6 -1
  38. data/lib/openc3/models/reaction_model.rb +14 -10
  39. data/lib/openc3/models/scope_model.rb +60 -42
  40. data/lib/openc3/models/target_model.rb +17 -1
  41. data/lib/openc3/models/timeline_model.rb +17 -5
  42. data/lib/openc3/models/tool_model.rb +15 -3
  43. data/lib/openc3/models/trigger_group_model.rb +6 -3
  44. data/lib/openc3/operators/microservice_operator.rb +8 -0
  45. data/lib/openc3/packets/commands.rb +17 -6
  46. data/lib/openc3/packets/limits.rb +0 -12
  47. data/lib/openc3/packets/packet.rb +1 -1
  48. data/lib/openc3/packets/packet_item.rb +30 -36
  49. data/lib/openc3/packets/structure_item.rb +2 -2
  50. data/lib/openc3/script/script.rb +0 -10
  51. data/lib/openc3/script/web_socket_api.rb +2 -2
  52. data/lib/openc3/streams/mqtt_stream.rb +41 -33
  53. data/lib/openc3/streams/serial_stream.rb +27 -27
  54. data/lib/openc3/streams/stream.rb +17 -17
  55. data/lib/openc3/streams/tcpip_client_stream.rb +1 -1
  56. data/lib/openc3/streams/tcpip_socket_stream.rb +19 -19
  57. data/lib/openc3/system/system.rb +1 -1
  58. data/lib/openc3/system.rb +2 -3
  59. data/lib/openc3/tools/table_manager/table.rb +2 -2
  60. data/lib/openc3/tools/table_manager/table_parser.rb +1 -1
  61. data/lib/openc3/top_level.rb +0 -5
  62. data/lib/openc3/topics/command_decom_topic.rb +0 -7
  63. data/lib/openc3/utilities/bucket_utilities.rb +1 -1
  64. data/lib/openc3/utilities/cli_generator.rb +0 -1
  65. data/lib/openc3/version.rb +6 -6
  66. data/templates/plugin/README.md +1 -1
  67. data/templates/target/targets/TARGET/lib/target.rb +1 -1
  68. data/templates/tool_angular/package.json +8 -8
  69. data/templates/tool_angular/src/app/app.component.html +4 -13
  70. data/templates/tool_angular/src/app/app.component.scss +5 -13
  71. data/templates/tool_angular/src/app/app.component.ts +5 -4
  72. data/templates/tool_angular/src/app/custom-overlay-container.ts +2 -2
  73. data/templates/tool_angular/src/app/openc3-api.d.ts +1 -1
  74. data/templates/tool_angular/src/main.single-spa.ts +1 -1
  75. data/templates/tool_react/package.json +1 -0
  76. data/templates/tool_react/src/root.component.js +1 -1
  77. data/templates/tool_svelte/package.json +11 -9
  78. data/templates/tool_svelte/rollup.config.js +2 -0
  79. data/templates/tool_svelte/src/App.svelte +2 -2
  80. data/templates/tool_vue/eslint.config.mjs +68 -0
  81. data/templates/tool_vue/jsconfig.json +1 -1
  82. data/templates/tool_vue/package.json +26 -43
  83. data/templates/tool_vue/src/App.vue +3 -5
  84. data/templates/tool_vue/src/main.js +12 -23
  85. data/templates/tool_vue/src/router.js +19 -18
  86. data/templates/tool_vue/src/tools/tool_name/tool_name.vue +2 -2
  87. data/templates/tool_vue/vite.config.js +52 -0
  88. data/templates/widget/package.json +19 -26
  89. data/templates/widget/src/Widget.vue +13 -15
  90. data/templates/widget/vite.config.js +26 -0
  91. metadata +10 -41
  92. data/lib/openc3/core_ext/hash.rb +0 -40
  93. data/lib/openc3/core_ext/httpclient.rb +0 -11
  94. data/lib/openc3/interfaces/linc_interface.rb +0 -480
  95. data/lib/openc3/interfaces/protocols/override_protocol.rb +0 -4
  96. data/lib/openc3/microservices/critical_cmd_microservice.rb +0 -74
  97. data/lib/openc3/microservices/reaction_microservice.rb +0 -607
  98. data/lib/openc3/microservices/timeline_microservice.rb +0 -398
  99. data/lib/openc3/microservices/trigger_group_microservice.rb +0 -698
  100. data/lib/openc3/migrations/20230615000000_autonomic.rb +0 -86
  101. data/lib/openc3/migrations/20240915000000_activity_uuid.rb +0 -28
  102. data/lib/openc3/migrations/20241016000000_scope_critical_cmd.rb +0 -24
  103. data/lib/openc3/system/system_config.rb +0 -413
  104. data/templates/tool_svelte/src/services/api.js +0 -92
  105. data/templates/tool_svelte/src/services/axios.js +0 -85
  106. data/templates/tool_svelte/src/services/cable.js +0 -65
  107. data/templates/tool_svelte/src/services/config-parser.js +0 -198
  108. data/templates/tool_svelte/src/services/openc3-api.js +0 -606
  109. data/templates/tool_vue/.eslintrc.js +0 -43
  110. data/templates/tool_vue/babel.config.json +0 -11
  111. data/templates/tool_vue/vue.config.js +0 -38
  112. data/templates/widget/.eslintrc.js +0 -43
  113. data/templates/widget/babel.config.json +0 -11
  114. data/templates/widget/vue.config.js +0 -28
  115. /data/templates/tool_vue/{.prettierrc.js → .prettierrc.cjs} +0 -0
  116. /data/templates/widget/{.prettierrc.js → .prettierrc.cjs} +0 -0
@@ -1,86 +0,0 @@
1
- require 'openc3/utilities/migration'
2
- require 'openc3/models/trigger_group_model'
3
- require 'openc3/models/trigger_model'
4
- require 'openc3/models/reaction_model'
5
-
6
- module OpenC3
7
- class Autonomic < Migration
8
- def self.run
9
- ScopeModel.names.each do |scope|
10
- puts "Processing scope #{scope}"
11
-
12
- # Update all old TriggerModels just so they work when we delete the ReactionModel
13
- # Because the ReactionModel verifies the triggers
14
- delete_all_triggers = false
15
- groups = TriggerGroupModel.all(scope: scope)
16
- groups.each do |key, group_hash|
17
- puts "Processing group #{group_hash['name']}"
18
- if group_hash.has_key?('color')
19
- group_hash.delete('color')
20
- group = TriggerGroupModel.from_json(group_hash, name: group_hash['name'], scope: scope)
21
- group.update()
22
- end
23
- TriggerModel.all(group: group_hash['name'], scope: scope).each do |key, model_hash|
24
- if model_hash.has_key?('description') or model_hash.has_key?('active')
25
- puts "Updating TriggerModel: #{model_hash['name']}"
26
- model_hash.delete('description')
27
- model_hash.delete('active')
28
- model_hash['left'] = {'type' => 'item', 'target' => 'TGT', 'packet' => 'PKT', 'item' => 'ITEM', 'valueType' => 'CONVERTED'}
29
- model_hash['operator'] = 'CHANGES'
30
- model_hash['right'] = nil
31
- TriggerModel.from_json(model_hash, name: model_hash['name'], scope: scope).update()
32
- delete_all_triggers = true
33
- end
34
- end
35
- end
36
-
37
- # Remove all old ReactionModels
38
- ReactionModel.all(scope: scope).each do |key, model_hash|
39
- if model_hash.has_key?('description') or model_hash.has_key?('review') or model_hash.has_key?('active')
40
- # Can't delete directly because delete calls get which calls from_json which calls new
41
- # and at that point we get missing keyword: :triggerLevel (ArgumentError)
42
- # So update to add triggerLevel
43
- model_hash['triggerLevel'] = 'EDGE'
44
- model_hash.delete('description')
45
- model_hash.delete('review')
46
- model_hash.delete('active')
47
- ReactionModel.from_json(model_hash, name: model_hash['name'], scope: scope).update()
48
- puts "Deleting ReactionModel: #{model_hash['name']}"
49
- ReactionModel.delete(name: model_hash['name'], scope: scope)
50
- end
51
- end
52
-
53
- # Remove all old TriggerModels and TriggerGroupModels
54
- if delete_all_triggers
55
- groups = TriggerGroupModel.all(scope: scope)
56
- groups.each do |key, group_hash|
57
- TriggerModel.all(group: group_hash['name'], scope: scope).each do |key, trigger_hash|
58
- puts "Deleting TriggerModel: #{trigger_hash['name']}"
59
- TriggerModel.delete(name: trigger_hash['name'], group: group_hash['name'], scope: scope)
60
- end
61
- group = TriggerGroupModel.from_json(group_hash, name: group_hash['name'], scope: scope)
62
- group.undeploy()
63
- puts "Deleting TriggerGroupModel: #{group_hash['name']}"
64
- TriggerGroupModel.delete(name: group_hash['name'], scope: scope)
65
- end
66
- end
67
-
68
- # Create DEFAULT trigger group model
69
- model = TriggerGroupModel.get(name: 'DEFAULT', scope: scope)
70
- unless model
71
- puts "Creating TriggerGroupModel: DEFAULT"
72
- model = TriggerGroupModel.new(name: 'DEFAULT', scope: scope)
73
- model.create()
74
- model.deploy()
75
- end
76
- end
77
- rescue => error
78
- puts error.message
79
- puts error.backtrace
80
- end
81
- end
82
- end
83
-
84
- unless ENV['OPENC3_NO_MIGRATE']
85
- OpenC3::Autonomic.run
86
- end
@@ -1,28 +0,0 @@
1
- require 'openc3/utilities/migration'
2
- require 'openc3/models/scope_model'
3
- require 'openc3/models/timeline_model'
4
-
5
- module OpenC3
6
- class ActivityUuid < Migration
7
- def self.run
8
- ScopeModel.names.each do |scope|
9
- TimelineModel.names.each do |key|
10
- name = key.split('__').last
11
- json = Store.zrange("#{scope}#{ActivityModel::PRIMARY_KEY}__#{name}", 0, -1)
12
- parsed = json.map { |value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
13
- parsed.each_with_index do |activity, index|
14
- if activity['uuid'].nil?
15
- activity['uuid'] = SecureRandom.uuid
16
- Store.zrem("#{scope}#{ActivityModel::PRIMARY_KEY}__#{name}", json[index])
17
- Store.zadd("#{scope}#{ActivityModel::PRIMARY_KEY}__#{name}", activity['start'], JSON.generate(activity))
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
25
-
26
- unless ENV['OPENC3_NO_MIGRATE']
27
- OpenC3::ActivityUuid.run
28
- end
@@ -1,24 +0,0 @@
1
- require 'openc3/utilities/migration'
2
- require 'openc3/models/scope_model'
3
-
4
- module OpenC3
5
- class ScopeCriticalCmd < Migration
6
- def self.run
7
- ScopeModel.names.each do |scope|
8
- existing_model = MicroserviceModel.get_model(name: "#{scope}__CRITICALCMD__#{scope}", scope: scope)
9
- if not existing_model
10
- scope_model = ScopeModel.get_model(name: scope)
11
- parent = "#{scope}__SCOPEMULTI__#{scope}"
12
- scope_model.deploy_critical_cmd_microservice("/notexist", {}, parent)
13
- microservice_model = MicroserviceModel.get_model(name: parent, scope: scope)
14
- microservice_model.cmd << "#{scope}__CRITICALCMD__#{scope}"
15
- microservice_model.update
16
- end
17
- end
18
- end
19
- end
20
- end
21
-
22
- unless ENV['OPENC3_NO_MIGRATE']
23
- OpenC3::ScopeCriticalCmd.run
24
- end
@@ -1,413 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- # Copyright 2022 Ball Aerospace & Technologies Corp.
4
- # All Rights Reserved.
5
- #
6
- # This program is free software; you can modify and/or redistribute it
7
- # under the terms of the GNU Affero General Public License
8
- # as published by the Free Software Foundation; version 3 with
9
- # attribution addendums as found in the LICENSE.txt
10
- #
11
- # This program is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- # GNU Affero General Public License for more details.
15
-
16
- # Modified by OpenC3, Inc.
17
- # All changes Copyright 2024, OpenC3, Inc.
18
- # All Rights Reserved
19
- #
20
- # This file may also be used under the terms of a commercial license
21
- # if purchased from OpenC3, Inc.
22
-
23
- require 'openc3/config/config_parser'
24
- require 'openc3/packets/packet_config'
25
- require 'openc3/packets/commands'
26
- require 'openc3/packets/telemetry'
27
- require 'openc3/packets/limits'
28
- require 'openc3/system/target'
29
- require 'openc3/logs'
30
- require 'fileutils'
31
- require 'openc3/utilities/zip'
32
- require 'bundler'
33
-
34
- module OpenC3
35
- # System is the primary entry point into the OpenC3 framework. It captures
36
- # system wide configuration items such as the available ports and paths to
37
- # various files used by the system. The #commands, #telemetry, and #limits
38
- # class variables are the primary access points for applications. The
39
- # #targets variable provides access to all the targets defined by the system.
40
- # Its primary responsibily is to load the system configuration file and
41
- # create all the Target instances. It also saves and restores configurations
42
- # using a hashing checksum over the entire configuration to detect changes.
43
- class SystemConfig
44
- # @return [String] Base path of the configuration
45
- attr_reader :userpath
46
- # @return [Boolean] Whether to use sound for alerts
47
- attr_reader :sound
48
- # @return [Boolean] Whether to use DNS to lookup IP addresses or not
49
- attr_reader :use_dns
50
- # @return [Hash<String,Target>] Hash of all the known targets
51
- attr_reader :targets
52
- # @return [Integer] The number of seconds before a telemetry packet is considered stale
53
- attr_reader :staleness_seconds
54
- # @return [Boolean] Whether to use UTC or local times
55
- attr_reader :use_utc
56
- # @return [Hash<String,String>] Hash of the text/color to use for the classificaiton banner
57
- attr_reader :classificiation_banner
58
-
59
- # @param filename [String] Full path to the system configuration file to
60
- # read.
61
- def initialize(filename)
62
- reset_variables(filename)
63
- end
64
-
65
- # Resets the System's internal state to defaults.
66
- #
67
- # @param filename [String] Path to system.txt config file to process. Defaults to config/system/system.txt
68
- def reset_variables(filename)
69
- @targets = {}
70
- @config = nil
71
- @commands = nil
72
- @telemetry = nil
73
- @limits = nil
74
- @sound = false
75
- @use_dns = false
76
- @staleness_seconds = 30
77
- @use_utc = false
78
- @meta_init_filename = nil
79
- @userpath = File.expand_path(File.join(File.dirname(filename), '..', '..'))
80
- process_file(filename, File.join(@userpath, 'config', 'targets'))
81
- @initial_filename = filename
82
- @initial_config = nil
83
- @config_blacklist = {}
84
- end
85
-
86
- # Process the system.txt configuration file
87
- #
88
- # @param filename [String] The configuration file
89
- # @param targets_config_dir [String] The configuration directory to
90
- # search for the target command and telemetry files.
91
- def process_file(filename, targets_config_dir)
92
- parser = ConfigParser.new("https://docs.openc3.com/docs")
93
-
94
- # First pass - Everything except targets
95
- parser.parse_file(filename) do |keyword, parameters|
96
- case keyword
97
- when 'AUTO_DECLARE_TARGETS', 'DECLARE_TARGET', 'DECLARE_GEM_TARGET', 'DECLARE_GEM_MULTI_TARGET'
98
- # Will be handled by second pass
99
-
100
- when 'PORT', 'LISTEN_HOST', 'CONNECT_HOST', 'PATH', 'DEFAULT_PACKET_LOG_WRITER', 'DEFAULT_PACKET_LOG_READER',
101
- 'ALLOW_ACCESS', 'ADD_HASH_FILE', 'ADD_MD5_FILE', 'HASHING_ALGORITHM'
102
- # Not used by OpenC3 5
103
-
104
- when 'ENABLE_SOUND'
105
- usage = "#{keyword}"
106
- parser.verify_num_parameters(0, 0, usage)
107
- @sound = true
108
-
109
- when 'DISABLE_DNS'
110
- usage = "#{keyword}"
111
- parser.verify_num_parameters(0, 0, usage)
112
- @use_dns = false
113
-
114
- when 'ENABLE_DNS'
115
- usage = "#{keyword}"
116
- parser.verify_num_parameters(0, 0, usage)
117
- @use_dns = true
118
-
119
- when 'STALENESS_SECONDS'
120
- parser.verify_num_parameters(1, 1, "#{keyword} <Value in Seconds>")
121
- @staleness_seconds = Integer(parameters[0])
122
-
123
- when 'META_INIT'
124
- parser.verify_num_parameters(1, 1, "#{keyword} <Filename>")
125
- @meta_init_filename = ConfigParser.handle_nil(parameters[0])
126
-
127
- when 'TIME_ZONE_UTC'
128
- parser.verify_num_parameters(0, 0, "#{keyword}")
129
- @use_utc = true
130
-
131
- when 'CLASSIFICATION'
132
- parser.verify_num_parameters(2, 4, "#{keyword} <Display_Text> <Color Name|Red> <Green> <Blue>")
133
- # Determine if the OpenC3 color already exists, otherwise create a new one
134
- if OpenC3.constants.include? parameters[1].upcase.to_sym
135
- # We were given a named color that already exists in OpenC3
136
- color = parameters[1].upcase
137
- else
138
- if parameters.length < 4
139
- # We were given a named color, but it didn't exist in OpenC3 already
140
- color = OpenC3.getColor(parameters[1].upcase)
141
- else
142
- # We were given RGB values
143
- color = OpenC3.getColor(parameters[1], parameters[2], parameters[3])
144
- end
145
- end
146
-
147
- @classificiation_banner = { 'display_text' => parameters[0],
148
- 'color' => color }
149
-
150
- else
151
- # blank lines will have a nil keyword and should not raise an exception
152
- raise parser.error("Unknown keyword '#{keyword}'") if keyword
153
- end # case keyword
154
- end # parser.parse_file
155
-
156
- # Explicitly set up time to use UTC or local
157
- if @use_utc
158
- Time.use_utc()
159
- else
160
- Time.use_local()
161
- end
162
-
163
- # Second pass - Process targets
164
- process_targets(parser, filename, targets_config_dir)
165
- end # def process_file
166
-
167
- # Parse the system.txt configuration file looking for keywords associated
168
- # with targets and create all the Target instances in the system.
169
- #
170
- # @param parser [ConfigParser] Parser created by process_file
171
- # @param filename (see #process_file)
172
- # @param targets_config_dir (see #process_file)
173
- def process_targets(parser, filename, targets_config_dir)
174
- parser.parse_file(filename) do |keyword, parameters|
175
- case keyword
176
- when 'AUTO_DECLARE_TARGETS'
177
- usage = "#{keyword}"
178
- parser.verify_num_parameters(0, 0, usage)
179
- path = File.join(@userpath, 'config', 'targets')
180
- unless File.exist? path
181
- raise parser.error("#{path} must exist", usage)
182
- end
183
-
184
- dirs = []
185
- configuration_dir = File.join(@userpath, 'config', 'targets')
186
- Dir.foreach(configuration_dir) { |dir_filename| dirs << dir_filename }
187
- dirs.sort!
188
- dirs.each do |dir_filename|
189
- if dir_filename[0] != '.'
190
- if dir_filename == dir_filename.upcase
191
- # If any of the targets original directory name matches the
192
- # current directory then it must have been already processed by
193
- # DECLARE_TARGET so we skip it.
194
- next if @targets.select { |_name, target| target.original_name == dir_filename }.length > 0
195
- next if dir_filename == 'SYSTEM'
196
-
197
- target = Target.new(dir_filename, nil, targets_config_dir)
198
- @targets[target.name] = target
199
- else
200
- raise parser.error("Target folder must be uppercase: '#{dir_filename}'")
201
- end
202
- end
203
- end
204
- auto_detect_gem_based_targets()
205
-
206
- when 'DECLARE_TARGET'
207
- usage = "#{keyword} <TARGET NAME> <SUBSTITUTE TARGET NAME (Optional)> <TARGET FILENAME (Optional - defaults to target.txt)>"
208
- parser.verify_num_parameters(1, 3, usage)
209
- target_name = parameters[0].to_s.upcase
210
-
211
- if targets_config_dir
212
- folder_name = File.join(targets_config_dir, target_name)
213
- else
214
- folder_name = File.join(@userpath, 'config', 'targets', target_name)
215
- end
216
- unless Dir.exist?(folder_name)
217
- raise parser.error("Target folder must exist '#{folder_name}'.")
218
- end
219
-
220
- substitute_name = nil
221
- substitute_name = ConfigParser.handle_nil(parameters[1])
222
- if substitute_name
223
- substitute_name = substitute_name.to_s.upcase
224
- original_name = target_name
225
- target_name = substitute_name
226
- else
227
- original_name = nil
228
- end
229
-
230
- target = Target.new(target_name, original_name, targets_config_dir, ConfigParser.handle_nil(parameters[2]))
231
- @targets[target.name] = target
232
-
233
- when 'DECLARE_GEM_TARGET'
234
- usage = "#{keyword} <GEM NAME> <SUBSTITUTE TARGET NAME (Optional)> <TARGET FILENAME (Optional - defaults to target.txt)>"
235
- parser.verify_num_parameters(1, 3, usage)
236
- # Remove 'openc3' from the gem name 'openc3-power-supply'
237
- target_name = parameters[0].split('-')[1..-1].join('-').to_s.upcase
238
- gem_dir = Gem::Specification.find_by_name(parameters[0]).gem_dir
239
- substitute_name = nil
240
- substitute_name = ConfigParser.handle_nil(parameters[1])
241
- if substitute_name
242
- substitute_name = substitute_name.to_s.upcase
243
- original_name = target_name
244
- target_name = substitute_name
245
- else
246
- original_name = nil
247
- end
248
- target = Target.new(target_name, original_name, targets_config_dir, ConfigParser.handle_nil(parameters[2]), gem_dir)
249
- @targets[target.name] = target
250
-
251
- when 'DECLARE_GEM_MULTI_TARGET'
252
- usage = "#{keyword} <GEM NAME> <TARGET NAME> <SUBSTITUTE TARGET NAME (Optional)> <TARGET FILENAME (Optional - defaults to target.txt)>"
253
- parser.verify_num_parameters(2, 4, usage)
254
- target_name = parameters[1].to_s.upcase
255
- gem_dir = Gem::Specification.find_by_name(parameters[0]).gem_dir
256
- gem_dir = File.join(gem_dir, target_name)
257
- substitute_name = nil
258
- substitute_name = ConfigParser.handle_nil(parameters[2])
259
- if substitute_name
260
- substitute_name = substitute_name.to_s.upcase
261
- original_name = target_name
262
- target_name = substitute_name
263
- else
264
- original_name = nil
265
- end
266
- target = Target.new(target_name, original_name, targets_config_dir, ConfigParser.handle_nil(parameters[3]), gem_dir)
267
- @targets[target.name] = target
268
-
269
- end # case keyword
270
- end # parser.parse_file
271
-
272
- # Make sure SYSTEM target is always present and added last
273
- unless @targets.key?('SYSTEM')
274
- target = Target.new('SYSTEM', nil, targets_config_dir)
275
- @targets[target.name] = target
276
- end
277
- end
278
-
279
- protected
280
-
281
- def unzip(zip_file_name)
282
- zip_dir = File.join(@paths['TMP'], File.basename(zip_file_name, ".*"))
283
- # Only unzip if we have to. We assume the unzipped directory structure is
284
- # intact. If not they'll get a popop with the errors encountered when
285
- # loading the configuration.
286
- unless File.exist? zip_dir
287
- Zip::File.open(zip_file_name) do |zip_file|
288
- zip_file.each do |entry|
289
- path = File.join(@paths['TMP'], entry.name)
290
- FileUtils.mkdir_p(File.dirname(path))
291
- zip_file.extract(entry, path) unless File.exist?(path)
292
- end
293
- end
294
- end
295
- zip_dir
296
- end
297
-
298
- # A helper method to make the zip writing recursion work
299
- def write_zip_entries(base_dir, entries, zip_path, io)
300
- io.add(zip_path, base_dir) # Add the directory whether it has entries or not
301
- entries.each do |e|
302
- zip_file_path = File.join(zip_path, e)
303
- disk_file_path = File.join(base_dir, e)
304
- if File.directory? disk_file_path
305
- recursively_deflate_directory(disk_file_path, io, zip_file_path)
306
- else
307
- put_into_archive(disk_file_path, io, zip_file_path)
308
- end
309
- end
310
- end
311
-
312
- def recursively_deflate_directory(disk_file_path, io, zip_file_path)
313
- io.add(zip_file_path, disk_file_path)
314
- entries = Dir.entries(disk_file_path) - %w(. ..)
315
- write_zip_entries(disk_file_path, entries, zip_file_path, io)
316
- end
317
-
318
- def put_into_archive(disk_file_path, io, zip_file_path)
319
- io.get_output_stream(zip_file_path) do |f|
320
- data = nil
321
- File.open(disk_file_path, 'rb') do |file|
322
- data = file.read
323
- end
324
- f.write(data)
325
- end
326
- end
327
-
328
- def auto_detect_gem_based_targets
329
- Bundler.load.specs.each do |spec|
330
- spec_name_split = spec.name.split('-')
331
- if spec_name_split.length > 1 && (spec_name_split[0] == 'openc3')
332
- # search for multiple targets packaged in a single gem
333
- dirs = []
334
- Dir.foreach(spec.gem_dir) { |dir_filename| dirs << dir_filename }
335
- dirs.sort!
336
- dirs.each do |dir_filename|
337
- if dir_filename == "."
338
- # check the base directory
339
- curr_dir = spec.gem_dir
340
- target_name = spec_name_split[1..-1].join('-').to_s.upcase
341
- else
342
- # check for targets in other directories 1 level deep
343
- next if dir_filename[0] == '.' # skip dot directories and ".."
344
- next if dir_filename != dir_filename.upcase # skip non uppercase directories
345
-
346
- curr_dir = File.join(spec.gem_dir, dir_filename)
347
- target_name = dir_filename
348
- end
349
- # check for the cmd_tlm directory - if it has it, then we have found a target
350
- if File.directory?(File.join(curr_dir, 'cmd_tlm'))
351
- # If any of the targets original directory name matches the
352
- # current directory then it must have been already processed by
353
- # DECLARE_TARGET so we skip it.
354
- next if @targets.select { |_name, target| target.original_name == target_name }.length > 0
355
-
356
- target = Target.new(target_name, nil, nil, nil, spec.gem_dir)
357
- @targets[target.name] = target
358
- end
359
- end
360
- end
361
- end
362
- rescue Bundler::GemfileNotFound
363
- # No Gemfile - so no gem based targets
364
- end
365
-
366
- def save_configuration
367
- configuration = find_configuration(@config.name)
368
- configuration = File.join(@paths['SAVED_CONFIG'], File.build_timestamped_filename([@config.name], '.zip')) unless configuration
369
- unless File.exist?(configuration)
370
- configuration_tmp = File.join(@paths['SAVED_CONFIG'], File.build_timestamped_filename(['tmp_' + @config.name], '.zip.tmp'))
371
- begin
372
- Zip.continue_on_exists_proc = true
373
- Zip::File.open(configuration_tmp, Zip::File::CREATE) do |zipfile|
374
- zip_file_path = File.basename(configuration, ".zip")
375
- zipfile.mkdir zip_file_path
376
-
377
- # Copy target files into archive
378
- zip_targets = []
379
- @targets.each do |_target_name, target|
380
- entries = Dir.entries(target.dir) - %w(. ..)
381
- zip_target = File.join(zip_file_path, target.original_name)
382
- # Check the stored list of targets. We can't ask the zip file
383
- # itself because it's in progress and hasn't been saved
384
- unless zip_targets.include?(zip_target)
385
- write_zip_entries(target.dir, entries, zip_target, zipfile)
386
- zip_targets << zip_target
387
- end
388
- end
389
-
390
- # Create custom system.txt file
391
- zipfile.get_output_stream(File.join(zip_file_path, 'system.txt')) do |file|
392
- @targets.each do |_target_name, target|
393
- target_filename = File.basename(target.filename)
394
- target_filename = nil unless File.exist?(target.filename)
395
- # Create a newline character since Zip opens files in binary mode
396
- newline = Kernel.is_windows? ? "\r\n" : "\n"
397
- if target.substitute
398
- file.write "DECLARE_TARGET #{target.original_name} #{target.name} #{target_filename}#{newline}"
399
- else
400
- file.write "DECLARE_TARGET #{target.name} nil #{target_filename}#{newline}"
401
- end
402
- end
403
- end
404
- end
405
- File.rename(configuration_tmp, configuration)
406
- File.chmod(0444, configuration) # Mark readonly
407
- rescue Exception => e
408
- Logger.error "Problem saving configuration to #{configuration}: #{e.class}:#{e.message}\n#{e.backtrace.join("\n")}\n"
409
- end
410
- end
411
- end
412
- end
413
- end
@@ -1,92 +0,0 @@
1
- /*
2
- # Copyright 2022 Ball Aerospace & Technologies Corp.
3
- # All Rights Reserved.
4
- #
5
- # This program is free software; you can modify and/or redistribute it
6
- # under the terms of the GNU Affero General Public License
7
- # as published by the Free Software Foundation; version 3 with
8
- # attribution addendums as found in the LICENSE.txt
9
- #
10
- # This program is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- # GNU Affero General Public License for more details.
14
-
15
- # Modified by OpenC3, Inc.
16
- # All changes Copyright 2022, OpenC3, Inc.
17
- # All Rights Reserved
18
- #
19
- # This file may also be used under the terms of a commercial license
20
- # if purchased from OpenC3, Inc.
21
- */
22
-
23
- import axios from './axios.js'
24
-
25
- const request = async function (
26
- method,
27
- url,
28
- { data, params = {}, headers, noAuth = false, noScope = false } = {}
29
- ) {
30
- if (!noAuth) {
31
- try {
32
- let refreshed = await OpenC3Auth.updateToken(
33
- OpenC3Auth.defaultMinValidity
34
- )
35
- if (refreshed) {
36
- OpenC3Auth.setTokens()
37
- }
38
- } catch (error) {
39
- OpenC3Auth.login()
40
- }
41
- headers['Authorization'] = localStorage.openc3Token
42
- }
43
- if (!noScope && !params['scope']) {
44
- params['scope'] = window.openc3Scope
45
- }
46
- return axios({
47
- method,
48
- url,
49
- data,
50
- params,
51
- headers,
52
- })
53
- }
54
-
55
- const acceptOnlyDefaultHeaders = {
56
- Accept: 'application/json',
57
- }
58
-
59
- const fullDefaultHeaders = {
60
- ...acceptOnlyDefaultHeaders,
61
- 'Content-Type': 'application/json',
62
- }
63
-
64
- export default {
65
- get: function (
66
- path,
67
- { params, headers = acceptOnlyDefaultHeaders, noScope, noAuth } = {}
68
- ) {
69
- return request('get', path, { params, headers, noScope, noAuth })
70
- },
71
-
72
- put: function (
73
- path,
74
- { data, params, headers = fullDefaultHeaders, noScope, noAuth } = {}
75
- ) {
76
- return request('put', path, { data, params, headers, noScope, noAuth })
77
- },
78
-
79
- post: function (
80
- path,
81
- { data, params, headers = fullDefaultHeaders, noScope, noAuth } = {}
82
- ) {
83
- return request('post', path, { data, params, headers, noScope, noAuth })
84
- },
85
-
86
- delete: function (
87
- path,
88
- { params, headers = acceptOnlyDefaultHeaders, noScope, noAuth } = {}
89
- ) {
90
- return request('delete', path, { params, headers, noScope, noAuth })
91
- },
92
- }