cosmos 5.0.4 → 5.0.5
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.
- checksums.yaml +4 -4
- data/bin/cosmos +182 -41
- data/data/config/plugins.yaml +10 -0
- data/ext/cosmos/ext/cosmos_io/cosmos_io.c +14 -14
- data/ext/cosmos/ext/packet/packet.c +3 -3
- data/ext/cosmos/ext/structure/structure.c +31 -31
- data/lib/cosmos/api/tlm_api.rb +4 -4
- data/lib/cosmos/logs/log_writer.rb +76 -27
- data/lib/cosmos/microservices/interface_microservice.rb +0 -15
- data/lib/cosmos/models/gem_model.rb +7 -1
- data/lib/cosmos/models/interface_model.rb +16 -7
- data/lib/cosmos/models/metadata_model.rb +39 -15
- data/lib/cosmos/models/microservice_model.rb +6 -3
- data/lib/cosmos/models/model.rb +7 -0
- data/lib/cosmos/models/note_model.rb +4 -6
- data/lib/cosmos/models/plugin_model.rb +107 -47
- data/lib/cosmos/models/scope_model.rb +2 -0
- data/lib/cosmos/models/sorted_model.rb +3 -1
- data/lib/cosmos/models/target_model.rb +24 -15
- data/lib/cosmos/models/tool_model.rb +8 -8
- data/lib/cosmos/models/widget_model.rb +10 -10
- data/lib/cosmos/packets/packet.rb +1 -1
- data/lib/cosmos/packets/structure.rb +30 -33
- data/lib/cosmos/script/calendar.rb +13 -2
- data/lib/cosmos/tools/table_manager/table_config.rb +16 -1
- data/lib/cosmos/tools/table_manager/table_manager_core.rb +213 -309
- data/lib/cosmos/topics/config_topic.rb +68 -0
- data/lib/cosmos/version.rb +5 -5
- metadata +7 -8
- data/bin/xtce_converter +0 -92
@@ -32,12 +32,19 @@ module Cosmos
|
|
32
32
|
# @return [String] Table configuration filename
|
33
33
|
attr_reader :filename
|
34
34
|
|
35
|
+
def self.process_file(filename)
|
36
|
+
instance = self.new()
|
37
|
+
instance.process_file(filename)
|
38
|
+
instance
|
39
|
+
end
|
40
|
+
|
35
41
|
# Create the table configuration
|
36
42
|
def initialize
|
37
43
|
super
|
38
|
-
|
39
44
|
# Override commands with the Table::TARGET name to store tables
|
40
45
|
@commands[Table::TARGET] = {}
|
46
|
+
@definitions = {}
|
47
|
+
@last_config = [] # Stores array of [filename, contents]
|
41
48
|
end
|
42
49
|
|
43
50
|
# @return [Array<Table>] All tables defined in the configuration file
|
@@ -45,6 +52,11 @@ module Cosmos
|
|
45
52
|
@commands[Table::TARGET]
|
46
53
|
end
|
47
54
|
|
55
|
+
# @return [String] Table definition for the specific table
|
56
|
+
def definition(table_name)
|
57
|
+
@definitions[table_name.upcase]
|
58
|
+
end
|
59
|
+
|
48
60
|
# @return [Array<String>] All the table names
|
49
61
|
def table_names
|
50
62
|
tables.keys
|
@@ -64,6 +76,7 @@ module Cosmos
|
|
64
76
|
# Partial files are included into another file and thus aren't directly processed
|
65
77
|
return if File.basename(filename)[0] == '_' # Partials start with underscore
|
66
78
|
@filename = filename
|
79
|
+
@last_config = [File.basename(filename), File.read(filename)]
|
67
80
|
@converted_type = nil
|
68
81
|
@converted_bit_size = nil
|
69
82
|
@proc_text = ''
|
@@ -111,10 +124,12 @@ module Cosmos
|
|
111
124
|
)
|
112
125
|
end
|
113
126
|
process_file(table_filename)
|
127
|
+
|
114
128
|
when 'TABLE'
|
115
129
|
finish_packet
|
116
130
|
@current_packet =
|
117
131
|
TableParser.parse_table(parser, @commands, @warnings)
|
132
|
+
@definitions[@current_packet.packet_name] = @last_config
|
118
133
|
@current_cmd_or_tlm = COMMAND
|
119
134
|
@default_index = 0
|
120
135
|
|
@@ -31,50 +31,111 @@ module Cosmos
|
|
31
31
|
class MismatchError < CoreError
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
'No current table configuration'
|
39
|
-
end
|
34
|
+
def self.binary(binary, definition_filename, table_name)
|
35
|
+
config = TableConfig.process_file(definition_filename)
|
36
|
+
load_binary(config, binary)
|
37
|
+
return config.table(table_name).buffer
|
40
38
|
end
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
#
|
45
|
-
def message
|
46
|
-
'Table does not exist in current configuration'
|
47
|
-
end
|
40
|
+
def self.definition(definition_filename, table_name)
|
41
|
+
config = TableConfig.process_file(definition_filename)
|
42
|
+
return config.definition(table_name) # This returns an array: [filename, contents]
|
48
43
|
end
|
49
44
|
|
50
|
-
|
51
|
-
|
45
|
+
def self.report(binary, definition_filename, requested_table_name = nil)
|
46
|
+
report = StringIO.new
|
47
|
+
config = TableConfig.process_file(definition_filename)
|
48
|
+
begin
|
49
|
+
load_binary(config, binary)
|
50
|
+
rescue CoreError => err
|
51
|
+
report.puts "Error: #{err.message}\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
config.tables.each do |table_name, table|
|
55
|
+
next if requested_table_name && table_name != requested_table_name
|
56
|
+
items = table.sorted_items
|
57
|
+
report.puts(table.table_name)
|
52
58
|
|
53
|
-
|
54
|
-
|
55
|
-
|
59
|
+
# Write the column headers
|
60
|
+
if table.type == :ROW_COLUMN
|
61
|
+
columns = ['Item']
|
62
|
+
|
63
|
+
# Remove the '0' from the 'itemname0'
|
64
|
+
table.num_columns.times.each do |x|
|
65
|
+
columns << items[x].name[0...-1]
|
66
|
+
end
|
67
|
+
report.puts columns.join(', ')
|
68
|
+
else
|
69
|
+
report.puts 'Label, Value'
|
70
|
+
end
|
71
|
+
|
72
|
+
# Write the table item values
|
73
|
+
(0...table.num_rows).each do |r|
|
74
|
+
if table.type == :ROW_COLUMN
|
75
|
+
rowtext = "#{r + 1}"
|
76
|
+
else
|
77
|
+
rowtext = items[r].name
|
78
|
+
end
|
79
|
+
|
80
|
+
report.write "#{rowtext}, "
|
81
|
+
(0...table.num_columns).each do |c|
|
82
|
+
if table.type == :ROW_COLUMN
|
83
|
+
table_item = items[c + r * table.num_columns]
|
84
|
+
else
|
85
|
+
table_item = items[r]
|
86
|
+
end
|
87
|
+
value = table.read(table_item.name, :FORMATTED)
|
88
|
+
if value.is_printable?
|
89
|
+
report.write "#{value}, "
|
90
|
+
else
|
91
|
+
report.write "#{value.simple_formatted}, "
|
92
|
+
end
|
93
|
+
end
|
94
|
+
report.write("\n") # newline after each row
|
95
|
+
end
|
96
|
+
report.write("\n") # newline after each table
|
97
|
+
end
|
98
|
+
report.string
|
56
99
|
end
|
57
100
|
|
58
|
-
|
59
|
-
|
60
|
-
|
101
|
+
def self.generate(definition_filename)
|
102
|
+
config = TableConfig.process_file(definition_filename)
|
103
|
+
binary = ''
|
104
|
+
config.tables.each do |table_name, table|
|
105
|
+
table.restore_defaults
|
106
|
+
binary += table.buffer
|
107
|
+
end
|
108
|
+
binary
|
61
109
|
end
|
62
110
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
111
|
+
def self.save(definition_filename, tables)
|
112
|
+
config = TableConfig.process_file(definition_filename)
|
113
|
+
tables.each do |table|
|
114
|
+
table_def = config.tables[table['name']]
|
115
|
+
table['rows'].each do |row|
|
116
|
+
row.each do |item|
|
117
|
+
# TODO: I don't know how the frontend could edit an item like this:
|
118
|
+
# item:{"name"=>"BINARY", "value"=>{"json_class"=>"String", "raw"=>[222, 173, 190, 239]} }
|
119
|
+
next if item['value'].is_a? Hash
|
120
|
+
table_def.write(item['name'], item['value'])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
binary = ''
|
125
|
+
config.tables.each { |table_name, table| binary += table.buffer }
|
126
|
+
binary
|
67
127
|
end
|
68
128
|
|
69
|
-
def
|
129
|
+
def self.build_json(binary, definition_filename)
|
130
|
+
config = TableConfig.process_file(definition_filename)
|
70
131
|
tables = []
|
71
132
|
json = { tables: tables }
|
72
133
|
begin
|
73
|
-
|
134
|
+
load_binary(config, binary)
|
74
135
|
rescue CoreError => err
|
75
136
|
json['errors'] = err.message
|
76
137
|
end
|
77
|
-
|
138
|
+
config.tables.each do |table_name, table|
|
78
139
|
tables << {
|
79
140
|
name: table_name,
|
80
141
|
numRows: table.num_rows,
|
@@ -129,301 +190,144 @@ module Cosmos
|
|
129
190
|
json.to_json
|
130
191
|
end
|
131
192
|
|
132
|
-
def
|
133
|
-
file_open(bin_path, def_path)
|
134
|
-
tables.each do |table|
|
135
|
-
table_def = @config.tables[table['name']]
|
136
|
-
table['rows'].each do |row|
|
137
|
-
row.each do |item|
|
138
|
-
# TODO: I don't know how the frontend could edit an item like this:
|
139
|
-
# item:{"name"=>"BINARY", "value"=>{"json_class"=>"String", "raw"=>[222, 173, 190, 239]} }
|
140
|
-
next if item['value'].is_a? Hash
|
141
|
-
table_def.write(item['name'], item['value'])
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
file_save(bin_path)
|
146
|
-
bin_path
|
147
|
-
end
|
148
|
-
|
149
|
-
# @param def_path [String] Definition file to process
|
150
|
-
# @param output_dir [String] Output directory to create the new file
|
151
|
-
# @return [String] Binary file path
|
152
|
-
def file_new(def_path, output_dir)
|
153
|
-
process_definition(def_path)
|
154
|
-
@config.table_names.each do |table_name|
|
155
|
-
set_binary_data_to_default(table_name)
|
156
|
-
end
|
157
|
-
bin_path = File.join(output_dir, def_to_bin_filename(def_path))
|
158
|
-
file_save(bin_path)
|
159
|
-
bin_path
|
160
|
-
end
|
161
|
-
|
162
|
-
# @param bin_path [String] Binary file to open
|
163
|
-
# @param def_path [String] Definition file to use when opening
|
164
|
-
def file_open(bin_path, def_path)
|
165
|
-
process_definition(def_path)
|
166
|
-
open_and_load_binary_file(bin_path)
|
167
|
-
end
|
168
|
-
|
169
|
-
# Saves the current tables in the config instance to the given filename.
|
170
|
-
#
|
171
|
-
# @param filename [String] Filename to write, overwritten if it exists.
|
172
|
-
def file_save(filename)
|
173
|
-
raise NoConfigError unless @config
|
174
|
-
file_check
|
175
|
-
File.open(filename, 'wb') do |file|
|
176
|
-
@config.tables.each { |table_name, table| file.write(table.buffer) }
|
177
|
-
end
|
178
|
-
# file_report(filename, @config.filename)
|
179
|
-
end
|
180
|
-
|
181
|
-
# @return [String] Success string if parameters all check. Raises
|
182
|
-
# a CoreError if errors are found.
|
183
|
-
def file_check
|
184
|
-
raise NoConfigError unless @config
|
185
|
-
result = ''
|
186
|
-
@config.table_names.each do |name|
|
187
|
-
table_result = table_check(name)
|
188
|
-
unless table_result.empty?
|
189
|
-
result << "Errors in #{name}:\n" + table_result
|
190
|
-
end
|
191
|
-
end
|
192
|
-
raise CoreError, result unless result.empty?
|
193
|
-
'All parameters are within their constraints.'
|
194
|
-
end
|
195
|
-
|
196
|
-
# Create a CSV report file based on the file contents.
|
197
|
-
#
|
198
|
-
# @param bin_path [String] Binary filename currently open. Used to generate the
|
199
|
-
# report name such that it matches the binary filename.
|
200
|
-
# @param def_path [String] Definition filename currently open
|
201
|
-
# @return [String] Report filename path
|
202
|
-
def file_report(bin_path, def_path)
|
203
|
-
raise NoConfigError unless @config
|
204
|
-
file_check
|
205
|
-
|
206
|
-
basename = File.basename(bin_path, '.bin')
|
207
|
-
report_path = File.join(File.dirname(bin_path), "#{basename}.csv")
|
208
|
-
File.open(report_path, 'w+') do |file|
|
209
|
-
file.write("File Definition, #{def_path}\n")
|
210
|
-
file.write("File Binary, #{bin_path}\n\n")
|
211
|
-
@config.tables.values.each do |table|
|
212
|
-
items = table.sorted_items
|
213
|
-
file.puts(table.table_name)
|
214
|
-
|
215
|
-
# Write the column headers
|
216
|
-
if table.type == :ROW_COLUMN
|
217
|
-
columns = ['Item']
|
218
|
-
|
219
|
-
# Remove the '0' from the 'itemname0'
|
220
|
-
table.num_columns.times.each do |x|
|
221
|
-
columns << items[x].name[0...-1]
|
222
|
-
end
|
223
|
-
file.puts columns.join(', ')
|
224
|
-
else
|
225
|
-
file.puts 'Label, Value'
|
226
|
-
end
|
227
|
-
|
228
|
-
# Write the table item values
|
229
|
-
(0...table.num_rows).each do |r|
|
230
|
-
if table.type == :ROW_COLUMN
|
231
|
-
rowtext = "#{r + 1}"
|
232
|
-
else
|
233
|
-
rowtext = items[r].name
|
234
|
-
end
|
235
|
-
|
236
|
-
file.write "#{rowtext}, "
|
237
|
-
(0...table.num_columns).each do |c|
|
238
|
-
if table.type == :ROW_COLUMN
|
239
|
-
table_item = items[c + r * table.num_columns]
|
240
|
-
else
|
241
|
-
table_item = items[r]
|
242
|
-
end
|
243
|
-
|
244
|
-
file.write "#{table.read(table_item.name, :FORMATTED).to_s}, "
|
245
|
-
end
|
246
|
-
file.write("\n") # newline after each row
|
247
|
-
end
|
248
|
-
file.write("\n") # newline after each table
|
249
|
-
end
|
250
|
-
end
|
251
|
-
report_path
|
252
|
-
end
|
253
|
-
|
254
|
-
# Create a hex formatted string of all the file data
|
255
|
-
def file_hex
|
256
|
-
raise NoConfigError unless @config
|
257
|
-
data = ''
|
258
|
-
@config.tables.values.each { |table| data << table.buffer }
|
259
|
-
"#{data.formatted}\n\nTotal Bytes Read: #{data.length}"
|
260
|
-
end
|
261
|
-
|
262
|
-
# @param table_name [String] Name of the table to check for out of range values
|
263
|
-
def table_check(table_name)
|
264
|
-
raise NoConfigError unless @config
|
265
|
-
table = @config.table(table_name)
|
266
|
-
raise NoTableError unless table
|
267
|
-
|
268
|
-
result = ''
|
269
|
-
table_items = table.sorted_items
|
270
|
-
|
271
|
-
# Check the ranges and constraints for each item in the table
|
272
|
-
# We go through it this way (by row and columns) so we can grab the actual
|
273
|
-
# user input when we display any errors found
|
274
|
-
(0...table.num_rows).each do |r|
|
275
|
-
(0...table.num_columns).each do |c|
|
276
|
-
# get the table item definition so we know how to save it
|
277
|
-
table_item = table_items[r * table.num_columns + c]
|
278
|
-
|
279
|
-
value = table.read(table_item.name)
|
280
|
-
unless table_item.range.nil?
|
281
|
-
# If the item has states which include the value, then convert
|
282
|
-
# the state back to the numeric value for range checking
|
283
|
-
if table_item.states && table_item.states.include?(value)
|
284
|
-
value = table_item.states[value]
|
285
|
-
end
|
286
|
-
|
287
|
-
# check to see if the value lies within its valid range
|
288
|
-
unless table_item.range.include?(value)
|
289
|
-
if table_item.format_string
|
290
|
-
value = table.read(table_item.name, :FORMATTED)
|
291
|
-
range_first =
|
292
|
-
sprintf(table_item.format_string, table_item.range.first)
|
293
|
-
range_last =
|
294
|
-
sprintf(table_item.format_string, table_item.range.last)
|
295
|
-
else
|
296
|
-
range_first = table_item.range.first
|
297
|
-
range_last = table_item.range.last
|
298
|
-
end
|
299
|
-
result <<
|
300
|
-
" #{table_item.name}: #{value} outside valid range of #{range_first}..#{range_last}\n"
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end # end each column
|
304
|
-
end # end each row
|
305
|
-
result
|
306
|
-
end
|
307
|
-
|
308
|
-
# @param table_name [String] Name of the table to revert all values to default
|
309
|
-
def table_default(table_name)
|
310
|
-
raise NoConfigError unless @config
|
311
|
-
set_binary_data_to_default(table_name)
|
312
|
-
end
|
313
|
-
|
314
|
-
# @param table_name [String] Create a hex formatted string of the given table data
|
315
|
-
def table_hex(table_name)
|
316
|
-
raise NoConfigError unless @config
|
317
|
-
table = @config.table(table_name)
|
318
|
-
raise NoTableError unless table
|
319
|
-
"#{table.buffer.formatted}\n\nTotal Bytes Read: #{table.buffer.length}"
|
320
|
-
end
|
321
|
-
|
322
|
-
# @param table_name [String] Table name to write as a stand alone file
|
323
|
-
# @param filename [String] Filename to write the table data to. Existing
|
324
|
-
# files will be overwritten.
|
325
|
-
def table_save(table_name, filename)
|
326
|
-
raise NoConfigError unless @config
|
327
|
-
result = table_check(table_name)
|
328
|
-
unless result.empty?
|
329
|
-
raise CoreError, "Errors in #{table_name}:\n#{result}"
|
330
|
-
end
|
331
|
-
File.open(filename, 'wb') do |file|
|
332
|
-
file.write(@config.table(table_name).buffer)
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
# Commit a table from the current configuration into a new binary
|
337
|
-
#
|
338
|
-
# @param table_name [String] Table name to commit to an existing binary
|
339
|
-
# @param bin_file [String] Binary file to open
|
340
|
-
# @param def_file [String] Definition file to use when opening
|
341
|
-
def table_commit(table_name, bin_file, def_file)
|
342
|
-
raise NoConfigError unless @config
|
343
|
-
save_table = @config.table(table_name)
|
344
|
-
raise NoTableError unless save_table
|
345
|
-
|
346
|
-
result = table_check(table_name)
|
347
|
-
unless result.empty?
|
348
|
-
raise CoreError, "Errors in #{table_name}:\n#{result}"
|
349
|
-
end
|
350
|
-
|
351
|
-
config = TableConfig.new
|
352
|
-
begin
|
353
|
-
config.process_file(def_file)
|
354
|
-
rescue => err
|
355
|
-
raise CoreError,
|
356
|
-
"The table definition file:#{def_file} has the following errors:\n#{err}"
|
357
|
-
end
|
358
|
-
|
359
|
-
if !config.table_names.include?(table_name.upcase)
|
360
|
-
raise NoTableError,
|
361
|
-
"#{table_name} not found in #{def_file} table definition file."
|
362
|
-
end
|
363
|
-
|
364
|
-
saved_config = @config
|
365
|
-
@config = config
|
366
|
-
open_and_load_binary_file(bin_file)
|
367
|
-
|
368
|
-
# Store the saved table data in the new table definition
|
369
|
-
table = config.table(save_table.table_name)
|
370
|
-
table.buffer = save_table.buffer[0...table.length]
|
371
|
-
file_save(bin_file)
|
372
|
-
@config = saved_config
|
373
|
-
end
|
374
|
-
|
375
|
-
protected
|
376
|
-
|
377
|
-
# Set all the binary data in the table to the default values
|
378
|
-
def set_binary_data_to_default(table_name)
|
379
|
-
table = @config.table(table_name)
|
380
|
-
raise NoTableError unless table
|
381
|
-
table.restore_defaults
|
382
|
-
end
|
383
|
-
|
384
|
-
# Get the binary filename equivalent for the given definition filename
|
385
|
-
def def_to_bin_filename(def_path)
|
386
|
-
if File.basename(def_path) =~ /_def\.txt$/
|
387
|
-
# Remove _def.txt if present (should be)
|
388
|
-
basename = File.basename(def_path)[0...-8]
|
389
|
-
else
|
390
|
-
# Remove any extension if present
|
391
|
-
basename = File.basename(def_path, File.extname(def_path))
|
392
|
-
end
|
393
|
-
"#{basename}.bin"
|
394
|
-
end
|
395
|
-
|
396
|
-
# Opens the given binary file and populates the table definition.
|
397
|
-
# The filename parameter should be a properly formatted file path.
|
398
|
-
def open_and_load_binary_file(filename)
|
399
|
-
begin
|
400
|
-
data = nil
|
401
|
-
|
402
|
-
# read the binary file and store it into an array
|
403
|
-
File.open(filename, 'rb') { |file| data = file.read }
|
404
|
-
rescue => err
|
405
|
-
raise "Unable to open and load #{filename} due to #{err}."
|
406
|
-
end
|
407
|
-
|
193
|
+
def self.load_binary(config, data)
|
408
194
|
binary_data_index = 0
|
409
195
|
total_table_length = 0
|
410
|
-
|
196
|
+
config.tables.each do |table_name, table|
|
411
197
|
total_table_length += table.length
|
412
198
|
end
|
413
|
-
|
199
|
+
config.tables.each do |table_name, table|
|
414
200
|
if binary_data_index + table.length > data.length
|
415
201
|
table.buffer = data[binary_data_index..-1]
|
416
202
|
raise MismatchError,
|
417
|
-
|
203
|
+
"Binary size of #{data.length} not large enough to fully represent table definition of length #{total_table_length}. "+
|
204
|
+
"The remaining table definition (starting with byte #{data.length - binary_data_index} in #{table.table_name}) will be filled with 0."
|
418
205
|
end
|
419
|
-
table.buffer =
|
420
|
-
data[binary_data_index...binary_data_index + table.length]
|
206
|
+
table.buffer = data[binary_data_index...binary_data_index + table.length]
|
421
207
|
binary_data_index += table.length
|
422
208
|
end
|
423
209
|
if binary_data_index < data.length
|
424
210
|
raise MismatchError,
|
425
|
-
|
211
|
+
"Binary size of #{data.length} larger than table definition of length #{total_table_length}. "+
|
212
|
+
"Discarding the remaing #{data.length - binary_data_index} bytes."
|
426
213
|
end
|
427
214
|
end
|
215
|
+
|
216
|
+
# TODO: Potentially useful methods?
|
217
|
+
# # @return [String] Success string if parameters all check. Raises
|
218
|
+
# # a CoreError if errors are found.
|
219
|
+
# def file_check
|
220
|
+
# raise NoConfigError unless @config
|
221
|
+
# result = ''
|
222
|
+
# @config.table_names.each do |name|
|
223
|
+
# table_result = table_check(name)
|
224
|
+
# unless table_result.empty?
|
225
|
+
# result << "Errors in #{name}:\n" + table_result
|
226
|
+
# end
|
227
|
+
# end
|
228
|
+
# raise CoreError, result unless result.empty?
|
229
|
+
# 'All parameters are within their constraints.'
|
230
|
+
# end
|
231
|
+
|
232
|
+
# # Create a hex formatted string of all the file data
|
233
|
+
# def file_hex
|
234
|
+
# raise NoConfigError unless @config
|
235
|
+
# data = ''
|
236
|
+
# @config.tables.values.each { |table| data << table.buffer }
|
237
|
+
# "#{data.formatted}\n\nTotal Bytes Read: #{data.length}"
|
238
|
+
# end
|
239
|
+
|
240
|
+
# # @param table_name [String] Name of the table to check for out of range values
|
241
|
+
# def table_check(table_name)
|
242
|
+
# raise NoConfigError unless @config
|
243
|
+
# table = @config.table(table_name)
|
244
|
+
# raise NoTableError unless table
|
245
|
+
|
246
|
+
# result = ''
|
247
|
+
# table_items = table.sorted_items
|
248
|
+
|
249
|
+
# # Check the ranges and constraints for each item in the table
|
250
|
+
# # We go through it this way (by row and columns) so we can grab the actual
|
251
|
+
# # user input when we display any errors found
|
252
|
+
# (0...table.num_rows).each do |r|
|
253
|
+
# (0...table.num_columns).each do |c|
|
254
|
+
# # get the table item definition so we know how to save it
|
255
|
+
# table_item = table_items[r * table.num_columns + c]
|
256
|
+
|
257
|
+
# value = table.read(table_item.name)
|
258
|
+
# unless table_item.range.nil?
|
259
|
+
# # If the item has states which include the value, then convert
|
260
|
+
# # the state back to the numeric value for range checking
|
261
|
+
# if table_item.states && table_item.states.include?(value)
|
262
|
+
# value = table_item.states[value]
|
263
|
+
# end
|
264
|
+
|
265
|
+
# # check to see if the value lies within its valid range
|
266
|
+
# unless table_item.range.include?(value)
|
267
|
+
# if table_item.format_string
|
268
|
+
# value = table.read(table_item.name, :FORMATTED)
|
269
|
+
# range_first =
|
270
|
+
# sprintf(table_item.format_string, table_item.range.first)
|
271
|
+
# range_last =
|
272
|
+
# sprintf(table_item.format_string, table_item.range.last)
|
273
|
+
# else
|
274
|
+
# range_first = table_item.range.first
|
275
|
+
# range_last = table_item.range.last
|
276
|
+
# end
|
277
|
+
# result <<
|
278
|
+
# " #{table_item.name}: #{value} outside valid range of #{range_first}..#{range_last}\n"
|
279
|
+
# end
|
280
|
+
# end
|
281
|
+
# end # end each column
|
282
|
+
# end # end each row
|
283
|
+
# result
|
284
|
+
# end
|
285
|
+
|
286
|
+
# # @param table_name [String] Create a hex formatted string of the given table data
|
287
|
+
# def table_hex(table_name)
|
288
|
+
# raise NoConfigError unless @config
|
289
|
+
# table = @config.table(table_name)
|
290
|
+
# raise NoTableError unless table
|
291
|
+
# "#{table.buffer.formatted}\n\nTotal Bytes Read: #{table.buffer.length}"
|
292
|
+
# end
|
293
|
+
|
294
|
+
# # Commit a table from the current configuration into a new binary
|
295
|
+
# #
|
296
|
+
# # @param table_name [String] Table name to commit to an existing binary
|
297
|
+
# # @param bin_file [String] Binary file to open
|
298
|
+
# # @param def_file [String] Definition file to use when opening
|
299
|
+
# def table_commit(table_name, bin_file, def_file)
|
300
|
+
# raise NoConfigError unless @config
|
301
|
+
# save_table = @config.table(table_name)
|
302
|
+
# raise NoTableError unless save_table
|
303
|
+
|
304
|
+
# result = table_check(table_name)
|
305
|
+
# unless result.empty?
|
306
|
+
# raise CoreError, "Errors in #{table_name}:\n#{result}"
|
307
|
+
# end
|
308
|
+
|
309
|
+
# config = TableConfig.new
|
310
|
+
# begin
|
311
|
+
# config.process_file(def_file)
|
312
|
+
# rescue => err
|
313
|
+
# raise CoreError,
|
314
|
+
# "The table definition file:#{def_file} has the following errors:\n#{err}"
|
315
|
+
# end
|
316
|
+
|
317
|
+
# if !config.table_names.include?(table_name.upcase)
|
318
|
+
# raise NoTableError,
|
319
|
+
# "#{table_name} not found in #{def_file} table definition file."
|
320
|
+
# end
|
321
|
+
|
322
|
+
# saved_config = @config
|
323
|
+
# @config = config
|
324
|
+
# open_and_load_binary_file(bin_file)
|
325
|
+
|
326
|
+
# # Store the saved table data in the new table definition
|
327
|
+
# table = config.table(save_table.table_name)
|
328
|
+
# table.buffer = save_table.buffer[0...table.length]
|
329
|
+
# file_save(bin_file)
|
330
|
+
# @config = saved_config
|
331
|
+
# end
|
428
332
|
end
|
429
333
|
end
|
@@ -0,0 +1,68 @@
|
|
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
|
+
# This program may also be used under the terms of a commercial or
|
17
|
+
# enterprise edition license of COSMOS if purchased from the
|
18
|
+
# copyright holder
|
19
|
+
|
20
|
+
require 'cosmos/topics/topic'
|
21
|
+
|
22
|
+
module Cosmos
|
23
|
+
class ConfigTopic < Topic
|
24
|
+
PRIMARY_KEY = "__CONFIG"
|
25
|
+
|
26
|
+
# Helper method to initialize the stream and ensure a consistent key
|
27
|
+
def self.initialize_stream(scope)
|
28
|
+
self.initialize_streams(["#{scope}#{PRIMARY_KEY}"])
|
29
|
+
end
|
30
|
+
|
31
|
+
# Write a configuration change to the topic
|
32
|
+
# @param config [Hash] Hash with required keys 'kind', 'name', 'type'
|
33
|
+
def self.write(config, scope:)
|
34
|
+
unless config.keys.include?(:kind)
|
35
|
+
raise "ConfigTopic error, required key kind: not given"
|
36
|
+
end
|
37
|
+
unless ['created', 'deleted'].include?(config[:kind])
|
38
|
+
raise "ConfigTopic error unknown kind: #{config[:kind]}"
|
39
|
+
end
|
40
|
+
unless config.keys.include?(:name)
|
41
|
+
raise "ConfigTopic error, required key name: not given"
|
42
|
+
end
|
43
|
+
unless config.keys.include?(:type)
|
44
|
+
raise "ConfigTopic error, required key type: not given"
|
45
|
+
end
|
46
|
+
# Limit the configuration topics to 1000 entries
|
47
|
+
Topic.write_topic("#{scope}#{PRIMARY_KEY}", config, '*', 1000)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.read(offset = nil, count: 100, scope:)
|
51
|
+
topic = "#{scope}#{PRIMARY_KEY}"
|
52
|
+
if offset
|
53
|
+
result = Topic.read_topics([topic], [offset], nil, count)
|
54
|
+
if result.empty?
|
55
|
+
[] # We want to return an empty array rather than an empty hash
|
56
|
+
else
|
57
|
+
# result is a hash with the topic key followed by an array of results
|
58
|
+
# This returns just the array of arrays [[offset, hash], [offset, hash], ...]
|
59
|
+
result[topic]
|
60
|
+
end
|
61
|
+
else
|
62
|
+
result = Topic.get_newest_message(topic)
|
63
|
+
return [result] if result
|
64
|
+
return []
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|