cosmos 4.0.1-universal-java-1.8 → 4.0.2-universal-java-1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/data/config/_id_items.yaml +1 -1
  3. data/data/config/_id_params.yaml +1 -1
  4. data/data/config/_items.yaml +1 -1
  5. data/data/config/_params.yaml +1 -1
  6. data/data/config/cmd_tlm_server.yaml +14 -0
  7. data/data/config/command.yaml +2 -0
  8. data/data/config/command_modifiers.yaml +18 -0
  9. data/data/config/item_modifiers.yaml +39 -0
  10. data/data/config/param_item_modifiers.yaml +5 -0
  11. data/data/config/parameter_modifiers.yaml +36 -0
  12. data/data/config/protocols.yaml +22 -6
  13. data/data/config/system.yaml +17 -0
  14. data/data/config/target.yaml +9 -0
  15. data/data/config/telemetry.yaml +7 -0
  16. data/data/config/telemetry_modifiers.yaml +13 -0
  17. data/data/config/tlm_viewer.yaml +5 -0
  18. data/data/crc.txt +20 -20
  19. data/demo/config/data/crc.txt +3 -3
  20. data/demo/config/targets/INST/screens/hs.txt +1 -1
  21. data/demo/config/targets/SYSTEM/cmd_tlm/limits_groups.txt +5 -10
  22. data/demo/config/targets/SYSTEM/lib/limits_groups.rb +33 -17
  23. data/lib/cosmos/core_ext/string.rb +1 -1
  24. data/lib/cosmos/gui/utilities/script_module_gui.rb +3 -3
  25. data/lib/cosmos/interfaces/protocols/length_protocol.rb +1 -1
  26. data/lib/cosmos/interfaces/protocols/template_protocol.rb +37 -6
  27. data/lib/cosmos/interfaces/udp_interface.rb +3 -3
  28. data/lib/cosmos/packets/packet_config.rb +1 -0
  29. data/lib/cosmos/packets/parsers/format_string_parser.rb +1 -0
  30. data/lib/cosmos/packets/parsers/limits_parser.rb +1 -0
  31. data/lib/cosmos/packets/parsers/state_parser.rb +3 -0
  32. data/lib/cosmos/packets/telemetry.rb +1 -1
  33. data/lib/cosmos/tools/cmd_sequence/cmd_sequence.rb +1 -1
  34. data/lib/cosmos/tools/cmd_tlm_server/limits_groups_background_task.rb +4 -3
  35. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_config.rb +1 -1
  36. data/lib/cosmos/tools/tlm_viewer/screen.rb +3 -3
  37. data/lib/cosmos/tools/tlm_viewer/widgets/formatfontvalue_widget.rb +5 -10
  38. data/lib/cosmos/tools/tlm_viewer/widgets/limits_widget.rb +1 -1
  39. data/lib/cosmos/tools/tlm_viewer/widgets/matrixbycolumns_widget.rb +3 -1
  40. data/lib/cosmos/top_level.rb +2 -4
  41. data/lib/cosmos/utilities/csv.rb +1 -1
  42. data/lib/cosmos/version.rb +4 -4
  43. data/spec/interfaces/protocols/template_protocol_spec.rb +119 -15
  44. data/spec/interfaces/udp_interface_spec.rb +8 -2
  45. data/spec/packets/packet_config_spec.rb +11 -0
  46. data/spec/packets/parsers/format_string_parser_spec.rb +11 -0
  47. data/spec/packets/parsers/limits_parser_spec.rb +21 -10
  48. data/spec/packets/parsers/packet_item_parser_spec.rb +5 -5
  49. data/spec/packets/parsers/state_parser_spec.rb +33 -0
  50. data/spec/tools/cmd_tlm_server/limits_groups_background_task_spec.rb +4 -4
  51. metadata +2 -2
@@ -30,6 +30,12 @@ module Cosmos
30
30
  # @param discard_leading_bytes (see TerminatedProtocol#initialize)
31
31
  # @param sync_pattern (see TerminatedProtocol#initialize)
32
32
  # @param fill_fields (see TerminatedProtocol#initialize)
33
+ # @param response_timeout [Float] Number of seconds to wait before timing out
34
+ # when waiting for a response
35
+ # @param response_polling_period [Float] Number of seconds to wait between polling
36
+ # for a response
37
+ # @param raise_exceptions [String] Whether to raise exceptions when errors
38
+ # occur in the protocol like unexpected responses or response timeouts.
33
39
  def initialize(
34
40
  write_termination_characters,
35
41
  read_termination_characters,
@@ -41,7 +47,8 @@ module Cosmos
41
47
  sync_pattern = nil,
42
48
  fill_fields = false,
43
49
  response_timeout = 5.0,
44
- response_polling_period = 0.02
50
+ response_polling_period = 0.02,
51
+ raise_exceptions = false
45
52
  )
46
53
  super(
47
54
  write_termination_characters,
@@ -62,6 +69,7 @@ module Cosmos
62
69
  @response_timeout = @response_timeout.to_f if @response_timeout
63
70
  @response_polling_period = response_polling_period.to_f
64
71
  @connect_complete_time = nil
72
+ @raise_exceptions = ConfigParser.handle_true_false(raise_exceptions)
65
73
  end
66
74
 
67
75
  def reset
@@ -128,9 +136,17 @@ module Cosmos
128
136
  # Scan the response for the variables in brackets <VARIABLE>
129
137
  # Write the packet value with each of the values received
130
138
  response_values = response_string.scan(response_regexp)[0]
131
- raise "Unexpected response received: #{response_string}" if !response_values || (response_values.length != response_item_names.length)
132
- response_values.each_with_index do |value, i|
133
- result_packet.write(response_item_names[i], value)
139
+ if !response_values || (response_values.length != response_item_names.length)
140
+ handle_error("#{@interface.name}: Unexpected response: #{response_string}")
141
+ else
142
+ response_values.each_with_index do |value, i|
143
+ begin
144
+ result_packet.write(response_item_names[i], value)
145
+ rescue => error
146
+ handle_error("#{@interface.name}: Could not write value #{value} due to #{error.message}")
147
+ break
148
+ end
149
+ end
134
150
  end
135
151
 
136
152
  @response_packets.clear
@@ -157,6 +173,13 @@ module Cosmos
157
173
  begin
158
174
  @response_template = packet.read("RSP_TEMPLATE").strip
159
175
  @response_packet = packet.read("RSP_PACKET").strip
176
+ # If the template or packet are empty set them to nil. This allows for
177
+ # the user to remove the RSP_TEMPLATE and RSP_PACKET values and avoid
178
+ # any response timeouts
179
+ if @response_template.empty? || @response_packet.empty?
180
+ @response_template = nil
181
+ @response_packet = nil
182
+ end
160
183
  rescue
161
184
  # If there is no response template we set to nil
162
185
  @response_template = nil
@@ -174,8 +197,11 @@ module Cosmos
174
197
  data = raw_packet.buffer(false)
175
198
  # Scan the template for variables in brackets <VARIABLE>
176
199
  # Read these values from the packet and substitute them in the template
200
+ # and in the @response_packet name
177
201
  @template.scan(/<(.*?)>/).each do |variable|
178
- data.gsub!("<#{variable[0]}>", packet.read(variable[0], :RAW).to_s)
202
+ value = packet.read(variable[0], :RAW).to_s
203
+ data.gsub!("<#{variable[0]}>", value)
204
+ @response_packet.gsub!("<#{variable[0]}>", value) if @response_packet
179
205
  end
180
206
 
181
207
  return raw_packet
@@ -196,7 +222,7 @@ module Cosmos
196
222
  sleep(@response_polling_period)
197
223
  retry if !response_timeout_time
198
224
  retry if response_timeout_time and Time.now < response_timeout_time
199
- raise Timeout::Error, "Timeout waiting for response"
225
+ handle_error("#{@interface.name}: Timeout waiting for response")
200
226
  end
201
227
 
202
228
  @response_template = nil
@@ -205,5 +231,10 @@ module Cosmos
205
231
  end
206
232
  return super(packet, data)
207
233
  end
234
+
235
+ def handle_error(msg)
236
+ Logger.error(msg)
237
+ raise msg if @raise_exceptions
238
+ end
208
239
  end
209
240
  end
@@ -42,7 +42,7 @@ module Cosmos
42
42
  @hostname = ConfigParser.handle_nil(hostname)
43
43
  if @hostname
44
44
  @hostname = @hostname.to_s
45
- @hostname = '127.0.0.1' if @hostname.casecmp('LOCALHOST')
45
+ @hostname = '127.0.0.1' if @hostname.casecmp('LOCALHOST').zero?
46
46
  end
47
47
  @write_dest_port = ConfigParser.handle_nil(write_dest_port)
48
48
  @write_dest_port = write_dest_port.to_i if @write_dest_port
@@ -51,7 +51,7 @@ module Cosmos
51
51
  @write_src_port = ConfigParser.handle_nil(write_src_port)
52
52
  @write_src_port = @write_src_port.to_i if @write_src_port
53
53
  @interface_address = ConfigParser.handle_nil(interface_address)
54
- if @interface_address && @interface_address.casecmp('LOCALHOST')
54
+ if @interface_address && @interface_address.casecmp('LOCALHOST').zero?
55
55
  @interface_address = '127.0.0.1'
56
56
  end
57
57
  @ttl = ttl.to_i
@@ -61,7 +61,7 @@ module Cosmos
61
61
  @read_timeout = ConfigParser.handle_nil(read_timeout)
62
62
  @read_timeout = @read_timeout.to_f if @read_timeout
63
63
  @bind_address = ConfigParser.handle_nil(bind_address)
64
- if @bind_address && @bind_address.casecmp('LOCALHOST')
64
+ if @bind_address && @bind_address.casecmp('LOCALHOST').zero?
65
65
  @bind_address = '127.0.0.1'
66
66
  end
67
67
  @write_socket = nil
@@ -1283,6 +1283,7 @@ module Cosmos
1283
1283
 
1284
1284
  # Define the units of the current telemetry item
1285
1285
  when 'UNITS'
1286
+ raise parser.error("Items with STATE can't define UNITS") if @current_item.states
1286
1287
  usage = "UNITS <FULL UNITS NAME> <ABBREVIATED UNITS NAME>"
1287
1288
  parser.verify_num_parameters(2, 2, usage)
1288
1289
  @current_item.units_full = params[0]
@@ -14,6 +14,7 @@ module Cosmos
14
14
  # @param parser [ConfigParser] Configuration parser
15
15
  # @param item [Packet] The current item
16
16
  def self.parse(parser, item)
17
+ raise parser.error("Items with STATE can't define FORMAT_STRING") if item.states
17
18
  @parser = FormatStringParser.new(parser)
18
19
  @parser.verify_parameters()
19
20
  @parser.create_format_string(item)
@@ -18,6 +18,7 @@ module Cosmos
18
18
  # @param warnings [Array<String>] Array of string warnings which will be
19
19
  # appended with any warnings found when parsing the limits
20
20
  def self.parse(parser, packet, cmd_or_tlm, item, warnings)
21
+ raise parser.error("Items with STATE can't define LIMITS") if item.states
21
22
  @parser = LimitsParser.new(parser)
22
23
  @parser.verify_parameters(cmd_or_tlm)
23
24
  @parser.create_limits(packet, item, warnings)
@@ -20,6 +20,9 @@ module Cosmos
20
20
  # @param warnings [Array<String>] Array of string warnings which will be
21
21
  # appended with any warnings found when parsing the limits
22
22
  def self.parse(parser, packet, cmd_or_tlm, item, warnings)
23
+ raise parser.error("Items with LIMITS can't define STATE") if item.limits.values
24
+ raise parser.error("Items with FORMAT_STRING can't define STATE") if item.format_string
25
+ raise parser.error("Items with UNITS can't define STATE") if item.units
23
26
  @parser = StateParser.new(parser)
24
27
  @parser.verify_parameters(cmd_or_tlm)
25
28
  @parser.create_state(packet, cmd_or_tlm, item, warnings)
@@ -166,7 +166,7 @@ module Cosmos
166
166
  # @param packet_name (see #packet) The packet name. LATEST is supported.
167
167
  # @return [Array<PacketItem>] The telemetry item names for the given target and packet name
168
168
  def item_names(target_name, packet_name)
169
- if LATEST_PACKET_NAME.casecmp(packet_name) == 0
169
+ if LATEST_PACKET_NAME.casecmp(packet_name).zero?
170
170
  target_upcase = target_name.to_s.upcase
171
171
  target_latest_data = @config.latest_data[target_upcase]
172
172
  raise "Telemetry Target '#{target_upcase}' does not exist" unless target_latest_data
@@ -568,7 +568,7 @@ module Cosmos
568
568
  end
569
569
 
570
570
  # Finds the given filename by looking in the system sequences path as
571
- # well as in each target's sequences directory. If the file can not be
571
+ # well as in each target's sequences directory. If the file can't be
572
572
  # found an Error dialog is created.
573
573
  # @param filename [String] Filename to locate
574
574
  def find_sequence(filename)
@@ -18,9 +18,10 @@ module Cosmos
18
18
  FUTURE_TIME = Time.new("3000")
19
19
  PAST_TIME = Time.new("1900")
20
20
 
21
- def initialize(initial_delay = 0)
21
+ def initialize(initial_delay = 0, task_delay = 0.5)
22
22
  super()
23
23
  @initial_delay = Float(initial_delay)
24
+ @task_delay = Float(task_delay)
24
25
  @name = "Limits Groups"
25
26
  @groups = get_limits_groups()
26
27
  # Initialize all the group names as instance variables
@@ -87,9 +88,9 @@ module Cosmos
87
88
  check_methods.each {|method| self.send(method.intern) }
88
89
  now = Time.now
89
90
  @status = "#{now.formatted}: Checking groups took #{now - start}s"
90
- sleep_time = 1 - (now - start)
91
+ sleep_time = @task_delay - (now - start)
91
92
  sleep_time = 0 if sleep_time < 0
92
- broken = @sleeper.sleep(sleep_time) # Run the checks at 1Hz
93
+ broken = @sleeper.sleep(sleep_time)
93
94
  break if broken
94
95
  end
95
96
  end
@@ -428,7 +428,7 @@ module Cosmos
428
428
  # Build the index into the hash of the form "TARGET_NAME PACKET_NAME"
429
429
  # Note that + is used to create a new object and then << is used to concatenate
430
430
  # to the new object.
431
- if packet_name.casecmp(Telemetry::LATEST_PACKET_NAME) == 0
431
+ if packet_name.casecmp(Telemetry::LATEST_PACKET_NAME).zero?
432
432
  packets = System.telemetry.latest_packets(target_name, data_object.item_name)
433
433
  packets.each do |packet|
434
434
  index = (packet.target_name + ' ') << packet.packet_name
@@ -329,7 +329,7 @@ module Cosmos
329
329
  end
330
330
 
331
331
  unless @widgets.invalid.empty?
332
- Qt::MessageBox.information(self, "Screen #{@full_name}", "The following telemetry items could not be created: \n" + @widgets.invalid.join("\n"))
332
+ Qt::MessageBox.information(self, "Screen #{@full_name}", "In #{filename}, the following telemetry items could not be created: \n" + @widgets.invalid.join("\n"))
333
333
  end
334
334
 
335
335
  # Process all settings before we show the screen
@@ -383,8 +383,8 @@ module Cosmos
383
383
  System.telemetry.packet_and_item(*parameters[0..2])
384
384
  widget = klass.new(layout_stack[-1], *parameters)
385
385
  end
386
- rescue
387
- @widgets.invalid << parameters.join(" ")
386
+ rescue => err
387
+ @widgets.invalid << "#{parser.line_number}: #{parameters.join(" ").strip} due to #{err.message}"
388
388
  return nil
389
389
  end
390
390
  else
@@ -12,25 +12,20 @@ require 'cosmos'
12
12
  require 'cosmos/tools/tlm_viewer/widgets/formatvalue_widget'
13
13
 
14
14
  module Cosmos
15
-
16
- # FormatfontvalueWidget class
17
- #
18
15
  # This class implements a value with configurable font values. The font
19
16
  # can also be updated after it is created.
20
17
  # It inherits from the FormatvalueWidget class.
21
18
  class FormatfontvalueWidget < FormatvalueWidget
22
-
23
- def initialize (parent_layout, target_name, packet_name, item_name, format_string, value_type = :CONVERTED, characters = 12,
24
- font_name = 'arial', font_size = 100, font_weight = Qt::Font::Normal, font_slant = false)
19
+ def initialize(parent_layout, target_name, packet_name, item_name, format_string,
20
+ value_type = :CONVERTED, characters = 12, font_name = 'arial',
21
+ font_size = 100, font_weight = Qt::Font::Normal, font_italics = false)
25
22
  super(parent_layout, target_name, packet_name, item_name, format_string, value_type, characters)
26
- setFont(Cosmos.getFont(font_name, font_size.to_i, font_weight, font_slant))
23
+ setFont(Cosmos.getFont(font_name, font_size.to_i, font_weight, font_italics))
27
24
  setFixedWidth(self.fontMetrics.width('X') * characters.to_i + 10)
28
25
  end
29
26
 
30
27
  def font=(font)
31
28
  setFont(font)
32
29
  end
33
-
34
30
  end
35
-
36
- end # module Cosmos
31
+ end
@@ -20,7 +20,7 @@ module Cosmos
20
20
 
21
21
  def initialize(parent_layout, target_name, packet_name, item_name, value_type, width, height)
22
22
  super(target_name, packet_name, item_name, value_type)
23
- @value_type = :CONVERTED if @value_type == :WITH_UNITS
23
+ raise "Invalid value_type #{@value_type} for LimitsWidget" if @value_type == :RAW
24
24
  @width = width.to_i
25
25
  @height = height.to_i
26
26
  @value = 0
@@ -17,11 +17,13 @@ module Cosmos
17
17
  include Widget
18
18
  include LayoutWidget
19
19
 
20
- def initialize(parent_layout, num_columns, hSpacing = 0, vSpacing = 0)
20
+ def initialize(parent_layout, num_columns, horizontal_spacing = 0, vertical_spacing = 0)
21
21
  super()
22
22
  @num_columns = num_columns.to_i
23
23
  @row = 0
24
24
  @column = 0
25
+ setHorizontalSpacing(horizontal_spacing.to_i)
26
+ setVerticalSpacing(vertical_spacing.to_i)
25
27
  parent_layout.addLayout(self) if parent_layout
26
28
  end
27
29
 
@@ -607,8 +607,7 @@ module Cosmos
607
607
  #
608
608
  # @param class_name_or_class_filename [String] The name of the class or the file which contains the
609
609
  # Ruby class to require
610
- # @param log_error [Boolean] Whether to log an error if we can not require
611
- # the class
610
+ # @param log_error [Boolean] Whether to log an error if we can't require the class
612
611
  def self.require_class(class_name_or_class_filename, log_error = true)
613
612
  if class_name_or_class_filename.downcase[-3..-1] == '.rb' or (class_name_or_class_filename[0] == class_name_or_class_filename[0].downcase)
614
613
  class_filename = class_name_or_class_filename
@@ -627,8 +626,7 @@ module Cosmos
627
626
  # Requires a file with a standard error message if it fails
628
627
  #
629
628
  # @param filename [String] The name of the file to require
630
- # @param log_error [Boolean] Whether to log an error if we can not require
631
- # the class
629
+ # @param log_error [Boolean] Whether to log an error if we can't require the class
632
630
  def self.require_file(filename, log_error = true)
633
631
  begin
634
632
  require filename
@@ -25,7 +25,7 @@ module Cosmos
25
25
  @archive = nil
26
26
  @archive_file = ""
27
27
  Object::CSV.read(input_file).each do |line|
28
- @hash[line[0]] = line[1..-1].compact
28
+ @hash[line[0]] = line[1..-1]
29
29
  end
30
30
  end
31
31
 
@@ -1,12 +1,12 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- COSMOS_VERSION = '4.0.1'
3
+ COSMOS_VERSION = '4.0.2'
4
4
  module Cosmos
5
5
  module Version
6
6
  MAJOR = '4'
7
7
  MINOR = '0'
8
- PATCH = '1'
9
- BUILD = '012697c6665f9dd1c47b86ed75b21c9bc928973f'
8
+ PATCH = '2'
9
+ BUILD = 'e9da0b19c0c72edef48b0717440a06c0da1d5690'
10
10
  end
11
- VERSION = '4.0.1'
11
+ VERSION = '4.0.2'
12
12
  end
@@ -12,6 +12,7 @@ require 'spec_helper'
12
12
  require 'cosmos/interfaces/protocols/template_protocol'
13
13
  require 'cosmos/interfaces/interface'
14
14
  require 'cosmos/streams/stream'
15
+ require 'cosmos/utilities/logger'
15
16
 
16
17
  module Cosmos
17
18
  describe TemplateProtocol do
@@ -33,7 +34,7 @@ module Cosmos
33
34
 
34
35
  describe "initialize" do
35
36
  it "initializes attributes" do
36
- @interface.add_protocol(TemplateProtocol, ['0xABCD','0xABCD'], :READ_WRITE)
37
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD), :READ_WRITE)
37
38
  expect(@interface.read_protocols[0].instance_variable_get(:@data)).to eq ''
38
39
  end
39
40
  end
@@ -41,7 +42,7 @@ module Cosmos
41
42
  describe "connect" do
42
43
  it "supports an initial read delay" do
43
44
  @interface.instance_variable_set(:@stream, TemplateStream.new)
44
- @interface.add_protocol(TemplateProtocol, ['0xABCD', '0xABCD', 0, 2], :READ_WRITE)
45
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD 0 2), :READ_WRITE)
45
46
  time = Time.now
46
47
  @interface.connect
47
48
  expect(@interface.read_protocols[0].instance_variable_get(:@connect_complete_time)).to be >= time + 2.0
@@ -51,7 +52,7 @@ module Cosmos
51
52
  describe "disconnect" do
52
53
  it "unblocks writes waiting for responses" do
53
54
  @interface.instance_variable_set(:@stream, TemplateStream.new)
54
- @interface.add_protocol(TemplateProtocol, ['0xABCD', '0xABCD'], :READ_WRITE)
55
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD), :READ_WRITE)
55
56
  packet = Packet.new('TGT', 'CMD')
56
57
  packet.append_item("CMD_TEMPLATE", 1024, :STRING)
57
58
  packet.get_item("CMD_TEMPLATE").default = "SOUR:VOLT"
@@ -72,7 +73,7 @@ module Cosmos
72
73
  describe "read_data" do
73
74
  it "ignores all data during the connect period" do
74
75
  @interface.instance_variable_set(:@stream, TemplateStream.new)
75
- @interface.add_protocol(TemplateProtocol, ['0xABCD', '0xABCD', 0, 1.5], :READ_WRITE)
76
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD 0 1.5), :READ_WRITE)
76
77
  start = Time.now
77
78
  @interface.connect
78
79
  $read_buffer = "\x31\x30\xAB\xCD"
@@ -85,7 +86,7 @@ module Cosmos
85
86
  describe "write" do
86
87
  it "waits before writing during the initial delay period" do
87
88
  @interface.instance_variable_set(:@stream, TemplateStream.new)
88
- @interface.add_protocol(TemplateProtocol, ['0xABCD','0xABCD',0,1.5], :READ_WRITE)
89
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD 0 1.5), :READ_WRITE)
89
90
  packet = Packet.new('TGT', 'CMD')
90
91
  packet.append_item("VOLTAGE", 16, :UINT)
91
92
  packet.get_item("VOLTAGE").default = 1
@@ -102,7 +103,7 @@ module Cosmos
102
103
 
103
104
  it "works without a response" do
104
105
  @interface.instance_variable_set(:@stream, TemplateStream.new)
105
- @interface.add_protocol(TemplateProtocol, ['0xABCD','0xABCD'], :READ_WRITE)
106
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD), :READ_WRITE)
106
107
  packet = Packet.new('TGT', 'CMD')
107
108
  packet.append_item("VOLTAGE", 16, :UINT)
108
109
  packet.get_item("VOLTAGE").default = 1
@@ -115,9 +116,9 @@ module Cosmos
115
116
  expect($write_buffer).to eql("SOUR:VOLT 1, (@2)\xAB\xCD")
116
117
  end
117
118
 
118
- it "times out if it doesn't receive a response" do
119
+ it "logs an error if it doesn't receive a response" do
119
120
  @interface.instance_variable_set(:@stream, TemplateStream.new)
120
- @interface.add_protocol(TemplateProtocol, ['0xA','0xA',0,nil,1,true,0,nil,false,1.5], :READ_WRITE)
121
+ @interface.add_protocol(TemplateProtocol, %w(0xA 0xA 0 nil 1 true 0 nil false 1.5), :READ_WRITE)
121
122
  @interface.target_names = ['TGT']
122
123
  packet = Packet.new('TGT', 'CMD')
123
124
  packet.append_item("CMD_TEMPLATE", 1024, :STRING)
@@ -129,23 +130,61 @@ module Cosmos
129
130
  packet.restore_defaults
130
131
  @interface.connect
131
132
  start = Time.now
132
- expect { @interface.write(packet) }.to raise_error(Timeout::Error)
133
+ logger = class_double("Cosmos::Logger").as_stubbed_const(:transfer_nested_constants => true)
134
+ expect(logger).to receive(:error).with("StreamInterface: Timeout waiting for response")
135
+ @interface.write(packet)
133
136
  expect(Time.now - start).to be_within(0.1).of(1.5)
134
137
  end
135
138
 
139
+ it "disconnects if it doesn't receive a response" do
140
+ @interface.instance_variable_set(:@stream, TemplateStream.new)
141
+ @interface.add_protocol(TemplateProtocol, %w(0xA 0xA 0 nil 1 true 0 nil false 1.5 0.02 true), :READ_WRITE)
142
+ @interface.target_names = ['TGT']
143
+ packet = Packet.new('TGT', 'CMD')
144
+ packet.append_item("CMD_TEMPLATE", 1024, :STRING)
145
+ packet.get_item("CMD_TEMPLATE").default = "GO"
146
+ packet.append_item("RSP_TEMPLATE", 1024, :STRING)
147
+ packet.get_item("RSP_TEMPLATE").default = "<VOLTAGE>"
148
+ packet.append_item("RSP_PACKET", 1024, :STRING)
149
+ packet.get_item("RSP_PACKET").default = "DATA"
150
+ packet.restore_defaults
151
+ @interface.connect
152
+ start = Time.now
153
+ expect { @interface.write(packet) }.to raise_error(/Timeout waiting for response/)
154
+ expect(Time.now - start).to be_within(0.1).of(1.5)
155
+ end
156
+
157
+ it "doesn't expect responses for empty response fields" do
158
+ @interface.instance_variable_set(:@stream, TemplateStream.new)
159
+ @interface.add_protocol(TemplateProtocol, %w(0xA 0xA 0 nil 1 true 0 nil false nil), :READ_WRITE)
160
+ @interface.target_names = ['TGT']
161
+ packet = Packet.new('TGT', 'CMD')
162
+ packet.append_item("CMD_TEMPLATE", 1024, :STRING)
163
+ packet.get_item("CMD_TEMPLATE").default = "GO"
164
+ packet.append_item("RSP_TEMPLATE", 1024, :STRING)
165
+ packet.get_item("RSP_TEMPLATE").default = ""
166
+ packet.append_item("RSP_PACKET", 1024, :STRING)
167
+ packet.get_item("RSP_PACKET").default = ""
168
+ packet.restore_defaults
169
+ @interface.connect
170
+ start = Time.now
171
+ logger = class_double("Cosmos::Logger").as_stubbed_const(:transfer_nested_constants => true)
172
+ expect(logger).to_not receive(:error)
173
+ @interface.write(packet)
174
+ end
175
+
136
176
  it "processes responses" do
137
177
  rsp_pkt = Packet.new('TGT', 'READ_VOLTAGE')
138
178
  rsp_pkt.append_item("VOLTAGE", 16, :UINT)
139
179
  allow(System).to receive_message_chain(:telemetry, :packet).and_return(rsp_pkt)
140
180
  @interface.instance_variable_set(:@stream, TemplateStream.new)
141
- @interface.add_protocol(TemplateProtocol, ['0xABCD','0xABCD', 0, nil, 1, true, 0, nil, false, nil, nil], :READ_WRITE)
142
- #@interface.add_protocol(TemplateProtocol, ['0xABCD','0xABCD'], :READ_WRITE)
181
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD 0 nil 1 true 0 nil false nil nil), :READ_WRITE)
143
182
  @interface.target_names = ['TGT']
144
183
  packet = Packet.new('TGT', 'CMD')
145
184
  packet.append_item("VOLTAGE", 16, :UINT)
146
185
  packet.get_item("VOLTAGE").default = 11
147
186
  packet.append_item("CHANNEL", 16, :UINT)
148
- packet.get_item("CHANNEL").default = 20
187
+ packet.get_item("CHANNEL").default = 1
149
188
  packet.append_item("CMD_TEMPLATE", 1024, :STRING)
150
189
  packet.get_item("CMD_TEMPLATE").default = "SOUR:VOLT <VOLTAGE>, (@<CHANNEL>)"
151
190
  packet.append_item("RSP_TEMPLATE", 1024, :STRING)
@@ -158,16 +197,81 @@ module Cosmos
158
197
  $read_buffer = "\x31\x30\xAB\xCD" # ASCII 31, 30 is '10'
159
198
  Thread.new { sleep(0.5); read_result = @interface.read }
160
199
  @interface.write(packet)
161
- expect($write_buffer).to eql("SOUR:VOLT 11, (@20)\xAB\xCD")
200
+ sleep 0.55
201
+ expect($write_buffer).to eql("SOUR:VOLT 11, (@1)\xAB\xCD")
162
202
  expect(read_result.read("VOLTAGE")).to eq 10
163
203
  end
164
204
 
205
+ it "handles templates with more values than the response" do
206
+ rsp_pkt = Packet.new('TGT', 'READ_VOLTAGE')
207
+ rsp_pkt.append_item("VOLTAGE", 16, :UINT)
208
+ allow(System).to receive_message_chain(:telemetry, :packet).and_return(rsp_pkt)
209
+ @interface.instance_variable_set(:@stream, TemplateStream.new)
210
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD 0 nil 1 true 0 nil false nil), :READ_WRITE)
211
+ @interface.target_names = ['TGT']
212
+ packet = Packet.new('TGT', 'CMD')
213
+ packet.append_item("VOLTAGE", 16, :UINT)
214
+ packet.get_item("VOLTAGE").default = 12
215
+ packet.append_item("CHANNEL", 16, :UINT)
216
+ packet.get_item("CHANNEL").default = 2
217
+ packet.append_item("CMD_TEMPLATE", 1024, :STRING)
218
+ packet.get_item("CMD_TEMPLATE").default = "SOUR:VOLT <VOLTAGE>, (@<CHANNEL>)"
219
+ packet.append_item("RSP_TEMPLATE", 1024, :STRING)
220
+ packet.get_item("RSP_TEMPLATE").default = "<VOLTAGE>;<CURRENT>"
221
+ packet.append_item("RSP_PACKET", 1024, :STRING)
222
+ packet.get_item("RSP_PACKET").default = "READ_VOLTAGE"
223
+ packet.restore_defaults
224
+ @interface.connect
225
+ read_result = nil
226
+ $read_buffer = "\x31\x30\xAB\xCD" # ASCII 31, 30 is '10'
227
+ Thread.new { sleep(0.5); read_result = @interface.read }
228
+ logger = class_double("Cosmos::Logger").as_stubbed_const(:transfer_nested_constants => true)
229
+ expect(logger).to receive(:error) do |arg|
230
+ expect(arg).to match(/Unexpected response: 10/)
231
+ end
232
+ @interface.write(packet)
233
+ sleep 0.55
234
+ expect($write_buffer).to eql("SOUR:VOLT 12, (@2)\xAB\xCD")
235
+ end
236
+
237
+ it "handles responses with more values than the template" do
238
+ rsp_pkt = Packet.new('TGT', 'READ_VOLTAGE')
239
+ rsp_pkt.append_item("VOLTAGE", 16, :UINT)
240
+ allow(System).to receive_message_chain(:telemetry, :packet).and_return(rsp_pkt)
241
+ @interface.instance_variable_set(:@stream, TemplateStream.new)
242
+ @interface.add_protocol(TemplateProtocol, %w(0xABCD 0xABCD 0 nil 1 true 0 nil false nil), :READ_WRITE)
243
+ @interface.target_names = ['TGT']
244
+ packet = Packet.new('TGT', 'CMD')
245
+ packet.append_item("VOLTAGE", 16, :UINT)
246
+ packet.get_item("VOLTAGE").default = 12
247
+ packet.append_item("CHANNEL", 16, :UINT)
248
+ packet.get_item("CHANNEL").default = 2
249
+ packet.append_item("CMD_TEMPLATE", 1024, :STRING)
250
+ packet.get_item("CMD_TEMPLATE").default = "SOUR:VOLT <VOLTAGE>, (@<CHANNEL>)"
251
+ packet.append_item("RSP_TEMPLATE", 1024, :STRING)
252
+ packet.get_item("RSP_TEMPLATE").default = "<VOLTAGE>"
253
+ packet.append_item("RSP_PACKET", 1024, :STRING)
254
+ packet.get_item("RSP_PACKET").default = "READ_VOLTAGE"
255
+ packet.restore_defaults
256
+ @interface.connect
257
+ read_result = nil
258
+ $read_buffer = "\x31\x30\x3B\x31\x31\xAB\xCD" # ASCII is '10;11'
259
+ Thread.new { sleep(0.5); read_result = @interface.read }
260
+ logger = class_double("Cosmos::Logger").as_stubbed_const(:transfer_nested_constants => true)
261
+ expect(logger).to receive(:error) do |arg|
262
+ expect(arg).to match(/Could not write value 10;11/)
263
+ end
264
+ @interface.write(packet)
265
+ sleep 0.55
266
+ expect($write_buffer).to eql("SOUR:VOLT 12, (@2)\xAB\xCD")
267
+ end
268
+
165
269
  it "ignores response lines" do
166
270
  rsp_pkt = Packet.new('TGT', 'READ_VOLTAGE')
167
271
  rsp_pkt.append_item("VOLTAGE", 16, :UINT)
168
272
  allow(System).to receive_message_chain(:telemetry, :packet).and_return(rsp_pkt)
169
273
  @interface.instance_variable_set(:@stream, TemplateStream.new)
170
- @interface.add_protocol(TemplateProtocol, ['0xAD','0xA', 1], :READ_WRITE)
274
+ @interface.add_protocol(TemplateProtocol, %w(0xAD 0xA 1), :READ_WRITE)
171
275
  @interface.target_names = ['TGT']
172
276
  packet = Packet.new('TGT', 'CMD')
173
277
  packet.append_item("VOLTAGE", 16, :UINT)
@@ -196,7 +300,7 @@ module Cosmos
196
300
  rsp_pkt.append_item("STRING", 512, :STRING)
197
301
  allow(System).to receive_message_chain(:telemetry, :packet).and_return(rsp_pkt)
198
302
  @interface.instance_variable_set(:@stream, TemplateStream.new)
199
- @interface.add_protocol(TemplateProtocol, ['0xAD','0xA', 0, nil, 2], :READ_WRITE)
303
+ @interface.add_protocol(TemplateProtocol, %w(0xAD 0xA 0 nil 2), :READ_WRITE)
200
304
  @interface.target_names = ['TGT']
201
305
  packet = Packet.new('TGT', 'CMD')
202
306
  packet.append_item("CMD_TEMPLATE", 1024, :STRING)