ladder_drive 0.4.1 → 0.5.0
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 +4 -4
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +11 -3
- data/README.md +56 -0
- data/README_jp.md +70 -4
- data/ladder_drive.gemspec +1 -0
- data/lib/ladder_drive/config.rb +39 -0
- data/lib/ladder_drive/config_target.rb +4 -1
- data/lib/ladder_drive/plc_device.rb +4 -3
- data/lib/ladder_drive/protocol/keyence/kv_device.rb +8 -0
- data/lib/ladder_drive/protocol/keyence/kv_protocol.rb +11 -9
- data/lib/ladder_drive/version.rb +1 -1
- data/lib/plc/emulator/emu_device.rb +55 -7
- data/lib/plc/emulator/emu_plc.rb +99 -16
- data/lib/plc/emulator/emu_plc_server.rb +6 -4
- data/lib/plc/plc.rb +1 -0
- data/lib/plc/raspberrypi/raspberrypi.rb +27 -0
- data/lib/plc/raspberrypi/raspberrypi_plc.rb +86 -0
- data/lib/plc/raspberrypi/raspberrypi_plc_server.rb +74 -0
- data/template/escalator/config/plc.yml +24 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5139eaf8a467986626c8f4cbe5b819819ed495c0
|
4
|
+
data.tar.gz: e7e3bae04896ccfd9515c77c4e3457058861e26b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b321b78ab6bc3a1fe6a84b840c3961b70233d535a8830b9327300e129c922ccc3312432af44c468d101c94853d0971baeb2cc509e9c0b8b0ad41d00565074b99
|
7
|
+
data.tar.gz: 7ccdbc826b357d71587f49f59feb7a5f2183e5046debf9f9608d8bc20ebab7433eb4fa2548b47235b5b6fc6070ba4b684641fe9bc8c34596c02bfb2a56daa7e1
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ladder_drive (0.
|
4
|
+
ladder_drive (0.5.0)
|
5
5
|
activesupport (~> 4.2, >= 4.2.7)
|
6
|
+
pi_piper
|
6
7
|
thor (~> 0)
|
7
8
|
|
8
9
|
GEM
|
@@ -14,14 +15,20 @@ GEM
|
|
14
15
|
minitest (~> 5.1)
|
15
16
|
thread_safe (~> 0.3, >= 0.3.4)
|
16
17
|
tzinfo (~> 1.1)
|
18
|
+
eventmachine (1.0.9)
|
19
|
+
ffi (1.9.18)
|
20
|
+
ffi (1.9.18-x64-mingw32)
|
17
21
|
i18n (0.7.0)
|
18
22
|
json (1.8.3)
|
19
23
|
minitest (5.9.1)
|
24
|
+
pi_piper (2.0.0)
|
25
|
+
eventmachine (= 1.0.9)
|
26
|
+
ffi
|
20
27
|
power_assert (0.2.7)
|
21
28
|
rake (10.5.0)
|
22
29
|
test-unit (3.1.7)
|
23
30
|
power_assert
|
24
|
-
thor (0.
|
31
|
+
thor (0.20.0)
|
25
32
|
thread_safe (0.3.5)
|
26
33
|
tzinfo (1.2.2)
|
27
34
|
thread_safe (~> 0.1)
|
@@ -34,8 +41,9 @@ DEPENDENCIES
|
|
34
41
|
activesupport (>= 4.2.7)
|
35
42
|
bundler (~> 1.11)
|
36
43
|
ladder_drive!
|
44
|
+
pi_piper
|
37
45
|
rake (~> 10.0)
|
38
46
|
test-unit
|
39
47
|
|
40
48
|
BUNDLED WITH
|
41
|
-
1.
|
49
|
+
1.15.4
|
data/README.md
CHANGED
@@ -180,6 +180,62 @@ OUT M1
|
|
180
180
|
<!-- [](https://youtu.be/qGbicGLB7Gs) -->
|
181
181
|
|
182
182
|
|
183
|
+
## Raspberry Pi
|
184
|
+
|
185
|
+
You can run it on the Raspberry Pi.
|
186
|
+
X or Y devices are assigned to GPIOs.
|
187
|
+
|
188
|
+
### Installation
|
189
|
+
|
190
|
+
```sh
|
191
|
+
$ sudo apt-get install ruby-dev
|
192
|
+
$ sudo apt-get install libssl-dev
|
193
|
+
$ sudo gem install ladder_drive
|
194
|
+
```
|
195
|
+
|
196
|
+
### Execution
|
197
|
+
|
198
|
+
```sh
|
199
|
+
$ ladder_drive create project
|
200
|
+
$ cd project
|
201
|
+
$ rake target=raspberrypi
|
202
|
+
```
|
203
|
+
|
204
|
+
### I/O settings
|
205
|
+
|
206
|
+
You can describe io configuration by confing/plc.yml file.
|
207
|
+
Define inputs pins by inputs section.
|
208
|
+
Devine outputs pins by outptus section.
|
209
|
+
You can specify pull up or pull down and invert options for input.
|
210
|
+
|
211
|
+
```
|
212
|
+
# Raspberry Pi
|
213
|
+
raspberrypi:
|
214
|
+
cpu: Raspberry Pi
|
215
|
+
io: # assign gpio to x and y
|
216
|
+
inputs:
|
217
|
+
x0:
|
218
|
+
pin: 4 # gpio no
|
219
|
+
pull: up # up | down | off
|
220
|
+
invert: true
|
221
|
+
x1:
|
222
|
+
pin: 17
|
223
|
+
pull: up
|
224
|
+
invert: true
|
225
|
+
x2:
|
226
|
+
pin: 27
|
227
|
+
pull: up
|
228
|
+
invert: true
|
229
|
+
outputs:
|
230
|
+
y0:
|
231
|
+
pin: 18
|
232
|
+
y1:
|
233
|
+
pin: 23
|
234
|
+
y2:
|
235
|
+
pin: 42
|
236
|
+
```
|
237
|
+
|
238
|
+
|
183
239
|
# Accessing a device values
|
184
240
|
|
185
241
|
You can use LadderDrive as a accessing tool for the PLC device.
|
data/README_jp.md
CHANGED
@@ -59,8 +59,8 @@ plc以下にLadderDriveを実行するPLCプロジェクトの雛形がありま
|
|
59
59
|
|
60
60
|
#### PLCの通信設定
|
61
61
|
|
62
|
-
plc
|
63
|
-
IP
|
62
|
+
plcディレクトリ以下の使用したいPLCのプロジェクトファイルを開きます。
|
63
|
+
IPアドレスなど必要な箇所を変更し、PLCに転送します。
|
64
64
|
|
65
65
|
[](https://youtu.be/fGdyIo9AmuE)
|
66
66
|
|
@@ -118,7 +118,7 @@ targetを指定するとplc.ymlのplcセクション内の該当するターゲ
|
|
118
118
|
$ rake target=iq-r
|
119
119
|
```
|
120
120
|
|
121
|
-
plc.ymlファイルのdefaultセクションのtarget
|
121
|
+
plc.ymlファイルのdefaultセクションのtargetでデフォルトのターゲットを設定できます。
|
122
122
|
|
123
123
|
```
|
124
124
|
# plc.yml
|
@@ -148,7 +148,7 @@ done uploading
|
|
148
148
|
コンソールモードではコマンドを打つ事でデバイスの読み書きができます。
|
149
149
|
|
150
150
|
デバイスの値を読み取るにはrコマンドを使用します。
|
151
|
-
下の例ではm0から8
|
151
|
+
下の例ではm0から8個分のデバイスを読み出します。
|
152
152
|
|
153
153
|
```sh
|
154
154
|
> r m0 8
|
@@ -183,6 +183,72 @@ OUT M1
|
|
183
183
|
|
184
184
|
<!-- [](https://youtu.be/qGbicGLB7Gs) -->
|
185
185
|
|
186
|
+
## Raspberry Pi
|
187
|
+
|
188
|
+
Raspberry Pi上で動作させることもできます。
|
189
|
+
XとYデバイスはGPIOに割り付けます。
|
190
|
+
|
191
|
+
### インストール
|
192
|
+
|
193
|
+
OSとしてRaspbianを使用した場合は次の手順でインストールできます。
|
194
|
+
|
195
|
+
```sh
|
196
|
+
$ sudo apt-get install ruby-dev
|
197
|
+
$ sudo apt-get install libssl-dev
|
198
|
+
$ sudo gem install ladder_drive
|
199
|
+
```
|
200
|
+
|
201
|
+
この時の環境は下の通りです。
|
202
|
+
|
203
|
+
```
|
204
|
+
$ uname -a
|
205
|
+
Linux raspberrypi 4.9.41+ #1023 Tue Aug 8 15:47:12 BST 2017 armv6l GNU/Linux
|
206
|
+
```
|
207
|
+
|
208
|
+
|
209
|
+
### 実行
|
210
|
+
|
211
|
+
```sh
|
212
|
+
$ ladder_drive create project
|
213
|
+
$ cd project
|
214
|
+
$ rake target=raspberrypi
|
215
|
+
```
|
216
|
+
|
217
|
+
### I/O設定
|
218
|
+
|
219
|
+
Project下のconfig/plc.ymlファイルで変更できます。
|
220
|
+
inputsで入力ピンを定義します。outputsで出力ピンを定義します。
|
221
|
+
pinにGPIO番号を指定します。
|
222
|
+
入力の場合は pull up, pull downの指定や invertで反転させることができます。
|
223
|
+
|
224
|
+
```
|
225
|
+
# Raspberry Pi
|
226
|
+
raspberrypi:
|
227
|
+
cpu: Raspberry Pi
|
228
|
+
io: # assign gpio to x and y
|
229
|
+
inputs:
|
230
|
+
x0:
|
231
|
+
pin: 4 # gpio no
|
232
|
+
pull: up # up | down | off で指定します。
|
233
|
+
invert: true
|
234
|
+
x1:
|
235
|
+
pin: 17
|
236
|
+
pull: up
|
237
|
+
invert: true
|
238
|
+
x2:
|
239
|
+
pin: 27
|
240
|
+
pull: up
|
241
|
+
invert: true
|
242
|
+
outputs:
|
243
|
+
y0:
|
244
|
+
pin: 18
|
245
|
+
y1:
|
246
|
+
pin: 23
|
247
|
+
y2:
|
248
|
+
pin: 42
|
249
|
+
```
|
250
|
+
|
251
|
+
|
186
252
|
## PLCデバイスへのアクセスツールとしての利用
|
187
253
|
|
188
254
|
LadderDriveはPLCデバイスの読み書きツールとしての利用もできます。
|
data/ladder_drive.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
|
17
17
|
spec.add_runtime_dependency 'thor', '~> 0'
|
18
18
|
spec.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.7'
|
19
|
+
spec.add_runtime_dependency 'pi_piper'
|
19
20
|
|
20
21
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
22
|
spec.bindir = "exe"
|
data/lib/ladder_drive/config.rb
CHANGED
@@ -62,11 +62,50 @@ module LadderDrive
|
|
62
62
|
host: "localhost",
|
63
63
|
port: 5555,
|
64
64
|
protocol: "emu_protocol",
|
65
|
+
keep: [
|
66
|
+
["L0", "L1023"],
|
67
|
+
["H0", "H1023"],
|
68
|
+
],
|
69
|
+
}
|
70
|
+
raspberrypi_default = {
|
71
|
+
host: "localhost",
|
72
|
+
port: 5555,
|
73
|
+
protocol: "emu_protocol",
|
74
|
+
keep: [
|
75
|
+
["L0", "L1023"],
|
76
|
+
["H0", "H1023"],
|
77
|
+
],
|
78
|
+
io: {
|
79
|
+
inputs: {
|
80
|
+
x0: 4,
|
81
|
+
x1: 17,
|
82
|
+
x2: 27,
|
83
|
+
x3: 22,
|
84
|
+
x4: 5,
|
85
|
+
x5: 6,
|
86
|
+
x6: 13,
|
87
|
+
x7: 19,
|
88
|
+
x8: 26,
|
89
|
+
},
|
90
|
+
outputs: {
|
91
|
+
=begin
|
92
|
+
y0: 18,
|
93
|
+
y1: 23,
|
94
|
+
y2: 24,
|
95
|
+
y3: 25,
|
96
|
+
y4: 12,
|
97
|
+
y5: 16,
|
98
|
+
y6: 20,
|
99
|
+
y7: 21,
|
100
|
+
=end
|
101
|
+
}
|
102
|
+
},
|
65
103
|
}
|
66
104
|
|
67
105
|
@config = default.merge options
|
68
106
|
@config[:plc] ||= {}
|
69
107
|
@config[:plc][:emulator] = @config[:plc][:emulator] ? emulator_default.merge(@config[:plc][:emulator]) : emulator_default
|
108
|
+
@config[:plc][:raspberrypi] = @config[:plc][:raspberrypi] ? emulator_default.merge(@config[:plc][:raspberrypi]) : raspberrypi_default
|
70
109
|
|
71
110
|
@config[:default] ||= {}
|
72
111
|
|
@@ -38,6 +38,7 @@ module LadderDrive
|
|
38
38
|
def finalize
|
39
39
|
proc {
|
40
40
|
EmuPlcServer.terminate
|
41
|
+
RaspberrypiPlcServer.terminate
|
41
42
|
}
|
42
43
|
end
|
43
44
|
|
@@ -81,7 +82,9 @@ module LadderDrive
|
|
81
82
|
def run
|
82
83
|
case self.name
|
83
84
|
when :emulator
|
84
|
-
Plc::Emulator::EmuPlcServer.launch
|
85
|
+
Plc::Emulator::EmuPlcServer.launch @target_info
|
86
|
+
when :raspberrypi
|
87
|
+
Plc::Raspberrypi::RaspberrypiPlcServer.launch @target_info
|
85
88
|
else
|
86
89
|
# DO NOTHIN
|
87
90
|
# Actual device is running independently.
|
@@ -57,12 +57,13 @@ module LadderDrive
|
|
57
57
|
when Fixnum
|
58
58
|
@suffix = ESC_SUFFIXES[a]
|
59
59
|
@number = b
|
60
|
-
when String
|
60
|
+
when String, Symbol
|
61
|
+
a = a.to_s # convert to string if it's a symbol
|
61
62
|
if b
|
62
63
|
@suffix = a.upcase
|
63
64
|
@number = b
|
64
65
|
else
|
65
|
-
/([A-Z]+)?(
|
66
|
+
/([A-Z]+)?([0-9A-F]+)/i =~ a
|
66
67
|
@suffix = ($1 || "").upcase
|
67
68
|
case number_type
|
68
69
|
when NUMBER_TYPE_DEC_HEX
|
@@ -90,7 +91,7 @@ module LadderDrive
|
|
90
91
|
end
|
91
92
|
"#{@suffix}#{ns}"
|
92
93
|
when NUMBER_TYPE_HEX
|
93
|
-
ns = @number.to_s(16)
|
94
|
+
ns = @number.to_s(16).upcase
|
94
95
|
ns = "0" + ns unless /^[0-9]/ =~ ns
|
95
96
|
"#{@suffix}#{ns}"
|
96
97
|
else
|
@@ -32,6 +32,14 @@ module Keyence
|
|
32
32
|
@suffix = "R" if @suffix.nil? || @suffix.length == 0
|
33
33
|
end
|
34
34
|
|
35
|
+
def + value
|
36
|
+
self.class.new self.suffix, self.number + value
|
37
|
+
end
|
38
|
+
|
39
|
+
def - value
|
40
|
+
self.class.new self.suffix, [self.number - value, 0].max
|
41
|
+
end
|
42
|
+
|
35
43
|
private
|
36
44
|
|
37
45
|
SUFFIXES_FOR_DEC = %w(DM EM FM ZF TM Z T TC TS C CC CS CTH CTC AT CM VM)
|
@@ -55,11 +55,13 @@ module Keyence
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def get_bits_from_device count, device
|
58
|
-
|
59
|
-
|
60
|
-
values
|
61
|
-
|
62
|
-
|
58
|
+
c = (count + 15) / 16
|
59
|
+
words = get_words_from_device c, device
|
60
|
+
values = []
|
61
|
+
count.times do |i|
|
62
|
+
index = i / 16
|
63
|
+
bit = i % 16
|
64
|
+
values << ((words[index] & (1 << bit)) != 0)
|
63
65
|
end
|
64
66
|
values
|
65
67
|
end
|
@@ -74,7 +76,7 @@ module Keyence
|
|
74
76
|
when false, 0
|
75
77
|
cmd = "RS"
|
76
78
|
end
|
77
|
-
packet = "#{cmd} #{device.name}\r"
|
79
|
+
packet = "#{cmd} #{device.name}\r\n"
|
78
80
|
@logger.debug("> #{dump_packet packet}")
|
79
81
|
open
|
80
82
|
@socket.puts(packet)
|
@@ -93,12 +95,12 @@ module Keyence
|
|
93
95
|
|
94
96
|
def get_words_from_device(count, device)
|
95
97
|
device = local_device device
|
96
|
-
packet = "RDS #{device.name} #{count}\r"
|
98
|
+
packet = "RDS #{device.name}.H #{count}\r\n"
|
97
99
|
@logger.debug("> #{dump_packet packet}")
|
98
100
|
open
|
99
101
|
@socket.puts(packet)
|
100
102
|
res = receive
|
101
|
-
values = res.split(/\s+/).map{|v| v.to_i}
|
103
|
+
values = res.split(/\s+/).map{|v| v.to_i(16)}
|
102
104
|
@logger.debug("#{device.name}[#{count}] => #{values}")
|
103
105
|
values
|
104
106
|
end
|
@@ -106,7 +108,7 @@ module Keyence
|
|
106
108
|
def set_words_to_device words, device
|
107
109
|
device = local_device device
|
108
110
|
words = [words] unless words.is_a? Array
|
109
|
-
packet = "WRS #{device.name} #{words.size} #{words.map{|w| w.to_s}.join(" ")}\r"
|
111
|
+
packet = "WRS #{device.name}.H #{words.size} #{words.map{|w| w.to_s(16)}.join(" ")}\r\n"
|
110
112
|
@logger.debug("> #{dump_packet packet}")
|
111
113
|
open
|
112
114
|
@socket.puts(packet)
|
data/lib/ladder_drive/version.rb
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
|
23
23
|
require 'ladder_drive/plc_device'
|
24
|
+
require 'weakref'
|
24
25
|
|
25
26
|
include LadderDrive
|
26
27
|
|
@@ -30,12 +31,14 @@ module Emulator
|
|
30
31
|
class EmuDevice < PlcDevice
|
31
32
|
|
32
33
|
attr_reader :in_value, :out_value
|
34
|
+
attr_accessor :plc
|
33
35
|
|
34
36
|
def initialize a, b = nil
|
35
37
|
super
|
36
38
|
@lock = Mutex.new
|
37
39
|
@in_value = 0
|
38
40
|
@out_value = 0
|
41
|
+
@changed = false
|
39
42
|
end
|
40
43
|
|
41
44
|
def reset
|
@@ -46,6 +49,14 @@ module Emulator
|
|
46
49
|
}
|
47
50
|
end
|
48
51
|
|
52
|
+
def plc= plc
|
53
|
+
@plc = WeakRef.new plc
|
54
|
+
end
|
55
|
+
|
56
|
+
def changed?
|
57
|
+
@changed
|
58
|
+
end
|
59
|
+
|
49
60
|
def value= value
|
50
61
|
set_value value
|
51
62
|
end
|
@@ -61,17 +72,41 @@ module Emulator
|
|
61
72
|
end
|
62
73
|
|
63
74
|
def bool= value
|
64
|
-
|
75
|
+
set_value value
|
65
76
|
end
|
66
77
|
|
67
78
|
def word kind=nil
|
68
|
-
|
79
|
+
if bit_device?
|
80
|
+
v = 0
|
81
|
+
d = self
|
82
|
+
f = 1
|
83
|
+
16.times do
|
84
|
+
v |= f if d.bool(kind)
|
85
|
+
d = d + 1
|
86
|
+
f <<= 1
|
87
|
+
end
|
88
|
+
v
|
89
|
+
else
|
90
|
+
value kind
|
91
|
+
end
|
69
92
|
end
|
70
93
|
|
71
94
|
def word= value
|
72
|
-
|
73
|
-
|
74
|
-
|
95
|
+
set_word value
|
96
|
+
end
|
97
|
+
|
98
|
+
def set_word value, kind=nil
|
99
|
+
if bit_device?
|
100
|
+
f = 1
|
101
|
+
d = self
|
102
|
+
16.times do
|
103
|
+
d.set_value (value & f) != 0, kind
|
104
|
+
d = d + 1
|
105
|
+
f <<= 1
|
106
|
+
end
|
107
|
+
else
|
108
|
+
set_value value, kind
|
109
|
+
end
|
75
110
|
end
|
76
111
|
|
77
112
|
def value kind=nil
|
@@ -95,7 +130,8 @@ module Emulator
|
|
95
130
|
when :out
|
96
131
|
@out_value = value
|
97
132
|
else
|
98
|
-
@
|
133
|
+
@changed = true unless @value == value
|
134
|
+
@value = value if @changed
|
99
135
|
end
|
100
136
|
}
|
101
137
|
end
|
@@ -103,7 +139,8 @@ module Emulator
|
|
103
139
|
def sync_input
|
104
140
|
@lock.synchronize {
|
105
141
|
unless @in_value.nil?
|
106
|
-
@
|
142
|
+
@changed = true unless @value == @in_value
|
143
|
+
@value = @in_value if @changed
|
107
144
|
@in_value = nil
|
108
145
|
end
|
109
146
|
}
|
@@ -112,9 +149,20 @@ module Emulator
|
|
112
149
|
def sync_output
|
113
150
|
@lock.synchronize {
|
114
151
|
@out_value = @value
|
152
|
+
@changed = false
|
115
153
|
}
|
116
154
|
end
|
117
155
|
|
156
|
+
def + value
|
157
|
+
d = super
|
158
|
+
plc ? plc.device_by_name(d.name) : d
|
159
|
+
end
|
160
|
+
|
161
|
+
def - value
|
162
|
+
d = super
|
163
|
+
plc ? plc.device_by_name(d.name) : d
|
164
|
+
end
|
165
|
+
|
118
166
|
end
|
119
167
|
|
120
168
|
end
|
data/lib/plc/emulator/emu_plc.rb
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
|
23
23
|
require 'ladder_drive/plc_device'
|
24
|
+
require 'yaml'
|
24
25
|
|
25
26
|
include LadderDrive
|
26
27
|
|
@@ -31,6 +32,7 @@ module Emulator
|
|
31
32
|
|
32
33
|
attr_accessor :program_data
|
33
34
|
attr_reader :program_pointer
|
35
|
+
attr_reader :config
|
34
36
|
attr_reader :device_dict
|
35
37
|
attr_reader :errors
|
36
38
|
|
@@ -49,11 +51,14 @@ module Emulator
|
|
49
51
|
INDEX_BIT_STACK = 5
|
50
52
|
SIZE_OF_BIT_STACK = 3
|
51
53
|
|
52
|
-
|
54
|
+
SAVE_INTERVAL = 1.0 #60.0
|
55
|
+
|
56
|
+
def initialize config={}
|
53
57
|
SUFFIXES.each do |k|
|
54
58
|
eval "@#{k}_devices = []"
|
55
59
|
end
|
56
60
|
@lock = Mutex.new
|
61
|
+
@config = config
|
57
62
|
reset
|
58
63
|
end
|
59
64
|
|
@@ -61,8 +66,14 @@ module Emulator
|
|
61
66
|
@lock.synchronize {
|
62
67
|
d = device_dict[name]
|
63
68
|
unless d
|
64
|
-
|
65
|
-
|
69
|
+
# try normalized name again
|
70
|
+
new_d = EmuDevice.new(name)
|
71
|
+
d = device_dict[new_d.name]
|
72
|
+
unless d
|
73
|
+
d = new_d
|
74
|
+
d.plc = self
|
75
|
+
device_dict[d.name] = d
|
76
|
+
end
|
66
77
|
end
|
67
78
|
d
|
68
79
|
}
|
@@ -94,6 +105,7 @@ module Emulator
|
|
94
105
|
sleep 0.1
|
95
106
|
when STOP_PLC_FLAG | CLEAR_PROGRAM_FLAG
|
96
107
|
reset
|
108
|
+
load
|
97
109
|
status_form_plc.value = CLEAR_PROGRAM_FLAG
|
98
110
|
sleep 0.1
|
99
111
|
when 0
|
@@ -105,6 +117,8 @@ module Emulator
|
|
105
117
|
else
|
106
118
|
sleep 0.1
|
107
119
|
end
|
120
|
+
# Save must be executed berofe sync_output, because changed flag was clear after sync_output
|
121
|
+
save
|
108
122
|
sync_output
|
109
123
|
end
|
110
124
|
|
@@ -130,20 +144,27 @@ module Emulator
|
|
130
144
|
|
131
145
|
def execute_console_commands line
|
132
146
|
a = line.chomp.split(/\s+/)
|
133
|
-
|
147
|
+
|
148
|
+
# separate data type except eval command.
|
149
|
+
unless a[0] == "E"
|
150
|
+
word = /([A-Z0-9]+)(\.H)?/ =~ a[1]
|
151
|
+
a[1] = $1
|
152
|
+
end
|
153
|
+
|
154
|
+
case a[0]
|
134
155
|
when /^ST/i
|
135
156
|
d = device_by_name a[1]
|
136
157
|
d.set_value true, :in
|
137
|
-
"OK\r"
|
158
|
+
"OK\r\n"
|
138
159
|
when /^RS/i
|
139
160
|
d = device_by_name a[1]
|
140
161
|
d.set_value false, :in
|
141
|
-
"OK\r"
|
162
|
+
"OK\r\n"
|
142
163
|
when /^RDS/i
|
143
164
|
d = device_by_name a[1]
|
144
165
|
c = a[2].to_i
|
145
166
|
r = []
|
146
|
-
if d.bit_device?
|
167
|
+
if !word && d.bit_device?
|
147
168
|
c.times do
|
148
169
|
r << (d.bool(:out) ? 1 : 0)
|
149
170
|
d = device_by_name (d+1).name
|
@@ -162,31 +183,38 @@ module Emulator
|
|
162
183
|
end
|
163
184
|
end
|
164
185
|
end
|
165
|
-
r.map{|e| e.to_s}.join(" ") + "\r"
|
166
|
-
|
186
|
+
r.map{|e| e.to_s(16)}.join(" ") + "\r\n"
|
187
|
+
|
188
|
+
# LadderDrive communication is bases KV protocol.
|
189
|
+
# LadderDrive console is use WRS command only. (Not use WR command)
|
190
|
+
# WR command is for irBoard. (http://irboard.itosoft.com)
|
191
|
+
# A word value is communicated with hex format only in this product. (hard coding)
|
192
|
+
when /^WRS?/i
|
167
193
|
d = device_by_name a[1]
|
194
|
+
a.insert 2, "1" if /^WR$/i =~ a.first
|
168
195
|
c = a[2].to_i
|
169
196
|
case d.suffix
|
170
197
|
when "PRG"
|
171
198
|
a[3, c].each do |v|
|
172
|
-
program_data[d.number * 2, 2] = [v.to_i].pack("n").unpack("C*")
|
199
|
+
program_data[d.number * 2, 2] = [v.to_i(16)].pack("n").unpack("C*")
|
173
200
|
d = device_by_name (d+1).name
|
174
201
|
end
|
175
202
|
else
|
176
|
-
if d.bit_device?
|
203
|
+
if !word && d.bit_device?
|
177
204
|
a[3, c].each do |v|
|
178
205
|
d.set_value v == "0" ? false : true, :in
|
179
|
-
d = device_by_name (d+1).name
|
206
|
+
d = d + 1#device_by_name (d+1).name
|
180
207
|
end
|
181
208
|
else
|
182
209
|
a[3, c].each do |v|
|
183
|
-
|
184
|
-
d.
|
185
|
-
d
|
210
|
+
v = v.to_i(16)
|
211
|
+
d.set_word v, :in
|
212
|
+
#d.set_value v, :in
|
213
|
+
d = d + 1#device_by_name (d+1).name
|
186
214
|
end
|
187
215
|
end
|
188
216
|
end
|
189
|
-
"OK\r"
|
217
|
+
"OK\r\n"
|
190
218
|
when /E/
|
191
219
|
eval(a[1..-1].join(" ")).inspect
|
192
220
|
else
|
@@ -194,8 +222,63 @@ module Emulator
|
|
194
222
|
end
|
195
223
|
end
|
196
224
|
|
225
|
+
# --- queries
|
226
|
+
def keep_device? device
|
227
|
+
@keep_ranges ||= begin
|
228
|
+
if config[:keep]
|
229
|
+
config[:keep].map do |a|
|
230
|
+
[device_by_name(a.first), device_by_name(a.last)]
|
231
|
+
end
|
232
|
+
else
|
233
|
+
[]
|
234
|
+
end
|
235
|
+
end
|
236
|
+
@keep_ranges.each do |a|
|
237
|
+
f = a.first
|
238
|
+
e = a.last
|
239
|
+
next unless f.suffix == device.suffix
|
240
|
+
return true if (f.number..e.number).include? device.number
|
241
|
+
end
|
242
|
+
false
|
243
|
+
end
|
244
|
+
|
197
245
|
private
|
198
246
|
|
247
|
+
# ----------------
|
248
|
+
# Load / Save keep devices
|
249
|
+
|
250
|
+
def dump_memory_path
|
251
|
+
@dump_memory_paty ||= File.join("cache", "dump_memory.yml").tap do |path|
|
252
|
+
FileUtils.mkdir_p( File.dirname(path))
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def load
|
257
|
+
path = dump_memory_path
|
258
|
+
return unless File.exist? path
|
259
|
+
|
260
|
+
YAML.load(File.read(path)).each do |a|
|
261
|
+
d = device_by_name(a.first)
|
262
|
+
d.value = a.last
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def save
|
267
|
+
devices = @device_dict.values.select{|d| self.keep_device?(d)}
|
268
|
+
needs_save = @pending_save || !(devices.select{|d| d.changed?}).empty?
|
269
|
+
return unless needs_save
|
270
|
+
now = Time.now
|
271
|
+
if @saved_at && ((now - @saved_at) < SAVE_INTERVAL)
|
272
|
+
@pending_save ||= true
|
273
|
+
return
|
274
|
+
end
|
275
|
+
|
276
|
+
File.write dump_memory_path, YAML.dump(devices.map{|d| [d.name, d.value]})
|
277
|
+
@saved_at = now
|
278
|
+
@pending_save = false
|
279
|
+
end
|
280
|
+
|
281
|
+
|
199
282
|
# ----------------
|
200
283
|
# stack operations
|
201
284
|
def stack_device index=0
|
@@ -28,11 +28,13 @@ module Emulator
|
|
28
28
|
|
29
29
|
class EmuPlcServer
|
30
30
|
|
31
|
+
attr_reader :config
|
32
|
+
|
31
33
|
class << self
|
32
34
|
|
33
|
-
def launch
|
35
|
+
def launch config={}
|
34
36
|
@server ||= begin
|
35
|
-
server = new
|
37
|
+
server = new config
|
36
38
|
server.run
|
37
39
|
server
|
38
40
|
end
|
@@ -42,7 +44,7 @@ module Emulator
|
|
42
44
|
|
43
45
|
def initialize config = {}
|
44
46
|
@port = config[:port] || 5555
|
45
|
-
@plc = EmuPlc.new
|
47
|
+
@plc = EmuPlc.new config
|
46
48
|
end
|
47
49
|
|
48
50
|
def run
|
@@ -58,7 +60,7 @@ module Emulator
|
|
58
60
|
r = @plc.execute_console_commands line
|
59
61
|
socket.puts r
|
60
62
|
rescue => e
|
61
|
-
socket.puts "E0 #{e}\r"
|
63
|
+
socket.puts "E0 #{e}\r\n"
|
62
64
|
end
|
63
65
|
end
|
64
66
|
end
|
data/lib/plc/plc.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2017 ITO SOFT DESIGN Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
24
|
+
$:.unshift dir unless $:.include? dir
|
25
|
+
|
26
|
+
require 'raspberrypi_plc'
|
27
|
+
require 'raspberrypi_plc_server'
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2017 ITO SOFT DESIGN Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'ladder_drive/plc_device'
|
24
|
+
require 'plc/emulator/emulator'
|
25
|
+
require 'pi_piper'
|
26
|
+
|
27
|
+
include LadderDrive
|
28
|
+
include PiPiper
|
29
|
+
|
30
|
+
|
31
|
+
module Plc
|
32
|
+
module Raspberrypi
|
33
|
+
|
34
|
+
class RaspberrypiPlc < Plc::Emulator::EmuPlc
|
35
|
+
|
36
|
+
def initialize config={}
|
37
|
+
super
|
38
|
+
setup_io
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def setup_io
|
44
|
+
@available_pi_piper = false
|
45
|
+
@io_dict = { inputs:[], outputs:[] }
|
46
|
+
config[:io][:inputs].each do |dev, info|
|
47
|
+
@io_dict[:inputs] << [device_by_name(dev), Pin.new(pin:info[:pin], direction: :in, pull:(info[:pull].to_sym || :off), invert:info[:invert])]
|
48
|
+
end
|
49
|
+
config[:io][:outputs].each do |dev, info|
|
50
|
+
@io_dict[:outputs] << [device_by_name(dev), Pin.new(pin:info[:pin], direction: :out)]
|
51
|
+
end
|
52
|
+
@available_pi_piper = true
|
53
|
+
rescue NoMethodError
|
54
|
+
puts "WARN: defention of io is missing!"
|
55
|
+
rescue LoadError
|
56
|
+
puts "WARN: pi_piper is not available this system!"
|
57
|
+
end
|
58
|
+
|
59
|
+
def sync_input
|
60
|
+
if @available_pi_piper
|
61
|
+
@io_dict[:inputs].each do |device, pin|
|
62
|
+
pin.read
|
63
|
+
device.set_value pin.on?, :in
|
64
|
+
end
|
65
|
+
end
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
69
|
+
def sync_output
|
70
|
+
super
|
71
|
+
return unless @available_pi_piper
|
72
|
+
|
73
|
+
@io_dict[:outputs].each do |device, pin|
|
74
|
+
if device.bool
|
75
|
+
pin.on
|
76
|
+
else
|
77
|
+
pin.off
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2017 ITO SOFT DESIGN Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'socket'
|
24
|
+
require 'ladder_drive/plc_device'
|
25
|
+
|
26
|
+
module Plc
|
27
|
+
module Raspberrypi
|
28
|
+
|
29
|
+
class RaspberrypiPlcServer
|
30
|
+
|
31
|
+
class << self
|
32
|
+
|
33
|
+
def launch config={}
|
34
|
+
@server ||= begin
|
35
|
+
server = new config
|
36
|
+
server.run
|
37
|
+
server
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize config = {}
|
44
|
+
@port = config[:port] || 5555
|
45
|
+
@plc = RaspberrypiPlc.new config
|
46
|
+
end
|
47
|
+
|
48
|
+
def run
|
49
|
+
puts "launching respberrypi plc ... "
|
50
|
+
@plc.run
|
51
|
+
puts "done launching"
|
52
|
+
|
53
|
+
Thread.new do
|
54
|
+
server = TCPServer.open @port
|
55
|
+
loop do
|
56
|
+
Thread.start(server.accept) do |socket|
|
57
|
+
while line = socket.gets
|
58
|
+
begin
|
59
|
+
r = @plc.execute_console_commands line
|
60
|
+
socket.puts r
|
61
|
+
rescue => e
|
62
|
+
socket.puts "E0 #{e}\r\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -19,6 +19,30 @@ plc:
|
|
19
19
|
host: 192.168.0.11
|
20
20
|
# port: 8501
|
21
21
|
|
22
|
+
# Raspberry Pi
|
23
|
+
raspberrypi:
|
24
|
+
cpu: Raspberry Pi
|
25
|
+
io: # assign gpio to x and y
|
26
|
+
inputs:
|
27
|
+
x0:
|
28
|
+
pin: 4 # gpio no
|
29
|
+
pull: up # up | down | off
|
30
|
+
invert: true
|
31
|
+
x1:
|
32
|
+
pin: 17
|
33
|
+
pull: up
|
34
|
+
invert: true
|
35
|
+
x2:
|
36
|
+
pin: 27
|
37
|
+
pull: up
|
38
|
+
invert: true
|
39
|
+
outputs:
|
40
|
+
y0:
|
41
|
+
pin: 18
|
42
|
+
y1:
|
43
|
+
pin: 23
|
44
|
+
y2:
|
45
|
+
pin: 42
|
22
46
|
|
23
47
|
# default section
|
24
48
|
default:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ladder_drive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katsuyoshi Ito
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -44,6 +44,20 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 4.2.7
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: pi_piper
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
47
61
|
- !ruby/object:Gem::Dependency
|
48
62
|
name: bundler
|
49
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,6 +156,9 @@ files:
|
|
142
156
|
- lib/plc/keyence/kv/kv-5000/sample.al2
|
143
157
|
- lib/plc/mitsubishi/iq-r/r08/r08.gx3
|
144
158
|
- lib/plc/plc.rb
|
159
|
+
- lib/plc/raspberrypi/raspberrypi.rb
|
160
|
+
- lib/plc/raspberrypi/raspberrypi_plc.rb
|
161
|
+
- lib/plc/raspberrypi/raspberrypi_plc_server.rb
|
145
162
|
- sample/ladder_drive/sample1.esc
|
146
163
|
- sample/ladder_drive/sample2.esc
|
147
164
|
- sample/ladder_drive/sample2.png
|