cosmos 4.4.2 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +6 -2
  3. data/Manifest.txt +25 -0
  4. data/README.md +4 -0
  5. data/Rakefile +3 -8
  6. data/bin/rubysloc +73 -28
  7. data/cosmos.gemspec +1 -1
  8. data/data/config/interface_modifiers.yaml +3 -2
  9. data/data/config/system.yaml +81 -24
  10. data/data/crc.txt +426 -426
  11. data/demo/config/data/crc.txt +233 -233
  12. data/demo/config/system/system.txt +15 -7
  13. data/demo/config/system/system2.txt +15 -7
  14. data/demo/config/system/system_alt_ports.txt +15 -7
  15. data/demo/config/targets/INST/cmd_tlm/inst_cmds.txt +1 -1
  16. data/extensions/vscode/.gitignore +4 -0
  17. data/extensions/vscode/.vscode/launch.json +32 -0
  18. data/extensions/vscode/.vscode/settings.json +13 -0
  19. data/extensions/vscode/.vscode/tasks.json +79 -0
  20. data/extensions/vscode/License.txt +879 -0
  21. data/extensions/vscode/README.md +9 -0
  22. data/extensions/vscode/client/License.txt +879 -0
  23. data/extensions/vscode/client/README.md +39 -0
  24. data/extensions/vscode/client/cosmos.configuration.json +23 -0
  25. data/extensions/vscode/client/images/icon.png +0 -0
  26. data/extensions/vscode/client/package-lock.json +414 -0
  27. data/extensions/vscode/client/package.json +105 -0
  28. data/extensions/vscode/client/src/extension.ts +132 -0
  29. data/extensions/vscode/client/src/screen_preview.rb +25 -0
  30. data/extensions/vscode/client/syntaxes/cosmos.tmLanguage.json +219 -0
  31. data/extensions/vscode/client/tsconfig.json +17 -0
  32. data/extensions/vscode/package-lock.json +26 -0
  33. data/extensions/vscode/package.json +35 -0
  34. data/extensions/vscode/server/License.txt +879 -0
  35. data/extensions/vscode/server/package-lock.json +236 -0
  36. data/extensions/vscode/server/package.json +29 -0
  37. data/extensions/vscode/server/src/server.ts +59 -0
  38. data/extensions/vscode/server/tsconfig.json +16 -0
  39. data/install/config/data/crc.txt +132 -132
  40. data/install/config/system/system.txt +15 -7
  41. data/lib/cosmos/core_ext/time.rb +3 -1
  42. data/lib/cosmos/dart/examples/dart_decom_client.rb +1 -1
  43. data/lib/cosmos/dart/lib/dart_decommutator.rb +4 -4
  44. data/lib/cosmos/dart/processes/dart_decom_server.rb +1 -1
  45. data/lib/cosmos/dart/processes/dart_master.rb +1 -1
  46. data/lib/cosmos/gui/qt_tool.rb +10 -12
  47. data/lib/cosmos/gui/widgets/dart_meta_frame.rb +1 -1
  48. data/lib/cosmos/interfaces/dart_status_interface.rb +1 -1
  49. data/lib/cosmos/interfaces/serial_interface.rb +7 -1
  50. data/lib/cosmos/io/json_drb.rb +2 -2
  51. data/lib/cosmos/io/json_drb_object.rb +7 -2
  52. data/lib/cosmos/io/json_drb_rack.rb +25 -5
  53. data/lib/cosmos/io/json_rpc.rb +1 -1
  54. data/lib/cosmos/io/posix_serial_driver.rb +60 -22
  55. data/lib/cosmos/io/serial_driver.rb +11 -8
  56. data/lib/cosmos/io/win32_serial_driver.rb +8 -1
  57. data/lib/cosmos/packets/structure.rb +11 -2
  58. data/lib/cosmos/script/api_shared.rb +8 -1
  59. data/lib/cosmos/script/script.rb +2 -9
  60. data/lib/cosmos/streams/serial_stream.rb +11 -6
  61. data/lib/cosmos/system/system.rb +43 -12
  62. data/lib/cosmos/tools/cmd_sender/cmd_param_table_item_delegate.rb +15 -0
  63. data/lib/cosmos/tools/cmd_sender/cmd_params.rb +25 -3
  64. data/lib/cosmos/tools/cmd_sender/cmd_sender.rb +7 -0
  65. data/lib/cosmos/tools/cmd_sequence/sequence_item.rb +0 -5
  66. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +2 -2
  67. data/lib/cosmos/tools/cmd_tlm_server/router_thread.rb +5 -0
  68. data/lib/cosmos/tools/config_editor/config_editor.rb +1 -1
  69. data/lib/cosmos/tools/handbook_creator/handbook_creator_config.rb +1 -1
  70. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_config.rb +1 -4
  71. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_processor.rb +3 -3
  72. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_dart_thread.rb +1 -1
  73. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +2 -2
  74. data/lib/cosmos/version.rb +5 -5
  75. data/spec/core_ext/time_spec.rb +4 -0
  76. data/spec/io/json_drb_rack_spec.rb +166 -0
  77. data/spec/io/json_rpc_spec.rb +4 -5
  78. data/spec/io/posix_serial_driver_spec.rb +81 -0
  79. data/spec/io/win32_serial_driver_spec.rb +17 -1
  80. data/spec/system/system_spec.rb +108 -0
  81. data/spec/tools/cmd_tlm_server/router_thread_spec.rb +2 -3
  82. metadata +31 -6
@@ -5,16 +5,17 @@ AUTO_DECLARE_TARGETS
5
5
  # DECLARE_TARGET SYSTEM
6
6
 
7
7
  # Listen Hosts - Ip addresses or hostnames to listen on when running the tools
8
+ # Set these to 0.0.0.0 if you need external connections from other computers
8
9
  LISTEN_HOST CTS_API 127.0.0.1
9
10
  LISTEN_HOST TLMVIEWER_API 127.0.0.1
10
- LISTEN_HOST CTS_PREIDENTIFIED 0.0.0.0 # 127.0.0.1 is more secure if you don't need external connections
11
- LISTEN_HOST CTS_CMD_ROUTER 0.0.0.0 # 127.0.0.1 is more secure if you don't need external connections
11
+ LISTEN_HOST CTS_PREIDENTIFIED 127.0.0.1
12
+ LISTEN_HOST CTS_CMD_ROUTER 127.0.0.1
12
13
  LISTEN_HOST REPLAY_API 127.0.0.1
13
- LISTEN_HOST REPLAY_PREIDENTIFIED 0.0.0.0 # 127.0.0.1 is more secure if you don't need external connections
14
- LISTEN_HOST REPLAY_CMD_ROUTER 0.0.0.0 # 127.0.0.1 is more secure if you don't need external connections
15
- LISTEN_HOST DART_STREAM 0.0.0.0 # 127.0.0.1 is more secure if you don't need external connections
16
- LISTEN_HOST DART_DECOM 0.0.0.0 # 127.0.0.1 is more secure if you don't need external connections
17
- LISTEN_HOST DART_MASTER 0.0.0.0 # 127.0.0.1 is more secure if you don't need external connections
14
+ LISTEN_HOST REPLAY_PREIDENTIFIED 127.0.0.1
15
+ LISTEN_HOST REPLAY_CMD_ROUTER 127.0.0.1
16
+ LISTEN_HOST DART_STREAM 127.0.0.1
17
+ LISTEN_HOST DART_DECOM 127.0.0.1
18
+ LISTEN_HOST DART_MASTER 127.0.0.1
18
19
 
19
20
  # Connect Hosts - Ip addresses or hostnames to connect to when running the tools
20
21
  CONNECT_HOST CTS_API 127.0.0.1
@@ -65,3 +66,10 @@ ALLOW_ACCESS ALL
65
66
  # META_INIT config/data/meta_init.txt
66
67
 
67
68
  # ADD_HASH_FILE lib/user_version.rb
69
+
70
+ # ALLOW_ROUTER_COMMANDING # Enable this to chain servers with commanding
71
+ # ALLOW_HOST localhost:7777 # This is provided by default. Add with HostIP:7777 for chaining
72
+ # ALLOW_ORIGIN 1.2.3.4:8080 # Add this if you have a webserver that should be able to access the COSMOS API
73
+
74
+ # *** IMPORTANT *** Change this value to a unique value for each project
75
+ X_CSRF_TOKEN SuperSecret # Secret used to secure requests - Change for each project
@@ -389,7 +389,9 @@ class Time
389
389
  # @return [Float] The number of seconds from the given epoch to the given
390
390
  # CCSDS day, milliseconds, and microseconds.
391
391
  def self.ccsds2sec(day, ms, us, sec_epoch_jd = JULIAN_DATE_OF_CCSDS_EPOCH)
392
- (self.ccsds2julian(day, ms, us) - sec_epoch_jd) * SEC_PER_DAY_FLOAT
392
+ # NOTE: We don't call ccsds2julian to avoid loss of precision
393
+ (day + JULIAN_DATE_OF_CCSDS_EPOCH - sec_epoch_jd +
394
+ ((ms.to_f + (us / 1000.0)) / MSEC_PER_DAY_FLOAT)) * SEC_PER_DAY_FLOAT
393
395
  end
394
396
 
395
397
  # @param sec [Float] Number of seconds to convert
@@ -31,7 +31,7 @@ request['value_type'] = 'RAW_AVG'
31
31
  request['meta_filters'] = ["OPERATOR_NAME == 'Unspecified'"]
32
32
 
33
33
  puts "Connecting to Dart Decom Server..."
34
- server = Cosmos::JsonDRbObject.new(Cosmos::System.connect_hosts['DART_DECOM'], Cosmos::System.ports['DART_DECOM'])
34
+ server = Cosmos::JsonDRbObject.new(Cosmos::System.connect_hosts['DART_DECOM'], Cosmos::System.ports['DART_DECOM'], 1.0, Cosmos::System.x_csrf_token)
35
35
  puts "Getting SYSTEM META fields"
36
36
  result = server.item_names("SYSTEM", "META")
37
37
  puts result.inspect(10000)
@@ -36,16 +36,16 @@ class DartDecommutator
36
36
 
37
37
  # Wait 60s before giving up on the PacketConfig becoming ready
38
38
  PACKET_CONFIG_READY_TIMEOUT = 60
39
-
39
+
40
40
  # Delay between updating the DART status packet. Simply throttles this rarely viewed status
41
41
  STATUS_UPDATE_PERIOD_SECONDS = 60.seconds
42
-
42
+
43
43
  def initialize(worker_id = 0, num_workers = 1)
44
44
  sync_targets_and_packets()
45
45
  @worker_id = worker_id
46
46
  @num_workers = num_workers
47
47
  @status = DartDecommutatorStatus.new
48
- @master = Cosmos::JsonDRbObject.new(Cosmos::System.connect_hosts['DART_MASTER'], Cosmos::System.ports['DART_MASTER'])
48
+ @master = Cosmos::JsonDRbObject.new(Cosmos::System.connect_hosts['DART_MASTER'], Cosmos::System.ports['DART_MASTER'], 1.0, Cosmos::System.x_csrf_token)
49
49
  end
50
50
 
51
51
  def timeit(message, &block)
@@ -84,7 +84,7 @@ class DartDecommutator
84
84
  # If we timeout this code will simply exit the application
85
85
  wait_for_ready_packet_config(packet_config)
86
86
  decom_packet(ple, packet, packet_config)
87
-
87
+
88
88
  # Update status
89
89
  if Time.now > status_time
90
90
  status_time = Time.now + 60.seconds
@@ -25,7 +25,7 @@ Cosmos.catch_fatal_exception do
25
25
  json_drb.method_whitelist = ['query', 'item_names', 'dart_status', 'clear_errors']
26
26
  begin
27
27
  json_drb.start_service(Cosmos::System.listen_hosts['DART_DECOM'],
28
- Cosmos::System.ports['DART_DECOM'], DartDecomQuery.new)
28
+ Cosmos::System.ports['DART_DECOM'], DartDecomQuery.new, 1000, Cosmos::System)
29
29
  rescue Exception => error
30
30
  raise Cosmos::FatalError.new("Error starting JsonDRb on port #{Cosmos::System.ports['DART_DECOM']}.\nPerhaps another DART Decom Server is already running?\n#{error.formatted}")
31
31
  end
@@ -29,7 +29,7 @@ Cosmos.catch_fatal_exception do
29
29
  json_drb.method_whitelist = ['get_decom_ple_ids']
30
30
  begin
31
31
  json_drb.start_service(Cosmos::System.listen_hosts['DART_MASTER'],
32
- Cosmos::System.ports['DART_MASTER'], DartMasterQuery.new(ples_per_request))
32
+ Cosmos::System.ports['DART_MASTER'], DartMasterQuery.new(ples_per_request), 1000, Cosmos::System)
33
33
  rescue Exception => error
34
34
  raise Cosmos::FatalError.new("Error starting JsonDRb on port #{Cosmos::System.ports['DART_MASTER']}.\nPerhaps another DART Master is already running?\n#{error.formatted}")
35
35
  end
@@ -82,7 +82,7 @@ module Cosmos
82
82
  else
83
83
  options.config_file = nil
84
84
  options.stylesheet = nil
85
- end
85
+ end
86
86
  end
87
87
 
88
88
  # Creates a path to a configuration file. If the file is given it is
@@ -209,28 +209,26 @@ module Cosmos
209
209
 
210
210
  # Handle manually positioning the window
211
211
  unless @options.auto_position
212
- # Get the desktop's geometry
213
- desktop = Qt::Application.desktop
214
-
215
- # Handle position relative to right edge
216
- @options.x = desktop.width - frameGeometry().width + @options.x + 1 if @options.x < 0
217
-
218
- # Handle position relative to bottom edge
219
- @options.y = desktop.height - frameGeometry().height + @options.y + 1 if @options.y < 0
220
-
221
212
  # Move to the desired position
222
213
  move(@options.x, @options.y)
223
214
  end
224
215
 
225
216
  if @options.remember_geometry and !@options.command_line_geometry
217
+ # Get the desktop's geometry
218
+ desktop = Qt::Application.desktop
219
+ screen = desktop.screen
226
220
  settings = Qt::Settings.new('Ball Aerospace', self.class.to_s)
227
221
  if settings.contains('size') and @options.restore_size and @options.startup_state != :DEFAULT
228
222
  size = settings.value('size').toSize
229
- resize(size)
223
+ if size.height > 0 and size.height < screen.height and size.width > 0 and size.width < screen.width
224
+ resize(size)
225
+ end
230
226
  end
231
227
  if settings.contains('position') and @options.restore_position
232
228
  position = settings.value('position').toPoint
233
- move(position)
229
+ if position.x > 0 and position.y > 0 and position.x < screen.width and position.y < screen.height
230
+ move(position)
231
+ end
234
232
  end
235
233
  end
236
234
 
@@ -114,7 +114,7 @@ module Cosmos
114
114
  if !@got_meta_item_names and !@update_thread
115
115
  @update_thread = Thread.new do
116
116
  begin
117
- server = JsonDRbObject.new(System.connect_hosts['DART_DECOM'], System.ports['DART_DECOM'])
117
+ server = JsonDRbObject.new(System.connect_hosts['DART_DECOM'], System.ports['DART_DECOM'], 1.0, Cosmos::System.x_csrf_token)
118
118
  item_names = server.item_names("SYSTEM", "META")
119
119
  Qt.execute_in_main_thread do
120
120
  unless self.disposed?
@@ -36,7 +36,7 @@ module Cosmos
36
36
  @status_packet.write('PACKET_ID', 1)
37
37
  @clear_errors_command = System.commands.packet('DART', 'CLEAR_ERRORS')
38
38
  @sleeper = Sleeper.new
39
- @dart = JsonDRbObject.new(System.connect_hosts['DART_DECOM'], System.ports['DART_DECOM'])
39
+ @dart = JsonDRbObject.new(System.connect_hosts['DART_DECOM'], System.ports['DART_DECOM'], 1.0, Cosmos::System.x_csrf_token)
40
40
  end
41
41
 
42
42
  # Indicates if the interface is connected to its target(s) or not. Must be
@@ -52,6 +52,7 @@ module Cosmos
52
52
  @read_allowed = false unless @read_port_name
53
53
  @flow_control = :NONE
54
54
  @data_bits = 8
55
+ @struct = []
55
56
  end
56
57
 
57
58
  # Creates a new {SerialStream} using the parameters passed in the constructor
@@ -65,13 +66,16 @@ module Cosmos
65
66
  @write_timeout,
66
67
  @read_timeout,
67
68
  @flow_control,
68
- @data_bits
69
+ @data_bits,
70
+ @struct
69
71
  )
70
72
  super()
71
73
  end
72
74
 
73
75
  # Supported Options
74
76
  # FLOW_CONTROL - Flow control method NONE or RTSCTS. Defaults to NONE
77
+ # DATA_BITS - How many data bits to use
78
+ # STRUCT - Directly set fields in the Win32 DCB or POSIX termios structure
75
79
  def set_option(option_name, option_values)
76
80
  super(option_name, option_values)
77
81
  case option_name.upcase
@@ -79,6 +83,8 @@ module Cosmos
79
83
  @flow_control = option_values[0]
80
84
  when 'DATA_BITS'
81
85
  @data_bits = option_values[0].to_i
86
+ when 'STRUCT'
87
+ @struct << option_values
82
88
  end
83
89
  end
84
90
  end
@@ -123,7 +123,7 @@ module Cosmos
123
123
  # @param object [Object] The object to send the DRb requests to. This
124
124
  # object must either include the Cosmos::Script module or be the
125
125
  # CmdTlmServer.
126
- def start_service(hostname = nil, port = nil, object = nil, max_threads = 1000)
126
+ def start_service(hostname = nil, port = nil, object = nil, max_threads = 1000, system = nil)
127
127
  server_started = false
128
128
  @server_mutex.synchronize do
129
129
  server_started = true if @server
@@ -147,7 +147,7 @@ module Cosmos
147
147
  }
148
148
 
149
149
  # The run call will block until the server is stopped.
150
- Rack::Handler::Puma.run(JsonDrbRack.new(self), server_config) do |server|
150
+ Rack::Handler::Puma.run(JsonDrbRack.new(self, system), server_config) do |server|
151
151
  @server_mutex.synchronize do
152
152
  @server = server
153
153
  end
@@ -38,7 +38,7 @@ module Cosmos
38
38
  # @param hostname [String] The name of the machine which has started
39
39
  # the JSON service
40
40
  # @param port [Integer] The port number of the JSON service
41
- def initialize(hostname, port, connect_timeout = 1.0)
41
+ def initialize(hostname, port, connect_timeout = 1.0, x_csrf_token = nil)
42
42
  hostname = '127.0.0.1' if (hostname.to_s.upcase == 'LOCALHOST')
43
43
  begin
44
44
  Socket.pack_sockaddr_in(port, hostname)
@@ -61,6 +61,7 @@ module Cosmos
61
61
  @connect_timeout = connect_timeout
62
62
  @connect_timeout = @connect_timeout.to_f if @connect_timeout
63
63
  @shutdown = false
64
+ @x_csrf_token = x_csrf_token
64
65
  end
65
66
 
66
67
  # Disconnects from http server
@@ -127,7 +128,11 @@ module Cosmos
127
128
  STDOUT.puts "\nRequest:\n" if JsonDRb.debug?
128
129
  STDOUT.puts @request_data if JsonDRb.debug?
129
130
  @request_in_progress = true
130
- headers = {'Content-Type' => 'application/json-rpc'}
131
+ if @x_csrf_token
132
+ headers = {'Content-Type' => 'application/json-rpc', 'X-Csrf-Token' => @x_csrf_token}
133
+ else
134
+ headers = {'Content-Type' => 'application/json-rpc'}
135
+ end
131
136
  res = @http.post(@uri,
132
137
  :body => @request_data,
133
138
  :header => headers)
@@ -17,8 +17,9 @@ module Cosmos
17
17
  class JsonDrbRack
18
18
  # @param drb [JsonDRb] - An instance of the JsonDRb class that'll be used
19
19
  # to process the JSON request and generate a response
20
- def initialize(drb)
20
+ def initialize(drb, system = nil)
21
21
  @drb = drb
22
+ @system = system
22
23
  end
23
24
 
24
25
  # Handles a request.
@@ -32,22 +33,41 @@ module Cosmos
32
33
  # ACL allow_addr? function takes address in the form returned by
33
34
  # IPSocket.peeraddr.
34
35
  req_addr = ["AF_INET", request.port, request.host.to_s, request.ip.to_s]
36
+ status = nil
35
37
 
36
38
  if @drb.acl and !@drb.acl.allow_addr?(req_addr)
37
39
  status = 403
38
40
  content_type = "text/plain"
39
41
  body = "Forbidden"
40
42
  elsif request.post?
41
- status, content_type, body = handle_post(request)
43
+ if @system
44
+ if @system.x_csrf_token and (request.env['HTTP_X_CSRF_TOKEN'] != @system.x_csrf_token)
45
+ status = 403
46
+ content_type = "text/plain"
47
+ body = "Forbidden: Bad X-Csrf-Token: #{request.env['HTTP_X_CSRF_TOKEN']}"
48
+ end
49
+ if !@system.allowed_hosts.include?(request.env['HTTP_HOST'])
50
+ status = 403
51
+ content_type = "text/plain"
52
+ body = "Forbidden: #{request.env['HTTP_HOST']} not in allowed hosts"
53
+ end
54
+ if request.env['HTTP_ORIGIN'] and !@system.allowed_origins.include?(request.env['HTTP_ORIGIN'])
55
+ status = 403
56
+ content_type = "text/plain"
57
+ body = "Forbidden: #{request.env['HTTP_ORIGIN']} not in allowed origins"
58
+ end
59
+ end
60
+
61
+ status, content_type, body = handle_post(request) unless status
42
62
  else
43
63
  status = 405
44
64
  content_type = "text/plain"
45
65
  body = "Request not allowed"
46
66
  end
47
-
67
+
48
68
  return status, {'Content-Type' => content_type}, [body]
49
69
  end
50
-
70
+
51
71
  # Handles an http post.
52
72
  #
53
73
  # @param request [Rack::Request] - A rack post request
@@ -57,7 +77,7 @@ module Cosmos
57
77
  request_data = request.body.read
58
78
  start_time = Time.now.sys
59
79
  response_data, error_code = @drb.process_request(request_data, start_time)
60
-
80
+
61
81
  # Convert json error code into html status code
62
82
  # see http://www.jsonrpc.org/historical/json-rpc-over-http.html#errors
63
83
  if error_code
@@ -269,7 +269,7 @@ module Cosmos
269
269
  # @param response_data [String] JSON encoded string representing the response
270
270
  # @return [JsonRpcResponse]
271
271
  def self.from_json(response_data)
272
- msg = "Invalid JSON-RPC 2.0 Response"
272
+ msg = "Invalid JSON-RPC 2.0 Response: #{response_data.inspect}"
273
273
  begin
274
274
  hash = JSON.parse(response_data, :allow_nan => true, :create_additions => true)
275
275
  rescue
@@ -25,19 +25,20 @@ module Cosmos
25
25
  write_timeout = 10.0,
26
26
  read_timeout = nil,
27
27
  flow_control = :NONE,
28
- data_bits = 8)
28
+ data_bits = 8,
29
+ struct = [])
29
30
 
30
31
  # Convert Baud Rate into Termios constant
31
32
  begin
32
33
  baud_rate = Object.const_get("Termios::B#{baud_rate}")
33
34
  rescue NameError
34
- raise(ArgumentError, "Invalid Baud Rate, Not Defined by Termios: #{baud_rate}")
35
+ raise(ArgumentError, "Invalid baud rate: #{baud_rate}")
35
36
  end
36
37
 
37
38
  # Verify Parameters
38
- raise(ArgumentError, "Invalid Data Bits: #{data_bits}") unless [5,6,7,8].include?(data_bits)
39
+ raise(ArgumentError, "Invalid data bits: #{data_bits}") unless [5,6,7,8].include?(data_bits)
39
40
  raise(ArgumentError, "Invalid parity: #{parity}") if parity and !SerialDriver::VALID_PARITY.include?(parity)
40
- raise(ArgumentError, "Invalid Stop Bits: #{stop_bits}") unless [1,2].include?(stop_bits)
41
+ raise(ArgumentError, "Invalid stop bits: #{stop_bits}") unless [1,2].include?(stop_bits)
41
42
  @write_timeout = write_timeout
42
43
  @read_timeout = read_timeout
43
44
 
@@ -51,22 +52,61 @@ module Cosmos
51
52
 
52
53
  # Configure the serial Port
53
54
  tio = Termios::new_termios()
54
- iflags = 0
55
- iflags |= Termios::IGNPAR unless parity
56
- cflags = 0
57
- cflags |= Termios::CREAD # Enable receiver
58
- cflags |= Termios.const_get("CS#{data_bits}") # data bits
59
- cflags |= Termios::CLOCAL # Ignore Modem Control Lines
60
- cflags |= Termios::CSTOPB if stop_bits == 2
61
- cflags |= Termios::PARENB if parity
62
- cflags |= Termios::PARODD if parity == :ODD
63
- cflags |= Termios::CRTSCTS if flow_control == :RTSCTS
64
- tio.iflag = iflags
65
- tio.oflag = 0
66
- tio.cflag = cflags
67
- tio.lflag = 0
55
+ iflag = 0
56
+ iflag |= Termios::IGNPAR unless parity
57
+ oflag = 0
58
+ cflag = 0
59
+ cflag |= Termios::CREAD # Enable receiver
60
+ cflag |= Termios.const_get("CS#{data_bits}") # data bits
61
+ cflag |= Termios::CLOCAL # Ignore Modem Control Lines
62
+ cflag |= Termios::CSTOPB if stop_bits == 2
63
+ cflag |= Termios::PARENB if parity
64
+ cflag |= Termios::PARODD if parity == :ODD
65
+ cflag |= Termios::CRTSCTS if flow_control == :RTSCTS
66
+ lflag = 0
68
67
  tio.cc[Termios::VTIME] = 0
69
68
  tio.cc[Termios::VMIN] = 1
69
+ unless struct.empty?
70
+ struct.each do |field, key, value|
71
+ case field
72
+ when 'iflag'
73
+ if value == "0"
74
+ iflag &= ~Termios.const_get(key)
75
+ else
76
+ iflag |= Termios.const_get(key)
77
+ end
78
+ when 'oflag'
79
+ if value == "0"
80
+ oflag &= ~Termios.const_get(key)
81
+ else
82
+ oflag |= Termios.const_get(key)
83
+ end
84
+ when 'cflag'
85
+ if value == "0"
86
+ cflag &= ~Termios.const_get(key)
87
+ else
88
+ cflag |= Termios.const_get(key)
89
+ end
90
+ when 'lflag'
91
+ if value == "0"
92
+ lflag &= ~Termios.const_get(key)
93
+ else
94
+ lflag |= Termios.const_get(key)
95
+ end
96
+ when 'cc'
97
+ begin
98
+ value = Integer(value) # Try to convert to int
99
+ rescue ArgumentError
100
+ # Ignore this error and use the string
101
+ end
102
+ tio.cc[Termios.const_get(key)] = value
103
+ end
104
+ end
105
+ end
106
+ tio.iflag = iflag
107
+ tio.oflag = oflag
108
+ tio.cflag = cflag
109
+ tio.lflag = lflag
70
110
  tio.ispeed = baud_rate
71
111
  tio.ospeed = baud_rate
72
112
  @handle.tcflush(Termios::TCIOFLUSH)
@@ -143,7 +183,5 @@ module Cosmos
143
183
 
144
184
  data
145
185
  end
146
-
147
- end # class PosixSerialDriver
148
-
149
- end # module Cosmos
186
+ end
187
+ end
@@ -32,8 +32,10 @@ module Cosmos
32
32
  # complete or nil to block
33
33
  # @param read_timeout [Float|nil] Number of seconds to wait for the read to
34
34
  # complete or nil to block
35
- # @param flow_control [Symbol] Currently supported :NONE and :RTSCTS (default :NONE)
35
+ # @param flow_control [Symbol] Currently supported :NONE, :RTSCTS (default :NONE)
36
36
  # @param data_bits [Integer] Number of data bits (default 8)
37
+ # @param struct [Array] Array of arrays of fields and values to set in the
38
+ # Windows DCB or POSIX structure
37
39
  def initialize(port_name,
38
40
  baud_rate,
39
41
  parity = :NONE,
@@ -41,7 +43,8 @@ module Cosmos
41
43
  write_timeout = 10.0,
42
44
  read_timeout = nil,
43
45
  flow_control = :NONE,
44
- data_bits = 8)
46
+ data_bits = 8,
47
+ struct = [])
45
48
  raise(ArgumentError, "Invalid parity: #{parity}") unless VALID_PARITY.include? parity
46
49
  if Kernel.is_windows?
47
50
  @driver = Win32SerialDriver.new(port_name,
@@ -53,7 +56,8 @@ module Cosmos
53
56
  0.01,
54
57
  1000,
55
58
  flow_control,
56
- data_bits)
59
+ data_bits,
60
+ struct)
57
61
  elsif RUBY_ENGINE == 'ruby'
58
62
  @driver = PosixSerialDriver.new(port_name,
59
63
  baud_rate,
@@ -62,7 +66,8 @@ module Cosmos
62
66
  write_timeout,
63
67
  read_timeout,
64
68
  flow_control,
65
- data_bits)
69
+ data_bits,
70
+ struct)
66
71
  else
67
72
  @driver = nil # JRuby Serial on Linux not currently supported
68
73
  end
@@ -92,7 +97,5 @@ module Cosmos
92
97
  def read_nonblock
93
98
  @driver.read_nonblock
94
99
  end
95
-
96
- end # class SerialDriver
97
-
98
- end # module Cosmos
100
+ end
101
+ end