butler-mainframe 0.0.6 → 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 +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:
|