cosmos 3.1.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Manifest.txt +17 -1
  4. data/autohotkey/tools/test_runner2.ahk +1 -0
  5. data/autohotkey/tools/tlm_grapher.ahk +13 -1
  6. data/data/crc.txt +39 -30
  7. data/demo/config/data/crc.txt +3 -3
  8. data/demo/config/targets/TEMPLATED/lib/templated_interface.rb +3 -1
  9. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +7 -1
  10. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +6 -1
  11. data/lib/cosmos.rb +2 -2
  12. data/lib/cosmos/gui/dialogs/about_dialog.rb +18 -5
  13. data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +0 -7
  14. data/lib/cosmos/gui/line_graph/overview_graph.rb +12 -2
  15. data/lib/cosmos/gui/utilities/script_module_gui.rb +11 -3
  16. data/lib/cosmos/interfaces/interface.rb +12 -0
  17. data/lib/cosmos/interfaces/stream_interface.rb +1 -21
  18. data/lib/cosmos/interfaces/tcpip_server_interface.rb +10 -0
  19. data/lib/cosmos/io/json_drb_object.rb +75 -56
  20. data/lib/cosmos/io/tcpip_server.rb +1 -11
  21. data/lib/cosmos/packet_logs.rb +1 -0
  22. data/lib/cosmos/packet_logs/ccsds_log_reader.rb +103 -0
  23. data/lib/cosmos/packets/packet.rb +70 -1
  24. data/lib/cosmos/packets/packet_config.rb +59 -611
  25. data/lib/cosmos/packets/parsers/format_string_parser.rb +58 -0
  26. data/lib/cosmos/packets/parsers/limits_parser.rb +146 -0
  27. data/lib/cosmos/packets/parsers/limits_response_parser.rb +52 -0
  28. data/lib/cosmos/packets/parsers/macro_parser.rb +116 -0
  29. data/lib/cosmos/packets/parsers/packet_item_parser.rb +215 -0
  30. data/lib/cosmos/packets/parsers/packet_parser.rb +123 -0
  31. data/lib/cosmos/packets/parsers/processor_parser.rb +63 -0
  32. data/lib/cosmos/packets/parsers/state_parser.rb +116 -0
  33. data/lib/cosmos/packets/structure.rb +59 -22
  34. data/lib/cosmos/packets/structure_item.rb +1 -1
  35. data/lib/cosmos/script/script.rb +4 -5
  36. data/lib/cosmos/streams/serial_stream.rb +5 -0
  37. data/lib/cosmos/streams/stream.rb +8 -2
  38. data/lib/cosmos/streams/stream_protocol.rb +1 -0
  39. data/lib/cosmos/streams/tcpip_client_stream.rb +37 -7
  40. data/lib/cosmos/streams/tcpip_socket_stream.rb +9 -6
  41. data/lib/cosmos/system/target.rb +3 -6
  42. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +57 -48
  43. data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +7 -3
  44. data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +1 -1
  45. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +7 -1
  46. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +1 -2
  47. data/lib/cosmos/top_level.rb +22 -11
  48. data/lib/cosmos/utilities/message_log.rb +14 -9
  49. data/lib/cosmos/version.rb +5 -5
  50. data/spec/interfaces/cmd_tlm_server_interface_spec.rb +16 -16
  51. data/spec/interfaces/linc_interface_spec.rb +3 -0
  52. data/spec/interfaces/tcpip_client_interface_spec.rb +1 -0
  53. data/spec/interfaces/tcpip_server_interface_spec.rb +9 -0
  54. data/spec/io/json_drb_object_spec.rb +1 -1
  55. data/spec/io/serial_driver_spec.rb +0 -1
  56. data/spec/packet_logs/packet_log_writer_spec.rb +5 -3
  57. data/spec/packets/packet_config_spec.rb +22 -837
  58. data/spec/packets/packet_item_spec.rb +10 -10
  59. data/spec/packets/packet_spec.rb +239 -1
  60. data/spec/packets/parsers/format_string_parser_spec.rb +122 -0
  61. data/spec/packets/parsers/limits_parser_spec.rb +282 -0
  62. data/spec/packets/parsers/limits_response_parser_spec.rb +149 -0
  63. data/spec/packets/parsers/macro_parser_spec.rb +184 -0
  64. data/spec/packets/parsers/packet_item_parser_spec.rb +306 -0
  65. data/spec/packets/parsers/packet_parser_spec.rb +99 -0
  66. data/spec/packets/parsers/processor_parser_spec.rb +114 -0
  67. data/spec/packets/parsers/state_parser_spec.rb +156 -0
  68. data/spec/packets/structure_item_spec.rb +14 -14
  69. data/spec/packets/structure_spec.rb +162 -16
  70. data/spec/streams/fixed_stream_protocol_spec.rb +7 -4
  71. data/spec/streams/length_stream_protocol_spec.rb +3 -0
  72. data/spec/streams/preidentified_stream_protocol_spec.rb +3 -0
  73. data/spec/streams/serial_stream_spec.rb +12 -0
  74. data/spec/streams/stream_protocol_spec.rb +14 -0
  75. data/spec/streams/stream_spec.rb +1 -0
  76. data/spec/streams/tcpip_client_stream_spec.rb +3 -0
  77. data/spec/streams/tcpip_socket_stream_spec.rb +15 -3
  78. data/spec/streams/template_stream_protocol_spec.rb +5 -0
  79. data/spec/streams/terminated_stream_protocol_spec.rb +4 -0
  80. data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +21 -1
  81. data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +1 -1
  82. data/spec/tools/cmd_tlm_server/interfaces_spec.rb +1 -1
  83. metadata +19 -3
@@ -0,0 +1,123 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2014 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 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
+ require 'cosmos/packets/packet'
12
+
13
+ module Cosmos
14
+
15
+ class PacketParser
16
+ # @param parser [ConfigParser] Configuration parser
17
+ # @param target_name [String] The name of the target to create the packet
18
+ # under. If the target name is 'SYSTEM' the keyword parameter will be
19
+ # used instead of this parameter.
20
+ # @param commands [Hash] Hash of the currently defined commands
21
+ # @param warnings [Array<String>] Any warning strings generated while
22
+ # parsing this command will be appened to this array
23
+ def self.parse_command(parser, target_name, commands, warnings)
24
+ parser = PacketParser.new(parser)
25
+ parser.verify_parameters()
26
+ parser.create_command(target_name, commands, warnings)
27
+ end
28
+
29
+ # @param parser [ConfigParser] Configuration parser
30
+ # @param target_name [String] The name of the target to create the packet
31
+ # under. If the target name is 'SYSTEM' the keyword parameter will be
32
+ # used instead of this parameter.
33
+ # @param telemetry [Hash] Hash of the currently defined telemetry packets
34
+ # @param latest_data [Hash<String=>Hash<String=>Array(Packet)>>] Hash of hashes keyed
35
+ # first by the target name and then by the item name. This results in an
36
+ # array of packets containing that target and item. This structure is
37
+ # used to perform lookups when the packet and item are known but the
38
+ # packet is not.
39
+ # @param warnings [Array<String>] Any warning strings generated while
40
+ # parsing this command will be appened to this array
41
+ def self.parse_telemetry(parser, target_name, telemetry, latest_data, warnings)
42
+ parser = PacketParser.new(parser)
43
+ parser.verify_parameters()
44
+ parser.create_telemetry(target_name, telemetry, latest_data, warnings)
45
+ end
46
+
47
+ # @param packet [Packet] Packet to check all default and range items for
48
+ # appropriate data types. Only applicable to COMMAND packets.
49
+ def self.check_item_data_types(packet)
50
+ packet.sorted_items.each do |item|
51
+ item.check_default_and_range_data_types()
52
+ end
53
+ rescue => err
54
+ # Add the target name and packet name to the error message so the user
55
+ # can debug where the error occurred
56
+ raise $!, "#{packet.target_name} #{packet.packet_name} #{$!}", $!.backtrace
57
+ end
58
+
59
+ # @param parser [ConfigParser] Configuration parser
60
+ def initialize(parser)
61
+ @parser = parser
62
+ end
63
+
64
+ def verify_parameters
65
+ @usage = "#{@parser.keyword} <TARGET NAME> <PACKET NAME> <ENDIANNESS: BIG_ENDIAN/LITTLE_ENDIAN> <DESCRIPTION (Optional)>"
66
+ @parser.verify_num_parameters(3, 4, @usage)
67
+ end
68
+
69
+ def create_command(target_name, commands, warnings)
70
+ packet = create_packet(target_name)
71
+ warning = check_for_duplicate('Command', commands, packet)
72
+ warnings << warning if warning
73
+ commands[packet.target_name] ||= {}
74
+ packet
75
+ end
76
+
77
+ def create_telemetry(target_name, telemetry, latest_data, warnings)
78
+ packet = create_packet(target_name)
79
+ warning = check_for_duplicate('Telemetry', telemetry, packet)
80
+ warnings << warning if warning
81
+
82
+ # Add received time packet items
83
+ item = packet.define_item('RECEIVED_TIMESECONDS', 0, 0, :DERIVED, nil, packet.default_endianness, :ERROR, '%0.6f', ReceivedTimeSecondsConversion.new)
84
+ item.description = 'COSMOS Received Time (UTC, Floating point, Unix epoch)'
85
+ item = packet.define_item('RECEIVED_TIMEFORMATTED', 0, 0, :DERIVED, nil, packet.default_endianness, :ERROR, nil, ReceivedTimeFormattedConversion.new)
86
+ item.description = 'COSMOS Received Time (Local time zone, Formatted string)'
87
+ item = packet.define_item('RECEIVED_COUNT', 0, 0, :DERIVED, nil, packet.default_endianness, :ERROR, nil, ReceivedCountConversion.new)
88
+ item.description = 'COSMOS packet received count'
89
+
90
+ unless telemetry[packet.target_name]
91
+ telemetry[packet.target_name] = {}
92
+ latest_data[packet.target_name] = {}
93
+ end
94
+ packet
95
+ end
96
+
97
+ private
98
+
99
+ def create_packet(target_name)
100
+ params = @parser.parameters
101
+ target_name = params[0].to_s.upcase if target_name == 'SYSTEM'
102
+ packet_name = params[1].to_s.upcase
103
+ endianness = params[2].to_s.upcase.to_sym
104
+ description = params[3].to_s
105
+ if endianness != :BIG_ENDIAN and endianness != :LITTLE_ENDIAN
106
+ raise @parser.error("Invalid endianness #{params[2]}. Must be BIG_ENDIAN or LITTLE_ENDIAN.", @usage)
107
+ end
108
+ Packet.new(target_name, packet_name, endianness, description)
109
+ end
110
+
111
+ def check_for_duplicate(type, list, packet)
112
+ msg = nil
113
+ if list[packet.target_name]
114
+ if list[packet.target_name][packet.packet_name]
115
+ msg = "#{type} Packet #{packet.target_name} #{packet.packet_name} redefined."
116
+ Logger.instance.warn msg
117
+ end
118
+ end
119
+ msg
120
+ end
121
+
122
+ end
123
+ end # module Cosmos
@@ -0,0 +1,63 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2014 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 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
+ require 'cosmos/processors'
12
+
13
+ module Cosmos
14
+
15
+ class ProcessorParser
16
+ # @param parser [ConfigParser] Configuration parser
17
+ # @param packet [Packet] The current packet
18
+ # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
19
+ def self.parse(parser, packet, cmd_or_tlm)
20
+ @parser = ProcessorParser.new(parser)
21
+ @parser.verify_parameters(cmd_or_tlm)
22
+ @parser.create_processor(packet)
23
+ end
24
+
25
+ # @param parser [ConfigParser] Configuration parser
26
+ def initialize(parser)
27
+ @parser = parser
28
+ end
29
+
30
+ # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
31
+ def verify_parameters(cmd_or_tlm)
32
+ if cmd_or_tlm == PacketConfig::COMMAND
33
+ raise @parser.error("PROCESSOR only applies to telemetry packets")
34
+ end
35
+ @usage = "PROCESSOR <PROCESSOR NAME> <PROCESSOR CLASS FILENAME> <PROCESSOR SPECIFIC OPTIONS>"
36
+ @parser.verify_num_parameters(2, nil, @usage)
37
+ end
38
+
39
+ # @param packet [Packet] The packet the processor should be added to
40
+ def create_processor(packet)
41
+ # require should be performed in target.txt
42
+ klass = @parser.parameters[1].filename_to_class_name.to_class
43
+ raise @parser.error("#{@parser.parameters[1].filename_to_class_name} class not found. Did you require the file in target.txt?", @usage) unless klass
44
+ if @parser.parameters[2]
45
+ processor = klass.new(*@parser.parameters[2..(@parser.parameters.length - 1)])
46
+ else
47
+ processor = klass.new
48
+ end
49
+ raise ArgumentError, "processor must be a Cosmos::Processor but is a #{processor.class}" unless Cosmos::Processor === processor
50
+ processor.name = get_processor_name()
51
+ packet.processors[processor.name] = processor
52
+ rescue Exception => err
53
+ raise @parser.error(err, @usage)
54
+ end
55
+
56
+ private
57
+
58
+ def get_processor_name
59
+ @parser.parameters[0].to_s.upcase
60
+ end
61
+
62
+ end
63
+ end # module Cosmos
@@ -0,0 +1,116 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2014 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 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
+ require 'cosmos/packets/packet_item'
12
+
13
+ module Cosmos
14
+
15
+ class StateParser
16
+ # @param parser [ConfigParser] Configuration parser
17
+ # @param packet [Packet] The current packet
18
+ # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
19
+ # @param item [PacketItem] The packet item to create states on
20
+ # @param warnings [Array<String>] Array of string warnings which will be
21
+ # appended with any warnings found when parsing the limits
22
+ def self.parse(parser, packet, cmd_or_tlm, item, warnings)
23
+ @parser = StateParser.new(parser)
24
+ @parser.verify_parameters(cmd_or_tlm)
25
+ @parser.create_state(packet, cmd_or_tlm, item, warnings)
26
+ end
27
+
28
+ # @param parser [ConfigParser] Configuration parser
29
+ def initialize(parser)
30
+ @parser = parser
31
+ end
32
+
33
+ # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
34
+ def verify_parameters(cmd_or_tlm)
35
+ @usage = "STATE <STATE NAME> <STATE VALUE> "
36
+ if cmd_or_tlm == PacketConfig::COMMAND
37
+ @usage << "<HAZARDOUS (Optional)> <Hazardous Description (Optional)>"
38
+ @parser.verify_num_parameters(2, 4, @usage)
39
+ else
40
+ @usage << "<COLOR: GREEN/YELLOW/RED (Optional)>"
41
+ @parser.verify_num_parameters(2, 3, @usage)
42
+ end
43
+ end
44
+
45
+ # @param packet [Packet] The current packet
46
+ # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
47
+ # @param item [PacketItem] The packet item to create states on
48
+ # @param warnings [Array<String>] Array of string warnings which will be
49
+ # appended with any warnings found when parsing the limits
50
+ def create_state(packet, cmd_or_tlm, item, warnings)
51
+ item.states ||= {}
52
+
53
+ state_name = get_state_name()
54
+ check_for_duplicate_states(item, warnings)
55
+ item.states[state_name] = get_state_value(item.data_type)
56
+ parse_additional_parameters(packet, cmd_or_tlm, item)
57
+ end
58
+
59
+ private
60
+
61
+ def get_state_name
62
+ @parser.parameters[0].upcase
63
+ end
64
+
65
+ def get_state_value(data_type)
66
+ if data_type == :STRING || data_type == :BLOCK
67
+ @parser.parameters[1]
68
+ else
69
+ @parser.parameters[1].convert_to_value
70
+ end
71
+ end
72
+
73
+ def check_for_duplicate_states(item, warnings)
74
+ if item.states[get_state_name()]
75
+ msg = "Duplicate state defined on line #{@parser.line_number}: #{@parser.line}"
76
+ Logger.instance.warn(msg)
77
+ warnings << msg
78
+ end
79
+ end
80
+
81
+ def parse_additional_parameters(packet, cmd_or_tlm, item)
82
+ return unless @parser.parameters.length > 2
83
+
84
+ if cmd_or_tlm == PacketConfig::COMMAND
85
+ get_hazardous(item)
86
+ else
87
+ get_state_colors(item)
88
+ packet.update_limits_items_cache
89
+ end
90
+ end
91
+
92
+ def get_hazardous(item)
93
+ if @parser.parameters[2].upcase == 'HAZARDOUS'
94
+ item.hazardous ||= {}
95
+ if @parser.parameters[3]
96
+ item.hazardous[get_state_name()] = @parser.parameters[3]
97
+ else
98
+ item.hazardous[get_state_name()] = ""
99
+ end
100
+ else
101
+ raise @parser.error("HAZARDOUS expected as third parameter for this line.", @usage)
102
+ end
103
+ end
104
+
105
+ def get_state_colors(item)
106
+ color = @parser.parameters[2].upcase.to_sym
107
+ unless PacketItem::STATE_COLORS.include? color
108
+ raise @parser.error("Invalid state color #{color}. Must be one of #{PacketItem::STATE_COLORS.join(' ')}.", @usage)
109
+ end
110
+ item.limits.enabled = true
111
+ item.state_colors ||= {}
112
+ item.state_colors[get_state_name()] = color
113
+ end
114
+
115
+ end
116
+ end # module Cosmos
@@ -66,6 +66,20 @@ module Cosmos
66
66
  @sorted_items.length > 0
67
67
  end
68
68
 
69
+ # Rename an existing item
70
+ #
71
+ # @param item_name [String] Name of the currently defined item
72
+ # @param new_item_name [String] New name for the item
73
+ def rename_item(item_name, new_item_name)
74
+ item = get_item(item_name)
75
+ item.name = new_item_name
76
+ @items.delete(item_name)
77
+ @items[new_item_name] = item
78
+ # Since @sorted_items contains the actual item reference it is
79
+ # updated when we set the item.name
80
+ item
81
+ end
82
+
69
83
  # Define an item in the structure. This creates a new instance of the
70
84
  # item_class as given in the constructor and adds it to the items hash. It
71
85
  # also resizes the buffer to accomodate the new item.
@@ -89,12 +103,20 @@ module Cosmos
89
103
 
90
104
  # Create the item
91
105
  item = @item_class.new(name_upcase, bit_offset, bit_size, data_type, endianness, array_size, overflow)
106
+ define(item)
107
+ end
92
108
 
109
+ # Adds the given item to the items hash. It also resizes the buffer to
110
+ # accomodate the new item.
111
+ #
112
+ # @param item [StructureItem] The structure item to add
113
+ # @return [StrutureItem] The struture item defined
114
+ def define(item)
93
115
  # Handle Overwriting Existing Item
94
- if @items[name_upcase]
116
+ if @items[item.name]
95
117
  item_index = nil
96
118
  @sorted_items.each_with_index do |sorted_item, index|
97
- if sorted_item.name == name_upcase
119
+ if sorted_item.name == item.name
98
120
  item_index = index
99
121
  break
100
122
  end
@@ -117,9 +139,9 @@ module Cosmos
117
139
  end
118
140
 
119
141
  # Add to the overall hash of defined items
120
- @items[name_upcase] = item
142
+ @items[item.name] = item
121
143
  # Update fixed size knowledge
122
- @fixed_size = false if ((data_type != :DERIVED and bit_size <= 0) or (array_size and array_size <= 0))
144
+ @fixed_size = false if ((item.data_type != :DERIVED and item.bit_size <= 0) or (item.array_size and item.array_size <= 0))
123
145
 
124
146
  # Recalculate the overall defined length of the structure
125
147
  update_needed = false
@@ -178,6 +200,17 @@ module Cosmos
178
200
  return define_item(name, @defined_length_bits, bit_size, data_type, array_size, endianness, overflow)
179
201
  end
180
202
 
203
+ # Adds an item at the end of the structure. It adds the item to the items
204
+ # hash and resizes the buffer to accomodate the new item.
205
+ #
206
+ # @param item (see #define)
207
+ # @return (see #define)
208
+ def append(item)
209
+ raise ArgumentError, "Can't append an item after a variably sized item" if !@fixed_size
210
+ item.bit_offset = @defined_length_bits
211
+ return define(item)
212
+ end
213
+
181
214
  # @param name [String] Name of the item to look up in the items Hash
182
215
  # @return [StructureItem] StructureItem or one of its subclasses
183
216
  def get_item(name)
@@ -329,6 +362,28 @@ module Cosmos
329
362
  end
330
363
  end
331
364
 
365
+ # Make a light weight clone of this structure. This only creates a new buffer
366
+ # of data. The defined structure items are the same.
367
+ #
368
+ # @return [Structure] A copy of the current structure with a new underlying
369
+ # buffer of data
370
+ def clone
371
+ structure = super()
372
+ # Use instance_variable_set since we have overriden buffer= to do
373
+ # additional work that isn't neccessary here
374
+ structure.instance_variable_set("@buffer", @buffer.clone) if @buffer
375
+ return structure
376
+ end
377
+ alias dup clone
378
+
379
+ # Enable the ability to read and write item values as if they were methods
380
+ # to the class
381
+ def enable_method_missing
382
+ extend(MethodMissing)
383
+ end
384
+
385
+ protected
386
+
332
387
  # Take the structure mutex to ensure the buffer does not change while you perform activities
333
388
  def synchronize
334
389
  @mutex ||= Mutex.new
@@ -365,24 +420,6 @@ module Cosmos
365
420
  end
366
421
  end
367
422
 
368
- # Make a light weight clone of this structure. This only creates a new buffer
369
- # of data. The defined structure items are the same.
370
- #
371
- # @return [Structure] A copy of the current structure with a new underlying
372
- # buffer of data
373
- def clone
374
- structure = super()
375
- @buffer = @buffer.clone if @buffer # Deep Copy @buffer
376
- return structure
377
- end
378
- alias dup clone
379
-
380
- def enable_method_missing
381
- extend(MethodMissing)
382
- end
383
-
384
- protected
385
-
386
423
  module MethodMissing
387
424
  # Method missing provides reading/writing item values as if they were methods to the class
388
425
  def method_missing(name, value = nil)
@@ -89,7 +89,7 @@ module Cosmos
89
89
  raise ArgumentError, "name must be a String but is a #{name.class}" unless String === name
90
90
  raise ArgumentError, "name must contain at least one character" if name.empty?
91
91
 
92
- @name = name.clone.freeze
92
+ @name = name.upcase.clone.freeze
93
93
  verify_overall() if @structure_item_constructed
94
94
  end
95
95
 
@@ -438,7 +438,7 @@ module Cosmos
438
438
  script_runner.script_set_status(message) if script_runner
439
439
  end
440
440
 
441
- def ask_string(question, allow_blank = false)
441
+ def ask_string(question, allow_blank = false, password = false)
442
442
  answer = ''
443
443
  while answer.empty?
444
444
  print question + " "
@@ -449,8 +449,8 @@ module Cosmos
449
449
  return answer
450
450
  end
451
451
 
452
- def ask(question, allow_blank = false)
453
- string = ask_string(question, allow_blank)
452
+ def ask(question, allow_blank = false, password = false)
453
+ string = ask_string(question, allow_blank, password)
454
454
  value = string.convert_to_value
455
455
  return value
456
456
  end
@@ -1504,8 +1504,7 @@ module Cosmos
1504
1504
  end
1505
1505
 
1506
1506
  def shutdown_cmd_tlm
1507
- script_disconnect()
1508
- $cmd_tlm_server = nil
1507
+ $cmd_tlm_server.shutdown if $cmd_tlm_server && !$cmd_tlm_disconnect
1509
1508
  end
1510
1509
 
1511
1510
  end # module Script