sgslib 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7ecb05d8a8096186dd2d79b8126cc50e501e71d5b7c80a58f7b8bcc9a0f7ec0
4
- data.tar.gz: c4d5073036b612bc4333ad2e176e58c6dae5c262d748a981eb55992526c0a29e
3
+ metadata.gz: b5dffcc970035c48e2b6026c72884b6ab97cc14ac43ba255cb4386244b46287c
4
+ data.tar.gz: 347c958575c0e9510047c75b997426083d4e3abf8ee479ff9b407c3395bbe118
5
5
  SHA512:
6
- metadata.gz: 891aa77f6714876a1c2ec5432b760aeab2a7a9421c2e97713dac0ad618f068961142d06020a24eec586222f3ddc4762e1e549461152fa3ab32d18b93b1f046b9
7
- data.tar.gz: 03d3070b475d819e0f1f855ba6a83fb83596b4c2900a4bb201348570ca9b2e7e5769ab5c9a7f00a3d71c1b31cac12023d362224dd190af01a46695b20df43f7d
6
+ metadata.gz: bdee04756fe59509497bc455f7874e9c05ecca6c9031f55133460a28ea987d9a0dd788588e2b449b9fe0630f734f23b6c968722361153b9130fa0362c354b67c
7
+ data.tar.gz: de267f01e59622938cfb1ad3ebbcb7c08c72bb02a625f1de72c93255b5e3be868cae29a1625383890c4c4aa57292b27942d735adb0abb4f36763dd481ca62893
data/exe/sgs_otto CHANGED
File without changes
data/lib/sgs/alarm.rb CHANGED
@@ -44,7 +44,9 @@ module SGS
44
44
  class Alarm < RedisBase
45
45
  attr_accessor :last_report, :time
46
46
 
47
- OTTO_RESTART = 0
47
+ #
48
+ # Alarms generated by Otto.
49
+ MISSION_SWITCH = 0
48
50
  RUDDSRV_FAULT = 1
49
51
  SAILSRV_FAULT = 2
50
52
  VBATT_CRITICAL = 3
@@ -60,16 +62,18 @@ module SGS
60
62
  RUDDER_NOZERO = 13
61
63
  SAIL_NOZERO = 14
62
64
  MOTHER_UNRESP = 15
63
-
64
- MISSION_COMMENCE = 16
65
- MISSION_COMPLETE = 17
66
- MISSION_ABORT = 18
67
- WAYPOINT_REACHED = 19
68
- CROSS_TRACK_ERROR = 20
69
- INSIDE_FENCE = 21
65
+ #
66
+ # Alarms generated by Mother.
67
+ OTTO_RESTART = 16
68
+ MISSION_COMMENCE = 17
69
+ MISSION_COMPLETE = 18
70
+ MISSION_ABORT = 19
71
+ WAYPOINT_REACHED = 20
72
+ CROSS_TRACK_ERROR = 21
73
+ INSIDE_FENCE = 22
70
74
 
71
75
  ALARM_NAMES = [
72
- "OTTO Restarted",
76
+ "Mission Activation Switch",
73
77
  "Rudder Servo Fault",
74
78
  "Sail Servo Fault",
75
79
  "Battery voltage is critically low",
@@ -85,6 +89,7 @@ module SGS
85
89
  "Cannot zero the rudder position",
86
90
  "Cannot zero the sail position",
87
91
  "Mother is unresponsive",
92
+ "OTTO Restarted",
88
93
  "Mission has commenced",
89
94
  "Mission is completed",
90
95
  "*** MISSION ABORT ***",
@@ -103,8 +108,13 @@ module SGS
103
108
  #
104
109
  # Main daemon function (called from executable)
105
110
  def self.daemon
111
+ puts "Alarm daemon starting up..."
112
+ otto = SGS::RPCClient.new(:otto)
106
113
  loop do
107
- sleep 300
114
+ #puts "Check for any alarms..."
115
+ #resp = otto.command "A?"
116
+ #puts "Response: #{resp}"
117
+ sleep 30
108
118
  end
109
119
  end
110
120
 
data/lib/sgs/bearing.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  #
2
+ #
2
3
  # Copyright (c) 2013-2023, Kalopa Robotics Limited. All rights
3
4
  # reserved.
4
5
  #
@@ -41,6 +41,7 @@ module SGS
41
41
  #
42
42
  # Main daemon function (called from executable)
43
43
  def self.daemon
44
+ puts "Diagnostics subsystem starting up..."
44
45
  loop do
45
46
  sleep 300
46
47
  end
data/lib/sgs/gps.rb CHANGED
@@ -32,6 +32,8 @@
32
32
  #
33
33
  # ABSTRACT
34
34
  #
35
+ require 'serialport'
36
+
35
37
  module SGS
36
38
  class GPS < RedisBase
37
39
  attr_accessor :time, :location, :sog, :cmg, :magvar
@@ -49,8 +51,19 @@ module SGS
49
51
  #
50
52
  # Main daemon function (called from executable)
51
53
  def self.daemon
54
+ puts "GPS reader starting up..."
55
+ config = SGS::Config.load
56
+
57
+ sp = SerialPort.new config.gps_device, config.gps_speed
58
+ sp.read_timeout = 10000
59
+
52
60
  loop do
53
- sleep 300
61
+ nmea = SGS::NMEA.parse sp.readline
62
+ if nmea.is_gprmc?
63
+ gps = nmea.parse_gprmc
64
+ p gps
65
+ gps.save_and_publish if gps and gps.valid?
66
+ end
54
67
  end
55
68
  end
56
69
 
data/lib/sgs/logger.rb CHANGED
@@ -38,12 +38,5 @@
38
38
  #
39
39
  module SGS
40
40
  class Logger < RedisBase
41
- #
42
- # Main daemon function (called from executable)
43
- def self.daemon
44
- loop do
45
- sleep 300
46
- end
47
- end
48
41
  end
49
42
  end
data/lib/sgs/mission.rb CHANGED
@@ -51,6 +51,7 @@ module SGS
51
51
  #
52
52
  # Main daemon function (called from executable)
53
53
  def self.daemon
54
+ puts "Mission management system starting up..."
54
55
  loop do
55
56
  sleep 300
56
57
  end
@@ -73,7 +73,6 @@ module SGS
73
73
  @state = STATE_AWAITING
74
74
  @current_waypoint = 0
75
75
  @start_time = @end_time = nil
76
- @logger = Logger.new(STDOUT)
77
76
  end
78
77
 
79
78
  #
@@ -91,7 +90,7 @@ module SGS
91
90
  #
92
91
  # Commence a mission...
93
92
  def start_test!(time = nil)
94
- @logger.warn "***** Starting test phase *****"
93
+ puts "***** Starting test phase *****"
95
94
  @start_time = time || Time.now
96
95
  @state = STATE_START_TEST
97
96
  @current_waypoint = 0
@@ -104,7 +103,7 @@ module SGS
104
103
  @end_time = time || Time.now
105
104
  @state = STATE_COMPLETE
106
105
  save_and_publish
107
- @logger.warn "***** Mission completed! *****"
106
+ puts "***** Mission completed! *****"
108
107
  end
109
108
 
110
109
  #
@@ -113,7 +112,7 @@ module SGS
113
112
  @end_time = time || Time.now
114
113
  @state = STATE_TERMINATED
115
114
  save_and_publish
116
- @logger.warn "***** Mission terminated! *****"
115
+ puts "***** Mission terminated! *****"
117
116
  end
118
117
 
119
118
  #
@@ -122,7 +121,7 @@ module SGS
122
121
  @end_time = time || Time.now
123
122
  @state = STATE_FAILURE
124
123
  save_and_publish
125
- @logger.warn "***** Mission failure! *****"
124
+ puts "***** Mission failure! *****"
126
125
  end
127
126
  end
128
127
  end
data/lib/sgs/navigate.rb CHANGED
@@ -71,8 +71,27 @@ module SGS
71
71
  #
72
72
  # Main daemon function (called from executable)
73
73
  def self.daemon
74
- loop do
75
- sleep 300
74
+ puts "Navigation system starting up..."
75
+ #
76
+ # Load the mission data from Redis and augment it with the
77
+ # contents of the mission file.
78
+ config = SGS::Config.load
79
+ mission = SGS::Mission.file_load config.mission_file
80
+ #
81
+ # Now listen for GPS data...
82
+ SGS::GPS.subscribe do |count|
83
+ puts "Received new GPS count: #{count}"
84
+ case SGS::MissionStatus.state
85
+ when STATE_COMPASS_FOLLOW
86
+ when STATE_WIND_FOLLOW
87
+ mission.navigate
88
+ when STATE_COMPLETE
89
+ when STATE_TERMINATED
90
+ when STATE_FAILURE
91
+ mission.hold_station
92
+ end
93
+ gps = SGS::GPS.load
94
+ p gps
76
95
  end
77
96
  end
78
97
 
data/lib/sgs/otto.rb CHANGED
@@ -32,6 +32,8 @@
32
32
  #
33
33
  # ABSTRACT
34
34
  #
35
+ require 'serialport'
36
+ require 'msgpack'
35
37
 
36
38
  ##
37
39
  # Routines for interfacing with the low-level microcontroller.
@@ -41,6 +43,9 @@ module SGS
41
43
  attr_accessor :raw_rudder, :raw_sail, :raw_compass, :raw_awa, :raw_tc, :raw_ta
42
44
  attr_accessor :mode, :rudder_m, :rudder_c, :sail_m, :sail_c
43
45
  attr_accessor :bv_m, :bv_c, :bi_m, :bi_c, :bt_m, :bt_c, :sv_m, :sv_c
46
+ attr_accessor :serial_port
47
+ attr_reader :alarm_status, :wind, :compass, :actual_rudder, :actual_sail
48
+ attr_reader :otto_mode, :otto_timestamp, :telemetry
44
49
 
45
50
  MODE_INERT = 0
46
51
  MODE_DIAGNOSTICS = 1
@@ -57,8 +62,9 @@ module SGS
57
62
  # Set up some useful defaults. We assume rudder goes from 0 to 200 as does
58
63
  # the sail angle.
59
64
  def initialize
65
+ serial_port = nil
60
66
  #
61
- # Configure the Mx + C values for sail and rudder
67
+ # Configure the Mx + C values for sail and rudder
62
68
  @rudder_m = 2.5
63
69
  @rudder_c = 100.0
64
70
  @sail_m = 2.0
@@ -68,6 +74,12 @@ module SGS
68
74
  rudder = 0.0
69
75
  sail = 0.0
70
76
  #
77
+ # Set some defaults for the read-back parameters
78
+ @alarm_status = @wind = @compass = @actual_rudder = @actual_sail = 0
79
+ @otto_mode = 0
80
+ @otto_timestamp = 1000
81
+ @telemetry = Array.new(16)
82
+ #
71
83
  # Set up some basic parameters for battery/solar readings
72
84
  @bv_m = @bi_m = @bt_m = @sv_m = 1.0
73
85
  @bv_c = @bi_c = @bt_c = @sv_c = 0.0
@@ -75,13 +87,170 @@ module SGS
75
87
  end
76
88
 
77
89
  #
78
- # Main daemon function (called from executable)
90
+ # Main daemon function (called from executable). The job of
91
+ # this daemon is to accept commands from the Redis pub/sub
92
+ # stream and send them to the low-level device, recording the
93
+ # response and sending it back to the caller. Note that we need
94
+ # to do an initial sync with the device as it will ignore the
95
+ # usual serial console boot-up gumph awaiting our sync message.
79
96
  def self.daemon
80
- loop do
81
- sleep 300
97
+ puts "Low-level (Otto) communication subsystem starting up..."
98
+ otto = new
99
+ config = SGS::Config.load
100
+ otto.serial_port = SerialPort.new config.otto_device, config.otto_speed
101
+ otto.serial_port.read_timeout = 10000
102
+ #
103
+ # Start by getting a sync message from Otto.
104
+ otto.synchronize()
105
+ #
106
+ # Run the communications service with Otto. Two threads are used, one for
107
+ # reading and one for writing. Don't let the command stack get too big.
108
+ t1 = Thread.new { otto.reader_thread }
109
+ t2 = Thread.new { otto.writer_thread }
110
+ t1.join
111
+ t2.join
112
+ end
113
+
114
+ #
115
+ # Synchronize with the low-level board by sending CQ messages until
116
+ # they respond.
117
+ def synchronize
118
+ index = 0
119
+ backoffs = [1, 1, 1, 1, 2, 2, 3, 5, 10, 10, 20, 30, 60]
120
+ puts "Attempting to synchronize with Otto..."
121
+ while true do
122
+ begin
123
+ @serial_port.puts "@@CQ!"
124
+ resp = read_data
125
+ break if resp =~ /^\+CQOK/ or resp =~ /^\+OK/
126
+ sleep backoffs[index]
127
+ index += 1 if index < (backoffs.count - 1)
128
+ end
129
+ end
130
+ puts "Synchronization complete!"
131
+ end
132
+
133
+ #
134
+ # Thread to read status messages from Otto and handle them
135
+ def reader_thread
136
+ puts "Starting OTTO reader thread..."
137
+ while true
138
+ data = read_data
139
+ next if data.nil? or data.length == 0
140
+ case data[0]
141
+ when '$'
142
+ #
143
+ # Status message (every second)
144
+ parse_status(data[1..])
145
+ when '@'
146
+ #
147
+ # Otto elapsed time (every four seconds)
148
+ parse_tstamp(data[1..])
149
+ when '!'
150
+ #
151
+ # Otto mode state (every four seconds)
152
+ parse_mode(data[1..])
153
+ when '>'
154
+ #
155
+ # Telemetry data (every two seconds)
156
+ parse_telemetry(data[1..])
157
+ end
158
+ end
159
+ end
160
+
161
+ #
162
+ # Thread to write commands direct to Otto.
163
+ def writer_thread
164
+ puts "Starting OTTO writer thread..."
165
+ #
166
+ # Now listen for Redis PUB/SUB requests and act on each one.
167
+ while true
168
+ channel, request = SGS::RedisBase.redis.brpop("otto")
169
+ request = MessagePack.unpack(request)
170
+ puts "Req:[#{request.inspect}]"
171
+ cmd = {
172
+ id: request['id'],
173
+ args: request['params'].unshift(request['method'])
174
+ }
175
+ puts "CMD:#{cmd.inspect}"
176
+ #
177
+ # Don't let the command stack get too big.
178
+ while @command_stack.length > 5
179
+ sleep 5
180
+ end
181
+
182
+ puts "> Sending command: #{str}"
183
+ @serial_port.puts "#{str}"
184
+
185
+ reply = {
186
+ 'id' => id,
187
+ 'jsonrpc' => '2.0',
188
+ 'result' => result
189
+ }
190
+ SGS::RedisBase.redis.rpush(id, MessagePack.pack(reply))
191
+ SGS::RedisBase.redis.expire(id, 30)
82
192
  end
83
193
  end
84
194
 
195
+ #
196
+ # Read data from the serial port
197
+ def read_data
198
+ begin
199
+ data = @serial_port.readline.chomp
200
+ rescue EOFError => error
201
+ puts "Otto Read Timeout!"
202
+ data = nil
203
+ end
204
+ data
205
+ end
206
+
207
+ #
208
+ # Parse a status message from Otto. In the form:
209
+ # 0001:C000:0000
210
+ def parse_status(status)
211
+ puts "Parse status: #{status}"
212
+ args = status.split /:/
213
+ @alarm_status = args[0].to_i(16)
214
+ wc = args[1].to_i(16)
215
+ rs = args[2].to_i(16)
216
+ @wind = (wc >> 8) & 0xff
217
+ @compass = (wc & 0xff)
218
+ @actual_rudder = (rs >> 8) & 0xff
219
+ @actual_sail = (rs & 0xff)
220
+ p self
221
+ self.save_and_publish
222
+ end
223
+
224
+ #
225
+ # Parse a timestamp message from Otto. In the form: "000FE2" 24 bits
226
+ # representing the elapsed seconds since Otto restarted.
227
+ def parse_tstamp(tstamp)
228
+ puts "Parse timestamp: #{tstamp}"
229
+ newval = tstamp.to_i(16)
230
+ if newval < @otto_timestamp
231
+ puts "ALARM! Otto rebooted (or something)..."
232
+ end
233
+ @otto_timestamp = newval
234
+ end
235
+
236
+ #
237
+ # Parse a mode state message from Otto. In the form: "00". An eight bit
238
+ # quantity.
239
+ def parse_mode(mode)
240
+ puts "Parse Otto Mode State: #{mode}"
241
+ @otto_mode = mode.to_i(16)
242
+ end
243
+
244
+ #
245
+ # Parse a telemetry message from Otto. In the form: "7327" where the first
246
+ # character is the channel (0->9) and the remaining 12 bits are the value.
247
+ def parse_telemetry(telemetry)
248
+ puts "Parse Otto Telemetry Data: #{telemetry}"
249
+ data = telemetry.to_i(16)
250
+ chan = (data >> 12) & 0xf
251
+ @telemetry[chan] = data & 0xff
252
+ end
253
+
85
254
  #
86
255
  # Set the required rudder angle. Input values range from +/- 40.0 degrees
87
256
  def rudder=(val)
data/lib/sgs/report.rb CHANGED
@@ -32,6 +32,7 @@
32
32
  #
33
33
  # ABSTRACT
34
34
  #
35
+ require 'serialport'
35
36
 
36
37
  ##
37
38
  # Routines for handling sailboat navigation and route planning.
@@ -41,6 +42,10 @@ module SGS
41
42
  #
42
43
  # Main daemon function (called from executable)
43
44
  def self.daemon
45
+ puts "Reporting subsystem starting up..."
46
+ config = SGS::Config.load
47
+ sp = SerialPort.new config.comm_device, config.comm_speed
48
+ sp.read_timeout = 10000
44
49
  loop do
45
50
  sleep 300
46
51
  end
data/lib/sgs/version.rb CHANGED
@@ -33,5 +33,5 @@
33
33
  # ABSTRACT
34
34
  #
35
35
  module SGS
36
- VERSION = "1.5.0"
36
+ VERSION = "1.5.1"
37
37
  end
data/lib/sgslib.rb CHANGED
@@ -31,13 +31,10 @@
31
31
  # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
32
  #
33
33
  # ABSTRACT
34
- #
34
+ # Include all of the various components.
35
35
 
36
36
  ##
37
37
  #
38
- require 'date'
39
- require 'logger'
40
-
41
38
  require "sgs/version"
42
39
  require 'sgs/redis_base'
43
40
  require 'sgs/config'
data/sgslib.gemspec CHANGED
@@ -40,8 +40,10 @@ Gem::Specification.new do |spec|
40
40
  spec.add_development_dependency "rake", ">= 12.3.3"
41
41
  spec.add_development_dependency "rspec", "~> 3.0"
42
42
 
43
- spec.add_runtime_dependency "logger", "~> 1.4"
43
+ spec.add_runtime_dependency "redis", "~> 4.7"
44
+ spec.add_runtime_dependency "serialport", "~> 1.3"
44
45
  spec.add_runtime_dependency "msgpack", "~> 1.3"
45
-
46
- spec.add_dependency "redis", "~> 4.7"
46
+ spec.add_runtime_dependency "json"
47
+ spec.add_runtime_dependency "securerandom"
48
+ spec.add_runtime_dependency "yaml"
47
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sgslib
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dermot Tynan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-08 00:00:00.000000000 Z
11
+ date: 2023-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,21 +53,21 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: logger
56
+ name: redis
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.4'
61
+ version: '4.7'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.4'
68
+ version: '4.7'
69
69
  - !ruby/object:Gem::Dependency
70
- name: msgpack
70
+ name: serialport
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
@@ -81,19 +81,61 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.3'
83
83
  - !ruby/object:Gem::Dependency
84
- name: redis
84
+ name: msgpack
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '4.7'
89
+ version: '1.3'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '4.7'
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: json
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: securerandom
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: yaml
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
97
139
  description: Sailboat Guidance System - an autonomous navigation and control system
98
140
  for robotic sailboats.
99
141
  email:
@@ -164,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
206
  - !ruby/object:Gem::Version
165
207
  version: '0'
166
208
  requirements: []
167
- rubygems_version: 3.0.3.1
209
+ rubygems_version: 3.1.6
168
210
  signing_key:
169
211
  specification_version: 4
170
212
  summary: Sailboat Guidance System