mc_protocol_e 0.1.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 +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +35 -0
- data/LICENSE.txt +21 -0
- data/README.md +105 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/mc_protocol_e.rb +4 -0
- data/lib/mc_protocol_e/client.rb +83 -0
- data/lib/mc_protocol_e/frame_1e/access_route.rb +32 -0
- data/lib/mc_protocol_e/frame_1e/batch_read_in_word.rb +37 -0
- data/lib/mc_protocol_e/frame_1e/batch_write_in_word.rb +54 -0
- data/lib/mc_protocol_e/frame_1e/device_range.rb +49 -0
- data/lib/mc_protocol_e/frame_1e/request.rb +84 -0
- data/lib/mc_protocol_e/frame_1e/response.rb +54 -0
- data/lib/mc_protocol_e/frame_3e/access_route.rb +66 -0
- data/lib/mc_protocol_e/frame_3e/base_command.rb +27 -0
- data/lib/mc_protocol_e/frame_3e/batch_read_in_word.rb +40 -0
- data/lib/mc_protocol_e/frame_3e/batch_write_in_word.rb +52 -0
- data/lib/mc_protocol_e/frame_3e/device_range.rb +47 -0
- data/lib/mc_protocol_e/frame_3e/error_info.rb +32 -0
- data/lib/mc_protocol_e/frame_3e/request.rb +89 -0
- data/lib/mc_protocol_e/frame_3e/response.rb +57 -0
- data/lib/mc_protocol_e/version.rb +5 -0
- data/mc_protocol_e.gemspec +42 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b36cc1712b0d48b344b81c44b982cb5f5d55dc0bb392416cd39ef74ea625943a
|
4
|
+
data.tar.gz: b87f147713b5ecb2170ae12858d59fef773cec849a3d34858eae14b873b20a2b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf757452f3bf39db9311d5de2a1da4538d599e8b37c5e69da93cd5220b09e8a7e616e8b1906684cec05920c54ee88b7a95858da005106e051380dcc4d3f2b6a9
|
7
|
+
data.tar.gz: b0905894fafd044aa1c01068d1f2f82d4d7c9569d15ecda2e4cdedf6929bcb9cad6fd7b8d42a355d0ff5d323054e8ec4028b1c483ab2295d1e80d3b55126d6c3
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at yusuke.saito@smartscape.co.jp. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
mc_protocol_e (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.3)
|
10
|
+
rake (10.5.0)
|
11
|
+
rspec (3.8.0)
|
12
|
+
rspec-core (~> 3.8.0)
|
13
|
+
rspec-expectations (~> 3.8.0)
|
14
|
+
rspec-mocks (~> 3.8.0)
|
15
|
+
rspec-core (3.8.0)
|
16
|
+
rspec-support (~> 3.8.0)
|
17
|
+
rspec-expectations (3.8.2)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.8.0)
|
20
|
+
rspec-mocks (3.8.0)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.8.0)
|
23
|
+
rspec-support (3.8.0)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
bundler (~> 2.0)
|
30
|
+
mc_protocol_e!
|
31
|
+
rake (~> 10.0)
|
32
|
+
rspec (~> 3.0)
|
33
|
+
|
34
|
+
BUNDLED WITH
|
35
|
+
2.0.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 yusuke.saito
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# McProtocolE
|
2
|
+
|
3
|
+
McProtocolE is an implementation of MC protocol client over ethernet by Ruby. MC protocol is communication protocol to access MELSEC (PLC). See below for details.
|
4
|
+
|
5
|
+
https://dl.mitsubishielectric.com/dl/fa/document/manual/plc/sh080008/sh080008x.pdf
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'mc_protocol_e'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install mc_protocol_e
|
22
|
+
|
23
|
+
## Example
|
24
|
+
Below is an example of use for this library.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'optparse'
|
28
|
+
require_relative 'frame_1e/request'
|
29
|
+
require_relative 'frame_1e/access_route'
|
30
|
+
require_relative 'frame_1e/device_range'
|
31
|
+
require_relative 'frame_3e/request'
|
32
|
+
require_relative 'frame_3e/access_route'
|
33
|
+
require_relative 'frame_3e/device_range'
|
34
|
+
|
35
|
+
opts = ARGV.getopts("h:p:f:", "rw:", "device_num:", "device_points:", "values:")
|
36
|
+
raise ArgumentError, "required option is not specified" unless %w[h p f device_num device_points].all? {|key| opts[key] }
|
37
|
+
|
38
|
+
req =
|
39
|
+
case opts["f"]
|
40
|
+
when "1e"
|
41
|
+
case opts["rw"]
|
42
|
+
when "r"
|
43
|
+
McProtocolE::Frame1e::Request.batch_read_in_word(
|
44
|
+
access_route: McProtocolE::Frame1e::AccessRoute.own_station,
|
45
|
+
wait_sec: 3,
|
46
|
+
device_range: McProtocolE::Frame1e::DeviceRange.data_register(device_num: opts["device_num"].to_i, device_points: opts["device_points"].to_i)
|
47
|
+
)
|
48
|
+
when "w"
|
49
|
+
raise ArgumentError, "values is required" unless opts["values"]
|
50
|
+
|
51
|
+
McProtocolE::Frame1e::Request.batch_write_in_word(
|
52
|
+
access_route: McProtocolE::Frame1e::AccessRoute.own_station,
|
53
|
+
wait_sec: 3,
|
54
|
+
device_range: McProtocolE::Frame1e::DeviceRange.data_register(device_num: opts["device_num"].to_i, device_points: opts["device_points"].to_i),
|
55
|
+
values: opts["values"].split(",").map(&:to_i),
|
56
|
+
)
|
57
|
+
else
|
58
|
+
raise ArgumentError
|
59
|
+
end
|
60
|
+
when "3e"
|
61
|
+
case opts["rw"]
|
62
|
+
when "r"
|
63
|
+
McProtocolE::Frame3e::Request.batch_read_in_word(
|
64
|
+
access_route: McProtocolE::Frame3e::AccessRoute.own_station,
|
65
|
+
wait_sec: 3,
|
66
|
+
device_range: McProtocolE::Frame3e::DeviceRange.data_register(device_num: opts["device_num"].to_i, device_points: opts["device_points"].to_i)
|
67
|
+
)
|
68
|
+
when "w"
|
69
|
+
raise ArgumentError, "values is required" unless opts["values"]
|
70
|
+
|
71
|
+
McProtocolE::Frame3e::Request.batch_write_in_word(
|
72
|
+
access_route: McProtocolE::Frame3e::AccessRoute.own_station,
|
73
|
+
wait_sec: 3,
|
74
|
+
device_range: McProtocolE::Frame3e::DeviceRange.data_register(device_num: opts["device_num"].to_i, device_points: opts["device_points"].to_i),
|
75
|
+
values: opts["values"].split(",").map(&:to_i),
|
76
|
+
)
|
77
|
+
else
|
78
|
+
raise ArgumentError
|
79
|
+
end
|
80
|
+
else
|
81
|
+
raise ArgumentError
|
82
|
+
end
|
83
|
+
|
84
|
+
McProtocolE::Client.start(address: opts["h"], port: opts["p"].to_i) {|client|
|
85
|
+
pp req.to_b
|
86
|
+
res = client.request(req)
|
87
|
+
pp res.map {|raw| raw.unpack("s").first }
|
88
|
+
}
|
89
|
+
|
90
|
+
```
|
91
|
+
|
92
|
+
## Limitations
|
93
|
+
|
94
|
+
* Support only TCP.
|
95
|
+
* Support only binary code.
|
96
|
+
* Support only below frames.
|
97
|
+
* 3E frame
|
98
|
+
* 1E frame
|
99
|
+
* Support only below commands.
|
100
|
+
* Batch read in word units (0401)
|
101
|
+
* Batch write in word units (1401)
|
102
|
+
|
103
|
+
## License
|
104
|
+
|
105
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "mc_protocol_e"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
# This client shows a MC protocol client.
|
7
|
+
class Client
|
8
|
+
|
9
|
+
class NotStartedError < StandardError; end
|
10
|
+
|
11
|
+
DEFAULT_OPEN_TIMEOUT = 3
|
12
|
+
DEFAULT_READ_TIMEOUT = 3
|
13
|
+
|
14
|
+
# Constructor.
|
15
|
+
# @param [String] address server IP address
|
16
|
+
# @param [Integer] port server port number
|
17
|
+
# @param [Numeric] open_timeout second of open timeout
|
18
|
+
# @param [Numeric] read_timeout second of read timeout
|
19
|
+
def initialize(address:, port:, open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT)
|
20
|
+
@address = address
|
21
|
+
@port = port
|
22
|
+
@open_timeout = open_timeout
|
23
|
+
@read_timeout = read_timeout
|
24
|
+
@socket = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Starts MC protocol communication.
|
28
|
+
# @param [String] address IP address
|
29
|
+
# @param [Integer] port port number
|
30
|
+
# @param [Numeric] open_timeout second of open timeout
|
31
|
+
# @param [Numeric] read_timeout second of read timeout
|
32
|
+
# @yield [client] MC protocol client
|
33
|
+
def self.start(address:, port:, open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT, &block)
|
34
|
+
client = new(address: address, port: port, open_timeout: open_timeout, read_timeout: read_timeout)
|
35
|
+
|
36
|
+
if block_given?
|
37
|
+
client.start(&block)
|
38
|
+
else
|
39
|
+
client
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Closes MC protocol communication.
|
44
|
+
def close
|
45
|
+
socket.close if started?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sends request.
|
49
|
+
# @param [Request] req request
|
50
|
+
# @return [Object] response
|
51
|
+
# @raise [NotStartedError] when not started
|
52
|
+
def request(req)
|
53
|
+
raise NotStartedError, "not started" unless started?
|
54
|
+
|
55
|
+
req.exec(socket, read_timeout)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Starts MC protocol communication.
|
59
|
+
def start
|
60
|
+
@socket = Socket.tcp(address, port, connect_timeout: open_timeout) unless started?
|
61
|
+
|
62
|
+
if block_given?
|
63
|
+
begin
|
64
|
+
yield self
|
65
|
+
ensure
|
66
|
+
close
|
67
|
+
end
|
68
|
+
else
|
69
|
+
self
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns true if communication has started.
|
74
|
+
def started?
|
75
|
+
socket && !socket.closed?
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_reader :address, :port, :open_timeout, :read_timeout, :socket
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module McProtocolE
|
4
|
+
module Frame1e
|
5
|
+
# This class shows a access route.
|
6
|
+
class AccessRoute
|
7
|
+
|
8
|
+
OWN_PC_NUM = 255
|
9
|
+
|
10
|
+
attr_reader :pc_num
|
11
|
+
|
12
|
+
# Constructor.
|
13
|
+
# @param [Integer] pc_num PC number
|
14
|
+
def initialize(pc_num:)
|
15
|
+
@pc_num = pc_num
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns instance of own station.
|
19
|
+
# @return [AccessRoute] a access route to own station
|
20
|
+
def self.own_station
|
21
|
+
new(pc_num: OWN_PC_NUM)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns binary string.
|
25
|
+
# @return [String] binary string
|
26
|
+
def to_b
|
27
|
+
[pc_num].pack("v")[0]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame1e
|
7
|
+
# This class shows a command to batch read in word.
|
8
|
+
class BatchReadInWord
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
REQUEST_HEADER = "\x01".b
|
12
|
+
RESPONSE_HEADER = "\x81".b
|
13
|
+
|
14
|
+
attr_reader :request_header
|
15
|
+
def_delegators :@device_range, :to_b
|
16
|
+
|
17
|
+
# Constructor.
|
18
|
+
# @param [DeviceRange] device_range device range
|
19
|
+
def initialize(device_range:)
|
20
|
+
@request_header = REQUEST_HEADER
|
21
|
+
@device_range = device_range
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns array of word.
|
25
|
+
# @param [Response] res response
|
26
|
+
# @return[Array] array of word
|
27
|
+
def parse(res)
|
28
|
+
res.data&.each_char&.each_slice(2)&.map(&:join)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :device_range
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame1e
|
7
|
+
# This class shows a command to batch read in word.
|
8
|
+
class BatchWriteInWord
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
REQUEST_HEADER = "\x03".b
|
12
|
+
RESPONSE_HEADER = "\x83".b
|
13
|
+
|
14
|
+
attr_reader :request_header
|
15
|
+
|
16
|
+
# Constructor.
|
17
|
+
# @param [DeviceRange] device_range device range
|
18
|
+
# @param [Array<Integer>] values values to write
|
19
|
+
# @raise [ArgumentError] when arguments is invalid
|
20
|
+
def initialize(device_range:, values:)
|
21
|
+
@request_header = REQUEST_HEADER
|
22
|
+
@device_range = device_range
|
23
|
+
@values = values
|
24
|
+
|
25
|
+
validate
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns array of word.
|
29
|
+
# @param [Response] res response
|
30
|
+
# @return[Array] array of word
|
31
|
+
def parse(res)
|
32
|
+
res.data&.each_char&.each_slice(2)&.map(&:join)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns binary string.
|
36
|
+
# @return [String] binary string
|
37
|
+
def to_b
|
38
|
+
@to_b ||= device_range.to_b + values.pack("v*")
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :device_range, :values
|
44
|
+
|
45
|
+
def validate
|
46
|
+
raise ArgumentError, "device points have to equal values size" unless device_range.size == values.size
|
47
|
+
raise ArgumentError, "all value have to be an integer" unless values.all? {|value| value.is_a?(Integer) }
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module McProtocolE
|
4
|
+
module Frame1e
|
5
|
+
# This class shows a device range.
|
6
|
+
class DeviceRange
|
7
|
+
|
8
|
+
FIXED_VALUE = "\x00".b
|
9
|
+
|
10
|
+
module DeviceCode
|
11
|
+
DATA_REGISTER = "\x20\x44".b
|
12
|
+
end
|
13
|
+
|
14
|
+
# Constructor.
|
15
|
+
# @param [String] device_code device code
|
16
|
+
# @param [Integer] device_num first device number in range
|
17
|
+
# @param [Integer] device_points number in range
|
18
|
+
def initialize(device_code:, device_num:, device_points:)
|
19
|
+
@device_code = device_code
|
20
|
+
@device_num = device_num
|
21
|
+
@device_points = device_points
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns device range of data register.
|
25
|
+
# @param [Integer] device_num first device number in range
|
26
|
+
# @param [Integer] device_points number in range
|
27
|
+
def self.data_register(device_num:, device_points:)
|
28
|
+
new(device_code: DeviceCode::DATA_REGISTER, device_num: device_num, device_points: device_points)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns range size.
|
32
|
+
# @return [Integer] range size
|
33
|
+
def size
|
34
|
+
device_points
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns binary string.
|
38
|
+
# @return [String] binary string
|
39
|
+
def to_b
|
40
|
+
[[device_num].pack("V"), device_code, [device_points].pack("v")[0], FIXED_VALUE].join
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :device_code, :device_num, :device_points
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'batch_read_in_word'
|
4
|
+
require_relative 'batch_write_in_word'
|
5
|
+
require_relative 'response'
|
6
|
+
|
7
|
+
module McProtocolE
|
8
|
+
module Frame1e
|
9
|
+
# This class shows a request of MC protocol.
|
10
|
+
class Request
|
11
|
+
|
12
|
+
DEFAULT_READ_TIMEOUT = 3
|
13
|
+
|
14
|
+
# Constructor.
|
15
|
+
# @param [AccessRoute] access_route access route
|
16
|
+
# @param [Numeric] wait_sec waiting second
|
17
|
+
# @param [Object] command command of MC protocol
|
18
|
+
def initialize(access_route:, wait_sec:, command:)
|
19
|
+
@sub_header = command.request_header
|
20
|
+
@access_route = access_route
|
21
|
+
@wait_sec = WaitSec.new(wait_sec)
|
22
|
+
@command = command
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a request to batch read.
|
26
|
+
# @param [AccessRoute] access_route access route
|
27
|
+
# @param [Numeric] wait_sec waiting second
|
28
|
+
# @param [DeviceRange] device_range device_range
|
29
|
+
def self.batch_read_in_word(access_route:, wait_sec:, device_range:)
|
30
|
+
new(access_route: access_route, wait_sec: wait_sec, command: BatchReadInWord.new(device_range: device_range))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns a request to batch write.
|
34
|
+
# @param [AccessRoute] access_route access route
|
35
|
+
# @param [Numeric] wait_sec waiting second
|
36
|
+
# @param [DeviceRange] device_range device_range
|
37
|
+
# @param [Array] values values to write
|
38
|
+
def self.batch_write_in_word(access_route:, wait_sec:, device_range:, values:)
|
39
|
+
new(access_route: access_route, wait_sec: wait_sec, command: BatchWriteInWord.new(device_range: device_range, values: values))
|
40
|
+
end
|
41
|
+
|
42
|
+
# Writes and returns a response.
|
43
|
+
# @param [IO] socket TCP socket
|
44
|
+
# @param [Integer] read_timeout read timeout second
|
45
|
+
# @return [Object] responce
|
46
|
+
def exec(socket, read_timeout = DEFAULT_READ_TIMEOUT)
|
47
|
+
socket.write(to_b)
|
48
|
+
res = Response.recv(socket, read_timeout)
|
49
|
+
command.parse(res)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns binary string.
|
53
|
+
# @return [String] binary string
|
54
|
+
def to_b
|
55
|
+
sub_header + access_route.to_b + wait_sec.to_b + command.to_b
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
attr_reader :sub_header, :access_route, :wait_sec, :command
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# This class shows waiting second for MC protocol.
|
65
|
+
class WaitSec
|
66
|
+
|
67
|
+
def initialize(sec)
|
68
|
+
@sec = sec
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_b
|
72
|
+
return @binary if @binary
|
73
|
+
|
74
|
+
quarter_sec = (sec * 4).to_i
|
75
|
+
@binary = [quarter_sec].pack("v")
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_reader :sec
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame1e
|
7
|
+
# This class shows a responce of MC protocol.
|
8
|
+
class Response
|
9
|
+
|
10
|
+
class TimeoutError < Timeout::Error; end
|
11
|
+
|
12
|
+
DEFAULT_READ_TIMEOUT = 3
|
13
|
+
MAX_RECV_LEN = 1024 * 1024
|
14
|
+
SUCCEED_CODE = 0
|
15
|
+
|
16
|
+
attr_reader :data
|
17
|
+
|
18
|
+
# Constructor.
|
19
|
+
# @param [String] raw_res binary string of response
|
20
|
+
def initialize(raw_res)
|
21
|
+
@sub_header = raw_res[0]
|
22
|
+
@code = raw_res[1].unpack1("C")
|
23
|
+
@data = raw_res[2..-1] || ""
|
24
|
+
end
|
25
|
+
|
26
|
+
# Receives responce and returns.
|
27
|
+
# @param [IO] socket TCP socket
|
28
|
+
# @param [Integer] read_timeout read timeout second
|
29
|
+
# @return [Responce] received a response
|
30
|
+
def self.recv(socket, read_timeout = DEFAULT_READ_TIMEOUT)
|
31
|
+
selected = IO.select([socket], nil, nil, read_timeout)
|
32
|
+
raise TimeoutError unless selected
|
33
|
+
|
34
|
+
raw_res = socket.recv(MAX_RECV_LEN)
|
35
|
+
new(raw_res)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns true if a command succeed.
|
39
|
+
def succeed?
|
40
|
+
code == SUCCEED_CODE
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns true if a command failed.
|
44
|
+
def failed?
|
45
|
+
!succeed?
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_reader :code
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module McProtocolE
|
4
|
+
module Frame3e
|
5
|
+
# This class shows a access route.
|
6
|
+
class AccessRoute
|
7
|
+
|
8
|
+
OWN_NETWORK_NUM = 0
|
9
|
+
OWN_PC_NUM = 255
|
10
|
+
NORMAL_UNIT_IO_NUM = 1023
|
11
|
+
NORMAL_UNIT_STATION_NUM = 0
|
12
|
+
|
13
|
+
attr_reader :network_num, :pc_num, :unit_io_num, :unit_station_num
|
14
|
+
|
15
|
+
# Constructor.
|
16
|
+
# @param [Integer] network_num network number
|
17
|
+
# @param [Integer] pc_num PC number
|
18
|
+
# @param [Integer] unit_io_num unit io number
|
19
|
+
# @param [Integer] unit_station_num unit station number
|
20
|
+
def initialize(network_num:, pc_num:, unit_io_num:, unit_station_num:)
|
21
|
+
@network_num = network_num
|
22
|
+
@pc_num = pc_num
|
23
|
+
@unit_io_num = unit_io_num
|
24
|
+
@unit_station_num = unit_station_num
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns instance from binary string.
|
28
|
+
# @param [String] raw binary string showing a access route
|
29
|
+
# @return [AccessRoute] a access route
|
30
|
+
def self.from_raw(raw)
|
31
|
+
raise ArgumentError, "raw string is not access route" unless raw && raw.size >= 5
|
32
|
+
|
33
|
+
new(
|
34
|
+
network_num: raw[0].unpack1("C"),
|
35
|
+
pc_num: raw[1].unpack1("C"),
|
36
|
+
unit_io_num: raw[2..3].unpack1("v"),
|
37
|
+
unit_station_num: raw[4].unpack1("C"),
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns instance of own station.
|
42
|
+
# @return [AccessRoute] a access route to own station
|
43
|
+
def self.own_station
|
44
|
+
new(network_num: OWN_NETWORK_NUM, pc_num: OWN_PC_NUM, unit_io_num: NORMAL_UNIT_IO_NUM, unit_station_num: NORMAL_UNIT_STATION_NUM)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns binary string.
|
48
|
+
# @return [String] binary string
|
49
|
+
def to_b
|
50
|
+
[
|
51
|
+
[network_num].pack("v")[0],
|
52
|
+
[pc_num].pack("v")[0],
|
53
|
+
[unit_io_num].pack("v"),
|
54
|
+
[unit_station_num].pack("v")[0],
|
55
|
+
].join
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns string.
|
59
|
+
# @return [String] string
|
60
|
+
def to_s
|
61
|
+
"network num: #{network_num} pc num: #{pc_num} unit io num: #{unit_io_num} unit station num: #{unit_station_num}"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'error_info'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame3e
|
7
|
+
|
8
|
+
class CommandError < StandardError; end
|
9
|
+
|
10
|
+
class BaseCommand
|
11
|
+
|
12
|
+
def initialize(command)
|
13
|
+
@command = command
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(res)
|
17
|
+
raise CommandError, ErrorInfo.new(res.code, res.data, self).to_s unless res.succeed?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :command
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_command'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame3e
|
7
|
+
# This class shows a command to batch read in word.
|
8
|
+
class BatchReadInWord < BaseCommand
|
9
|
+
|
10
|
+
COMMAND = "\x01\x04\x00\x00".b
|
11
|
+
|
12
|
+
# Constructor.
|
13
|
+
# @param [DeviceRange] device_range device range
|
14
|
+
def initialize(device_range:)
|
15
|
+
super(COMMAND)
|
16
|
+
@device_range = device_range
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns array of word.
|
20
|
+
# @param [Response] res response
|
21
|
+
# @return[Array] array of word
|
22
|
+
def parse(res)
|
23
|
+
super(res)
|
24
|
+
|
25
|
+
res.data&.each_char&.each_slice(2)&.map(&:join)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns binary string.
|
29
|
+
# @return [String] binary string
|
30
|
+
def to_b
|
31
|
+
@to_b ||= command + device_range.to_b
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :device_range
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_command'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame3e
|
7
|
+
# This class shows a command to batch write in word.
|
8
|
+
class BatchWriteInWord < BaseCommand
|
9
|
+
|
10
|
+
COMMAND = "\x01\x14\x00\x00".b
|
11
|
+
|
12
|
+
# Constructor.
|
13
|
+
# @param [DeviceRange] device_range device range
|
14
|
+
# @param [Array<Integer>] values values to write
|
15
|
+
# @raise [ArgumentError] when arguments is invalid
|
16
|
+
def initialize(device_range:, values:)
|
17
|
+
super(COMMAND)
|
18
|
+
@device_range = device_range
|
19
|
+
@values = values
|
20
|
+
|
21
|
+
validate
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns array of word.
|
25
|
+
# @param [Response] res response
|
26
|
+
# @return[Array] array of word
|
27
|
+
def parse(res)
|
28
|
+
super(res)
|
29
|
+
|
30
|
+
res.data&.each_char&.each_slice(2)&.map(&:join)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns binary string.
|
34
|
+
# @return [String] binary string
|
35
|
+
def to_b
|
36
|
+
@to_b ||= command + device_range.to_b + values.pack("v*")
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :device_range, :values
|
42
|
+
|
43
|
+
def validate
|
44
|
+
raise ArgumentError, "device points have to equal values size" unless device_range.size == values.size
|
45
|
+
raise ArgumentError, "all value have to be an integer" unless values.all? {|value| value.is_a?(Integer) }
|
46
|
+
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module McProtocolE
|
4
|
+
module Frame3e
|
5
|
+
# This class shows a device range.
|
6
|
+
class DeviceRange
|
7
|
+
|
8
|
+
module DeviceCode
|
9
|
+
DATA_REGISTER = "\xA8".b
|
10
|
+
end
|
11
|
+
|
12
|
+
# Constructor.
|
13
|
+
# @param [String] device_code device code
|
14
|
+
# @param [Integer] device_num first device number in range
|
15
|
+
# @param [Integer] device_points number in range
|
16
|
+
def initialize(device_code:, device_num:, device_points:)
|
17
|
+
@device_code = device_code
|
18
|
+
@device_num = device_num
|
19
|
+
@device_points = device_points
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns device range of data register.
|
23
|
+
# @param [Integer] device_num first device number in range
|
24
|
+
# @param [Integer] device_points number in range
|
25
|
+
def self.data_register(device_num:, device_points:)
|
26
|
+
new(device_code: DeviceCode::DATA_REGISTER, device_num: device_num, device_points: device_points)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns range size.
|
30
|
+
# @return [Integer] range size
|
31
|
+
def size
|
32
|
+
device_points
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns binary string.
|
36
|
+
# @return [String] binary string
|
37
|
+
def to_b
|
38
|
+
[[device_num].pack("V")[0..2], device_code, [device_points].pack("v")].join
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :device_code, :device_num, :device_points
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'access_route'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame3e
|
7
|
+
# This class shows a error information.
|
8
|
+
class ErrorInfo
|
9
|
+
|
10
|
+
# Constructor.
|
11
|
+
# @param [Integer] code MC protocol error code
|
12
|
+
# @param [String] data response
|
13
|
+
# @param [Object] command command
|
14
|
+
def initialize(code, data, command)
|
15
|
+
@code = code
|
16
|
+
@access_route = AccessRoute.from_raw(data[0..4])
|
17
|
+
@command = command
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns binary string.
|
21
|
+
# @return [String] binary string
|
22
|
+
def to_s
|
23
|
+
"code: #{code} command: #{command.class.name} #{access_route}"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :code, :access_route, :command
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'batch_read_in_word'
|
4
|
+
require_relative 'batch_write_in_word'
|
5
|
+
require_relative 'response'
|
6
|
+
|
7
|
+
module McProtocolE
|
8
|
+
module Frame3e
|
9
|
+
# This class shows a request of MC protocol.
|
10
|
+
class Request
|
11
|
+
|
12
|
+
DEFAULT_READ_TIMEOUT = 3
|
13
|
+
SUB_HEADER = "\x50\x00".b
|
14
|
+
|
15
|
+
# Constructor.
|
16
|
+
# @param [AccessRoute] access_route access route
|
17
|
+
# @param [Numeric] wait_sec waiting second
|
18
|
+
# @param [Object] command command of MC protocol
|
19
|
+
def initialize(access_route:, wait_sec:, command:)
|
20
|
+
@sub_header = SUB_HEADER
|
21
|
+
@access_route = access_route
|
22
|
+
@wait_sec = WaitSec.new(wait_sec)
|
23
|
+
@command = command
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a request to batch read.
|
27
|
+
# @param [AccessRoute] access_route access route
|
28
|
+
# @param [Numeric] wait_sec waiting second
|
29
|
+
# @param [DeviceRange] device_range device_range
|
30
|
+
def self.batch_read_in_word(access_route:, wait_sec:, device_range:)
|
31
|
+
new(access_route: access_route, wait_sec: wait_sec, command: BatchReadInWord.new(device_range: device_range))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a request to batch write.
|
35
|
+
# @param [AccessRoute] access_route access route
|
36
|
+
# @param [Numeric] wait_sec waiting second
|
37
|
+
# @param [DeviceRange] device_range device_range
|
38
|
+
# @param [Array] values values to write
|
39
|
+
def self.batch_write_in_word(access_route:, wait_sec:, device_range:, values:)
|
40
|
+
new(access_route: access_route, wait_sec: wait_sec, command: BatchWriteInWord.new(device_range: device_range, values: values))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Writes and returns a response.
|
44
|
+
# @param [IO] socket TCP socket
|
45
|
+
# @param [Integer] read_timeout read timeout second
|
46
|
+
# @return [Object] responce
|
47
|
+
def exec(socket, read_timeout = DEFAULT_READ_TIMEOUT)
|
48
|
+
socket.write(to_b)
|
49
|
+
res = Response.recv(socket, read_timeout)
|
50
|
+
command.parse(res)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns binary string.
|
54
|
+
# @return [String] binary string
|
55
|
+
def to_b
|
56
|
+
sub_header + access_route.to_b + request_len_to_b + wait_sec.to_b + command.to_b
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
attr_reader :sub_header, :access_route, :wait_sec, :command
|
62
|
+
|
63
|
+
def request_len_to_b
|
64
|
+
[wait_sec.to_b.size + command.to_b.size].pack("v")
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
# This class shows waiting second for MC protocol.
|
70
|
+
class WaitSec
|
71
|
+
|
72
|
+
def initialize(sec)
|
73
|
+
@sec = sec
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_b
|
77
|
+
return @binary if @binary
|
78
|
+
|
79
|
+
quarter_sec = (sec * 4).to_i
|
80
|
+
@binary = [quarter_sec].pack("v")
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
attr_reader :sec
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module McProtocolE
|
6
|
+
module Frame3e
|
7
|
+
# This class shows a responce of MC protocol.
|
8
|
+
class Response
|
9
|
+
|
10
|
+
class TimeoutError < Timeout::Error; end
|
11
|
+
|
12
|
+
DEFAULT_READ_TIMEOUT = 3
|
13
|
+
SUB_HEADER = "\xD0\x00".b
|
14
|
+
MAX_RECV_LEN = 1024 * 1024
|
15
|
+
SUCCEED_CODE = 0
|
16
|
+
|
17
|
+
attr_reader :data
|
18
|
+
|
19
|
+
# Constructor.
|
20
|
+
# @param [String] raw_res binary string of response
|
21
|
+
def initialize(raw_res)
|
22
|
+
@sub_header = raw_res[0..1]
|
23
|
+
@access_route = raw_res[2..6]
|
24
|
+
@response_len = raw_res[7..8]
|
25
|
+
@code = raw_res[9..10].unpack1("v")
|
26
|
+
@data = raw_res[11..-1] || ""
|
27
|
+
end
|
28
|
+
|
29
|
+
# Receives responce and returns.
|
30
|
+
# @param [IO] socket TCP socket
|
31
|
+
# @param [Integer] read_timeout read timeout second
|
32
|
+
# @return [Responce] received a response
|
33
|
+
def self.recv(socket, read_timeout = DEFAULT_READ_TIMEOUT)
|
34
|
+
selected = IO.select([socket], nil, nil, read_timeout)
|
35
|
+
raise TimeoutError unless selected
|
36
|
+
|
37
|
+
raw_res = socket.recv(MAX_RECV_LEN)
|
38
|
+
new(raw_res)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns true if a command succeed.
|
42
|
+
def succeed?
|
43
|
+
code == SUCCEED_CODE
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns true if a command failed.
|
47
|
+
def failed?
|
48
|
+
!succeed?
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :code
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "mc_protocol_e/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "mc_protocol_e"
|
9
|
+
spec.version = McProtocolE::VERSION
|
10
|
+
spec.authors = ["commis1059"]
|
11
|
+
spec.email = ["commis.1059@gmail.com"]
|
12
|
+
|
13
|
+
spec.summary = %q{A Ruby implementation of a MC protocol client on the ethernet.}
|
14
|
+
spec.homepage = "https://github.com/commis1059"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
+
|
22
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
23
|
+
spec.metadata["source_code_uri"] = "https://github.com/commis1059"
|
24
|
+
spec.metadata["changelog_uri"] = "https://github.com/commis1059"
|
25
|
+
else
|
26
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
27
|
+
"public gem pushes."
|
28
|
+
end
|
29
|
+
|
30
|
+
# Specify which files should be added to the gem when it is released.
|
31
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
33
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
|
+
end
|
35
|
+
spec.bindir = "exe"
|
36
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ["lib"]
|
38
|
+
|
39
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
40
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
41
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mc_protocol_e
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- commis1059
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- commis.1059@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- ".rubocop.yml"
|
65
|
+
- ".travis.yml"
|
66
|
+
- CODE_OF_CONDUCT.md
|
67
|
+
- Gemfile
|
68
|
+
- Gemfile.lock
|
69
|
+
- LICENSE.txt
|
70
|
+
- README.md
|
71
|
+
- Rakefile
|
72
|
+
- bin/console
|
73
|
+
- bin/setup
|
74
|
+
- lib/mc_protocol_e.rb
|
75
|
+
- lib/mc_protocol_e/client.rb
|
76
|
+
- lib/mc_protocol_e/frame_1e/access_route.rb
|
77
|
+
- lib/mc_protocol_e/frame_1e/batch_read_in_word.rb
|
78
|
+
- lib/mc_protocol_e/frame_1e/batch_write_in_word.rb
|
79
|
+
- lib/mc_protocol_e/frame_1e/device_range.rb
|
80
|
+
- lib/mc_protocol_e/frame_1e/request.rb
|
81
|
+
- lib/mc_protocol_e/frame_1e/response.rb
|
82
|
+
- lib/mc_protocol_e/frame_3e/access_route.rb
|
83
|
+
- lib/mc_protocol_e/frame_3e/base_command.rb
|
84
|
+
- lib/mc_protocol_e/frame_3e/batch_read_in_word.rb
|
85
|
+
- lib/mc_protocol_e/frame_3e/batch_write_in_word.rb
|
86
|
+
- lib/mc_protocol_e/frame_3e/device_range.rb
|
87
|
+
- lib/mc_protocol_e/frame_3e/error_info.rb
|
88
|
+
- lib/mc_protocol_e/frame_3e/request.rb
|
89
|
+
- lib/mc_protocol_e/frame_3e/response.rb
|
90
|
+
- lib/mc_protocol_e/version.rb
|
91
|
+
- mc_protocol_e.gemspec
|
92
|
+
homepage: https://github.com/commis1059
|
93
|
+
licenses:
|
94
|
+
- MIT
|
95
|
+
metadata:
|
96
|
+
homepage_uri: https://github.com/commis1059
|
97
|
+
source_code_uri: https://github.com/commis1059
|
98
|
+
changelog_uri: https://github.com/commis1059
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubygems_version: 3.0.3
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: A Ruby implementation of a MC protocol client on the ethernet.
|
118
|
+
test_files: []
|