simatic 0.0.1
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 +10 -0
- data/Gemfile +4 -0
- data/README.md +91 -0
- data/Rakefile +1 -0
- data/bin/read +39 -0
- data/lib/simatic.rb +7 -0
- data/lib/simatic/memory_mapper.rb +191 -0
- data/lib/simatic/plc.rb +100 -0
- data/lib/simatic/sessions.rb +9 -0
- data/lib/simatic/sessions/exchange_session.rb +50 -0
- data/lib/simatic/sessions/open_session.rb +27 -0
- data/lib/simatic/sessions/read_session.rb +83 -0
- data/lib/simatic/sessions/session.rb +44 -0
- data/lib/simatic/sessions/setup_session.rb +32 -0
- data/lib/simatic/sessions/write_session.rb +80 -0
- data/lib/simatic/types.rb +134 -0
- data/lib/simatic/types/bool.rb +18 -0
- data/lib/simatic/types/byte.rb +10 -0
- data/lib/simatic/types/char.rb +15 -0
- data/lib/simatic/types/date_and_time.rb +43 -0
- data/lib/simatic/types/dint.rb +10 -0
- data/lib/simatic/types/dword.rb +10 -0
- data/lib/simatic/types/iec_date.rb +20 -0
- data/lib/simatic/types/iec_time.rb +16 -0
- data/lib/simatic/types/int.rb +10 -0
- data/lib/simatic/types/real.rb +10 -0
- data/lib/simatic/types/s5_time.rb +47 -0
- data/lib/simatic/types/s7_string.rb +38 -0
- data/lib/simatic/types/simatic_simple_type.rb +20 -0
- data/lib/simatic/types/simatic_type.rb +34 -0
- data/lib/simatic/types/time_of_day.rb +8 -0
- data/lib/simatic/types/word.rb +10 -0
- data/lib/simatic/version.rb +3 -0
- data/simatic.gemspec +27 -0
- metadata +94 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'simatic/types/simatic_type'
|
|
2
|
+
|
|
3
|
+
module Simatic
|
|
4
|
+
module Types
|
|
5
|
+
class Bool < SimaticType
|
|
6
|
+
LENGTH = 1
|
|
7
|
+
|
|
8
|
+
def self.parse_one raw_data
|
|
9
|
+
super
|
|
10
|
+
raw_data.unpack('b*').first.index('1') ? true : false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.serialize value
|
|
14
|
+
[value ? 1 : 0].pack('c')
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'simatic/types/simatic_simple_type'
|
|
2
|
+
|
|
3
|
+
module Simatic
|
|
4
|
+
module Types
|
|
5
|
+
class Char < SimaticSimpleType
|
|
6
|
+
LENGTH = 1
|
|
7
|
+
PATTERN = 'a'
|
|
8
|
+
|
|
9
|
+
def self.serialize value
|
|
10
|
+
raise "Value must be String class (value: #{value})" unless value.kind_of? String
|
|
11
|
+
[value].pack(self::PATTERN)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'simatic/types/simatic_type'
|
|
3
|
+
|
|
4
|
+
module Simatic
|
|
5
|
+
module Types
|
|
6
|
+
class DateAndTime < SimaticType
|
|
7
|
+
LENGTH = 8
|
|
8
|
+
def self.parse_one raw_value
|
|
9
|
+
super
|
|
10
|
+
array = raw_value.unpack('H*').first
|
|
11
|
+
|
|
12
|
+
year = array[0,2].to_i + (array[0,2].to_i <= 89 ? 2000 : 1900)
|
|
13
|
+
month = array[2,2].to_i
|
|
14
|
+
day = array[4,2].to_i
|
|
15
|
+
hour = array[6,2].to_i
|
|
16
|
+
min = array[8,2].to_i
|
|
17
|
+
sec = array[10,2].to_i
|
|
18
|
+
|
|
19
|
+
microsecond = array[12,3].to_i * 1000
|
|
20
|
+
|
|
21
|
+
Time.utc(year, month, day, hour, min, sec, microsecond)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.serialize value
|
|
25
|
+
raise "Value must be Time class" unless value.kind_of? Time
|
|
26
|
+
|
|
27
|
+
hex_string =
|
|
28
|
+
'%02d' % (value.year - ((value.year >= 2000) ? 2000 : 1900)) +
|
|
29
|
+
'%02d' % value.month +
|
|
30
|
+
'%02d' % value.day +
|
|
31
|
+
'%02d' % value.hour +
|
|
32
|
+
'%02d' % value.min +
|
|
33
|
+
'%02d' % value.sec +
|
|
34
|
+
'%03d' % (value.usec/1000.0).to_i +
|
|
35
|
+
'%01d' % (value.wday+1)
|
|
36
|
+
|
|
37
|
+
# puts hex_string
|
|
38
|
+
|
|
39
|
+
[hex_string].pack('H*')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
require 'simatic/types/word'
|
|
3
|
+
|
|
4
|
+
module Simatic
|
|
5
|
+
module Types
|
|
6
|
+
class IECDate < Word
|
|
7
|
+
LENGTH = 2
|
|
8
|
+
def self.parse_one raw_value
|
|
9
|
+
days = super # days since 1990-01-01
|
|
10
|
+
Date.new(1990,1,1)+days
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.serialize value
|
|
14
|
+
raise "Value #{value} must be Date class instead of #{value.class}" unless value.kind_of? Date
|
|
15
|
+
days = value - Date.new(1990,01,01)
|
|
16
|
+
super days
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'simatic/types/dword'
|
|
2
|
+
|
|
3
|
+
module Simatic
|
|
4
|
+
module Types
|
|
5
|
+
class IECTime < Dword
|
|
6
|
+
def self.parse_one raw_value
|
|
7
|
+
super / 1000.0 # seconds
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.serialize value
|
|
11
|
+
raise "Value must be numeric in seconds" unless value.kind_of? Numeric
|
|
12
|
+
super (value * 1000.0).to_i # convert to milliseconds and write
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'simatic/types/simatic_type'
|
|
2
|
+
|
|
3
|
+
module Simatic
|
|
4
|
+
module Types
|
|
5
|
+
class S5time < SimaticType
|
|
6
|
+
LENGTH = 2
|
|
7
|
+
def self.parse_one raw_value
|
|
8
|
+
super
|
|
9
|
+
hex_string = raw_value.unpack('h*').first
|
|
10
|
+
time_base = hex_string[0].to_i
|
|
11
|
+
time_value = hex_string[1,3].to_i
|
|
12
|
+
case time_base
|
|
13
|
+
when 0 # 10 ms
|
|
14
|
+
time_value * 100.0
|
|
15
|
+
when 1 # 100 ms
|
|
16
|
+
time_value * 10.0
|
|
17
|
+
when 2 # 1 s
|
|
18
|
+
time_value * 1.0
|
|
19
|
+
when 3 # 10 s
|
|
20
|
+
time_value / 10.0
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.serialize value
|
|
25
|
+
raise "Value must be numeric in seconds" unless value.kind_of? Numeric
|
|
26
|
+
|
|
27
|
+
params = case value.round(2)
|
|
28
|
+
when 0.00...9.99
|
|
29
|
+
[0, 100.0]
|
|
30
|
+
when 9.99...99.9
|
|
31
|
+
[1, 10.0]
|
|
32
|
+
when 99.9...999.0
|
|
33
|
+
[2, 1.0]
|
|
34
|
+
when 999.0..9990.0
|
|
35
|
+
[3, 0.1]
|
|
36
|
+
else
|
|
37
|
+
raise "Value is to large"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
time_base = params.first.to_s
|
|
41
|
+
time_value = (value*params.last).to_i.to_s
|
|
42
|
+
|
|
43
|
+
[time_base+time_value].pack('h*')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'simatic/types/simatic_type'
|
|
2
|
+
|
|
3
|
+
module Simatic
|
|
4
|
+
module Types
|
|
5
|
+
class S7String < SimaticType
|
|
6
|
+
def initialize value, size = nil
|
|
7
|
+
@size = size
|
|
8
|
+
@value = value
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.serialize value, size
|
|
12
|
+
raise "Value must be String class" unless value.kind_of? String
|
|
13
|
+
size = size || value.length
|
|
14
|
+
[size, value.length, value].pack("CCa#{size}x")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def serialize
|
|
18
|
+
if @value.kind_of? Array
|
|
19
|
+
@value.map { |single_val| self.class.serialize single_val, @size }
|
|
20
|
+
else
|
|
21
|
+
self.class.serialize @value, @size
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.parse_one raw_value
|
|
26
|
+
res = raw_value.unpack('CCa*')
|
|
27
|
+
|
|
28
|
+
buf_size = res[0]
|
|
29
|
+
string_size = res[1]
|
|
30
|
+
string = res[2]
|
|
31
|
+
|
|
32
|
+
raise "S7String is broken, cant parse" if buf_size.nil? || string_size.nil? || string.nil? || buf_size < string_size
|
|
33
|
+
|
|
34
|
+
string[0, string_size]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'simatic/types/simatic_type'
|
|
2
|
+
|
|
3
|
+
module Simatic
|
|
4
|
+
module Types
|
|
5
|
+
class SimaticSimpleType < SimaticType
|
|
6
|
+
def self.parse_one raw_value
|
|
7
|
+
super
|
|
8
|
+
raw_value.unpack(self::PATTERN).first
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.serialize value
|
|
12
|
+
raise "Value must be numeric (value: #{value} type: #{value.class})" unless value.kind_of? Numeric
|
|
13
|
+
|
|
14
|
+
# puts "class #{self::PATTERN}"
|
|
15
|
+
# puts " PATTERN #{self::PATTERN} LENGTH #{self::LENGTH}"
|
|
16
|
+
[value].pack(self::PATTERN)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Simatic
|
|
2
|
+
module Types
|
|
3
|
+
class SimaticType
|
|
4
|
+
def initialize value
|
|
5
|
+
@value = value
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.parse raw_data
|
|
9
|
+
if raw_data.kind_of? Array
|
|
10
|
+
# puts "raw_data #{raw_data}"
|
|
11
|
+
raw_data.map { |raw_value| self.parse_one raw_value }
|
|
12
|
+
else
|
|
13
|
+
parse_one raw_data
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.parse_one raw_value
|
|
18
|
+
raw_value_length = raw_value.length
|
|
19
|
+
if raw_value_length != self::LENGTH
|
|
20
|
+
raise "Cant parse, cause raw data length #{raw_value_length} (must be #{self::LENGTH})."
|
|
21
|
+
end
|
|
22
|
+
raw_value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def serialize
|
|
26
|
+
if @value.kind_of? Array
|
|
27
|
+
@value.map { |single_val| self.class.serialize single_val }
|
|
28
|
+
else
|
|
29
|
+
self.class.serialize @value
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/simatic.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'simatic/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "simatic"
|
|
8
|
+
spec.version = Simatic::VERSION
|
|
9
|
+
spec.authors = ["Konstantin Ryzhov"]
|
|
10
|
+
spec.email = ["konstantin.ryzhov@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{Ruby library for Siemens Simatic S7-300 PLC data exchange.}
|
|
13
|
+
spec.description = %q{Ruby library for Siemens Simatic S7-300 PLC data exchange.}
|
|
14
|
+
spec.homepage = "https://github.com/konstantin-ryzhov/ruby_s7plc"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
18
|
+
spec.executables = ['read']
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
if spec.respond_to?(:metadata)
|
|
22
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
|
26
|
+
# spec.add_development_dependency "rake", "~> 10.0"
|
|
27
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: simatic
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Konstantin Ryzhov
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-07-14 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: '1.9'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.9'
|
|
27
|
+
description: Ruby library for Siemens Simatic S7-300 PLC data exchange.
|
|
28
|
+
email:
|
|
29
|
+
- konstantin.ryzhov@gmail.com
|
|
30
|
+
executables:
|
|
31
|
+
- read
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- ".gitignore"
|
|
36
|
+
- Gemfile
|
|
37
|
+
- README.md
|
|
38
|
+
- Rakefile
|
|
39
|
+
- bin/read
|
|
40
|
+
- lib/simatic.rb
|
|
41
|
+
- lib/simatic/memory_mapper.rb
|
|
42
|
+
- lib/simatic/plc.rb
|
|
43
|
+
- lib/simatic/sessions.rb
|
|
44
|
+
- lib/simatic/sessions/exchange_session.rb
|
|
45
|
+
- lib/simatic/sessions/open_session.rb
|
|
46
|
+
- lib/simatic/sessions/read_session.rb
|
|
47
|
+
- lib/simatic/sessions/session.rb
|
|
48
|
+
- lib/simatic/sessions/setup_session.rb
|
|
49
|
+
- lib/simatic/sessions/write_session.rb
|
|
50
|
+
- lib/simatic/types.rb
|
|
51
|
+
- lib/simatic/types/bool.rb
|
|
52
|
+
- lib/simatic/types/byte.rb
|
|
53
|
+
- lib/simatic/types/char.rb
|
|
54
|
+
- lib/simatic/types/date_and_time.rb
|
|
55
|
+
- lib/simatic/types/dint.rb
|
|
56
|
+
- lib/simatic/types/dword.rb
|
|
57
|
+
- lib/simatic/types/iec_date.rb
|
|
58
|
+
- lib/simatic/types/iec_time.rb
|
|
59
|
+
- lib/simatic/types/int.rb
|
|
60
|
+
- lib/simatic/types/real.rb
|
|
61
|
+
- lib/simatic/types/s5_time.rb
|
|
62
|
+
- lib/simatic/types/s7_string.rb
|
|
63
|
+
- lib/simatic/types/simatic_simple_type.rb
|
|
64
|
+
- lib/simatic/types/simatic_type.rb
|
|
65
|
+
- lib/simatic/types/time_of_day.rb
|
|
66
|
+
- lib/simatic/types/word.rb
|
|
67
|
+
- lib/simatic/version.rb
|
|
68
|
+
- simatic.gemspec
|
|
69
|
+
homepage: https://github.com/konstantin-ryzhov/ruby_s7plc
|
|
70
|
+
licenses:
|
|
71
|
+
- MIT
|
|
72
|
+
metadata:
|
|
73
|
+
allowed_push_host: https://rubygems.org
|
|
74
|
+
post_install_message:
|
|
75
|
+
rdoc_options: []
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
requirements: []
|
|
89
|
+
rubyforge_project:
|
|
90
|
+
rubygems_version: 2.2.3
|
|
91
|
+
signing_key:
|
|
92
|
+
specification_version: 4
|
|
93
|
+
summary: Ruby library for Siemens Simatic S7-300 PLC data exchange.
|
|
94
|
+
test_files: []
|