urest 0.99.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ <img align="right" height="100" src="https://github.com/etm/urest/blob/main/logo/urest_logo.png">
2
+
3
+ # UREST
4
+
5
+ A simple REST server for Universal robots in ruby, which uses the ur-sock library.
6
+
7
+ ## Getting Started
8
+
9
+ A simple ruby server, which uses the ur-sock library.
10
+
11
+ ### Prerequisites & Installation
12
+
13
+ This server has no special prerequisites. But it is always advisable to prepare
14
+ your machine for installation and development, and install some common
15
+ libraries and headers, which are used by common dependencies:
16
+
17
+ * Windows 10 users with WSL2 can use Fedora/Ubuntu instructions below 
18
+ * Mac OSX users can use brew: brew install libxml2 libxslt 
19
+ * Ubuntu (>=20.04) / Debian users can use their package manager: ```sudo apt-get install build-essential ruby-dev libxml2-dev libxslt-dev libz-dev libssl-dev librasqal-dev libraptor2-dev libicu-dev```
20
+ * Fedora (>=32) users can use their package manager: ```sudo dnf install @buildsys-build @development-tools @c-development ruby-devel libxml2-devel libxslt-devel zlib-devel rasqal-devel raptor2-devel libicu-devel``` 
21
+
22
+ You can then install the UREST gem with:
23
+
24
+ ```
25
+ gem install urest
26
+ ```
27
+
28
+ If you want to develop or extend the server, just use the following instruction
29
+ ```
30
+ git clone https://github.com/etm/urest
31
+ git clone https://github.com/fpauker/ur-sock
32
+ ```
33
+
34
+ Just follow the install instructions of the 2 projects.
35
+
36
+ ### Starting the server
37
+
38
+ To scaffold a server, first create a directory, then use the urest
39
+
40
+ ```
41
+ mkdir -p ~/run/urest
42
+ cd ~/run/urest
43
+ urest scaffold
44
+ ```
45
+
46
+ After changing the \.conf file to point to your UR, you can start the server with
47
+
48
+ ```
49
+ ./urest start
50
+ ```
51
+
52
+ or
53
+
54
+ ```
55
+ ./urest -v start
56
+ ```
57
+
58
+ to see verbose output.
59
+
60
+ ## Contributing
61
+
62
+ Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us.
63
+
64
+ ## Versioning
65
+
66
+ We use [SemVer](http://semver.org/) for versioning.
67
+
68
+ ## Authors
69
+
70
+ * **Jürgen Mangler**
71
+
72
+ See also the list of [contributors](https://github.com/etm/urest/AUTHORS) who participated in this project.
73
+
74
+ ## License
75
+
76
+ This project is licensed under the GPL3 License - see the [LICENSE.md](./LICENSE) file for details
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rubygems/package_task'
3
+
4
+ spec = eval(File.read('urest.gemspec'))
5
+
6
+ task :default => [:gem]
7
+
8
+ Gem::PackageTask.new(spec) do |pkg|
9
+ pkg.need_zip = true
10
+ pkg.need_tar = true
11
+ FileUtils.mkdir 'pkg' rescue nil
12
+ FileUtils.rm_rf Dir.glob('pkg/*')
13
+ FileUtils.ln_sf "#{pkg.name}.gem", "pkg/#{spec.name}.gem"
14
+ end
15
+
16
+ task :push => :gem do |r|
17
+ `gem push pkg/urest.gem`
18
+ end
19
+
20
+ task :install => :gem do |r|
21
+ `gem install pkg/urest.gem`
22
+ end
data/lib/rtde.conf.xml ADDED
@@ -0,0 +1,125 @@
1
+ <?xml version="1.0"?>
2
+ <rtde_config>
3
+ <recipe key="speed">
4
+ <field name="speed_slider_mask" type="UINT32"/>
5
+ <field name="speed_slider_fraction" type="DOUBLE"/>
6
+ </recipe>
7
+ <recipe key="inbit">
8
+ <!--Only put Input Registers here which are used-->
9
+ <field name="input_bit_register_64" type="BOOL"/>
10
+ <field name="input_bit_register_65" type="BOOL"/>
11
+ </recipe>
12
+ <recipe key="inint">
13
+ <!--Only put Input Registers here which are used-->
14
+ <field name="input_int_register_0" type="INT32"/>
15
+ <field name="input_int_register_1" type="INT32"/>
16
+ <field name="input_int_register_2" type="INT32"/>
17
+ <field name="input_int_register_3" type="INT32"/>
18
+ <field name="input_int_register_4" type="INT32"/>
19
+ <field name="input_int_register_5" type="INT32"/>
20
+ <field name="input_int_register_6" type="INT32"/>
21
+ <field name="input_int_register_7" type="INT32"/>
22
+ <field name="input_int_register_8" type="INT32"/>
23
+ <field name="input_int_register_9" type="INT32"/>
24
+ </recipe>
25
+ <recipe key="indoub">
26
+ <!--Only put Input Registers here which are used-->
27
+ <field name="input_double_register_0" type="DOUBLE"/>
28
+ <field name="input_double_register_1" type="DOUBLE"/>
29
+ <field name="input_double_register_2" type="DOUBLE"/>
30
+ <field name="input_double_register_3" type="DOUBLE"/>
31
+ <field name="input_double_register_4" type="DOUBLE"/>
32
+ <field name="input_double_register_5" type="DOUBLE"/>
33
+ <field name="input_double_register_6" type="DOUBLE"/>
34
+ <field name="input_double_register_7" type="DOUBLE"/>
35
+ <field name="input_double_register_8" type="DOUBLE"/>
36
+ <field name="input_double_register_9" type="DOUBLE"/>
37
+ </recipe>
38
+ <recipe key="out">
39
+ <field name="timestamp" type="DOUBLE"/>
40
+ <field name="target_q" type="VECTOR6D"/>
41
+ <field name="target_qd" type="VECTOR6D"/>
42
+ <field name="target_qdd" type="VECTOR6D"/>
43
+ <field name="target_current" type="VECTOR6D"/>
44
+ <field name="target_moment" type="VECTOR6D"/>
45
+ <field name="actual_q" type="VECTOR6D"/>
46
+ <field name="actual_qd" type="VECTOR6D"/>
47
+ <field name="actual_current" type="VECTOR6D"/>
48
+ <field name="joint_control_output" type="VECTOR6D"/>
49
+ <field name="actual_TCP_pose" type="VECTOR6D"/>
50
+ <field name="actual_TCP_speed" type="VECTOR6D"/>
51
+ <field name="actual_TCP_force" type="VECTOR6D"/>
52
+ <field name="target_TCP_pose" type="VECTOR6D"/>
53
+ <field name="target_TCP_speed" type="VECTOR6D"/>
54
+ <field name="actual_digital_input_bits" type="UINT64"/>
55
+ <field name="joint_temperatures" type="VECTOR6D"/>
56
+ <field name="actual_execution_time" type="DOUBLE"/>
57
+ <field name="robot_mode" type="INT32"/>
58
+ <field name="joint_mode" type="VECTOR6INT32"/>
59
+ <field name="safety_mode" type="INT32"/>
60
+ <field name="actual_tool_accelerometer" type="VECTOR3D"/>
61
+ <field name="speed_scaling" type="DOUBLE"/>
62
+ <field name="actual_momentum" type="DOUBLE"/>
63
+ <field name="actual_main_voltage" type="DOUBLE"/>
64
+ <field name="actual_robot_voltage" type="DOUBLE"/>
65
+ <field name="actual_robot_current" type="DOUBLE"/>
66
+ <field name="actual_joint_voltage" type="VECTOR6D"/>
67
+ <field name="actual_digital_output_bits" type="UINT64"/>
68
+ <field name="runtime_state" type="UINT32"/>
69
+ <field name="output_bit_register_64" type="BOOL"/>
70
+ <field name="target_speed_fraction" type="DOUBLE"/>
71
+
72
+ <!-- field name="output_bit_registers0_to_31" type="UINT32"/-->
73
+ <!-- field name="output_bit_registers32_to_63" type="UINT32"/-->
74
+
75
+ <!-- field name="output_int_register_0" type="INT32"/-->
76
+ <!-- field name="output_int_register_1" type="INT32"/-->
77
+ <!-- field name="output_int_register_2" type="INT32"/-->
78
+ <!-- field name="output_int_register_3" type="INT32"/-->
79
+ <!-- field name="output_int_register_4" type="INT32"/-->
80
+ <!-- field name="output_int_register_5" type="INT32"/-->
81
+ <!-- field name="output_int_register_6" type="INT32"/-->
82
+ <!-- field name="output_int_register_7" type="INT32"/-->
83
+ <!-- field name="output_int_register_8" type="INT32"/-->
84
+ <!-- field name="output_int_register_9" type="INT32"/-->
85
+ <!-- field name="output_int_register_10" type="INT32"/-->
86
+ <!-- field name="output_int_register_11" type="INT32"/-->
87
+ <!-- field name="output_int_register_12" type="INT32"/-->
88
+ <!-- field name="output_int_register_13" type="INT32"/-->
89
+ <!-- field name="output_int_register_14" type="INT32"/-->
90
+ <!-- field name="output_int_register_15" type="INT32"/-->
91
+ <!-- field name="output_int_register_16" type="INT32"/-->
92
+ <!-- field name="output_int_register_17" type="INT32"/-->
93
+ <!-- field name="output_int_register_18" type="INT32"/-->
94
+ <!-- field name="output_int_register_19" type="INT32"/-->
95
+ <!-- field name="output_int_register_20" type="INT32"/-->
96
+ <!-- field name="output_int_register_21" type="INT32"/-->
97
+ <!-- field name="output_int_register_22" type="INT32"/-->
98
+ <!-- field name="output_int_register_23" type="INT32"/-->
99
+
100
+ <!-- field name="output_double_register_0" type="DOUBLE"/-->
101
+ <!-- field name="output_double_register_1" type="DOUBLE"/-->
102
+ <!-- field name="output_double_register_2" type="DOUBLE"/-->
103
+ <!-- field name="output_double_register_3" type="DOUBLE"/-->
104
+ <!-- field name="output_double_register_4" type="DOUBLE"/-->
105
+ <!-- field name="output_double_register_5" type="DOUBLE"/-->
106
+ <!-- field name="output_double_register_6" type="DOUBLE"/-->
107
+ <!-- field name="output_double_register_7" type="DOUBLE"/-->
108
+ <!-- field name="output_double_register_8" type="DOUBLE"/-->
109
+ <!-- field name="output_double_register_9" type="DOUBLE"/-->
110
+ <!-- field name="output_double_register_10" type="DOUBLE"/-->
111
+ <!-- field name="output_double_register_11" type="DOUBLE"/-->
112
+ <!-- field name="output_double_register_12" type="DOUBLE"/-->
113
+ <!-- field name="output_double_register_13" type="DOUBLE"/-->
114
+ <!-- field name="output_double_register_14" type="DOUBLE"/-->
115
+ <!-- field name="output_double_register_15" type="DOUBLE"/-->
116
+ <!-- field name="output_double_register_16" type="DOUBLE"/-->
117
+ <!-- field name="output_double_register_17" type="DOUBLE"/-->
118
+ <!-- field name="output_double_register_18" type="DOUBLE"/-->
119
+ <!-- field name="output_double_register_19" type="DOUBLE"/-->
120
+ <!-- field name="output_double_register_20" type="DOUBLE"/-->
121
+ <!-- field name="output_double_register_21" type="DOUBLE"/-->
122
+ <!-- field name="output_double_register_22" type="DOUBLE"/-->
123
+ <!-- field name="output_double_register_23" type="DOUBLE"/-->
124
+ </recipe>
125
+ </rtde_config>
data/lib/urest.rb ADDED
@@ -0,0 +1,405 @@
1
+ require 'riddl/server'
2
+ require 'json'
3
+ require 'typhoeus'
4
+ if $dev
5
+ require_relative '../../ur-sock/lib/ur-sock'
6
+ else
7
+ require 'ur-sock'
8
+ end
9
+ require 'net/ssh'
10
+ require 'net/scp'
11
+
12
+ module UREST
13
+ SERVER = File.expand_path(File.join(__dir__,'urest.xml'))
14
+
15
+ def self::start_dash(opts) #{{{
16
+ opts['dash'] = UR::Dash.new(opts['ipadress']).connect rescue nil
17
+ end #}}}
18
+
19
+ def self::start_psi(opts) #{{{
20
+ opts['psi'] = UR::Psi.new(opts['ipadress']).connect rescue nil
21
+ end #}}}
22
+
23
+ def self::start_rtde(opts) #{{{
24
+ ### Loading config file
25
+ conf = UR::XMLConfigFile.new opts['rtde_config']
26
+ output_names, output_types = conf.get_recipe opts['rtde_config_recipe_base']
27
+ opts['rtde'] = UR::Rtde.new(opts['ipadress']).connect
28
+
29
+ ### Set Speed
30
+ if opts['rtde_config_recipe_speed']
31
+ speed_names, speed_types = conf.get_recipe opts['rtde_config_recipe_speed']
32
+ opts['speed'] = opts['rtde'].send_input_setup(speed_names, speed_types)
33
+ opts['speed']['speed_slider_mask'] = 1
34
+ end
35
+
36
+ ### Set register
37
+ if opts['rtde_config_recipe_inbit']
38
+ bit_names, bit_types = conf.get_recipe opts['rtde_config_recipe_inbit']
39
+ opts['inbit'] = opts['rtde'].send_input_setup(bit_names,bit_types)
40
+ end
41
+ if opts['rtde_config_recipe_inint']
42
+ int_names, int_types = conf.get_recipe opts['rtde_config_recipe_inint']
43
+ opts['inint'] = opts['rtde'].send_input_setup(int_names,int_types)
44
+ end
45
+ if opts['rtde_config_recipe_indoub']
46
+ doub_names, doub_types = conf.get_recipe opts['rtde_config_recipe_indoub']
47
+ opts['indoub'] = opts['rtde'].send_input_setup(doub_names,doub_types)
48
+ end
49
+
50
+ ### Setup output
51
+ if not opts['rtde'].send_output_setup(output_names, output_types,10)
52
+ puts 'Unable to configure output'
53
+ end
54
+ if not opts['rtde'].send_start
55
+ puts 'Unable to start synchronization'
56
+ end
57
+
58
+ ###Initialize all inputs
59
+ bit_names.each do |i|
60
+ opts['inbit'][i] = false
61
+ end
62
+ int_names.each do |i|
63
+ opts['inint'][i] = 0
64
+ end
65
+ doub_names.each do |i|
66
+ opts['indoub'][i] = 0.0
67
+ end
68
+ end #}}}
69
+
70
+ def self::protect_reconnect_run(opts) #{{{
71
+ tries = 0
72
+ begin
73
+ yield
74
+ rescue UR::Dash::Reconnect => e
75
+ puts e.message
76
+ tries += 1
77
+ if tries < 2
78
+ UREST::start_dash opts
79
+ retry
80
+ end
81
+ rescue UR::Psi::Reconnect => e
82
+ puts e.message
83
+ tries += 1
84
+ if tries < 2
85
+ UREST::start_psi opts
86
+ retry
87
+ end
88
+ end
89
+ end #}}}
90
+
91
+ def self::ssh_start(opts) #{{{
92
+ if opts['certificate']
93
+ opts['ssh'] = Net::SSH.start(opts['ipadress'], opts['username'], :port => opts['sshport'], :keys => [ opts['certificate'] ])
94
+ else
95
+ opts['ssh'] = opts['password'] ? Net::SSH.start(opts['ipadress'], opts['username'], :port => opts['sshport'], auth_methods: ['password'], password: opts['password']) : Net::SSH.start(opts['ipadress'], opts['username'], :port => opts['sshport'])
96
+ end
97
+ end #}}}
98
+
99
+ def self::download_program(opts,name) #{{{
100
+ counter = 0
101
+ begin
102
+ opts['ssh'].scp.download! name
103
+ rescue => e
104
+ counter += 1
105
+ UREST::ssh_start opts
106
+ retry if counter < 3
107
+ end
108
+ end #}}}
109
+
110
+ def self::upload_program(opts,name,program) #{{{
111
+ counter = 0
112
+ begin
113
+ opts['ssh'].scp.upload StringIO.new(program), File.join(opts['dir'],name)
114
+ rescue => e
115
+ counter += 1
116
+ UREST::ssh_start opts
117
+ retry if counter < 3
118
+ end
119
+ nil
120
+ end #}}}
121
+
122
+ def self::get_robot_programs(opts) #{{{
123
+ progs = []
124
+ begin
125
+ progs = opts['ssh'].exec!('ls ' + File.join(opts['dir'],'*.urp') + ' 2>/dev/null').split("\n")
126
+ progs.shift if progs[0] =~ /^bash:/
127
+ rescue => e
128
+ UREST::ssh_start opts
129
+ end
130
+ progs
131
+ end #}}}
132
+
133
+ def self::robotprogram_running?(opts) #{{{
134
+ opts['ps']== 'Playing'
135
+ end #}}}
136
+
137
+ def self::start_program(opts,fname) #{{{
138
+ unless UREST::robotprogram_running?(opts)
139
+ UREST::protect_reconnect_run(opts) do
140
+ opts['dash'].load_program(fname)
141
+ opts['dash'].start_program
142
+ end
143
+ end
144
+ end #}}}
145
+
146
+ class GetValue < Riddl::Implementation #{{{
147
+ def response
148
+ Riddl::Parameter::Complex.new('value','text/plain',@a[0])
149
+ end
150
+ end #}}}
151
+ class GetValues < Riddl::Implementation #{{{
152
+ def response
153
+ Riddl::Parameter::Complex.new('values','application/json',JSON::generate(@a[0]))
154
+ end
155
+ end #}}}
156
+
157
+ class GetProg < Riddl::Implementation #{{{
158
+ def response
159
+ opts = @a[0]
160
+ fname = File.join(opts['dir'],@r[-1] + '.urp')
161
+ if opts['progs'].include? fname
162
+ return Riddl::Parameter::Complex.new('file','application/octet-stream',UREST::download_program(opts,fname),File.basename(fname))
163
+ else
164
+ @status = 403
165
+ end
166
+ nil
167
+ end
168
+ end #}}}
169
+ class ForkProg < Riddl::Implementation #{{{
170
+ def response
171
+ opts = @a[0]
172
+ fname = File.join(opts['dir'],@r[-2])
173
+ if opts['progs'].include? fname + '.urp'
174
+ UREST::start_program(opts,fname)
175
+ else
176
+ @status = 403
177
+ end
178
+ nil
179
+ end
180
+ end #}}}
181
+ class WaitProg < Riddl::Implementation #{{{
182
+ def response
183
+ opts = @a[0]
184
+ fname = File.join(opts['dir'],@r[-2])
185
+ if opts['progs'].include? fname + '.urp'
186
+ if @h['CPEE_CALLBACK']
187
+ EM.defer do
188
+ UREST::start_program(opts,fname)
189
+ sleep 2
190
+ while UREST::robotprogram_running?(opts)
191
+ sleep 0.25
192
+ end
193
+ Typhoeus.put(@h['CPEE_CALLBACK'])
194
+ end
195
+ @headers << Riddl::Header.new("CPEE-CALLBACK", 'true')
196
+ else
197
+ UREST::start_program(opts,fname)
198
+ end
199
+ else
200
+ @status = 403
201
+ end
202
+ nil
203
+ end
204
+ end #}}}
205
+
206
+ class SetInt < Riddl::Implementation #{{{
207
+ def response
208
+ opts = @a[0]
209
+ value = @p[0].value
210
+ z = @r[-1].to_i
211
+ if z < 10
212
+ opts['inint']["input_int_register_" + z.to_s] = value.to_i
213
+ opts['rtde'].send(opts['inint'])
214
+ else
215
+ @status = 403
216
+ end
217
+ nil
218
+ end
219
+ end #}}}
220
+
221
+ def self::implementation(opts)
222
+ Proc.new do
223
+ startup do #{{{ # Init RTDE and DASH
224
+ opts['sshport'] ||= 22
225
+ opts['frequency'] ||= 0.5
226
+
227
+ opts['rtde_config'] ||= File.join(__dir__,'rtde.conf.xml')
228
+ opts['rtde_config_recipe_base'] ||= 'out'
229
+ opts['rtde_config_recipe_speed'] ||= 'speed'
230
+ opts['rtde_config_recipe_inbit'] ||= 'inbit'
231
+ opts['rtde_config_recipe_inint'] ||= 'inint'
232
+ opts['rtde_config_recipe_indoub'] ||= 'indoub'
233
+
234
+ opts['dash'] = nil
235
+ opts['rtde'] = nil
236
+ opts['programs'] = nil
237
+ opts['psi'] = nil
238
+
239
+ ### Connecting to universal robot
240
+ UREST::start_rtde opts
241
+ UREST::start_dash opts
242
+ UREST::start_psi opts
243
+
244
+ ### check if interfaces are ok
245
+ raise if !opts['dash'] || !opts['rtde'] || !opts['psi']
246
+
247
+ # Functionality for threading in loop
248
+ opts['doit_state'] = Time.now.to_i
249
+ opts['doit_progs'] = Time.now.to_i
250
+ opts['doit_rtde'] = Time.now.to_i
251
+
252
+ # Serious comment (we do the obvious stuff)
253
+ opts['speed'] = {}
254
+ opts['sn'] = opts['dash'].get_serial_number
255
+ opts['model'] = opts['dash'].get_robot_model
256
+
257
+ ### Manifest programs
258
+ opts['semaphore'] = Mutex.new
259
+ opts['progs'] = []
260
+ rescue Errno::ECONNREFUSED => e
261
+ print 'ECONNREFUSED: '
262
+ puts e.message
263
+ rescue UR::Dash::Reconnect => e
264
+ UREST::start_dash opts
265
+ puts e.message
266
+ puts e.backtrace
267
+ rescue UR::Psi::Reconnect => e
268
+ UREST::start_psi opts
269
+ puts e.message
270
+ puts e.backtrace
271
+ rescue => e
272
+ puts e.message
273
+ puts e.backtrace
274
+ raise
275
+ end # }}}
276
+
277
+ parallel do #{{{ periodic reading of data from UR
278
+ EM.add_periodic_timer(opts[:frequency]) do
279
+ if Time.now.to_i - 1 > opts['doit_state']
280
+ opts['doit_state'] = Time.now.to_i
281
+ opts['cp'] = opts['dash'].get_loaded_program
282
+ opts['rs'] = opts['dash'].get_program_state.split(' ')[0]
283
+ # update remote control state from dashboard server
284
+ opts['mo'] = opts['dash'].is_in_remote_control
285
+ opts['op'] = opts['dash'].get_operational_mode
286
+ end
287
+
288
+ if Time.now.to_i - 10 > opts['doit_progs']
289
+ opts['doit_progs'] = Time.now.to_i
290
+ Thread.new do
291
+ opts['semaphore'].synchronize do
292
+ # Content of thread
293
+ # check every 10 seconds for new programs
294
+ opts['progs'] = UREST::get_robot_programs(opts)
295
+ end unless opts['semaphore'].locked?
296
+ end
297
+ end
298
+
299
+ data = opts['rtde'].receive
300
+ if data
301
+ opts['ss'] = data['speed_scaling']
302
+
303
+ # State objects
304
+ opts['rm'] = UR::Rtde::ROBOTMODE[data['robot_mode']]
305
+ opts['sm'] = UR::Rtde::SAFETYMODE[data['safety_mode']]
306
+ opts['ps'] = UR::Rtde::PROGRAMSTATE[data['runtime_state']]
307
+
308
+ #speed slider or override
309
+ if opts['ov'] != (data['target_speed_fraction'] * 100).to_i
310
+ opts['ov'] = (data['target_speed_fraction'] * 100).to_i
311
+ end
312
+ else
313
+ if Time.now.to_i - 10 > opts['doit_rtde']
314
+ opts['doit_rtde'] = Time.now.to_i
315
+ UREST::start_rtde opts
316
+ end
317
+ end
318
+ rescue Errno::ECONNREFUSED => e
319
+ print 'ECONNREFUSED: '
320
+ puts e.message
321
+ rescue UR::Dash::Reconnect => e
322
+ UREST::start_dash opts
323
+ puts e.message
324
+ puts e.backtrace
325
+ rescue UR::Psi::Reconnect => e
326
+ UREST::start_psi opts
327
+ puts e.message
328
+ puts e.backtrace
329
+ rescue => e
330
+ puts e.message
331
+ puts e.backtrace
332
+ raise
333
+ end
334
+ end #}}}
335
+
336
+ on resource do
337
+ run GetValues, [ 'messages', 'registers', 'model', 'serialnumber', 'state', 'programs'] if get
338
+ on resource 'model' do #{{{
339
+ run GetValue, opts['model'] if get
340
+ end #}}}
341
+ on resource 'serialnumber' do #{{{
342
+ run GetValue, opts['sn'] if get
343
+ end #}}}
344
+ on resource 'state' do #{{{
345
+ run GetValues, {
346
+ :mode => opts['rm'].downcase,
347
+ :power => opts['rm'] == 'Running' ? 'on' : 'off',
348
+ :remote => opts['mo'] || 'false',
349
+ :program => File.basename(opts['cp'] || ''),
350
+ :program_state => (opts['rs'] || 'stopped').downcase,
351
+ :safety_mode => opts['sm'].downcase,
352
+ :speed => opts['ov'],
353
+ :speed_scaling => opts['ss']
354
+ } if get
355
+ on resource 'mode' do
356
+ run GetValue, opts['rm'].downcase if get
357
+ end
358
+ on resource 'power' do
359
+ run GetValue, opts['rm'] == 'Running' ? 'on' : 'off' if get
360
+ end
361
+ on resource 'remote' do
362
+ run GetValue, opts['mo'] || 'false' if get
363
+ end
364
+ on resource 'program' do
365
+ run GetValue, File.basename(opts['cp'] || '') if get
366
+ end
367
+ on resource 'program_state' do
368
+ run GetValue, (opts['rs'] || 'stopped').downcase if get
369
+ end
370
+ on resource 'safety_mode' do
371
+ run GetValue, opts['sm'].downcase if get
372
+ end
373
+ on resource 'speed' do
374
+ run GetValue, opts['ov'] if get
375
+ end
376
+ on resource 'speed_scaling' do
377
+ run GetValue, opts['ss'] if get
378
+ end
379
+ end #}}}
380
+ on resource 'programs' do
381
+ run GetValues, opts['progs'].map{|f|File.basename(f,'.urp')} if get
382
+ on resource do
383
+ run GetProg, opts if get
384
+ on resource 'fork' do
385
+ run ForkProg, opts if put
386
+ end
387
+ on resource 'wait' do
388
+ run WaitProg, opts if put
389
+ end
390
+ end
391
+ end
392
+ on resource 'registers' do
393
+ on resource 'input' do
394
+ on resource 'int' do
395
+ on resource '\d+' do
396
+ run SetInt, opts if put 'tint'
397
+ end
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end
403
+ end
404
+
405
+ end