te3270 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ require 'te3270/emulators/extra'
2
+ require 'te3270/emulators/quick3270'
3
+ require 'te3270/emulators/x3270'
4
+
5
+ module TE3270
6
+ #
7
+ # Provides a mapping between a key used in the +emulator_for+ method
8
+ # and the class that implements the access to the emulator.
9
+ #
10
+ module EmulatorFactory
11
+
12
+ EMULATORS = {
13
+ extra: TE3270::Emulators::Extra,
14
+ quick3270: TE3270::Emulators::Quick3270,
15
+ x3270: TE3270::Emulators::X3270
16
+ }
17
+
18
+ def self.emulator_for(platform)
19
+ EMULATORS[platform]
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,227 @@
1
+ module TE3270
2
+ module Emulators
3
+ #
4
+ # This class has the code necessary to communicate with the terminal emulator called EXTRA! X-treme.
5
+ # You can use this emulator by providing the +:extra+ parameter to the constructor of your screen
6
+ # object or by passing the same value to the +emulator_for+ method on the +TE3270+ module.
7
+ #
8
+ class Extra
9
+
10
+ attr_writer :session_file, :visible, :window_state, :max_wait_time
11
+
12
+
13
+ def initialize
14
+ if jruby?
15
+ require 'jruby-win32ole'
16
+ require 'java'
17
+ include_class 'java.awt.Dimension'
18
+ include_class 'java.awt.Rectangle'
19
+ include_class 'java.awt.Robot'
20
+ include_class 'java.awt.Toolkit'
21
+ include_class 'java.awt.event.InputEvent'
22
+ include_class 'java.awt.image.BufferedImage'
23
+ include_class 'javax.imageio.ImageIO'
24
+ else
25
+ require 'win32ole'
26
+ require 'win32/screenshot'
27
+ end
28
+ end
29
+
30
+ #
31
+ # Creates a method to connect to Extra System. This method expects a block in which certain
32
+ # platform specific values can be set. Extra can take the following parameters.
33
+ #
34
+ # * session_file - this value is required and should be the filename of the session.
35
+ # * visible - determines if the emulator is visible or not. If not set it will default to +true+.
36
+ # * window_state - determines the state of the session window. Valid values are +:minimized+,
37
+ # +:normal+, and +:maximized+. If not set it will default to +:normal+.
38
+ #
39
+ # @example Example calling screen object constructor with a block
40
+ # screen_object = MyScreenObject.new(:extra)
41
+ # screen_object.connect do |emulator|
42
+ # emulator.session_file = 'path_to_session_file'
43
+ # emulator.visible = true
44
+ # emulator.window_state = :maximized
45
+ # end
46
+ #
47
+ def connect
48
+ start_extra_system
49
+
50
+ yield self if block_given?
51
+ raise 'The session file must be set in a block when calling connect with the Extra emulator.' if @session_file.nil?
52
+ open_session
53
+ @screen = session.Screen
54
+ @area = screen.SelectAll
55
+ end
56
+
57
+ #
58
+ # Disconnects the Extra System connection
59
+ #
60
+ def disconnect
61
+ session.Close
62
+ end
63
+
64
+ #
65
+ # Extracts text of specified length from a start point.
66
+ #
67
+ # @param [Fixnum] row the x coordinate of location on the screen.
68
+ # @param [Fixnum] column the y coordinate of location on the screen.
69
+ # @param [Fixnum] length the length of string to extract
70
+ # @return [String]
71
+ #
72
+ def get_string(row, column, length)
73
+ screen.GetString(row, column, length)
74
+ end
75
+
76
+ #
77
+ # Puts string at the coordinates specified.
78
+ #
79
+ # @param [String] str the string to set
80
+ # @param [Fixnum] row the x coordinate of the location on the screen.
81
+ # @param [Fixnum] column the y coordinate of the location on the screen.
82
+ #
83
+ def put_string(str, row, column)
84
+ screen.PutString(str, row, column)
85
+ quiet_period
86
+ end
87
+
88
+ #
89
+ # Sends keystrokes to the host, including function keys.
90
+ #
91
+ # @param [String] keys keystokes up to 255 in length
92
+ #
93
+ def send_keys(keys)
94
+ screen.SendKeys(keys)
95
+ quiet_period
96
+ end
97
+
98
+ #
99
+ # Wait for the string to appear at the specified location
100
+ #
101
+ # @param [String] str the string to wait for
102
+ # @param [Fixnum] row the x coordinate of location
103
+ # @param [Fixnum] column the y coordinate of location
104
+ #
105
+ def wait_for_string(str, row, column)
106
+ wait_for do
107
+ screen.WaitForString(str, row, column)
108
+ end
109
+ end
110
+
111
+ #
112
+ # Waits for the host to not send data for a specified number of seconds
113
+ #
114
+ # @param [Fixnum] seconds the maximum number of seconds to wait
115
+ #
116
+ def wait_for_host(seconds)
117
+ wait_for(seconds) do
118
+ screen.WaitHostQuiet
119
+ end
120
+ end
121
+
122
+ #
123
+ # Waits until the cursor is at the specified location.
124
+ #
125
+ # @param [Fixnum] row the x coordinate of the location
126
+ # @param [Fixnum] column the y coordinate of the location
127
+ #
128
+ def wait_until_cursor_at(row, column)
129
+ wait_for do
130
+ screen.WaitForCursor(row, column)
131
+ end
132
+ end
133
+
134
+ #
135
+ # Creates a method to take screenshot of the active screen. If you have set the +:visible+
136
+ # property to false it will be made visible prior to taking the screenshot and then changed
137
+ # to invisible after.
138
+ #
139
+ # @param [String] filename the path and name of the screenshot file to be saved
140
+ #
141
+ def screenshot(filename)
142
+ File.delete(filename) if File.exists?(filename)
143
+ session.Visible = true unless visible
144
+
145
+ if jruby?
146
+ toolkit = Toolkit::getDefaultToolkit()
147
+ screen_size = toolkit.getScreenSize()
148
+ rect = Rectangle.new(screen_size)
149
+ robot = Robot.new
150
+ image = robot.createScreenCapture(rect)
151
+ f = java::io::File.new(filename)
152
+ ImageIO::write(image, "png", f)
153
+ else
154
+ hwnd = session.WindowHandle
155
+ Win32::Screenshot::Take.of(:window, hwnd: hwnd).write(filename)
156
+ end
157
+
158
+ session.Visible = false unless visible
159
+ end
160
+
161
+ #
162
+ # Returns the text of the active screen
163
+ #
164
+ # @return [String]
165
+ #
166
+ def text
167
+ area.Value
168
+ end
169
+
170
+ private
171
+
172
+ attr_reader :system, :sessions, :session, :screen, :area
173
+
174
+ WINDOW_STATES = {
175
+ minimized: 0,
176
+ normal: 1,
177
+ maximized: 2
178
+ }
179
+
180
+ def wait_for(seconds = system.TimeoutValue / 1000)
181
+ wait_collection = yield
182
+ wait_collection.Wait(seconds * 1000)
183
+ end
184
+
185
+ def quiet_period
186
+ wait_for_host(max_wait_time)
187
+ end
188
+
189
+ def max_wait_time
190
+ @max_wait_time ||= 1
191
+ end
192
+
193
+ def window_state
194
+ @window_state.nil? ? 1 : WINDOW_STATES[@window_state]
195
+ end
196
+
197
+ def visible
198
+ @visible.nil? ? true : @visible
199
+ end
200
+
201
+ def hide_splash_screen
202
+ version = system.Version
203
+ sessions.VisibleOnStartup = true if version.to_i >= 9
204
+ end
205
+
206
+ def open_session
207
+ @sessions = system.Sessions
208
+ hide_splash_screen
209
+ @session = sessions.Open @session_file
210
+ @session.WindowState = window_state
211
+ @session.Visible = visible
212
+ end
213
+
214
+ def start_extra_system
215
+ begin
216
+ @system = WIN32OLE.new('EXTRA.System')
217
+ rescue Exception => e
218
+ $stderr.puts e
219
+ end
220
+ end
221
+
222
+ def jruby?
223
+ RUBY_PLATFORM == 'java'
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,180 @@
1
+ if Gem.win_platform?
2
+ require 'win32ole'
3
+ require 'win32/screenshot'
4
+ end
5
+
6
+ module TE3270
7
+ module Emulators
8
+
9
+ #
10
+ # This class has the code necessary to communicate with the terminal emulator called Quick3270.
11
+ # You can use this emulator by providing the +:quick+ parameter to the constructor of your screen
12
+ # object or by passing the same value to the +emulator_for+ method on the +TE3270+ module.
13
+ #
14
+
15
+ class Quick3270
16
+
17
+ attr_writer :session_file, :visible, :max_wait_time
18
+
19
+ #
20
+ # Creates a method to connect to Quick System. This method expects a block in which certain
21
+ # platform specific values can be set. Quick can take the following parameters.
22
+ #
23
+ # * session_file - this value is required and should be the filename of the session.
24
+ # * visible - determines if the emulator is visible or not. If not set it will default to +true+.
25
+ #
26
+ # @example Example calling screen object constructor with a block
27
+ # screen_object = MyScreenObject.new(:quick3270)
28
+ # screen_object.connect do |emulator|
29
+ # emulator.session_file = 'path_to_session_file'
30
+ # emulator.visible = true
31
+ # end
32
+ #
33
+ def connect
34
+ start_quick_system
35
+ yield self if block_given?
36
+ raise "The session file must be set in a block when calling connect with the Quick3270 emulator." if @session_file.nil?
37
+ establish_session
38
+ end
39
+
40
+ #
41
+ # Disconnects the Quick System connection
42
+ #
43
+ def disconnect
44
+ session.Disconnect
45
+ system.Application.Quit
46
+ end
47
+
48
+ #
49
+ # Extracts text of specified length from a start point.
50
+ #
51
+ # @param [Fixnum] row the x coordinate of location on the screen.
52
+ # @param [Fixnum] column the y coordinate of location on the screen.
53
+ # @param [Fixnum] length the length of string to extract
54
+ # @return [String]
55
+ #
56
+ def get_string(row, column, length)
57
+ screen.GetString(row, column, length)
58
+ end
59
+
60
+ #
61
+ # Puts string at the coordinates specified.
62
+ #
63
+ # @param [String] str the string to set
64
+ # @param [Fixnum] row the x coordinate of the location on the screen.
65
+ # @param [Fixnum] column the y coordinate of the location on the screen.
66
+ #
67
+ def put_string(str, row, column)
68
+ screen.MoveTo(row, column)
69
+ screen.PutString(str)
70
+ quiet_period
71
+ end
72
+
73
+ #
74
+ # Sends keystrokes to the host, including function keys.
75
+ #
76
+ # @param [String] keys keystokes up to 255 in length
77
+ #
78
+ def send_keys(keys)
79
+ screen.SendKeys(keys)
80
+ quiet_period
81
+ end
82
+
83
+ #
84
+ # Wait for the string to appear at the specified location
85
+ #
86
+ # @param [String] str the string to wait for
87
+ # @param [Fixnum] row the x coordinate of location
88
+ # @param [Fixnum] column the y coordinate of location
89
+ #
90
+ def wait_for_string(str, row, column)
91
+ screen.WaitForString(str, row, column)
92
+ end
93
+
94
+ #
95
+ # Waits for the host to not send data for a specified number of seconds
96
+ #
97
+ # @param [Fixnum] seconds the maximum number of seconds to wait
98
+ #
99
+ def wait_for_host(seconds)
100
+ screen.WaitHostQuiet(seconds * 1000)
101
+ end
102
+
103
+ #
104
+ # Waits until the cursor is at the specified location.
105
+ #
106
+ # @param [Fixnum] row the x coordinate of the location
107
+ # @param [Fixnum] column the y coordinate of the location
108
+ #
109
+ def wait_until_cursor_at(row, column)
110
+ screen.WaitForCursor(row, column)
111
+ end
112
+
113
+ #
114
+ # Creates a method to take screenshot of the active screen. If you have set the +:visible+
115
+ # property to false it will be made visible prior to taking the screenshot and then changed
116
+ # to invisible after.
117
+ #
118
+ # @param [String] filename the path and name of the screenshot file to be saved
119
+ #
120
+ def screenshot(filename)
121
+ File.delete(filename) if File.exists?(filename)
122
+ system.Visible = true unless visible
123
+ title = system.WindowTitle
124
+ Win32::Screenshot::Take.of(:window, title: title).write(filename)
125
+ system.Visible = false unless visible
126
+ end
127
+
128
+ #
129
+ # Returns the text of the active screen
130
+ #
131
+ # @return [String]
132
+ #
133
+ def text
134
+ rows = screen.Rows
135
+ columns = screen.Cols
136
+ result = ''
137
+ rows.times { |row| result += "#{screen.GetString(row+1, 1, columns)}\\n" }
138
+ result
139
+ end
140
+
141
+ private
142
+
143
+ attr_reader :system, :session, :screen
144
+
145
+ def quiet_period
146
+ screen.WaitHostQuiet(max_wait_time)
147
+ end
148
+
149
+ def max_wait_time
150
+ @max_wait_time ||= 3000
151
+ end
152
+
153
+ def visible
154
+ @visible.nil? ? true : @visible
155
+ end
156
+
157
+ def start_quick_system
158
+ begin
159
+ @system = WIN32OLE.new('Quick3270.Application')
160
+ rescue Exception => e
161
+ $stderr.puts e
162
+ end
163
+ end
164
+
165
+ def establish_session
166
+ system.Visible = visible
167
+ @session = system.ActiveSession
168
+ session.Open @session_file
169
+ @screen = session.Screen
170
+ session.Connect
171
+ connected = session.Connected
172
+ while not connected
173
+ screen.WaitHostQuiet(1000)
174
+ connected = session.Connected
175
+ end
176
+ end
177
+
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,185 @@
1
+ require 'open3'
2
+
3
+ module TE3270
4
+ module Emulators
5
+ #
6
+ # This class has the code necessary to communicate with the terminal emulator called EXTRA! X-treme.
7
+ # You can use this emulator by providing the +:extra+ parameter to the constructor of your screen
8
+ # object or by passing the same value to the +emulator_for+ method on the +TE3270+ module.
9
+ #
10
+ class X3270
11
+
12
+ attr_writer :executable_command, :host, :max_wait_time, :trace
13
+
14
+ #
15
+ # Creates a method to connect to x3270. This method expects a block in which certain
16
+ # platform specific values can be set. Extra can take the following parameters.
17
+ #
18
+ # * executable_command - this value is required and should be the name of the ws3270 executable
19
+ # * host - this is required and is the (DNS) name of the host to connect to
20
+ # * max_wait_time - max time to wait in wait_for_string (defaults to 10 if not specified)
21
+ #
22
+ # @example Example x3270 object constructor with a block
23
+ # screen_object = MyScreenObject.new(:x3270)
24
+ # screen_object.connect do |emulator|
25
+ # emulator.executable_command = 'path_to_executable'
26
+ # emulator.host = 'host.example.com'
27
+ # emulator.max_wait_time = 5
28
+ # end
29
+ #
30
+ def connect
31
+ @max_wait_time = 10
32
+ @trace = false
33
+ yield self if block_given?
34
+ raise 'The executable command must be set in a block when calling connect with the X3270 emulator.' if @executable_command.nil?
35
+ raise 'The host must be set in a block when calling connect with the X3270 emulator.' if @host.nil?
36
+ start_x3270_system
37
+ end
38
+
39
+ #
40
+ # Disconnects the x3270 System connection
41
+ #
42
+ def disconnect
43
+ @x3270_input.close
44
+ @x3270_output.close
45
+ end
46
+
47
+ #
48
+ # Extracts text of specified length from a start point.
49
+ #
50
+ # @param [Fixnum] row the x coordinate of location on the screen.
51
+ # @param [Fixnum] column the y coordinate of location on the screen.
52
+ # @param [Fixnum] length the length of string to extract
53
+ # @return [String]
54
+ #
55
+ def get_string row, column, length
56
+ x_send "ascii(#{row-1},#{column-1},#{length})"
57
+ result_string = ""
58
+ while line = x_read do
59
+ break if line == 'ok'
60
+ result_string = result_string + line[6..-1] if line[0..5] == 'data: '
61
+ end
62
+ result_string
63
+ end
64
+
65
+ #
66
+ # Puts string at the coordinates specified.
67
+ #
68
+ # @param [String] str the string to set
69
+ # @param [Fixnum] row the x coordinate of the location on the screen.
70
+ # @param [Fixnum] column the y coordinate of the location on the screen.
71
+ #
72
+ def put_string(str, row, column)
73
+ x_send_no_rsp "MoveCursor(#{row-1},#{column-1})"
74
+ x_send_no_rsp 'string "' + str.gsub('"', '\\"') + '"'
75
+ end
76
+
77
+ #
78
+ # Sends keystrokes to the host, including function keys.
79
+ #
80
+ # @param [String] keys keystokes up to 255 in length
81
+ #
82
+ def send_keys(keys)
83
+ key = keys[1..-2]
84
+ if m=/^(Pf|Pa)(\d+)$/.match(key)
85
+ key = "#{m[1]}(#{m[2]})"
86
+ end
87
+ x_send_no_rsp key
88
+ x_send_no_rsp "wait(output)"
89
+ end
90
+
91
+ #
92
+ # Wait for the string to appear at the specified location
93
+ #
94
+ # @param [String] str the string to wait for
95
+ # @param [Fixnum] row the x coordinate of location
96
+ # @param [Fixnum] column the y coordinate of location
97
+ #
98
+ def wait_for_string(str, row, column)
99
+ total_time = 0.0
100
+ sleep_time = 0.5
101
+ while get_string(row, column, str.length) != str do
102
+ sleep sleep_time
103
+ total_time = total_time + sleep_time
104
+ break if total_time >= @max_wait_time
105
+ end
106
+ end
107
+
108
+ #
109
+ # Waits for the host to not send data for a specified number of seconds
110
+ #
111
+ # @param [Fixnum] seconds the maximum number of seconds to wait
112
+ #
113
+ def wait_for_host(seconds)
114
+ x_send_no_rsp "Wait(#{seconds},Output)"
115
+ end
116
+
117
+ #
118
+ # Waits until the cursor is at the specified location.
119
+ #
120
+ # @param [Fixnum] row the x coordinate of the location
121
+ # @param [Fixnum] column the y coordinate of the location
122
+ #
123
+ def wait_until_cursor_at(row, column)
124
+ x_send_no_rsp "MoveCursor(#{row-1},#{column-1})"
125
+ end
126
+
127
+ #
128
+ # Creates a method to take screenshot of the active screen. If you have set the +:visible+
129
+ # property to false it will be made visible prior to taking the screenshot and then changed
130
+ # to invisible after.
131
+ #
132
+ # @param [String] filename the path and name of the screenshot file to be saved
133
+ #
134
+ def screenshot(filename)
135
+ File.delete(filename) if File.exists?(filename)
136
+ x_send_no_rsp "printtext(file,#{filename})"
137
+ end
138
+
139
+ #
140
+ # Returns the text of the active screen
141
+ #
142
+ # @return [String]
143
+ #
144
+ def text
145
+ get_string(1,1,24*80)
146
+ end
147
+
148
+ private
149
+
150
+ attr_reader :x3270_input, :x3270_output
151
+
152
+ def x_read
153
+ line = @x3270_output.gets.chomp
154
+ puts "x_read: '#{line}'" if @trace
155
+ line
156
+ end
157
+
158
+ def x_send cmd
159
+ puts "x_send: #{cmd}" if @trace
160
+ @x3270_input.print "#{cmd}\n"
161
+ @x3270_input.flush
162
+ end
163
+
164
+ def x_send_no_rsp cmd
165
+ x_send cmd
166
+ while line = x_read do
167
+ break if line == 'ok'
168
+ end
169
+ end
170
+
171
+ def start_x3270_system
172
+ begin
173
+ args = [
174
+ "-model", "2",
175
+ ""
176
+ ]
177
+ cmd = "#{@executable_command} #{args.join " "} #{@host}"
178
+ @x3270_input, @x3270_output, @x3270_thr = Open3.popen2e(cmd)
179
+ rescue Exception => e
180
+ raise "Could not start x3270 '#{@executable_command}': #{e}"
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end