ladder_drive 0.4.1 → 0.5.0

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
  SHA1:
3
- metadata.gz: 1c322d622e10b0ee64912b24117db7086fb6bd5c
4
- data.tar.gz: 21d0ef57b8e8d82140071a7d60397861ad9faaff
3
+ metadata.gz: 5139eaf8a467986626c8f4cbe5b819819ed495c0
4
+ data.tar.gz: e7e3bae04896ccfd9515c77c4e3457058861e26b
5
5
  SHA512:
6
- metadata.gz: 15b168caefe744fe6262e01108bd2ee845b7051c18c98367ddd81edf473b78f1e4aaa44bf0ba59853b48ab2593b9263a64a36877a7fcc7343a91d78f673980a4
7
- data.tar.gz: 7d1b836921e3eb79fea0a21ccba8a00ff2e9019e293857ffa7272269ec5632cf3e7888bde60bb91170d1908b8e9cbdd12803a38f8f05dff03b552525362c0c82
6
+ metadata.gz: b321b78ab6bc3a1fe6a84b840c3961b70233d535a8830b9327300e129c922ccc3312432af44c468d101c94853d0971baeb2cc509e9c0b8b0ad41d00565074b99
7
+ data.tar.gz: 7ccdbc826b357d71587f49f59feb7a5f2183e5046debf9f9608d8bc20ebab7433eb4fa2548b47235b5b6fc6070ba4b684641fe9bc8c34596c02bfb2a56daa7e1
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  *.gem
11
11
  /work/
12
+ cache
data/Gemfile CHANGED
@@ -6,3 +6,6 @@ gemspec
6
6
  gem "test-unit"
7
7
 
8
8
  gem "activesupport", ">=4.2.7"
9
+
10
+ gem 'pi_piper'
11
+
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ladder_drive (0.4.1)
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.19.4)
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.14.3
49
+ 1.15.4
data/README.md CHANGED
@@ -180,6 +180,62 @@ OUT M1
180
180
  <!-- [![](http://img.youtube.com/vi/qGbicGLB7Gs/0.jpg)](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以下の使用したいPLCのプロジェクトファイルを開きます。
63
- IPアドレスなど必要な箇所変更し、PLCに転送します。
62
+ plcディレクトリ以下の使用したいPLCのプロジェクトファイルを開きます。
63
+ IPアドレスなど必要な箇所を変更し、PLCに転送します。
64
64
 
65
65
  [![](http://img.youtube.com/vi/fGdyIo9AmuE/0.jpg)](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
  <!-- [![](http://img.youtube.com/vi/qGbicGLB7Gs/0.jpg)](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"
@@ -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]+)?(\d+)/i =~ a
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
- values = get_words_from_device count, device
59
- values = values.map{|v| v == 0 ? false : true}
60
- values.each do |v|
61
- device.bool = v
62
- device += 1
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)
@@ -22,5 +22,5 @@
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
24
  module LadderDrive
25
- VERSION = "0.4.1"
25
+ VERSION = "0.5.0"
26
26
  end
@@ -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
- @lock.synchronize { super }
75
+ set_value value
65
76
  end
66
77
 
67
78
  def word kind=nil
68
- value kind
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
- @lock.synchronize {
73
- super
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
- @value = value
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
- @value = @in_value
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
@@ -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
- def initialize
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
- d = EmuDevice.new name
65
- device_dict[name] = d
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
- case a.first
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
- when /^WRS/i
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
- d.word = v.to_i
184
- d.set_value v.to_i, :in
185
- d = device_by_name (d+1).name
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
@@ -25,3 +25,4 @@ $:.unshift dir unless $:.include? dir
25
25
 
26
26
  # Use load instead require, because there are two emulator files.
27
27
  load File.join(dir, 'emulator/emulator.rb')
28
+ load File.join(dir, 'raspberrypi/raspberrypi.rb')
@@ -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.1
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-05-22 00:00:00.000000000 Z
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