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

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 (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)