rsmp 0.1.12 → 0.1.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e45799eb31adc34f3c6d80fbf0b5e24c41a07cf5561231d085d2a931119417dc
4
- data.tar.gz: a9ceb38c14787707b493ade46c861d4ee336b0b394890f3a62c56c079c978a60
3
+ metadata.gz: d2c272fba5c28e4b5746aa03efac286b646d635cb23ae8c68d3c6cb08ba657ae
4
+ data.tar.gz: 1051548b65417edbbf31550f4a6a02ae4df7c98aba190682e5c4a5faed3af116
5
5
  SHA512:
6
- metadata.gz: ba448443412590959523070ac30bad578ff4402ba55b9dacb9ddc4fefd48f596cdd0f34cb839e104b5ebbdb0ade447e51ab1c5e48caa9eabb96b9fc418932811
7
- data.tar.gz: ab21f83999dfbce048bfb86f56d9b07a542828422f6e706ce92ab3ded1e5e75c1e4d202ab8d24de14681b8f295701f9d51569e8047e15e5e12d72d8948a474f9
6
+ metadata.gz: 33b1fb0ad681f71b58571e31a3ef59415ad45ee7ec4a5b947eb494f0474e9a46b85e25e25f977d88da28e4d91011f92b35dc2c1b49c6630d3c7eb5b14ec170a3
7
+ data.tar.gz: b0614210f4db03d2aba012f07f7df76aefaae0b96db5779558f46c5ca5433c1b0df1ae3f41e828936f45593fdd7e69723df84895510ba798e64964213c0c4cee
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rsmp (0.1.12)
4
+ rsmp (0.1.13)
5
5
  async (~> 1.23.0)
6
6
  async-io (~> 1.27.1)
7
7
  colorize (~> 0.8.1)
@@ -29,7 +29,7 @@ GEM
29
29
  childprocess (2.0.0)
30
30
  rake (< 13.0)
31
31
  colorize (0.8.1)
32
- console (1.6.0)
32
+ console (1.7.0)
33
33
  contracts (0.16.0)
34
34
  cucumber (3.1.2)
35
35
  builder (>= 2.1.2)
data/config/tlc.yaml ADDED
@@ -0,0 +1,47 @@
1
+ site_id: RN+SI0001
2
+ supervisors:
3
+ - ip: 127.0.0.1
4
+ port: 12111
5
+
6
+ sxl: traffic_light_controller
7
+
8
+ rsmp_versions:
9
+ - 3.1.1
10
+ - 3.1.2
11
+ - 3.1.3
12
+ - 3.1.4
13
+
14
+ components:
15
+ TC:
16
+ type: main
17
+ cycle_time: 6
18
+ A1:
19
+ type: signal_group
20
+ plan: 'GGyrrr'
21
+ B1:
22
+ type: signal_group
23
+ plan: 'rrrGGy'
24
+
25
+ watchdog_interval: 1
26
+ watchdog_timeout: 2
27
+ acknowledgement_timeout: 1
28
+ command_response_timeout: 1
29
+ status_response_timeout: 1
30
+ status_update_timeout: 1
31
+ reconnect_interval: 0.1
32
+
33
+ log:
34
+ active: true
35
+ color: true
36
+ timestamp: true
37
+ id: true
38
+ component: true
39
+ ip: false
40
+ site_id: true
41
+ level: false
42
+ text: true
43
+ direction: true
44
+ json: true
45
+ acknowledgements: false
46
+ watchdogs: false
47
+
data/lib/rsmp/cli.rb CHANGED
@@ -10,6 +10,7 @@ module RSMP
10
10
  method_option :supervisors, :type => :string, :aliases => "-s", banner: 'ip:port,... list of supervisor to connect to'
11
11
  method_option :log, :type => :string, :aliases => "-l", banner: 'Path to log file'
12
12
  method_option :json, :type => :boolean, :aliases => "-j", banner: 'Show JSON messages in log'
13
+ method_option :type, :type => :string, :aliases => "-t", banner: 'Type of site: [tlc]'
13
14
  def site
14
15
  settings = {}
15
16
  log_settings = { 'active' => true }
@@ -46,7 +47,16 @@ module RSMP
46
47
  log_settings['json'] = options[:json]
47
48
  end
48
49
 
49
- RSMP::Site.new(site_settings:settings, log_settings: log_settings).start
50
+ site_class = RSMP::Site
51
+ if options[:type]
52
+ case options[:type]
53
+ when 'tlc'
54
+ site_class = RSMP::Tlc
55
+ else
56
+ site_class = RSMP::Site
57
+ end
58
+ end
59
+ site_class.new(site_settings:settings, log_settings: log_settings).start
50
60
  end
51
61
 
52
62
  desc "supervisor", "Run RSMP supervisor"
@@ -61,5 +61,8 @@ module RSMP
61
61
  def status code:, value:
62
62
  end
63
63
 
64
+ def log str, options
65
+ @node.log str, options
66
+ end
64
67
  end
65
68
  end
data/lib/rsmp/error.rb CHANGED
@@ -43,4 +43,13 @@ module RSMP
43
43
 
44
44
  class UnknownComponent < Error
45
45
  end
46
+
47
+ class UnknownCommand < Error
48
+ end
49
+
50
+ class UnknownStatus < Error
51
+ end
52
+
53
+ class ConfigurationError < Error
54
+ end
46
55
  end
data/lib/rsmp/proxy.rb CHANGED
@@ -115,15 +115,19 @@ module RSMP
115
115
  interval = @settings["timer_interval"] || 1
116
116
  log "Starting #{name} with interval #{interval} seconds", level: :debug
117
117
  @latest_watchdog_received = RSMP.now_object
118
+
118
119
  @timer = @task.async do |task|
119
120
  task.annotate "timer"
121
+ next_time = Time.now.to_f
120
122
  loop do
121
123
  now = RSMP.now_object
122
124
  break if timer(now) == false
123
125
  rescue StandardError => e
124
126
  log ["#{name} exception: #{e}",e.backtrace].flatten.join("\n"), level: :error
125
127
  ensure
126
- task.sleep interval
128
+ next_time += interval
129
+ duration = next_time - Time.now.to_f
130
+ task.sleep duration
127
131
  end
128
132
  end
129
133
  end
data/lib/rsmp/site.rb CHANGED
@@ -27,6 +27,7 @@ module RSMP
27
27
  { 'ip' => '127.0.0.1', 'port' => 12111 }
28
28
  ],
29
29
  'rsmp_versions' => ['3.1.1','3.1.2','3.1.3','3.1.4'],
30
+ 'sxl' => 'traffic_light_controller',
30
31
  'sxl_version' => '1.0.7',
31
32
  'timer_interval' => 0.1,
32
33
  'watchdog_interval' => 1,
@@ -39,6 +40,9 @@ module RSMP
39
40
  'site_ready_timeout' => 1,
40
41
  'reconnect_interval' => 0.1,
41
42
  'send_after_connect' => true,
43
+ 'components' => {
44
+ 'C1' => {}
45
+ }
42
46
  }
43
47
  if options[:site_settings]
44
48
  converted = options[:site_settings].map { |k,v| [k.to_s,v] }.to_h #convert symbol keys to string keys
@@ -66,7 +70,7 @@ module RSMP
66
70
  end
67
71
  end
68
72
 
69
- def build_connector settings
73
+ def build_proxy settings
70
74
  SupervisorProxy.new settings
71
75
  end
72
76
 
@@ -77,7 +81,7 @@ module RSMP
77
81
  end
78
82
 
79
83
  def connect_to_supervisor task, supervisor_settings
80
- proxy = build_connector({
84
+ proxy = build_proxy({
81
85
  site: self,
82
86
  task: @task,
83
87
  settings: @site_settings,
@@ -137,5 +141,14 @@ module RSMP
137
141
  proxy.stop
138
142
  end
139
143
  end
144
+
145
+ def handle_command command_code, arg
146
+ raise UnknownCommand.new "Command #{command_code} not implemented"
147
+ end
148
+
149
+ def get_status status_code, status_name=nil
150
+ raise UnknownStatus.new "Status #{status_code}/#{status_name} not implemented"
151
+ end
152
+
140
153
  end
141
154
  end
@@ -54,7 +54,7 @@ module RSMP
54
54
  log "Received Version message for site #{@site_id} using RSMP #{@rsmp_version}", message: message, level: :log
55
55
  start_timer
56
56
  acknowledge message
57
- send_version @site_id, @rsmp_version
57
+ send_version @site_id, @settings['rsmp_versions']
58
58
  @version_determined = true
59
59
 
60
60
  if @settings['sites']
@@ -165,7 +165,7 @@ module RSMP
165
165
  "sS" => status_list
166
166
  })
167
167
  send_message message
168
- return message, wait_for_status_update(component: component, timeout: timeout)
168
+ return message, wait_for_status_update(component: component, timeout: timeout)[:message]
169
169
  end
170
170
 
171
171
  def unsubscribe_to_status component, status_list
@@ -187,21 +187,24 @@ module RSMP
187
187
 
188
188
  def wait_for_status_update options={}
189
189
  raise ArgumentError unless options[:component]
190
+ matching_status = nil
190
191
  item = @archive.capture(@task,options.merge(type: "StatusUpdate", with_message: true, num: 1)) do |item|
191
192
  # TODO check components
192
- found = false
193
+ matching_status = nil
193
194
  sS = item[:message].attributes['sS']
194
195
  sS.each do |status|
195
196
  next if options[:sCI] && options[:sCI] != status['sCI']
196
197
  next if options[:n] && options[:n] != status['n']
197
198
  next if options[:q] && options[:q] != status['q']
198
199
  next if options[:s] && options[:s] != status['s']
199
- found = true
200
+ matching_status = status
200
201
  break
201
202
  end
202
- found
203
+ matching_status != nil
204
+ end
205
+ if item
206
+ { message: item[:message], status: matching_status }
203
207
  end
204
- item[:message] if item
205
208
  end
206
209
 
207
210
  def send_command component, args
@@ -98,7 +98,7 @@ module RSMP
98
98
  true
99
99
  end
100
100
 
101
- def build_connector settings
101
+ def build_proxy settings
102
102
  SiteProxy.new settings
103
103
  end
104
104
 
@@ -117,7 +117,7 @@ module RSMP
117
117
  level: :info,
118
118
  timestamp: RSMP.now_object
119
119
 
120
- proxy = build_connector({
120
+ proxy = build_proxy({
121
121
  supervisor: self,
122
122
  task: @task,
123
123
  settings: @supervisor_settings[:sites],
@@ -72,6 +72,12 @@ module RSMP
72
72
  else
73
73
  super message
74
74
  end
75
+ rescue UnknownComponent => e
76
+ dont_acknowledge message, '', e.to_s
77
+ rescue UnknownCommand => e
78
+ dont_acknowledge message, '', e.to_s
79
+ rescue UnknownStatus => e
80
+ dont_acknowledge message, '', e.to_s
75
81
  end
76
82
 
77
83
  def acknowledged_first_ingoing message
@@ -135,20 +141,31 @@ module RSMP
135
141
  acknowledge message
136
142
  end
137
143
 
144
+ # reorganize rmsp command request arg attribute:
145
+ # [{"cCI":"M0002","cO":"setPlan","n":"status","v":"True"},{"cCI":"M0002","cO":"setPlan","n":"securityCode","v":"5678"},{"cCI":"M0002","cO":"setPlan","n":"timeplan","v":"3"}]
146
+ # into the simpler, but equivalent:
147
+ # {"M0002"=>{"status"=>"True", "securityCode"=>"5678", "timeplan"=>"3"}}
148
+ def simplify_command_requests arg
149
+ sorted = {}
150
+ arg.each do |item|
151
+ sorted[item['cCI']] ||= {}
152
+ sorted[item['cCI']][item['n']] = item['v']
153
+ end
154
+ sorted
155
+ end
156
+
138
157
  def process_command_request message
139
- log "Received #{message.type}", message: message, level: :log
140
- rvs = []
141
- message.attributes["arg"].each do |arg|
142
- unless arg['cCI'] && arg['n'] && arg['v']
143
- dont_acknowledge message, '', 'bad arguments'
144
- return
145
- end
146
- rvs << { "cCI" => arg["cCI"],
147
- "n" => arg["n"],
148
- "v" => arg["v"],
149
- "age" => "recent" }
158
+ log "Received #{message.type}", message: message, level: :log
159
+ commands = simplify_command_requests message.attributes["arg"]
160
+ commands.each_pair do |command_code,arg|
161
+ @site.handle_command(command_code,arg).merge('cCI'=>command_code)
150
162
  end
151
163
 
164
+ rvs = message.attributes["arg"].map do |item|
165
+ item = item.dup.merge('age'=>'recent')
166
+ item.delete 'cO'
167
+ item
168
+ end
152
169
  response = CommandResponse.new({
153
170
  "cId"=>message.attributes["cId"],
154
171
  "cTS"=>RSMP.now_string,
@@ -161,12 +178,10 @@ module RSMP
161
178
  def process_status_request message
162
179
  component_id = message.attributes["cId"]
163
180
  component = @site.find_component component_id
164
-
165
181
  log "Received #{message.type}", message: message, level: :log
166
- sS = message.attributes["sS"].clone.map do |request|
167
- request["s"] = rand(100).to_s
168
- request["q"] = "recent"
169
- request
182
+ sS = message.attributes["sS"].map do |arg|
183
+ value, quality = @site.get_status arg['sCI'], arg['n']
184
+ { "s" => value, "q" => quality }.merge arg
170
185
  end
171
186
  response = StatusResponse.new({
172
187
  "cId"=>component_id,
@@ -175,8 +190,6 @@ module RSMP
175
190
  })
176
191
  acknowledge message
177
192
  send_message response
178
- rescue UnknownComponent => e
179
- dont_acknowledge message, '', e.to_s
180
193
  end
181
194
 
182
195
  def process_status_subcribe message
@@ -273,11 +286,12 @@ module RSMP
273
286
  update_list.each_pair do |component,by_code|
274
287
  sS = []
275
288
  by_code.each_pair do |code,names|
276
- names.each do |name|
289
+ names.map do |status_name|
290
+ value,quality = @site.get_status code, status_name
277
291
  sS << { "sCI" => code,
278
- "n" => name,
279
- "s" => rand(100).to_s,
280
- "q" => "recent" }
292
+ "n" => status_name,
293
+ "s" => value.to_s,
294
+ "q" => quality }
281
295
  end
282
296
  end
283
297
  update = StatusUpdate.new({
data/lib/rsmp/tlc.rb ADDED
@@ -0,0 +1,236 @@
1
+ # Simulates a Traffic Light Controller
2
+
3
+ module RSMP
4
+
5
+ class TrafficController < Component
6
+ attr_reader :pos, :cycle_time
7
+ def initialize node:, id:, cycle_time:
8
+ super node: node, id: id, grouped: true
9
+ @pos = 0
10
+ @cycle_time = cycle_time
11
+ @signal_groups = []
12
+ @plan = 0
13
+ @dark_mode = 'False'
14
+ @yellow_flash = 'False'
15
+ @booting = 'False'
16
+ @police_key = 0
17
+ end
18
+
19
+ def add_signal_group group
20
+ @signal_groups << group
21
+ end
22
+
23
+ def timer now
24
+ pos = now.to_i % @cycle_time
25
+ if pos != @pos
26
+ @pos = pos
27
+ move pos
28
+ end
29
+ end
30
+
31
+ def move pos
32
+ @signal_groups.each do |group|
33
+ group.move pos
34
+ end
35
+ output_states
36
+
37
+ if pos == 0
38
+ aggrated_status_changed
39
+ end
40
+ end
41
+
42
+ def output_states
43
+ #print "\t#{pos.to_s.ljust(3)} #{format_signal_group_status}\r"
44
+ end
45
+
46
+ def format_signal_group_status
47
+ @signal_groups.map { |group|group.state }.join
48
+ end
49
+
50
+ def handle_command command_code, arg
51
+ case command_code
52
+ when 'M0001'
53
+ handle_m0001 arg
54
+ when 'M0002'
55
+ handle_m0002 arg
56
+ when 'M0007'
57
+ handle_m0007 arg
58
+ else
59
+ raise UnknownCommand.new "Unknown command #{command_code}"
60
+ end
61
+ end
62
+
63
+ def handle_m0001 arg
64
+ verify_security_code arg['securityCode']
65
+ switch_mode arg['status']
66
+ arg
67
+ end
68
+
69
+ def handle_m0002 arg
70
+ verify_security_code arg['securityCode']
71
+ if arg['status'] = 'True'
72
+ switch_plan arg['timeplan']
73
+ else
74
+ switch_plan 0 # TODO use clock/calender
75
+ end
76
+ arg
77
+ end
78
+
79
+ def handle_m0007 arg
80
+ verify_security_code arg['securityCode']
81
+ set_fixed_time_control arg['status']
82
+ arg
83
+ end
84
+
85
+ def set_fixed_time_control status
86
+ @fixed_time_control = status
87
+ end
88
+
89
+ def switch_plan plan
90
+ log "Switching to plan #{plan}", level: :info
91
+ @plan = plan.to_i
92
+ plan
93
+ end
94
+
95
+ def switch_mode mode
96
+ log "Switching to mode #{mode}", level: :info
97
+ case mode
98
+ when 'NormalControl'
99
+ @yellow_flash = 'False'
100
+ @dark_mode = 'False'
101
+ when 'YellowFlash'
102
+ @yellow_flash = 'True'
103
+ @dark_mode = 'False'
104
+ when 'Dark'
105
+ @yellow_flash = 'False'
106
+ @dark_mode = 'True'
107
+ end
108
+ mode
109
+ end
110
+
111
+ def get_status status_code, status_name=nil
112
+ case status_code
113
+ when 'S0001'
114
+ return format_signal_group_status, "recent"
115
+ when 'S0005'
116
+ return @booting, "recent"
117
+ when 'S0007'
118
+ if @dark_mode == 'True'
119
+ return 'False', "recent"
120
+ else
121
+ return 'True', "recent"
122
+ end
123
+ when 'S0009'
124
+ return @fixed_time_control.to_s, "recent"
125
+ when 'S0013'
126
+ return @police_key.to_s, "recent"
127
+ when 'S0014'
128
+ return @plan.to_s, "recent"
129
+ when 'S0011'
130
+ return @yellow_flash, "recent"
131
+ else
132
+ raise InvalidMessage.new "unknown status code #{status_code}"
133
+ end
134
+ end
135
+
136
+ def verify_security_code code
137
+ end
138
+
139
+ end
140
+
141
+ class SignalGroup < Component
142
+ attr_reader :plan, :state
143
+
144
+ def initialize node:, id:, plan:
145
+ super node: node, id: id, grouped: false
146
+ @plan = plan
147
+ move 0
148
+ end
149
+
150
+ def get_state pos
151
+ if pos > @plan.length
152
+ '.'
153
+ else
154
+ @plan[pos]
155
+ end
156
+ end
157
+
158
+ def move pos
159
+ @state = get_state pos
160
+ end
161
+ end
162
+
163
+ class Tlc < Site
164
+ def initialize options={}
165
+ super options
166
+
167
+ @sxl = 'traffic_light_controller'
168
+
169
+ unless @main
170
+ raise ConfigurationError.new "TLC must have a main component"
171
+ end
172
+ end
173
+
174
+ def build_component id, settings={}
175
+ component = case settings['type']
176
+ when 'main'
177
+ @main = TrafficController.new node: self, id: id, cycle_time: settings['cycle_time']
178
+ when 'signal_group'
179
+ group = SignalGroup.new node: self, id: id, plan: settings['plan']
180
+ @main.add_signal_group group
181
+ group
182
+ end
183
+ end
184
+
185
+ def start_action
186
+ super
187
+ start_timer
188
+ end
189
+
190
+ def start_timer
191
+ name = "tlc timer"
192
+ interval = 1 #@settings["timer_interval"] || 1
193
+ log "Starting #{name} with interval #{interval} seconds", level: :debug
194
+
195
+ @timer = @task.async do |task|
196
+ task.annotate "timer"
197
+ next_time = Time.now.to_f
198
+ loop do
199
+ now = RSMP.now_object
200
+ break if timer(now) == false
201
+ rescue StandardError => e
202
+ log ["#{name} exception: #{e}",e.backtrace].flatten.join("\n"), level: :error
203
+ ensure
204
+ # adjust sleep duration to avoid drift. so wake up always happens on the
205
+ # same fractional second.
206
+ # note that Time.now is not monotonic. If the clock si changed,
207
+ # either manaully or via NTP, the sleep interval might jump.
208
+ # an alternative is to use ::Process.clock_gettime(::Process::CLOCK_MONOTONIC),
209
+ # to get the current time. this ensures a constant interval, but
210
+ # if the clock is changed, the wake up would then happen on a different
211
+ # fractional second
212
+
213
+ next_time += interval
214
+ duration = next_time - Time.now.to_f
215
+ task.sleep duration
216
+ end
217
+ end
218
+ end
219
+
220
+ def timer now
221
+ return unless @main
222
+ @main.timer now
223
+ end
224
+
225
+ def handle_command command_code, arg
226
+ return unless @main
227
+ @main.handle_command command_code, arg
228
+ end
229
+
230
+ def get_status status_code, status_name=nil
231
+ return unless @main
232
+ @main.get_status status_code, status_name
233
+ end
234
+
235
+ end
236
+ end
data/lib/rsmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RSMP
2
- VERSION = "0.1.12"
2
+ VERSION = "0.1.13"
3
3
  end
data/lib/rsmp.rb CHANGED
@@ -27,3 +27,4 @@ require 'rsmp/probe_collection'
27
27
  require 'rsmp/message'
28
28
  require 'rsmp/logger'
29
29
  require 'rsmp/archive'
30
+ require 'rsmp/tlc'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emil Tin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-18 00:00:00.000000000 Z
11
+ date: 2020-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -213,6 +213,7 @@ files:
213
213
  - bin/setup
214
214
  - config/site.yaml
215
215
  - config/supervisor.yaml
216
+ - config/tlc.yaml
216
217
  - exe/rsmp
217
218
  - lib/rsmp.rb
218
219
  - lib/rsmp/alarm.rb
@@ -234,6 +235,7 @@ files:
234
235
  - lib/rsmp/supervisor.rb
235
236
  - lib/rsmp/supervisor_base.rb
236
237
  - lib/rsmp/supervisor_proxy.rb
238
+ - lib/rsmp/tlc.rb
237
239
  - lib/rsmp/version.rb
238
240
  - lib/rsmp/wait.rb
239
241
  - lib/rsmp_schema/schema/core/aggregated_status.json
@@ -357,7 +359,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
357
359
  - !ruby/object:Gem::Version
358
360
  version: '0'
359
361
  requirements: []
360
- rubygems_version: 3.0.6
362
+ rubygems_version: 3.0.3
361
363
  signing_key:
362
364
  specification_version: 4
363
365
  summary: RoadSide Message Protocol (RSMP) library.