urest 0.99.1
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 +7 -0
- data/AUTHORS +1 -0
- data/LICENSE +674 -0
- data/README.md +76 -0
- data/Rakefile +22 -0
- data/lib/rtde.conf.xml +125 -0
- data/lib/urest.rb +405 -0
- data/lib/urest.xml +147 -0
- data/server/dev +12 -0
- data/server/dev.conf +18 -0
- data/server/urest +11 -0
- data/server/urest.conf +18 -0
- data/tools/urest +50 -0
- metadata +138 -0
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
|