butler-mainframe 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/README.md +174 -6
- data/lib/butler-mainframe.rb +15 -11
- data/lib/config/config.rb +3 -2
- data/lib/config/config_EXAMPLE_passport.rb +8 -0
- data/lib/config/config_EXAMPLE_pcomm.rb +7 -0
- data/lib/config/settings.yml +3 -6
- data/lib/config/settings_EXAMPLE.yml +17 -0
- data/lib/core/configuration.rb +4 -1
- data/lib/generators/butler/install_generator.rb +5 -1
- data/lib/generators/butler/templates/custom_functions.rb +3 -1
- data/lib/mainframe/customization/generic_functions.rb +2 -0
- data/lib/mainframe/host_base.rb +53 -21
- data/lib/mainframe/passport.rb +50 -16
- data/lib/mainframe/pcomm.rb +105 -0
- data/rakefile +4 -0
- data/test/test.rake +26 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2be056e6d5c2cd0b24cdd1ba7c27a92f2e11d295
|
4
|
+
data.tar.gz: 0265a490b0c3891b40e0c336c05e4d73563d9150
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71d70a1deaf10b776b6e4b49a5eb57d608c5931b245f72010876f32d9946e05ac9aa1cee342c87df895719a63b79dd6ae2ebac3ab6b715269f173b454f6185f7
|
7
|
+
data.tar.gz: 721c158317b5282ca9e51df2988e1b081992420427176092a0a3001581ca2930792ae7392003ec6284a51a62f0e6b6247c07f97cdd872aa745a7d54092b5ee77
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,18 @@
|
|
1
|
+
0.1.0 [☰](https://github.com/marcomd/butler-mainframe/compare/v0.0.6...v0.1.0) October 14th, 2015
|
2
|
+
------------------------------
|
3
|
+
* Added IBM personal communication support (tested on 6.0.16), use :pcomm as host gateway in butler configuration
|
4
|
+
* Many documentation improvements with technical details and examples
|
5
|
+
* Little improvements in host base to manage ready? sub methods, a new check as well as created?
|
6
|
+
* Two new config parameters: timeout and session_tag to connect to the right session
|
7
|
+
* Write text now have an option parameter :check_protect which raise an error when on protect area
|
8
|
+
* Improved passport driver class: now it use a session screen @variable to optimize memory use
|
9
|
+
* Change include module methods to monkey patch to add ruby 1.8.7 support
|
10
|
+
|
1
11
|
0.0.6 [☰](https://github.com/marcomd/butler-mainframe/compare/v0.0.5...v0.0.6) October 12th, 2015
|
2
12
|
------------------------------
|
3
13
|
* Added a simple rake test
|
4
14
|
* Improved documentation
|
5
|
-
*
|
15
|
+
* Some little improvements
|
6
16
|
|
7
17
|
0.0.5 October 7th, 2015
|
8
18
|
------------------------------
|
data/README.md
CHANGED
@@ -20,17 +20,67 @@ Instal the gem from rubygems
|
|
20
20
|
Then you have to install your favorite emulator.
|
21
21
|
At the moment is supported only [Passport web to host by rocket software](http://www.rocketsoftware.com/products/rocket-passport-web-to-host)
|
22
22
|
|
23
|
+
## Emulator
|
24
|
+
|
25
|
+
At the moment are managed two emulators both are commercial which must be purchased and installed on the machine.
|
26
|
+
Both have x days free trial.
|
27
|
+
|
28
|
+
* [Passport web to host by Rocket Software](http://www.rocketsoftware.com/resource/rocket-passport-web-host-overview)
|
29
|
+
* [Personal communication by IBM](http://www-03.ibm.com/software/products/en/pcomm)
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
## Configuration
|
35
|
+
|
36
|
+
In the config folder there are two files:
|
37
|
+
|
38
|
+
* config.rb
|
39
|
+
* settings.yml
|
40
|
+
|
41
|
+
### Emulator configuration
|
42
|
+
|
43
|
+
config.rb can be used for the configuration of the gem and the emulator
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# Example to configura Personal communication
|
47
|
+
|
48
|
+
ButlerMainframe.configure do |config|
|
49
|
+
config.host_gateway = :pcomm
|
50
|
+
config.browser_path = "'C:/Program Files (x86)/IBM/Personal Communications/pcsws.exe'"
|
51
|
+
config.session_path = "'C:/Users/Marco/AppData/Roaming/IBM/Personal Communications/host3270.ws'"
|
52
|
+
config.session_tag = 'A'
|
53
|
+
config.timeout = 3000
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# Example to configura Passport web to host
|
59
|
+
|
60
|
+
ButlerMainframe.configure do |config|
|
61
|
+
config.host_gateway = :passport
|
62
|
+
config.browser_path = 'c:/Program Files (x86)/Internet Explorer/iexplore.exe'
|
63
|
+
config.session_path = 'https://localhost/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
|
64
|
+
config.session_tag = 1
|
65
|
+
config.timeout = 3000
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
### Use configuration
|
70
|
+
|
71
|
+
settings.yml for the variables necessary to use the emulator like user, password, cics selection and everything else. It has one section for every environment in rails style.
|
72
|
+
|
73
|
+
foo: add every variable you need and use it with => ButlerMainframe::Settings.foo
|
74
|
+
bar: sub variable are accessible with hash => ButlerMainframe::Settings.foo[:bar]
|
75
|
+
|
76
|
+
|
23
77
|
## How to use
|
24
78
|
|
25
79
|
In irb:
|
26
80
|
|
27
81
|
C:\Ruby>irb
|
28
82
|
irb(main):001:0> require 'butler-mainframe'
|
29
|
-
|
30
|
-
Use ButlerMainframe::Host.new
|
31
|
-
Please read the documentation on github for more information
|
32
83
|
=> true
|
33
|
-
irb(main):002:0>
|
34
84
|
|
35
85
|
You can start the emulator 3270 manually, the butler will lean on that and will let it open at the end.
|
36
86
|
In this example, I dont start the session and immediately create a new instance:
|
@@ -113,9 +163,9 @@ then
|
|
113
163
|
|
114
164
|
bundle install
|
115
165
|
|
116
|
-
run generator to copy configuration files
|
166
|
+
run generator to copy configuration files suitable for the emulator you need
|
117
167
|
|
118
|
-
rails g butler:install
|
168
|
+
rails g butler:install --emulator=passport
|
119
169
|
|
120
170
|
```ruby
|
121
171
|
Class Invoice
|
@@ -163,6 +213,124 @@ For more informations:
|
|
163
213
|
bundle exec rake -T
|
164
214
|
|
165
215
|
|
216
|
+
## More informations about supported emulators
|
217
|
+
|
218
|
+
I hope this can help to support my work or yours if you need something different
|
219
|
+
|
220
|
+
### Passport web to host
|
221
|
+
|
222
|
+
Documentation can be found [here](http://www.zephyrcorp.com/legacy-integration/Documentation/passport_host_integration_objects.htm) Rocket use old Zephyr documentation, unfortunately it's obsolete :disappointed:
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
require 'win32ole'
|
226
|
+
passport = WIN32OLE.new("PASSPORT.System")
|
227
|
+
```
|
228
|
+
|
229
|
+
passport.ole_methods:
|
230
|
+
|
231
|
+
# QueryInterface # AddRef # Release # GetTypeInfoCount
|
232
|
+
# GetTypeInfo # GetIDsOfNames # Invoke # ActiveSession
|
233
|
+
# Application # DefaultFilePath # DefaultFilePath # FullName
|
234
|
+
# Name # Parent # Path # Sessions
|
235
|
+
# TimeoutValue # Version # Quit # ViewStatus
|
236
|
+
# GetTypeInfoCount # GetTypeInfo # GetIDsOfNames # Invoke
|
237
|
+
# TimeoutValue
|
238
|
+
|
239
|
+
passport.Sessions(1).ole_methods
|
240
|
+
|
241
|
+
# QueryInterface # AddRef # Release # GetTypeInfoCount # Toolbars
|
242
|
+
# GetTypeInfo # GetIDsOfNames # Invoke # Application # Visible
|
243
|
+
# ColorScheme # ColorScheme # Connected # Connected # Activate
|
244
|
+
# EditScheme # EditScheme # FileTransferHostOS # FileTransferHostOS # SaveAs
|
245
|
+
# FileTransferScheme # FileTransferScheme # FullName # Height
|
246
|
+
# Height # HotSpotScheme # HotSpotScheme # KeyboardLocked
|
247
|
+
# KeyboardLocked # KeyMap # KeyMap # Left
|
248
|
+
# Name # PageRecognitionTime # PageRecognitionTime # Parent
|
249
|
+
# Path # QuickPads # Saved # Screen
|
250
|
+
# Top # Top # Type # Visible
|
251
|
+
# Width # Width # WindowState # WindowState
|
252
|
+
# Close # NavigateTo # ReceiveFile # Save
|
253
|
+
# SendFile # FileTransferOptions # GetTypeInfoCount # GetTypeInfo
|
254
|
+
# GetIDsOfNames # Invoke
|
255
|
+
|
256
|
+
passport.Sessions(1).Screen.ole_methods:
|
257
|
+
|
258
|
+
# QueryInterface # AddRef # Release # GetTypeInfoCount # GetTypeInfo
|
259
|
+
# GetIDsOfNames # Invoke # Application # Col # Col
|
260
|
+
# Cols # Name # OIA # Parent # Row
|
261
|
+
# Row # Rows # Selection # Updated # Area
|
262
|
+
# Copy # Cut # Delete # GetString # MoveRelative
|
263
|
+
# MoveTo # Paste # PutString # Search # Select
|
264
|
+
# SelectAll # SendInput # SendKeys # WaitForCursor # WaitForCursorMove
|
265
|
+
# WaitForKeys # WaitForStream # WaitForString # WaitHostQuiet # CheckTimeInterval
|
266
|
+
# CheckTimeInterval # WaitAfterAIDKey # WaitAfterAIDKey # GetTypeInfoCount # GetTypeInfo
|
267
|
+
# GetIDsOfNames # Invoke
|
268
|
+
|
269
|
+
### Personal communication
|
270
|
+
|
271
|
+
Documentation can be found [here](http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_6.0.0/com.ibm.pcomm.doc/books/html/host_access08.htm) Ibm did a good work!
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
require 'win32ole'
|
275
|
+
session = WIN32OLE.new("PComm.autECLSession")
|
276
|
+
session.SetConnectionByName 'A'
|
277
|
+
space = session.autECLPS
|
278
|
+
screen = session.autECLOIA
|
279
|
+
```
|
280
|
+
|
281
|
+
session.ole_methods:
|
282
|
+
|
283
|
+
# QueryInterface # AddRef # Release
|
284
|
+
# GetTypeInfoCount # GetTypeInfo # autECLWinMetrics
|
285
|
+
# GetIDsOfNames # Invoke # Handle
|
286
|
+
# autECLXfer # autECLPS # Started
|
287
|
+
# autECLOIA # Name # Ready
|
288
|
+
# ConnType # CodePage
|
289
|
+
# CommStarted # APIEnabled
|
290
|
+
# StartCommunication # StopCommunication
|
291
|
+
# SetConnectionByName # SetConnectionByHandle
|
292
|
+
# RegisterSessionEvent # UnregisterSessionEvent
|
293
|
+
# RegisterCommEvent # UnregisterCommEvent
|
294
|
+
# autECLPageSettings # autECLPrinterSettings
|
295
|
+
# GetTypeInfoCount # GetTypeInfo
|
296
|
+
# GetIDsOfNames # Invoke
|
297
|
+
|
298
|
+
session.autECLPS.ole_methods (presentation space):
|
299
|
+
|
300
|
+
# QueryInterface # AddRef # Release # GetTypeInfoCount
|
301
|
+
# GetTypeInfo # GetIDsOfNames # Invoke # autECLFieldList
|
302
|
+
# NumRows # NumCols # CursorPosRow # CursorPosCol
|
303
|
+
# SetConnectionByName # SetConnectionByHandle # SetCursorPos # GetTextRect
|
304
|
+
# SendKeys # SearchText # GetText # WaitWhileCursor
|
305
|
+
# SetText # Wait # StartMacro # WaitWhileStringInRect
|
306
|
+
# WaitForCursor # WaitWhileString # WaitForString # WaitForScreen
|
307
|
+
# WaitForStringInRect # WaitWhileAttrib # WaitForAttrib # UnregisterPSEvent
|
308
|
+
# WaitWhileScreen # CancelWaits # RegisterPSEvent # Handle
|
309
|
+
# RegisterKeyEvent # UnregisterKeyEvent # RegisterCommEvent # CommStarted
|
310
|
+
# UnregisterCommEvent # SetTextRect # Name # StopCommunication
|
311
|
+
# ConnType # CodePage # Started # Invoke
|
312
|
+
# APIEnabled # Ready # StartCommunication
|
313
|
+
# GetTypeInfoCount # GetTypeInfo # GetIDsOfNames
|
314
|
+
|
315
|
+
session.autECLOIA.ole_methods (screen):
|
316
|
+
|
317
|
+
# QueryInterface # AddRef # Release # GetTypeInfoCount # UpperShift
|
318
|
+
# GetTypeInfo # GetIDsOfNames # Invoke # Alphanumeric # CommErrorReminder
|
319
|
+
# APL # Katakana # Hiragana # DBCS # ConnType
|
320
|
+
# NumLock # Numeric # CapsLock # InsertMode # Ready
|
321
|
+
# MessageWaiting # InputInhibited # Name # Handle # Invoke
|
322
|
+
# CodePage # Started # CommStarted # APIEnabled
|
323
|
+
# SetConnectionByName # SetConnectionByHandle # StartCommunication # StopCommunication
|
324
|
+
# WaitForInputReady # WaitForSystemAvailable # WaitForAppAvailable # WaitForTransition
|
325
|
+
# CancelWaits # RegisterCommEvent # UnregisterCommEvent # RegisterOIAEvent
|
326
|
+
# UnregisterOIAEvent # GetTypeInfoCount # GetTypeInfo # GetIDsOfNames
|
327
|
+
|
328
|
+
|
329
|
+
## ToDo
|
330
|
+
|
331
|
+
* Improve unit test
|
332
|
+
* Improve static navigation
|
333
|
+
|
166
334
|
## License
|
167
335
|
|
168
336
|
The GNU Lesser General Public License, version 3.0 (LGPL-3.0)
|
data/lib/butler-mainframe.rb
CHANGED
@@ -30,26 +30,30 @@ require "mainframe/#{ButlerMainframe.configuration.host_gateway.to_s.downcase}"
|
|
30
30
|
|
31
31
|
ButlerMainframe::Settings.load!(File.join(ButlerMainframe.root,'lib','config','settings.yml'), :env => env)
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
require 'mainframe/customization/active_record'
|
34
|
+
# puts "Extending Host class with #{Host3270::ActiveRecord}" if debug
|
35
|
+
# Use monkey patch for 1.8 compatibility
|
36
|
+
class ButlerMainframe::Host
|
37
|
+
include Host3270::ActiveRecord
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
require 'mainframe/customization/generic_functions'
|
41
|
+
# puts "Extending Host class with #{Host3270::GenericFunctions}" if debug
|
42
|
+
class ButlerMainframe::Host
|
43
|
+
include Host3270::GenericFunctions
|
43
44
|
end
|
44
45
|
|
45
|
-
if
|
46
|
+
if defined?(Host3270::CustomFunctions)
|
46
47
|
# puts "Extending Host class with #{Host3270::CustomFunctions}" if debug
|
47
|
-
ButlerMainframe::Host
|
48
|
+
class ButlerMainframe::Host
|
49
|
+
include Host3270::CustomFunctions
|
50
|
+
end
|
48
51
|
end
|
49
52
|
|
50
53
|
=begin
|
51
54
|
# To test in irb
|
52
55
|
require 'butler-mainframe'
|
53
|
-
host=ButlerMainframe::Host.new
|
56
|
+
host=ButlerMainframe::Host.new(debug: :full)
|
54
57
|
host.scan_page
|
58
|
+
host.navigate :next
|
55
59
|
=end
|
data/lib/config/config.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
ButlerMainframe.configure do |config|
|
2
|
-
# config.language = :it #NOT READY YET
|
3
2
|
config.host_gateway = 'passport'
|
4
3
|
config.browser_path = 'c:/Program Files (x86)/Internet Explorer/iexplore.exe'
|
5
|
-
config.
|
4
|
+
config.session_url = 'https://ergmilict10/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
|
5
|
+
config.session_tag = 1
|
6
|
+
config.timeout = 3000
|
6
7
|
end
|
7
8
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
ButlerMainframe.configure do |config|
|
2
|
+
config.host_gateway = :passport
|
3
|
+
config.browser_path = 'c:/Program Files (x86)/Internet Explorer/iexplore.exe'
|
4
|
+
config.session_url = 'https://localhost/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
|
5
|
+
config.session_tag = 1
|
6
|
+
config.timeout = 3000
|
7
|
+
end
|
8
|
+
|
@@ -0,0 +1,7 @@
|
|
1
|
+
ButlerMainframe.configure do |config|
|
2
|
+
config.host_gateway = :pcomm
|
3
|
+
config.session_path = '"C:/Program Files (x86)/IBM/Personal Communications/pcsws.exe" "C:/Users/XXXXXXXXXX/AppData/Roaming/IBM/Personal Communications/host3270.ws"'
|
4
|
+
config.session_tag = 'A'
|
5
|
+
config.timeout = 6000
|
6
|
+
end
|
7
|
+
|
data/lib/config/settings.yml
CHANGED
@@ -2,12 +2,9 @@ defaults: &defaults
|
|
2
2
|
cics: 7
|
3
3
|
user: "IBM0001"
|
4
4
|
password: "password"
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
activerecord: yes
|
9
|
-
generic_functions: yes
|
10
|
-
custom_functions: yes
|
5
|
+
#foo: add every variable you need and use it with => ButlerMainframe::Settings.foo
|
6
|
+
# bar: sub variable are accessible with hash => ButlerMainframe::Settings.foo[:bar]
|
7
|
+
|
11
8
|
|
12
9
|
development:
|
13
10
|
<<: *defaults
|
@@ -0,0 +1,17 @@
|
|
1
|
+
defaults: &defaults
|
2
|
+
cics: 7
|
3
|
+
user: "IBM0001"
|
4
|
+
password: "password"
|
5
|
+
#foo: add every variable you need and use it with => ButlerMainframe::Settings.foo
|
6
|
+
# bar: sub variable are accessible with hash => ButlerMainframe::Settings.foo[:bar]
|
7
|
+
|
8
|
+
|
9
|
+
development:
|
10
|
+
<<: *defaults
|
11
|
+
|
12
|
+
test:
|
13
|
+
<<: *defaults
|
14
|
+
|
15
|
+
production:
|
16
|
+
<<: *defaults
|
17
|
+
cics: 4
|
data/lib/core/configuration.rb
CHANGED
@@ -2,13 +2,16 @@ module ButlerMainframe
|
|
2
2
|
class Configuration
|
3
3
|
attr_writer :allow_sign_up
|
4
4
|
|
5
|
-
attr_accessor :language, :host_gateway, :browser_path, :session_path
|
5
|
+
attr_accessor :language, :host_gateway, :browser_path, :session_url, :session_path, :session_tag, :timeout
|
6
6
|
|
7
7
|
def initialize
|
8
8
|
@language = :en
|
9
9
|
@host_gateway = nil
|
10
10
|
@browser_path = ""
|
11
|
+
@session_url = ""
|
11
12
|
@session_path = ""
|
13
|
+
@session_tag = nil
|
14
|
+
@timeout = 1000
|
12
15
|
end
|
13
16
|
|
14
17
|
end
|
@@ -1,16 +1,20 @@
|
|
1
1
|
require 'rails/generators/base'
|
2
|
+
require File.join(File.dirname(__FILE__), '../butler_mainframe')
|
2
3
|
|
3
4
|
module Butler
|
4
5
|
module Generators
|
5
6
|
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include ::ButlerMainframe::Base
|
8
|
+
|
6
9
|
desc "Installs ButlerMainframe configuration files"
|
7
10
|
source_root File.expand_path('../../', __FILE__)
|
11
|
+
class_option :emulator, :type => :string, :default => 'passport', :desc => "Choose your favorite emulator [passport]"
|
8
12
|
|
9
13
|
def copy_to_local
|
10
14
|
copy_file "butler/templates/custom_functions.rb", "config/initializers/butler_custom_functions.rb"
|
11
15
|
copy_file "../config/settings.yml", "config/butler.yml"
|
12
16
|
file = "config/initializers/butler.rb"
|
13
|
-
copy_file "../config/
|
17
|
+
copy_file "../config/config_EXAMPLE_#{options[:emulator]}.rb", file
|
14
18
|
append_file file do
|
15
19
|
<<-FILE.gsub(/^ /, '')
|
16
20
|
ButlerMainframe::Settings.load!(File.join(Rails.root,'config','butler.yml'), :env => Rails.env)
|
data/lib/mainframe/host_base.rb
CHANGED
@@ -11,12 +11,14 @@ module ButlerMainframe
|
|
11
11
|
|
12
12
|
def initialize options={}
|
13
13
|
options = {
|
14
|
-
:
|
14
|
+
:session_tag => ButlerMainframe.configuration.session_tag,
|
15
15
|
:wait => 0.01, #wait screen in seconds
|
16
16
|
:wait_debug => 2, #wait time for debug purpose
|
17
17
|
:debug => true,
|
18
18
|
:browser_path => ButlerMainframe.configuration.browser_path,
|
19
|
+
:session_url => ButlerMainframe.configuration.session_url,
|
19
20
|
:session_path => ButlerMainframe.configuration.session_path,
|
21
|
+
:timeout => ButlerMainframe.configuration.timeout,
|
20
22
|
:close_session => :evaluate
|
21
23
|
#:evaluate if the session is found will not be closed
|
22
24
|
#:never never close the session
|
@@ -26,8 +28,9 @@ module ButlerMainframe
|
|
26
28
|
@debug = options[:debug]
|
27
29
|
@wait = options[:wait]
|
28
30
|
@wait_debug = options[:wait_debug]
|
29
|
-
@
|
31
|
+
@session_tag = options[:session_tag]
|
30
32
|
@close_session = options[:close_session]
|
33
|
+
@timeout = options[:timeout]
|
31
34
|
@pid = nil
|
32
35
|
|
33
36
|
create_object options
|
@@ -59,7 +62,6 @@ module ButlerMainframe
|
|
59
62
|
|
60
63
|
# Execute keyboard command like PF1 or PA2 or ENTER ...
|
61
64
|
def exec_command cmd
|
62
|
-
cmd = "<#{cmd}>"
|
63
65
|
puts "Command: #{cmd}" if @debug
|
64
66
|
sub_exec_command cmd
|
65
67
|
wait_session
|
@@ -96,17 +98,18 @@ module ButlerMainframe
|
|
96
98
|
:clean_first_chars => nil
|
97
99
|
}.merge(options)
|
98
100
|
|
99
|
-
y=options[:y]
|
100
|
-
x=options[:x]
|
101
|
+
y = options[:y]
|
102
|
+
x = options[:x]
|
103
|
+
hooked_rows = 2
|
101
104
|
raise "Missing coordinates! y(row)=#{y} x(column)=#{x} " unless x && y
|
102
105
|
raise "Sorry, cannot write null values" unless text
|
103
106
|
|
104
107
|
bol_written = nil
|
105
108
|
if options[:hook]
|
106
|
-
(y-
|
107
|
-
if /#{options[:hook]}/ === scan_row(
|
108
|
-
puts "Change y from #{y} to #{
|
109
|
-
bol_written = write_clean_text_on_map text,
|
109
|
+
(y-hooked_rows..y+hooked_rows).each do |row_number|
|
110
|
+
if /#{options[:hook]}/ === scan_row(row_number, 1, MAX_TERMINAL_COLUMNS)
|
111
|
+
puts "Change y from #{y} to #{row_number} cause hook to:#{options[:hook]}" if row_number != y && @debug
|
112
|
+
bol_written = write_clean_text_on_map text, row_number, x, options
|
110
113
|
break
|
111
114
|
end
|
112
115
|
end
|
@@ -116,47 +119,76 @@ module ButlerMainframe
|
|
116
119
|
bol_written
|
117
120
|
end
|
118
121
|
|
119
|
-
#
|
122
|
+
# Return the coordinates of the cursor
|
120
123
|
def get_cursor_axes
|
121
124
|
sub_get_cursor_axes
|
122
125
|
end
|
123
126
|
|
127
|
+
# Move the cursor at given coordinates
|
128
|
+
def set_cursor_axes y, x, options={}
|
129
|
+
sub_set_cursor_axes y, x, options
|
130
|
+
end
|
131
|
+
|
124
132
|
private
|
125
133
|
|
126
134
|
# It creates the object calling subclass method
|
135
|
+
# It depends on the emulator chosen but typically the object is present after starting the terminal session
|
127
136
|
# These are the options with default values:
|
128
137
|
# :session => 1,
|
129
138
|
# :debug => true,
|
130
139
|
# :browser_path => ButlerMainframe::Settings.browser_path,
|
131
140
|
# :session_path => ButlerMainframe::Settings.session_path,
|
132
141
|
def create_object options={}
|
133
|
-
|
142
|
+
connection_attempts = 8
|
143
|
+
seconds_between_attempts = 2
|
144
|
+
|
145
|
+
sub_create_object options
|
146
|
+
|
147
|
+
# if the terminal is not found then we start it
|
134
148
|
unless sub_object_created?
|
135
|
-
puts "Session not found, starting new..." if @debug
|
149
|
+
puts "Session #{@session_tag} not found, starting new..." if @debug
|
150
|
+
|
151
|
+
executable, args = if options[:browser_path] && !options[:browser_path].empty?
|
152
|
+
[options[:browser_path], options[:session_url]]
|
153
|
+
elsif options[:session_path] && !options[:session_path].empty?
|
154
|
+
[options[:session_path], nil]
|
155
|
+
else
|
156
|
+
[nil, nil]
|
157
|
+
end
|
158
|
+
raise "Specify an executable in the configuration file!" unless executable
|
136
159
|
|
137
160
|
if /^1.8/ === RUBY_VERSION
|
138
|
-
Thread.new {system "#{
|
161
|
+
Thread.new {system "#{executable} #{args}"}
|
139
162
|
@pid = $?.pid if $?
|
140
163
|
else
|
141
164
|
#It works only on ruby 1.9+
|
142
|
-
@pid = Process.spawn
|
165
|
+
@pid = Process.spawn *[executable, args].compact
|
143
166
|
end
|
144
167
|
|
145
168
|
puts "Starting session with process id #{@pid}, wait please..." if @debug
|
146
169
|
sleep 2
|
147
|
-
|
148
|
-
puts "
|
170
|
+
connection_attempts.times do
|
171
|
+
puts "Detecting session #{@session_tag}, wait please..." if @debug
|
149
172
|
sub_create_object
|
150
|
-
sub_object_created? ? break : sleep(
|
173
|
+
sub_object_created? ? break : sleep(seconds_between_attempts)
|
151
174
|
end
|
152
175
|
@session_started_by_me = true
|
153
176
|
end
|
154
177
|
|
155
|
-
|
178
|
+
raise "Session #{@session_tag} not started. Check the session #{options[:browser_path]} #{options[:session_path]}" unless sub_object_created?
|
179
|
+
|
180
|
+
unless sub_object_ready?
|
181
|
+
connection_attempts.times do
|
182
|
+
puts "Waiting for the session to be ready..." if @debug
|
183
|
+
sub_object_ready? ? break : sleep(seconds_between_attempts)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
if sub_object_ready?
|
156
188
|
puts "** Connection established with #{sub_name} **"
|
157
189
|
puts "Session full name: #{sub_fullname}" if @debug == :full
|
158
190
|
else
|
159
|
-
raise "Connection refused. Check
|
191
|
+
raise "Connection refused. Check session #{@session_tag} with process id #{@pid}"
|
160
192
|
end
|
161
193
|
|
162
194
|
rescue
|
@@ -194,7 +226,7 @@ module ButlerMainframe
|
|
194
226
|
|
195
227
|
# Write a text on the screen
|
196
228
|
# It also contains the logic to control the successful writing
|
197
|
-
def write_text_on_map
|
229
|
+
def write_text_on_map text, y, x, options={}
|
198
230
|
options = {
|
199
231
|
:check => true,
|
200
232
|
:raise_error_on_check => true,
|
@@ -203,7 +235,7 @@ module ButlerMainframe
|
|
203
235
|
raise "Impossible to write beyond row #{MAX_TERMINAL_ROWS}" if y > MAX_TERMINAL_ROWS
|
204
236
|
raise "Impossible to write beyond column #{MAX_TERMINAL_COLUMNS}" if x > MAX_TERMINAL_COLUMNS
|
205
237
|
raise "Impossible to write a null value" unless text
|
206
|
-
sub_write_text text, y, x
|
238
|
+
sub_write_text text, y, x, :check_protect => options[:check]
|
207
239
|
# It returns the function's result
|
208
240
|
if options[:check]
|
209
241
|
# It expects the string is present on the session at the specified coordinates
|
data/lib/mainframe/passport.rb
CHANGED
@@ -2,64 +2,98 @@ require 'win32ole'
|
|
2
2
|
require 'mainframe/host_base'
|
3
3
|
|
4
4
|
# This class use Rocket 3270 emulator API
|
5
|
-
# http://www.
|
5
|
+
# http://www.zephyrcorp.com/legacy-integration/Documentation/passport_host_integration_objects.htm
|
6
6
|
module ButlerMainframe
|
7
7
|
class Host < HostBase
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
-
|
11
|
+
# Create objects from emulator library
|
12
|
+
def sub_create_object options={}
|
12
13
|
str_obj = 'PASSPORT.System'
|
13
14
|
puts "#{Time.now.strftime "%H:%M:%S"} Creating object #{str_obj}..." if @debug == :full
|
14
15
|
@action = WIN32OLE.new(str_obj)
|
16
|
+
@screen = @action.Sessions(@session_tag).Screen if sub_object_created?
|
15
17
|
end
|
16
18
|
|
19
|
+
# Check is session is started
|
17
20
|
def sub_object_created?
|
18
|
-
|
19
|
-
|
21
|
+
res = @action && @action.Sessions(@session_tag)
|
22
|
+
puts "#{Time.now.strftime "%H:%M:%S"} Terminal successfully detected" if @debug == :full && res
|
23
|
+
res
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check is session is operative
|
27
|
+
def sub_object_ready?
|
28
|
+
res = @action.Sessions(@session_tag).Connected == -1
|
29
|
+
puts "#{Time.now.strftime "%H:%M:%S"} Session ready" if @debug == :full && res
|
30
|
+
res
|
20
31
|
end
|
21
32
|
|
22
33
|
def sub_name
|
23
|
-
@action.Sessions(@
|
34
|
+
"#{@action.Name} #{@action.Sessions(@session_tag).Name}"
|
24
35
|
end
|
25
36
|
|
26
37
|
def sub_fullname
|
27
|
-
@action.Sessions(@
|
38
|
+
"#{sub_name} #{@action.Sessions(@session_tag).FullName}"
|
28
39
|
end
|
29
40
|
|
30
41
|
#Ends the connection and closes the session
|
31
42
|
def sub_close_session
|
32
|
-
@action.Sessions(@
|
43
|
+
@action.Sessions(@session_tag).Close
|
33
44
|
@action.Quit
|
34
45
|
@action = nil
|
35
46
|
end
|
36
47
|
|
37
48
|
#Execute keyboard command like PF1 or PA2 or ENTER ...
|
38
|
-
def sub_exec_command cmd
|
39
|
-
|
40
|
-
@
|
49
|
+
def sub_exec_command cmd, options={}
|
50
|
+
# Cast cmd to_s cause it could be passed as label
|
51
|
+
@screen.SendKeys "<#{cmd}>"
|
52
|
+
@screen.WaitHostQuiet
|
41
53
|
end
|
42
54
|
|
43
55
|
#It reads one line part of the screen
|
44
56
|
def sub_scan_row y, x, len
|
45
|
-
@
|
57
|
+
@screen.GetString(y, x, len)
|
46
58
|
end
|
47
59
|
|
48
60
|
#It reads a rectangle on the screen
|
49
61
|
def sub_scan_area y1, x1, y2, x2
|
50
|
-
@
|
62
|
+
@screen.Area(y1, x1, y2, x2).Value
|
51
63
|
end
|
52
64
|
|
65
|
+
# Get cursor coordinates
|
53
66
|
def sub_get_cursor_axes
|
54
|
-
[@
|
67
|
+
[@screen.Col, @screen.Row]
|
68
|
+
end
|
69
|
+
|
70
|
+
# Move cursor to given coordinates
|
71
|
+
def sub_set_cursor_axes y, x, options={}
|
72
|
+
options = {
|
73
|
+
:wait => true
|
74
|
+
}.merge(options)
|
75
|
+
@screen.MoveTo y, x
|
76
|
+
@screen.WaitForCursor(y, x) if options[:wait]
|
55
77
|
end
|
56
78
|
|
57
|
-
|
58
|
-
|
79
|
+
# Write text on the screen at given coordinates
|
80
|
+
# :check_protect => true add sensitivity to protected areas
|
81
|
+
def sub_write_text text, y, x, options={}
|
82
|
+
options = {
|
83
|
+
:check_protect => true
|
84
|
+
}.merge(options)
|
85
|
+
if options[:check_protect]
|
86
|
+
# This method is sensitive to protected areas
|
87
|
+
sub_set_cursor_axes y, x
|
88
|
+
@screen.SendKeys text
|
89
|
+
else
|
90
|
+
@screen.PutString(text, y, x)
|
91
|
+
end
|
59
92
|
end
|
60
93
|
|
94
|
+
# Wait text at given coordinates and wait the session is available again
|
61
95
|
def sub_wait_for_string text, y, x
|
62
|
-
@
|
96
|
+
@screen.WaitForString(text, y, x).Value == -1
|
63
97
|
end
|
64
98
|
|
65
99
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'win32ole'
|
2
|
+
require 'mainframe/host_base'
|
3
|
+
|
4
|
+
# This class use IBM personal communication
|
5
|
+
# http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_6.0.0/welcome.html
|
6
|
+
# http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_6.0.0/com.ibm.pcomm.doc/books/html/host_access08.htm
|
7
|
+
module ButlerMainframe
|
8
|
+
class Host < HostBase
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# Create objects from emulator library
|
13
|
+
def sub_create_object options={}
|
14
|
+
str_obj = 'PComm.autECLSession'
|
15
|
+
puts "#{Time.now.strftime "%H:%M:%S"} Creating object #{str_obj}..." if @debug == :full
|
16
|
+
@action = WIN32OLE.new(str_obj)
|
17
|
+
@action.SetConnectionByName @session_tag
|
18
|
+
@space = @action.autECLPS
|
19
|
+
@screen = @action.autECLOIA
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check is session is started
|
23
|
+
def sub_object_created?
|
24
|
+
res = @action && @action.CommStarted
|
25
|
+
puts "#{Time.now.strftime "%H:%M:%S"} Terminal successfully detected" if @debug == :full && res
|
26
|
+
res
|
27
|
+
end
|
28
|
+
|
29
|
+
# Check is session is operative
|
30
|
+
def sub_object_ready?
|
31
|
+
res = @action.Ready
|
32
|
+
puts "#{Time.now.strftime "%H:%M:%S"} Session ready" if @debug == :full && res
|
33
|
+
res
|
34
|
+
end
|
35
|
+
|
36
|
+
def sub_name
|
37
|
+
"PComm #{@action.Name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def sub_fullname
|
41
|
+
"#{sub_name} #{@action.ConnType}"
|
42
|
+
end
|
43
|
+
|
44
|
+
#Ends the connection and closes the session
|
45
|
+
def sub_close_session
|
46
|
+
@action.StopCommunication
|
47
|
+
@action = nil
|
48
|
+
Process.kill 9, @pid if @pid
|
49
|
+
end
|
50
|
+
|
51
|
+
#Execute keyboard command like PF1 or PA2 or ENTER ...
|
52
|
+
def sub_exec_command cmd, options={}
|
53
|
+
# Cast cmd to_s cause it could be passed as label
|
54
|
+
@space.SendKeys "[#{cmd}]"
|
55
|
+
@screen.WaitForAppAvailable @timeout
|
56
|
+
@screen.WaitForInputReady @timeout
|
57
|
+
end
|
58
|
+
|
59
|
+
#It reads one line part of the screen
|
60
|
+
def sub_scan_row y, x, len
|
61
|
+
@space.GetText(y, x, len)
|
62
|
+
end
|
63
|
+
|
64
|
+
#It reads a rectangle on the screen
|
65
|
+
def sub_scan_area y1, x1, y2, x2
|
66
|
+
@space.GetTextRect(y1, x1, y2, x2)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Get cursor coordinates
|
70
|
+
def sub_get_cursor_axes
|
71
|
+
[@space.CursorPosCol, @space.CursorPosRow]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Move cursor to given coordinates
|
75
|
+
def sub_set_cursor_axes y, x, options={}
|
76
|
+
options = {
|
77
|
+
:wait => true
|
78
|
+
}.merge(options)
|
79
|
+
@space.SetCursorPos y, x
|
80
|
+
@space.WaitForCursor(y, x, @timeout) if options[:wait]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Write text on the screen at given coordinates
|
84
|
+
# :check_protect => true add sensitivity to protected areas
|
85
|
+
def sub_write_text text, y, x, options={}
|
86
|
+
options = {
|
87
|
+
:check_protect => true
|
88
|
+
}.merge(options)
|
89
|
+
if options[:check_protect]
|
90
|
+
# This method is sensitive to protected areas
|
91
|
+
@space.SendKeys(text, y, x)
|
92
|
+
else
|
93
|
+
@space.SetText(text, y, x)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Wait text at given coordinates and wait the session is available again
|
98
|
+
def sub_wait_for_string text, y, x
|
99
|
+
@space.WaitForString text, y, x, @timeout
|
100
|
+
@screen.WaitForInputReady @timeout
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
data/rakefile
ADDED
data/test/test.rake
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
namespace :butler do
|
4
|
+
namespace :mainframe do
|
5
|
+
|
6
|
+
desc "Test butler mainframe gem"
|
7
|
+
task(:test) do |task_name, args|
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'butler-mainframe'
|
11
|
+
host = ButlerMainframe::Host.new
|
12
|
+
str_screen1 = host.scan_page
|
13
|
+
raise 'host.scan_page' if str_screen1.empty?
|
14
|
+
host.navigate :next
|
15
|
+
str_screen2 = host.scan_page
|
16
|
+
raise 'host.navigate :next' if str_screen1 == str_screen2
|
17
|
+
host.close_session
|
18
|
+
|
19
|
+
rescue
|
20
|
+
puts $!.message
|
21
|
+
exit(9)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: butler-mainframe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marco Mastrodonato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: This gem provides a virtual butler which can perform your custom tasks
|
14
14
|
on a 3270 emulator. You just have to choose your emulator (atm only one choice)
|
@@ -25,7 +25,10 @@ files:
|
|
25
25
|
- README.md
|
26
26
|
- lib/butler-mainframe.rb
|
27
27
|
- lib/config/config.rb
|
28
|
+
- lib/config/config_EXAMPLE_passport.rb
|
29
|
+
- lib/config/config_EXAMPLE_pcomm.rb
|
28
30
|
- lib/config/settings.yml
|
31
|
+
- lib/config/settings_EXAMPLE.yml
|
29
32
|
- lib/core/configuration.rb
|
30
33
|
- lib/core/configuration_dynamic.rb
|
31
34
|
- lib/generators/butler/USAGE
|
@@ -36,7 +39,10 @@ files:
|
|
36
39
|
- lib/mainframe/customization/generic_functions.rb
|
37
40
|
- lib/mainframe/host_base.rb
|
38
41
|
- lib/mainframe/passport.rb
|
42
|
+
- lib/mainframe/pcomm.rb
|
39
43
|
- lib/vendor/deep_symbolize.rb
|
44
|
+
- rakefile
|
45
|
+
- test/test.rake
|
40
46
|
homepage: https://github.com/marcomd/butler-mainframe
|
41
47
|
licenses:
|
42
48
|
- LGPL-3.0
|
@@ -56,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
62
|
- !ruby/object:Gem::Version
|
57
63
|
version: '0'
|
58
64
|
requirements:
|
59
|
-
-
|
65
|
+
- Be awake and rested
|
60
66
|
rubyforge_project:
|
61
67
|
rubygems_version: 2.2.5
|
62
68
|
signing_key:
|