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