ffi-snap7 0.2.0 → 0.3.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bc1fca00d3b8e5fd62c316df27514db3b4c757a3
4
+ data.tar.gz: 633799146945e5e12aa387126220c1088dc69dbb
5
+ SHA512:
6
+ metadata.gz: 616e9d56a95af7024fc547b57b810f1fcfdd5f1ed561146d9267476eed886ff6fe7a0ec72a80283273e8f92b1759d3ac4f0227fe597eb7625a4f58b7b99cb6cb
7
+ data.tar.gz: fb48641bfe01aae8198896d525afbea39922bdd664e14a7c54d0038e433fe56e6d507e26214376da2d601c4242015c01fd5d88ba14d9943d94e7dcb0cd01a54d
data/.gitignore CHANGED
@@ -22,3 +22,4 @@ tmp
22
22
  mkmf.log
23
23
  .idea
24
24
  .ruby-*
25
+ /.rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,16 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ # RSpec files
6
+ rspec = dsl.rspec
7
+ watch(rspec.spec_helper) { rspec.spec_dir }
8
+ watch(rspec.spec_support) { rspec.spec_dir }
9
+ watch(rspec.spec_files)
10
+
11
+ # Ruby files
12
+ ruby = dsl.ruby
13
+ dsl.watch_spec_files_for(ruby.lib_files)
14
+
15
+ watch(%r{^lib/ffi-snap7/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
16
+ end
data/Rakefile CHANGED
@@ -1,2 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
2
3
 
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -22,4 +22,7 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_development_dependency 'bundler', '~> 1.6'
24
24
  spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ spec.add_development_dependency "minitest" # for assertions
27
+ spec.add_development_dependency "guard-rspec"
25
28
  end
@@ -7,6 +7,23 @@ module Snap7
7
7
 
8
8
  # S7Object
9
9
  typedef :pointer, :s7obj
10
+
11
+ # parameter numbers
12
+ P_u16_LocalPort = 1
13
+ P_u16_RemotePort = 2
14
+ P_i32_PingTimeout = 3
15
+ P_i32_SendTimeout = 4
16
+ P_i32_RecvTimeout = 5
17
+ P_i32_WorkInterval = 6
18
+ P_u16_SrcRef = 7
19
+ P_u16_DstRef = 8
20
+ P_u16_SrcTSap = 9
21
+ P_i32_PDURequest = 10
22
+ P_i32_MaxClients = 11
23
+ P_i32_BSendTimeout = 12
24
+ P_i32_BRecvTimeout = 13
25
+ P_u32_RecoveryTime = 14
26
+ P_u32_KeepAliveTime = 15
10
27
  end
11
28
 
12
29
  require 'ffi-snap7/version'
@@ -4,9 +4,29 @@ module Snap7
4
4
 
5
5
  class Client
6
6
 
7
+ # @return [Proc]
8
+ # @see http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
9
+ #
10
+ def self.finalizer(ptr)
11
+ proc do
12
+ ptrptr = FFI::MemoryPointer.new :pointer
13
+ ptrptr.write_pointer ptr
14
+ Snap7.cli_destroy ptrptr
15
+ end
16
+ end
17
+
18
+
7
19
  def initialize
8
20
  @cli = Snap7.cli_create
9
21
  @connected = false
22
+
23
+ ObjectSpace.define_finalizer self, self.class.finalizer(@cli)
24
+ end
25
+
26
+
27
+ # @return [FFI::MemoryPointer] pointer to the native object
28
+ def to_ptr
29
+ @cli
10
30
  end
11
31
 
12
32
 
@@ -54,6 +74,22 @@ module Snap7
54
74
  end
55
75
 
56
76
 
77
+ # @param port [Integer] remote port to connect to
78
+ def remote_port=(port)
79
+ p_value = FFI::MemoryPointer.new(:uint16)
80
+ p_value.write_uint16 port
81
+ check_rc Snap7.cli_set_param(@cli, P_u16_RemotePort, p_value)
82
+ end
83
+
84
+
85
+ # @return [Integer]
86
+ def remote_port
87
+ p_port = FFI::MemoryPointer.new(:uint16)
88
+ check_rc Snap7.cli_get_param(@cli, P_u16_RemotePort, p_port)
89
+ p_port.read_uint16
90
+ end
91
+
92
+
57
93
  private
58
94
 
59
95
 
@@ -64,7 +100,7 @@ module Snap7
64
100
 
65
101
  def error_text(error)
66
102
  text_len = 1024
67
- text_ptr = FFI::MemoryPointer.new :pointer, text_len
103
+ text_ptr = FFI::MemoryPointer.new :char, text_len
68
104
  if Snap7.cli_error_text(error, text_ptr, text_len) == 0
69
105
  text_ptr.read_string
70
106
  else
@@ -3,8 +3,8 @@ module Snap7
3
3
  # EXPORTSPEC S7Object S7API Cli_Create();
4
4
  attach_function :cli_create, :Cli_Create, [], :s7obj
5
5
 
6
- # EXPORTSPEC void S7API Cli_Destroy(S7Object &Client);
7
- # attach_function :cli_destroy, :Cli_Destroy, [:s7obj], :void # FIXME crashes MRI
6
+ # EXPORTSPEC void S7API Cli_Destroy(S7Object **Client);
7
+ attach_function :cli_destroy, :Cli_Destroy, [:s7obj], :void
8
8
 
9
9
  # EXPORTSPEC int S7API Cli_SetSessionPassword(S7Object Client, char *Password);
10
10
  attach_function :cli_set_session_password, :Cli_SetSessionPassword, [:s7obj, :string], :int
@@ -27,6 +27,10 @@ module Snap7
27
27
  # EXPORTSPEC int S7API Cli_ErrorText(int Error, char *Text, int TextLen);
28
28
  attach_function :cli_error_text, :Cli_ErrorText, [:int, :pointer, :int], :int
29
29
 
30
+ # EXPORTSPEC int S7API Cli_GetParam(S7Object Client, int ParamNumber, void *pValue);
31
+ attach_function :cli_get_param, :Cli_GetParam, [:s7obj, :int, :pointer], :int
30
32
 
33
+ # EXPORTSPEC int S7API Cli_SetParam(S7Object Client, int ParamNumber, void *pValue);
34
+ attach_function :cli_set_param, :Cli_SetParam, [:s7obj, :int, :pointer], :int
31
35
 
32
36
  end
@@ -12,8 +12,28 @@ module Snap7
12
12
  S7AreaDB = 5
13
13
 
14
14
 
15
+ # @return [Proc]
16
+ # @see http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
17
+ #
18
+ def self.finalizer(ptr)
19
+ proc do
20
+ ptrptr = FFI::MemoryPointer.new :pointer
21
+ ptrptr.write_pointer ptr
22
+ Snap7.srv_destroy ptrptr
23
+ end
24
+ end
25
+
26
+
15
27
  def initialize
16
28
  @srv = Snap7.srv_create
29
+
30
+ ObjectSpace.define_finalizer self, self.class.finalizer(@srv)
31
+ end
32
+
33
+
34
+ # @return [FFI::MemoryPointer] pointer to the native object
35
+ def to_ptr
36
+ @srv
17
37
  end
18
38
 
19
39
 
@@ -38,6 +58,22 @@ module Snap7
38
58
  end
39
59
 
40
60
 
61
+ # @param port [Integer] local port to bind to
62
+ def local_port=(port)
63
+ p_port = FFI::MemoryPointer.new(:uint16)
64
+ p_port.write_uint16 port
65
+ check_rc Snap7.srv_set_param(@srv, P_u16_LocalPort, p_port)
66
+ end
67
+
68
+
69
+ # @return [Integer]
70
+ def local_port
71
+ p_port = FFI::MemoryPointer.new(:uint16)
72
+ check_rc Snap7.srv_get_param(@srv, P_u16_LocalPort, p_port)
73
+ p_port.read_uint16
74
+ end
75
+
76
+
41
77
  private
42
78
 
43
79
 
@@ -48,7 +84,7 @@ module Snap7
48
84
 
49
85
  def error_text(error)
50
86
  text_len = 1024
51
- text_ptr = FFI::MemoryPointer.new :pointer, text_len
87
+ text_ptr = FFI::MemoryPointer.new :char, text_len
52
88
  if Snap7.srv_error_text(error, text_ptr, text_len) == 0
53
89
  text_ptr.read_string
54
90
  else
@@ -6,13 +6,14 @@ module Snap7
6
6
  # EXPORTSPEC S7Object S7API Srv_Create();
7
7
  attach_function :srv_create, :Srv_Create, [], :s7obj
8
8
 
9
- # EXPORTSPEC void S7API Srv_Destroy(S7Object &Server);
10
- # FIXME crashes MRI
11
- # attach_function :srv_destroy, :Srv_Destroy, [:s7obj], :void
9
+ # EXPORTSPEC void S7API Srv_Destroy(S7Object **Server);
10
+ attach_function :srv_destroy, :Srv_Destroy, [:s7obj], :void
12
11
 
13
12
  # EXPORTSPEC int S7API Srv_GetParam(S7Object Server, int ParamNumber, void *pValue);
13
+ attach_function :srv_get_param, :Srv_GetParam, [:s7obj, :int, :pointer], :int
14
14
 
15
15
  # EXPORTSPEC int S7API Srv_SetParam(S7Object Server, int ParamNumber, void *pValue);
16
+ attach_function :srv_set_param, :Srv_SetParam, [:s7obj, :int, :pointer], :int
16
17
 
17
18
  # EXPORTSPEC int S7API Srv_Start(S7Object Server);
18
19
  attach_function :srv_start, :Srv_Start, [:s7obj], :int
@@ -1,3 +1,3 @@
1
1
  module Snap7
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,92 @@
1
+ RSpec.describe Snap7::Client do
2
+
3
+ it 'initializes' do
4
+ subject
5
+ end
6
+
7
+
8
+ describe 'native object' do
9
+ it 'is created' do
10
+ # NOTE: Calling original is important, otherwise the next GC will SEGV!
11
+ expect(Snap7).to receive(:cli_create).and_call_original
12
+ subject
13
+ end
14
+
15
+ it 'is destroyed' do
16
+ GC.start # minimize garbage objects
17
+ destroyed = []
18
+ allow(Snap7).to receive(:cli_destroy) { |ptrptr| destroyed << ptrptr.read_pointer }
19
+
20
+ cli_ptr = Snap7::Client.new.to_ptr # no ref to Snap7::Client object is kept
21
+ GC.start; GC.start # second call is often necessary
22
+
23
+ assert_includes destroyed, cli_ptr
24
+ end
25
+ end
26
+
27
+
28
+ describe '#connected?' do
29
+ it 'initializes disconnected' do
30
+ refute subject.connected?
31
+ end
32
+ end
33
+
34
+
35
+ describe '#connect' do
36
+ let(:address) { double }
37
+ let(:rack) { double }
38
+ let(:slot) { double }
39
+
40
+ it 'connects to default port' do
41
+ expect(Snap7).to receive(:cli_connect_to).with(FFI::Pointer, address, rack, slot) { 0 }
42
+ subject.connect address, rack, slot
43
+ end
44
+
45
+ it 'marks as connected' do
46
+ allow(Snap7).to receive(:cli_connect_to){ 0 }
47
+ subject.connect address, rack, slot
48
+
49
+ assert subject.connected?
50
+ end
51
+
52
+ it 'checks for error' do
53
+ expect(Snap7).to receive(:cli_connect_to) { 1 }
54
+ expect(Snap7).to receive(:cli_error_text).with(1, FFI::MemoryPointer, Integer) do |_, ptr, len|
55
+ assert_equal len, ptr.size
56
+ ptr.write_string 'error: foo'
57
+ 0
58
+ end
59
+
60
+ assert_raises do
61
+ subject.connect address, rack, slot
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+ describe '#disconnect' do
68
+ it 'disconnects' do
69
+ expect(Snap7).to receive(:cli_disconnect)
70
+ subject.disconnect
71
+ end
72
+
73
+ it 'marks as disconnected' do
74
+ allow(Snap7).to receive(:cli_connect_to){ 0 }
75
+ subject.connect double, double, double
76
+ assert subject.connected?
77
+
78
+ allow(Snap7).to receive(:cli_disconnect)
79
+ subject.disconnect
80
+ refute subject.connected?
81
+ end
82
+ end
83
+
84
+
85
+ describe '#remote_port' do
86
+ it 'sets remote port' do
87
+ assert_equal 102, subject.remote_port
88
+ subject.remote_port = 2102
89
+ assert_equal 2102, subject.remote_port
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.describe Snap7 do
2
+ it "has a version number" do
3
+ expect(Snap7::VERSION).not_to be nil
4
+ end
5
+ end
@@ -0,0 +1,64 @@
1
+ RSpec.describe Snap7::Server do
2
+
3
+ it 'initializes' do
4
+ subject
5
+ end
6
+
7
+
8
+ describe 'native object' do
9
+ it 'is created' do
10
+ # NOTE: Calling original is important, otherwise the next GC will SEGV!
11
+ expect(Snap7).to receive(:srv_create).and_call_original
12
+ subject
13
+ end
14
+
15
+ it 'is destroyed' do
16
+ GC.start # minimize garbage objects
17
+ destroyed = []
18
+ allow(Snap7).to receive(:srv_destroy) { |ptrptr| destroyed << ptrptr.read_pointer }
19
+
20
+ srv_ptr = Snap7::Server.new.to_ptr # no ref to Snap7::Server object is kept
21
+ GC.start; GC.start # second call is often necessary
22
+
23
+ assert_includes destroyed, srv_ptr
24
+ end
25
+ end
26
+
27
+
28
+ describe '#start' do
29
+ it 'binds to default port' do
30
+ expect(Snap7).to receive(:srv_start).with(FFI::Pointer) { 0 }
31
+ subject.start
32
+ end
33
+
34
+ it 'checks for error' do
35
+ expect(Snap7).to receive(:srv_start) { 1 }
36
+ expect(Snap7).to receive(:srv_error_text).with(1, FFI::MemoryPointer, Integer) do |_, ptr, len|
37
+ assert_equal len, ptr.size
38
+ ptr.write_string 'error: foo'
39
+ 0
40
+ end
41
+
42
+ assert_raises do
43
+ subject.start
44
+ end
45
+ end
46
+ end
47
+
48
+
49
+ describe '#stop' do
50
+ it 'stops' do
51
+ expect(Snap7).to receive(:srv_stop) { 0 }
52
+ subject.stop
53
+ end
54
+ end
55
+
56
+
57
+ describe '#local_port' do
58
+ it 'sets local port' do
59
+ assert_equal 102, subject.local_port
60
+ subject.local_port = 2102
61
+ assert_equal 2102, subject.local_port
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,23 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
+
5
+
6
+ require "bundler/setup"
7
+ require "ffi-snap7"
8
+
9
+ RSpec.configure do |config|
10
+ # Enable flags like --only-failures and --next-failure
11
+ config.example_status_persistence_file_path = ".rspec_status"
12
+
13
+ # Disable RSpec exposing methods globally on `Module` and `main`
14
+ config.disable_monkey_patching!
15
+
16
+ config.filter_run focus: true
17
+ config.run_all_when_everything_filtered = true
18
+
19
+ config.expect_with :rspec do |c|
20
+ c.syntax = :expect
21
+ end
22
+ config.expect_with :minitest
23
+ end
metadata CHANGED
@@ -1,77 +1,110 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ffi-snap7
3
- version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 0
10
- version: 0.2.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Andy Rohr
14
8
  autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2016-09-05 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
11
+ date: 2018-02-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
21
20
  type: :runtime
22
- version_requirements: &id001 !ruby/object:Gem::Requirement
23
- none: false
24
- requirements:
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
25
24
  - - ">="
26
- - !ruby/object:Gem::Version
27
- hash: 3
28
- segments:
29
- - 0
30
- version: "0"
31
- requirement: *id001
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
32
35
  prerelease: false
33
- name: ffi
34
- - !ruby/object:Gem::Dependency
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
35
48
  type: :development
36
- version_requirements: &id002 !ruby/object:Gem::Requirement
37
- none: false
38
- requirements:
39
- - - ~>
40
- - !ruby/object:Gem::Version
41
- hash: 3
42
- segments:
43
- - 1
44
- - 6
45
- version: "1.6"
46
- requirement: *id002
47
49
  prerelease: false
48
- name: bundler
49
- - !ruby/object:Gem::Dependency
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
50
62
  type: :development
51
- version_requirements: &id003 !ruby/object:Gem::Requirement
52
- none: false
53
- requirements:
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
54
73
  - - ">="
55
- - !ruby/object:Gem::Version
56
- hash: 3
57
- segments:
58
- - 0
59
- version: "0"
60
- requirement: *id003
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
61
77
  prerelease: false
62
- name: rake
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
63
97
  description: Access to Siemens PLCs, Simulation of Siemens PLCs
64
- email:
98
+ email:
65
99
  - andy.rohr@mindclue.ch
66
100
  executables: []
67
-
68
101
  extensions: []
69
-
70
102
  extra_rdoc_files: []
71
-
72
- files:
73
- - .gitignore
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
74
106
  - Gemfile
107
+ - Guardfile
75
108
  - LICENSE.txt
76
109
  - README.md
77
110
  - Rakefile
@@ -88,38 +121,36 @@ files:
88
121
  - lib/ffi-snap7/server/server.rb
89
122
  - lib/ffi-snap7/server/wrapper.rb
90
123
  - lib/ffi-snap7/version.rb
124
+ - spec/client/client_spec.rb
125
+ - spec/ffi-snap7_spec.rb
126
+ - spec/server/server_spec.rb
127
+ - spec/spec_helper.rb
91
128
  homepage: https://github.com/arohr/ffi-snap7
92
- licenses:
129
+ licenses:
93
130
  - LGPLv3
131
+ metadata: {}
94
132
  post_install_message:
95
133
  rdoc_options: []
96
-
97
- require_paths:
134
+ require_paths:
98
135
  - lib
99
- required_ruby_version: !ruby/object:Gem::Requirement
100
- none: false
101
- requirements:
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
102
138
  - - ">="
103
- - !ruby/object:Gem::Version
104
- hash: 3
105
- segments:
106
- - 0
107
- version: "0"
108
- required_rubygems_version: !ruby/object:Gem::Requirement
109
- none: false
110
- requirements:
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
111
143
  - - ">="
112
- - !ruby/object:Gem::Version
113
- hash: 3
114
- segments:
115
- - 0
116
- version: "0"
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
117
146
  requirements: []
118
-
119
147
  rubyforge_project:
120
- rubygems_version: 1.8.29
148
+ rubygems_version: 2.6.13
121
149
  signing_key:
122
- specification_version: 3
150
+ specification_version: 4
123
151
  summary: FFI Ruby wrapper for Snap7 (http://snap7.sourceforge.net/)
124
- test_files: []
125
-
152
+ test_files:
153
+ - spec/client/client_spec.rb
154
+ - spec/ffi-snap7_spec.rb
155
+ - spec/server/server_spec.rb
156
+ - spec/spec_helper.rb