escalator 0.1.2 → 0.2.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: c264fb022bd1412ffaed30646286e0a482d4f14c
4
- data.tar.gz: e4db446ef5fe43d1019276831145a95b42dc23a1
3
+ metadata.gz: 97963603ab6ca43c800f1457985665cde1595d64
4
+ data.tar.gz: 2655e9900ca290185648b1b5d5436a8eb2703c25
5
5
  SHA512:
6
- metadata.gz: 71972f246ec5255677e2e8efdc03a3f8c29fcfeeb65054cddca735ba4f726bd69f3a746208458b8b2728ab0e9d66b8671096a85b29dc3aacc68fe8f86033e986
7
- data.tar.gz: 9b80c9f45104c524e82464eba6daaf05e1ecd3504e458bc1a858691d7a67fbffdb729bc8e0779e04dcba164ec567de79a33ef18b79cfcae3680176d12c53373f
6
+ metadata.gz: a023765c76d3fa85ccb6c4c399e3cf57b040d0c94e5bceb0c75890d0fb60b0531ba4d1b95b3732a739b3c098a18d30e0867e8bd42e41a609073a357b8a6fa4ab
7
+ data.tar.gz: 4f772215bb2dca5a8b63e8a18f030fd3e6347ef55f355b8923baea04a049a442838788cabe2bac686b23b86e3c2cc25516237159ca9c20e9f547e5bc9ae3e949
data/Gemfile CHANGED
@@ -4,3 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem "test-unit"
7
+ gem "activesupport"
data/Gemfile.lock CHANGED
@@ -1,22 +1,34 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- escalator (0.1.1)
4
+ escalator (0.1.2)
5
5
  thor
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ activesupport (5.0.0.1)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (~> 0.7)
13
+ minitest (~> 5.1)
14
+ tzinfo (~> 1.1)
15
+ concurrent-ruby (1.0.2)
16
+ i18n (0.7.0)
17
+ minitest (5.9.1)
10
18
  power_assert (0.2.7)
11
19
  rake (10.5.0)
12
20
  test-unit (3.1.7)
13
21
  power_assert
14
22
  thor (0.19.1)
23
+ thread_safe (0.3.5)
24
+ tzinfo (1.2.2)
25
+ thread_safe (~> 0.1)
15
26
 
16
27
  PLATFORMS
17
28
  ruby
18
29
 
19
30
  DEPENDENCIES
31
+ activesupport
20
32
  bundler (~> 1.11)
21
33
  escalator!
22
34
  rake (~> 10.0)
data/README.md CHANGED
@@ -2,9 +2,99 @@
2
2
 
3
3
  The escalator is a simple abstract ladder for PLC (Programmable Logic Controller).
4
4
 
5
- My aim is to design runnable abstraction ladder which is running on any PLC with same ladder source or binary and prepare full stack tools.
5
+ We aim to design runnable abstraction ladder which is running on any PLC with same ladder source or binary and prepare full stack tools.
6
+
7
+ # Getting started
8
+
9
+ It's required the Ruby environment.
10
+ To prepare Ruby environment, please find web sites.
11
+
12
+ Install Escalator at the command prompt.
13
+
14
+ ```
15
+ $ gem install escalator
16
+ ```
17
+
18
+ # Create an Escalator project
19
+
20
+ At the command prompt, create a new Escalator project.
21
+
22
+ ```
23
+ $ escalator create my_project
24
+ $ cd my_project
25
+ ```
26
+
27
+ Created files are consisted like below the tree.
28
+
29
+ ```
30
+ .
31
+ ├── Rakefile
32
+ ├── asm
33
+ │ └── main.esc
34
+ ├── config
35
+ │ └── plc.yml
36
+ └── plc
37
+ └── mitsubishi
38
+ └── iq-r
39
+ └── r08
40
+ ├── LICENSE
41
+ └── r08.gx3
42
+ ```
43
+
44
+ # Connection configuration
45
+
46
+ ## PLC configuration
47
+
48
+ There is a plc project under the plc directory.
49
+ Launch the one of the plc project which you want to use.
50
+ (Currently we support MITSUBISHI iQ-R R08CUP only.)
51
+
52
+ Configure ethernet connection by the tool which is provided by plc maker.
53
+ Then upload settings and plc program to the plc.
54
+
55
+ ## Escalator configuration
56
+
57
+ There is a configuration file at config/plc.yml.
58
+ Though currently we support MITSUBISHI iQ-R R08CUP only, you only change host to an ip address of your plc.
59
+
60
+ ```
61
+ :plc:
62
+ :cpu: iq-r
63
+ :protocol: mc_protocol
64
+ :host: 192.168.0.1
65
+ :port: 5007
66
+ :program_area: d10000
67
+ :interaction_area: d9998
68
+ ```
69
+ ## Escalator programming
70
+
71
+ Escalator program file is located under the asm directory.
72
+ By default, use asm/main.esc.
73
+ Edit it and programming.
74
+
75
+ Refer [Wiki](https://github.com/ito-soft-design/escalator/wiki/mnemonic) to check mnemonic.
76
+
77
+ ```
78
+ LD X0
79
+ OUT Y0
80
+ LD X1
81
+ OR M0
82
+ ANI X2
83
+ OUT M0
84
+ END
85
+ ```
86
+
87
+ # Transfer the Escalator program
88
+
89
+ At the command prompt, use rake command to upload escalator program to the plc.
90
+ The Escalator program is running immediate after uploaded.
91
+
92
+ ```
93
+ $ rake plc
94
+ ```
95
+
96
+
6
97
 
7
98
  ## License
8
99
 
9
100
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
10
-
data/README_jp.md CHANGED
@@ -5,6 +5,108 @@
5
5
 
6
6
  まずはニーモニックレベルで実現し、その上に分かりやすい形のプログラミングツールを用意できればと考えています。
7
7
 
8
+ # 使い方
9
+
10
+ 使用するにはRubyが実行できる環境が必要です。
11
+ Rubyの実行環境の構築はWebサイト等を検索して構築してください。
12
+
13
+ ## インストール
14
+
15
+ gemでエスカレーターをインストールします。
16
+
17
+ ```
18
+ $ gem install escalator
19
+ ```
20
+
21
+ ## プロジェクト作成
22
+
23
+ エスカレーターをインストールするとescalatorコマンドが使用できる様になります。
24
+ escalatorコマンドでラダーを構成するプロジェクトファイルを作ります。
25
+
26
+ ```
27
+ $ escalator create my_project
28
+ $ cd my_project
29
+ ```
30
+
31
+ ファイルの構成は下の様になっています。
32
+ plc以下にエスカレーターを実行するPLCプロジェクトの雛形があります。
33
+ 現在は三菱電機のiQ-RシリーズR08CPUのみの対応となっています。
34
+ 他メーカーや他の機種は今後追加する予定です。
35
+
36
+
37
+ ```
38
+ .
39
+ ├── Rakefile
40
+ ├── asm
41
+ │   └── main.esc
42
+ ├── config
43
+ │   └── plc.yml
44
+ └── plc
45
+ └── mitsubishi
46
+ └── iq-r
47
+ └── r08
48
+ ├── LICENSE
49
+ └── r08.gx3
50
+ ```
51
+
52
+ ## 通信設定
53
+
54
+ ### PLCの通信設定
55
+
56
+ plc以下のプロジェクトファイルを開いてIPアドレスなど変更します。
57
+ 変更後PLCに設定とプログラムを書き込みます。
58
+
59
+ ### エスカレーターの設定
60
+
61
+ config/plc.ymlファイルで設定します。
62
+
63
+ 現在はiQ-Rのみの対応なので:host: 192.168.0.1の行でPLCに設定したIPアドレスを指定するのみで接続できます。
64
+
65
+ ```
66
+ :plc:
67
+ :cpu: iq-r
68
+ :protocol: mc_protocol
69
+ :host: 192.168.0.1
70
+ :port: 5007
71
+ :program_area: d10000
72
+ :interaction_area: d9998
73
+ ```
74
+
75
+ ## エスカレータープログラム作成
76
+
77
+ エスカレーターのプログラムはasm以下にあります。
78
+ 現在はmain.escファイルから生成します。
79
+
80
+ main.escを編集しプログラムを作成します。
81
+ PLC側の実装がまだ進んでいないので実行できるニーモニックはLD、OUT、AND、ORとその反転程度です。
82
+
83
+ ニーモニックについては[Wiki](https://github.com/ito-soft-design/escalator/wiki/mnemonic)の方を参照してください。
84
+
85
+ ```
86
+ LD X0
87
+ OUT Y0
88
+ LD X1
89
+ OR M0
90
+ ANI X2
91
+ OUT M0
92
+ END
93
+ ```
94
+
95
+ ## プログラムの転送
96
+
97
+ エスカレータープログラムをplcに転送するはrakeコマンドを使用します。
98
+ 転送後プログラムが実行されます。
99
+
100
+ ```
101
+ $ rake plc
102
+ ```
103
+
104
+
105
+ # エスカレーターに関する情報
106
+
107
+ - [一往確認日記 [escalator]](http://diary.itosoft.com/?category=escalator)
108
+ - [Wiki](https://github.com/ito-soft-design/escalator/wiki/)
109
+
8
110
  # ライセンス
9
111
 
10
112
  MIT
data/lib/escalator/asm.rb CHANGED
@@ -32,7 +32,7 @@ module Escalator
32
32
  BIG_ENDIAN = 1
33
33
 
34
34
  def initialize source, endian = nil
35
- @endian = endian || LITTLE_ENDIAN
35
+ @endian = endian || BIG_ENDIAN
36
36
  @lines = []
37
37
  address = 0
38
38
  source.each_line do | line|
data/lib/escalator/cli.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # The MIT License (MIT)
2
- #
2
+ #
3
3
  # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
- #
4
+ #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining
6
6
  # a copy of this software and associated documentation files (the
7
7
  # "Software"), to deal in the Software without restriction, including
@@ -9,10 +9,10 @@
9
9
  # distribute, sublicense, and/or sell copies of the Software, and to
10
10
  # permit persons to whom the Software is furnished to do so, subject to
11
11
  # the following conditions:
12
- #
12
+ #
13
13
  # The above copyright notice and this permission notice shall be
14
14
  # included in all copies or substantial portions of the Software.
15
- #
15
+ #
16
16
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
17
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
18
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -28,13 +28,14 @@ include FileUtils
28
28
 
29
29
  module Escalator
30
30
  class CLI < Thor
31
-
31
+
32
32
  desc "create", "Create a new project"
33
33
  def create(name)
34
34
  template_path = File.join(Escalator_root, "template", "escalator")
35
35
  cp_r template_path, name
36
-
36
+ temlate_plc_path = File.join(Escalator_root, "plc")
37
+ cp_r temlate_plc_path, name
37
38
  end
38
-
39
+
39
40
  end
40
41
  end
@@ -0,0 +1,84 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ require 'active_support/core_ext/string/inflections'
25
+ require "escalator/protocol/protocol"
26
+ require 'escalator/uploader'
27
+ require 'yaml'
28
+
29
+ include Escalator::Protocol::Mitsubishi
30
+
31
+ module Escalator
32
+
33
+
34
+ class EscalatorConfig
35
+
36
+ def self.default
37
+ @config ||= begin
38
+ config_path = File.join "config", "plc.yml"
39
+ h = YAML.load(File.read(config_path)) if File.exist?(config_path)
40
+ new h || {}
41
+ end
42
+ end
43
+
44
+ def initialize options={}
45
+ default = {input: "asm/main.asm", output: "build/main.hex"}
46
+ @config = options.merge default
47
+ end
48
+
49
+ def protocol
50
+ @protocol ||= begin
51
+ plc_info = @config[:plc]
52
+ p = eval("#{plc_info[:protocol].camelize}.new")
53
+ p.host = plc_info[:host] if plc_info[:host]
54
+ p.port = plc_info[:port] if plc_info[:port]
55
+ p.log_level = plc_info[:log_level] if plc_info[:log_level]
56
+ p
57
+ rescue
58
+ nil
59
+ end
60
+ end
61
+
62
+ def uploader
63
+ @uploader ||= begin
64
+ u = Uploader.new
65
+ u.protocol = self.protocol
66
+ u.program_area = u.protocol.device_by_name(@config[:plc][:program_area]) if @config[:plc] && @config[:plc][:program_area]
67
+ u.interaction_area = u.protocol.device_by_name(@config[:plc][:interaction_area]) if @config[:plc] && @config[:plc][:interaction_area]
68
+ u
69
+ end
70
+ end
71
+
72
+ def method_missing(name, *args)
73
+ name = name.to_s unless name.is_a? String
74
+ case name.to_s
75
+ when /(.*)=$/
76
+ @config[$1.to_sym] = args.first
77
+ else
78
+ @config[name.to_sym]
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -1,14 +1,57 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
1
24
  module Escalator
2
25
 
3
26
  class IntelHex
4
27
 
5
28
  attr_reader :offset, :codes
6
29
 
30
+ def self.load path
31
+ offset = 0
32
+ codes = []
33
+ File.read(path).lines.each do |l|
34
+ case l[7,2]
35
+ when "00"
36
+ address = l[3,4].to_i(16)
37
+ offset ||= address
38
+ l.chomp[9..-3].each_char.each_slice(2).each_with_index do |pair, i|
39
+ codes[address - offset + i] = pair.join("").to_i(16)
40
+ end
41
+ end
42
+ end
43
+ new codes, offset
44
+ end
45
+
7
46
  def initialize codes, offset = 0
8
47
  @offset = offset
9
48
  @codes = codes
10
49
  end
11
50
 
51
+ def write_to path
52
+ File.write(path, dump)
53
+ end
54
+
12
55
  def dump
13
56
  addr = offset
14
57
  lines = []
@@ -31,15 +74,6 @@ module Escalator
31
74
  lines.join("\n")
32
75
  end
33
76
 
34
- def gxworks_memory_image
35
- lines = []
36
- @codes.each_slice(8) do |line_codes|
37
- lines << line_codes.join("\t")
38
- end
39
-
40
- lines.join("\n")
41
- end
42
-
43
77
  end
44
78
 
45
79
  end
@@ -0,0 +1,228 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module Escalator
25
+ module Protocol
26
+ module Mitsubishi
27
+
28
+ class McProtocol < Protocol
29
+
30
+ def initialize options={}
31
+ super
32
+ @host = options[:host] || "192.168.0.1"
33
+ @port = options[:port] || 5010
34
+ end
35
+
36
+ def open
37
+ @socket ||= TCPSocket.open(@host, @port)
38
+ end
39
+
40
+ def close
41
+ @socket.close if @socket
42
+ @socket = nil
43
+ end
44
+
45
+ def get_bit_from_device device
46
+ device = device_by_name device
47
+ get_bits_from_device(1, device).first
48
+ end
49
+
50
+ def get_bits_from_device count, device
51
+ device = device_by_name device
52
+ packet = make_packet(body_for_get_bits_from_deivce(count, device))
53
+ @logger.debug("> #{dump_packet packet}")
54
+ open
55
+ @socket.write(packet.pack("c*"))
56
+ @socket.flush
57
+ res = receive
58
+ bits = []
59
+ count.times do |i|
60
+ v = res[11 + i / 2]
61
+ if i % 2 == 0
62
+ bits << ((v >> 4) != 0)
63
+ else
64
+ bits << ((v & 0xf) != 0)
65
+ end
66
+ end
67
+ @logger.debug("get #{device.name} => #{bits}")
68
+ bits
69
+ end
70
+
71
+ def set_bits_to_device bits, device
72
+ device = device_by_name device
73
+ packet = make_packet(body_for_set_bits_to_device(bits, device))
74
+ @logger.debug("> #{dump_packet packet}")
75
+ open
76
+ @socket.write(packet.pack("c*"))
77
+ @socket.flush
78
+ res = receive
79
+ @logger.debug("set #{bits} to:#{device.name}")
80
+ end
81
+
82
+
83
+ def get_word_from_device device
84
+ device = device_by_name device
85
+ get_words_from_device(1, device).first
86
+ end
87
+
88
+ def get_words_from_device(count, device)
89
+ device = device_by_name device
90
+ packet = make_packet(body_for_get_words_from_deivce(count, device))
91
+ @logger.debug("> #{dump_packet packet}")
92
+ open
93
+ @socket.write(packet.pack("c*"))
94
+ @socket.flush
95
+ res = receive
96
+ words = []
97
+ res[11, 2 * count].each_slice(2) do |pair|
98
+ words << pair.pack("c*").unpack("v").first
99
+ end
100
+ @logger.debug("get from: #{device.name} => #{words}")
101
+ words
102
+ end
103
+
104
+ def set_words_to_device words, device
105
+ device = device_by_name device
106
+ packet = make_packet(body_for_set_words_to_device(words, device))
107
+ @logger.debug("> #{dump_packet packet}")
108
+ open
109
+ @socket.write(packet.pack("c*"))
110
+ @socket.flush
111
+ res = receive
112
+ @logger.debug("set #{words} to: #{device.name}")
113
+ end
114
+
115
+
116
+ def device_by_name name
117
+ case name
118
+ when String
119
+ QDevice.new name
120
+ else
121
+ # it may be already QDevice
122
+ name
123
+ end
124
+ end
125
+
126
+
127
+ def receive
128
+ res = []
129
+ len = 0
130
+ begin
131
+ Timeout.timeout(0.1) do
132
+ while true
133
+ c = @socket.read(1)
134
+ next if c.nil? || c == ""
135
+
136
+ res << c.bytes.first
137
+ len = res[7] + res[8] << 8 if res.length >= 9
138
+ break if (len + 9 == res.length)
139
+ end
140
+ end
141
+ rescue Timeout::Error
142
+ end
143
+ @logger.debug("< #{dump_packet res}")
144
+ res
145
+ end
146
+
147
+ private
148
+
149
+ def make_packet body
150
+ header = [0x50, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00]
151
+ header[7..8] = data_for_short(body.length + 2)
152
+ header + body
153
+ end
154
+
155
+ def body_for_get_bit_from_deivce device
156
+ body_for_get_bits_from_deivce 1, device
157
+ end
158
+
159
+ def body_for_get_bits_from_deivce count, device
160
+ body_for_get_words_from_deivce count, device, false
161
+ end
162
+
163
+ def body_for_get_words_from_deivce count, device, word = true
164
+ body = [0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00]
165
+ body[2] = 1 unless word
166
+ body[4..7] = data_for_device(device)
167
+ body[8..9] = data_for_short count
168
+ body
169
+ end
170
+
171
+
172
+ def body_for_set_bits_to_device bits, device
173
+ body = [0x01, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00]
174
+ d = device
175
+ bits = [bits] unless bits.is_a? Array
176
+ bits.each_slice(2) do |pair|
177
+ body << (pair.first ? 0x10 : 0x00)
178
+ body[-1] |= (pair.last ? 0x1 : 0x00) if pair.size == 2
179
+ d = d.next_device
180
+ end
181
+ body[4..7] = data_for_device(device)
182
+ body[8..9] = data_for_short bits.size
183
+ body
184
+ end
185
+ alias :body_for_set_bit_to_device :body_for_set_bits_to_device
186
+
187
+ def body_for_set_words_to_device words, device
188
+ body = [0x01, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00]
189
+ d = device
190
+ words = [words] unless words.is_a? Array
191
+ words.each do |v|
192
+ body += data_for_short v
193
+ d = d.next_device
194
+ end
195
+ body[4..7] = data_for_device(device)
196
+ body[8..9] = data_for_short words.size
197
+ body
198
+ end
199
+
200
+ def data_for_device device
201
+ a = data_for_int device.number
202
+ a[3] = device.suffix_code
203
+ a
204
+ end
205
+
206
+ def data_for_short value
207
+ [value].pack("v").unpack("c*")
208
+ end
209
+
210
+ def data_for_int value
211
+ [value].pack("V").unpack("c*")
212
+ end
213
+
214
+ def dump_packet packet
215
+ a = []
216
+ len = packet.length
217
+ bytes = packet.dup
218
+ len.times do |i|
219
+ a << ("0" + bytes[i].to_s(16))[-2, 2]
220
+ end
221
+ "[" + a.join(", ") + "]"
222
+ end
223
+
224
+ end
225
+
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,36 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ $:.unshift File.dirname(__FILE__)
25
+
26
+ module Escalator
27
+ module Protocol
28
+
29
+ require 'socket'
30
+ require 'logger'
31
+ require 'timeout'
32
+ require 'qdevice'
33
+ require 'mc_protocol'
34
+
35
+ end
36
+ end
@@ -0,0 +1,114 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module Escalator
25
+ module Protocol
26
+ module Mitsubishi
27
+
28
+ class QDevice
29
+
30
+ attr_reader :suffix, :number
31
+
32
+ SUFFIXES = %w(SM SD X Y M L F V B D W TS TC TN SS SC SN CS CC CN SB SW S DX DY Z R ZR)
33
+ SUFFIX_CODES = [0x91, 0xa9, 0x9c, 0x9d, 0x90, 0x92, 0x93, 0x94, 0xa0, 0xa8, 0xb4, 0xc1, 0xc0, 0xc2, 0xc7, 0xc6, 0xc8, 0xc4, 0xc3, 0xc5, 0xa1, 0xb5, 0x98, 0xa2, 0xa3, 0xcc ,0xaf, 0xb0]
34
+
35
+ def initialize a, b = nil
36
+ case a
37
+ when Array
38
+ case a.size
39
+ when 4
40
+ @suffix = suffix_for_code(a[3])
41
+ @number = ((a[2] << 8 | a[1]) << 8) | a[0]
42
+ end
43
+ when String
44
+ if b
45
+ @suffix = a.upcase
46
+ @number = b
47
+ else
48
+ if a.length == 12
49
+ @suffix = [a[0,2].to_i(16), a[2,2].to_i(16)].pack "c*"
50
+ @suffix.strip!
51
+ @number = a[4,8].to_i(16)
52
+ elsif /(X|Y)(.+)/i =~ a
53
+ @suffix = $1.upcase
54
+ @number = $2.to_i(p_adic_number)
55
+ else
56
+ /(M|L|S|B|F|T|C|D|W|R)(.+)/i =~ a
57
+ @suffix = $1.upcase
58
+ @number = $2.to_i(p_adic_number)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def p_adic_number
65
+ case @suffix
66
+ when "X", "Y", "B", "W", "SB", "SW", "DX", "DY", "ZR"
67
+ 16
68
+ else
69
+ 10
70
+ end
71
+ end
72
+
73
+ def name
74
+ @suffix + @number.to_s(p_adic_number).upcase
75
+ end
76
+
77
+ def next_device
78
+ d = self.class.new @suffix, @number + 1
79
+ d
80
+ end
81
+
82
+ def bit_device?
83
+ case @suffix
84
+ when "SM", "X", "Y", "M", "L", "F", "V", "B",
85
+ "TS", "TC", "SS", "SC","CS", "CC", "SB", "S", "DX", "DY"
86
+ true
87
+ else
88
+ false
89
+ end
90
+ end
91
+
92
+ def suffix_for_code code
93
+ index = SUFFIX_CODES.index code
94
+ index ? SUFFIXES[index] : nil
95
+ end
96
+
97
+ def suffix_code
98
+ index = SUFFIXES.index suffix
99
+ index ? SUFFIX_CODES[index] : 0
100
+ end
101
+
102
+ def + value
103
+ QDevice.new self.suffix, self.number + value
104
+ end
105
+
106
+ def - value
107
+ QDevice.new self.suffix, [self.number - value, 0].max
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,78 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ $:.unshift File.dirname(__FILE__)
25
+
26
+ module Escalator
27
+ module Protocol
28
+
29
+ class Protocol
30
+
31
+ attr_accessor :host, :port, :log_level
32
+
33
+ def initialize options={}
34
+ @logger = Logger.new(STDOUT)
35
+ @logger.level = options[:log_level] || Logger::INFO
36
+ end
37
+
38
+ def log_level= level
39
+ @log_level = level.is_a?(String) ? level.to_sym : level
40
+ case @log_level
41
+ when :debug
42
+ @logger.level = Logger::DEBUG
43
+ when :error
44
+ @logger.level = Logger::ERROR
45
+ when :fatal
46
+ @logger.level = Logger::FATAL
47
+ when :info
48
+ @logger.level = Logger::INFO
49
+ when :unknown
50
+ @logger.level = Logger::UNKNOWN
51
+ when :warn
52
+ @logger.level = Logger::WARN
53
+ end
54
+ end
55
+
56
+ # abstract methods
57
+
58
+ def open; end
59
+ def close; end
60
+
61
+ def get_bit_from_device device; end
62
+ def get_bits_from_device count, device; end
63
+ def set_bits_to_device bits, device; end
64
+ def set_bit_to_device bit, device; set_bits_to_device bit, device; end
65
+
66
+ def get_word_from_device device; end
67
+ def get_words_from_device(count, device); end
68
+ def set_words_to_device words, device; end
69
+ def set_word_to_device word, device; set_words_to_device word, device; end
70
+
71
+ def device_by_name name; nil; end
72
+
73
+ end
74
+
75
+ end
76
+ end
77
+
78
+ require 'mitsubishi/mitsubishi'
@@ -23,9 +23,11 @@
23
23
 
24
24
  Escalator_root = File.expand_path(File.join(File.dirname(__FILE__), "../../../"))
25
25
 
26
+ require "escalator/config"
26
27
  require 'escalator/asm'
27
28
  require 'escalator/intel_hex'
28
29
  require 'rake/loaders/makefile'
30
+ require 'fileutils'
29
31
 
30
32
  #directory "build"
31
33
 
@@ -61,6 +63,7 @@ rule %r{^build/.+\.hex} => ['%{^build,asm}X.esc'] do |t|
61
63
  $stderr = STDERR
62
64
  end
63
65
  stream = StringIO.new
66
+ puts "hex #{t.source}"
64
67
  asm = Escalator::Asm.new File.read(t.source)
65
68
  hex = Escalator::IntelHex.new asm.codes
66
69
  File.write(t.name, hex.dump)
@@ -77,51 +80,28 @@ rule %r{^build/.+\.gxwm} => ['%{^build,asm}X.esc'] do |t|
77
80
  $stderr = STDERR
78
81
  end
79
82
  stream = StringIO.new
83
+ puts "gxwm #{t.source}"
80
84
  asm = Escalator::Asm.new File.read(t.source)
81
85
  hex = Escalator::IntelHex.new asm.codes
82
86
  File.write(t.name, hex.gxworks_memory_image)
83
87
  end
84
88
 
85
- =begin
86
- desc "Assemble codes"
87
- task :asm do
88
- # @refer: https://github.com/rsutphin/handbrake.rb/issues/1
89
- begin
90
- $stderr = File.open('hb.log', 'w')
91
- $stdout = $stderr
92
- mkdir_p "build"
93
- ensure
94
- $stdout = STDOUT
95
- $stderr = STDERR
96
- end
97
- dir = "./asm"
98
- stream = StringIO.new
99
- sources = Dir.glob("asm/**/*.esc").each do |filename|
100
- puts "asm #{filename}"
101
- asm = Escalator::Asm.new File.read(filename)
102
- dst = File.join("build", File.basename(filename, ".*") + ".lst")
103
- File.write(dst, asm.dump_line)
104
- end
89
+ desc "Clean all generated files."
90
+ task :clean do
91
+ FileUtils.rm_r "build"
105
92
  end
106
93
 
107
- desc "Make hex codes"
108
- task :hex do
109
- begin
110
- $stderr = File.open('hb.log', 'w')
111
- $stdout = $stderr
112
- mkdir_p "build"
113
- ensure
114
- $stdout = STDOUT
115
- $stderr = STDERR
116
- end
117
- dir = "./asm"
118
- stream = StringIO.new
119
- sources = Dir.glob("asm/**/*.esc").each do |filename|
120
- puts "hex #{filename}"
121
- asm = Escalator::Asm.new File.read(filename)
122
- dst = File.join("build", File.basename(filename, ".*") + ".hex")
123
- hex = Escalator::IntelHex.new asm.codes
124
- File.write(dst, hex.dump)
125
- end
94
+ @config = Escalator::EscalatorConfig.default
95
+
96
+ task :upload => @config.output do
97
+ u = @config.uploader
98
+ u.source = @config.output
99
+ u.upload
100
+ puts "upload #{u.source}"
126
101
  end
127
- =end
102
+
103
+ desc "Install program to PLCs."
104
+ task :plc => :upload do
105
+ end
106
+
107
+ task :default => %w(build/main.lst build/main.hex build/main.gxwm)
@@ -0,0 +1,114 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module Escalator
25
+
26
+ class Uploader
27
+
28
+ attr_accessor :protocol
29
+ attr_accessor :program_area, :interaction_area
30
+ attr_accessor :source
31
+ attr_accessor :data
32
+
33
+ def initialize options={}
34
+ @protocol = options[:protocol] if options[:protocol]
35
+ @program_area = options[:program_area] if options[:program_area]
36
+ @interaction_area = options[:interaction_area] if options[:interaction_area]
37
+ end
38
+
39
+ STOP_PLC_FLAG = 2 # bit 1
40
+ CLEAR_PROGRAM_FLAG = 4 # bit 2 require bit 1 on
41
+
42
+ CYCLE_RUN_FLAG = 2
43
+
44
+
45
+ def upload
46
+ # stop plc
47
+ stop_plc
48
+ clear_program
49
+ write_program
50
+ run_plc
51
+ end
52
+
53
+ def data
54
+ @data ||= begin
55
+ hex = IntelHex.load @source
56
+ hex.codes
57
+ end
58
+ end
59
+
60
+ def word_data
61
+ data.each_slice(2).map do |pair|
62
+ pair.pack("c*").unpack("n*")
63
+ end.flatten
64
+ end
65
+
66
+ private
67
+
68
+ def to_plc_status
69
+ @to_plc_status ||= interaction_area
70
+ end
71
+
72
+ def from_plc_status
73
+ @from_plc_status ||= interaction_area.next_device
74
+ end
75
+
76
+
77
+ def stop_plc
78
+ @protocol.set_word_to_device STOP_PLC_FLAG, to_plc_status
79
+ Timeout.timeout(5) do
80
+ v = @protocol.get_word_from_device from_plc_status
81
+ break if (v & STOP_PLC_FLAG) != 0
82
+ sleep 0.1
83
+ end
84
+ end
85
+
86
+ def clear_program
87
+ @protocol.set_word_to_device STOP_PLC_FLAG | CLEAR_PROGRAM_FLAG, to_plc_status
88
+ Timeout.timeout(5) do
89
+ v = @protocol.get_word_from_device from_plc_status
90
+ break if (v & CLEAR_PROGRAM_FLAG) != 0
91
+ sleep 0.1
92
+ end
93
+ @protocol.set_word_to_device STOP_PLC_FLAG, to_plc_status
94
+ end
95
+
96
+ def run_plc
97
+ @protocol.set_word_to_device 0, to_plc_status
98
+ Timeout.timeout(5) do
99
+ v = @protocol.get_word_from_device from_plc_status
100
+ break if (v & CYCLE_RUN_FLAG) != 0
101
+ sleep 0.1
102
+ end
103
+ end
104
+
105
+ def write_program
106
+ word_data.each_slice(2*1024) do |chunk|
107
+ @protocol.set_words_to_device chunk, @program_area
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+
114
+ end
@@ -22,5 +22,5 @@
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
24
  module Escalator
25
- VERSION = "0.1.2"
25
+ VERSION = "0.2.0"
26
26
  end
data/lib/escalator.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # The MIT License (MIT)
2
- #
2
+ #
3
3
  # Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
- #
4
+ #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining
6
6
  # a copy of this software and associated documentation files (the
7
7
  # "Software"), to deal in the Software without restriction, including
@@ -9,10 +9,10 @@
9
9
  # distribute, sublicense, and/or sell copies of the Software, and to
10
10
  # permit persons to whom the Software is furnished to do so, subject to
11
11
  # the following conditions:
12
- #
12
+ #
13
13
  # The above copyright notice and this permission notice shall be
14
14
  # included in all copies or substantial portions of the Software.
15
- #
15
+ #
16
16
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
17
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
18
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -25,6 +25,7 @@ require "escalator/version"
25
25
  require "escalator/cli"
26
26
  require "escalator/asm"
27
27
  require "escalator/intel_hex"
28
+ require "escalator/config"
28
29
 
29
30
  Escalator_root = File.expand_path(File.join(File.dirname(__FILE__), ".."))
30
31
 
@@ -32,4 +33,4 @@ module Escalator
32
33
  end
33
34
 
34
35
 
35
- Escalator::CLI.start
36
+ Escalator::CLI.start
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 ITO SOFT DESIGN Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
Binary file
@@ -0,0 +1,7 @@
1
+ :plc:
2
+ :cpu: iq-r
3
+ :protocol: mc_protocol
4
+ :ip: 192.168.0.1
5
+ :port: 5007
6
+ :program_area: d10000
7
+ :interaction_area: d9998
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: escalator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.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: 2016-10-03 00:00:00.000000000 Z
11
+ date: 2016-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -76,14 +76,23 @@ files:
76
76
  - lib/escalator.rb
77
77
  - lib/escalator/asm.rb
78
78
  - lib/escalator/cli.rb
79
+ - lib/escalator/config.rb
79
80
  - lib/escalator/intel_hex.rb
81
+ - lib/escalator/protocol/mitsubishi/mc_protocol.rb
82
+ - lib/escalator/protocol/mitsubishi/mitsubishi.rb
83
+ - lib/escalator/protocol/mitsubishi/qdevice.rb
84
+ - lib/escalator/protocol/protocol.rb
80
85
  - lib/escalator/tasks/build.rb
86
+ - lib/escalator/uploader.rb
81
87
  - lib/escalator/version.rb
88
+ - plc/mitsubishi/iq-r/r08/LICENSE
89
+ - plc/mitsubishi/iq-r/r08/r08.gx3
82
90
  - sample/escalator/sample1.esc
83
91
  - sample/escalator/sample2.esc
84
92
  - sample/escalator/sample2.png
85
93
  - template/escalator/Rakefile
86
94
  - template/escalator/asm/main.esc
95
+ - template/escalator/config/plc.yml
87
96
  homepage: https://github.com/ito-soft-design/escalator/wiki
88
97
  licenses:
89
98
  - MIT