rsmp 0.1.12 → 0.1.13

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: 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.