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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4002cfeff0a68711d48ded02dabdcfb246dfd8f
4
- data.tar.gz: d48bb6bd6986aa9b12cb2fe1695de8a73639ccbc
3
+ metadata.gz: 2be056e6d5c2cd0b24cdd1ba7c27a92f2e11d295
4
+ data.tar.gz: 0265a490b0c3891b40e0c336c05e4d73563d9150
5
5
  SHA512:
6
- metadata.gz: efbafb8fc1ae9f9416294f4f2b8dfd9e863d49f2651d6b16771ed9bf593466a4688f673d888a679c298f4b3c0e62b8e28ab520be9d99f54b28a8f0db44cf3d0d
7
- data.tar.gz: 5e4043550886c4bfc8540c8044da940f326e98fe3c3f7160f415d180bdca608c0fb80e12146f584f5f27cadd7e380b6598af7ea723ef9eb5cc43ccd98aa83ce3
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
- * Several little improvements
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)
@@ -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
- if ButlerMainframe::Settings.modules_included[:activerecord]
34
- require 'mainframe/customization/active_record'
35
- # puts "Extending Host class with #{Host3270::ActiveRecord}" if debug
36
- ButlerMainframe::Host.include Host3270::ActiveRecord
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
- if ButlerMainframe::Settings.modules_included[:generic_functions]
40
- require 'mainframe/customization/generic_functions'
41
- # puts "Extending Host class with #{Host3270::GenericFunctions}" if debug
42
- ButlerMainframe::Host.include Host3270::GenericFunctions
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 ButlerMainframe::Settings.modules_included[:custom_functions] && defined?(Host3270::CustomFunctions)
46
+ if defined?(Host3270::CustomFunctions)
46
47
  # puts "Extending Host class with #{Host3270::CustomFunctions}" if debug
47
- ButlerMainframe::Host.include Host3270::CustomFunctions
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.session_path = 'https://ergmilict10/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
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
+
@@ -2,12 +2,9 @@ defaults: &defaults
2
2
  cics: 7
3
3
  user: "IBM0001"
4
4
  password: "password"
5
- life_user: "LIFE1"
6
- life_password: "password"
7
- modules_included:
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
@@ -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/config.rb", file
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)
@@ -156,4 +156,6 @@ module Host3270
156
156
  end
157
157
  end
158
158
 
159
- ButlerMainframe::Host.include Host3270::CustomFunctions
159
+ class ButlerMainframe::Host
160
+ include Host3270::CustomFunctions
161
+ end
@@ -10,6 +10,8 @@ module Host3270
10
10
 
11
11
  def do_quit; exec_command "CLEAR" end
12
12
 
13
+ def do_erase; exec_command "ERASE EOF" end
14
+
13
15
  def destination_list
14
16
  [
15
17
  :company_menu,
@@ -11,12 +11,14 @@ module ButlerMainframe
11
11
 
12
12
  def initialize options={}
13
13
  options = {
14
- :session => 1,
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
- @session = options[:session]
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-2..y+2).each do |y_riga|
107
- if /#{options[:hook]}/ === scan_row(y_riga, 1, MAX_TERMINAL_COLUMNS)
108
- puts "Change y from #{y} to #{y_riga} cause hook to:#{options[:hook]}" if y_riga != y && @debug
109
- bol_written = write_clean_text_on_map text, y_riga, x, options
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
- # It returns the coordinates of the cursor
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
- sub_create_object
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 "#{options[:browser_path]} #{options[:session_path]}"}
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 "#{options[:browser_path]}", "#{options[:session_path]}"
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
- 5.times do
148
- puts "Wait please..." if @debug
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(10)
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
- if sub_object_created?
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 the session #{options[:session]} and it was on the initial screen."
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(text, y, x, options={})
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
@@ -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.rocketsoftware.com/products/rocket-passport-web-to-host
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
- def sub_create_object
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
- puts "#{Time.now.strftime "%H:%M:%S"} Terminal successfully detected" if @debug == :full
19
- @action.Sessions(@session)
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(@session).Name
34
+ "#{@action.Name} #{@action.Sessions(@session_tag).Name}"
24
35
  end
25
36
 
26
37
  def sub_fullname
27
- @action.Sessions(@session).FullName
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(@session).Close
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
- @action.Sessions(@session).Screen.SendKeys cmd
40
- @action.Sessions(@session).Screen.WaitHostQuiet
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
- @action.Sessions(@session).Screen.GetString(y, x, len)
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
- @action.Sessions(@session).Screen.Area(y1, x1, y2, x2).Value
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
- [@action.Sessions(@session).Screen.Col, @action.Sessions(@session).Screen.Row]
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
- def sub_write_text text, y, x
58
- @action.Sessions(@session).Screen.PutString(text, y, x)
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
- @action.Sessions(@session).Screen.WaitForString(text, y, x).Value == -1
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
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/test/*.rake"].sort.each { |ext| load ext }
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.6
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-12 00:00:00.000000000 Z
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
- - So many patience
65
+ - Be awake and rested
60
66
  rubyforge_project:
61
67
  rubygems_version: 2.2.5
62
68
  signing_key: