client_for_poslynx 0.9.0 → 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +172 -147
- data/examples/em_protocol_example.rb +82 -0
- data/examples/em_session_basic_example.rb +85 -0
- data/examples/em_session_interrupt_example.rb +155 -0
- data/lib/client_for_poslynx/data/requests/abstract_request.rb +6 -0
- data/lib/client_for_poslynx/net.rb +2 -1
- data/lib/client_for_poslynx/net/em_connector.rb +344 -0
- data/lib/client_for_poslynx/net/em_connector/callback_map.rb +50 -0
- data/lib/client_for_poslynx/net/em_connector/connection_handler.rb +14 -0
- data/lib/client_for_poslynx/net/em_connector/connector_state.rb +24 -0
- data/lib/client_for_poslynx/net/em_connector/event_dispatcher.rb +66 -0
- data/lib/client_for_poslynx/net/em_connector/handles_connection.rb +57 -0
- data/lib/client_for_poslynx/net/em_connector/request_call.rb +27 -0
- data/lib/client_for_poslynx/net/em_protocol.rb +1 -1
- data/lib/client_for_poslynx/net/em_session.rb +250 -0
- data/lib/client_for_poslynx/version.rb +1 -1
- data/spec/client_for_poslynx/net/em_connector_spec.rb +624 -0
- data/spec/client_for_poslynx/net/em_session_spec.rb +313 -0
- metadata +19 -8
- data/lib/client_for_poslynx/net/structured_client.rb +0 -104
- data/lib/client_for_poslynx/net/structured_client/em_connection.rb +0 -72
- data/spec/client_for_poslynx/net/structured_client_spec.rb +0 -118
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'client_for_poslynx'
|
2
|
+
|
3
|
+
# Given that fake_pos_terminal is running on the local system and
|
4
|
+
# listening on port 3010, this example will execute the following
|
5
|
+
# workflow...
|
6
|
+
#
|
7
|
+
# 1. Display a message on the PIN pad with buttons.
|
8
|
+
# 2. After a button is selected on the PIN pad, display a message
|
9
|
+
# showing info about the button response for 5 seconds.
|
10
|
+
# 3. Reset the PIN pad.
|
11
|
+
#
|
12
|
+
# This example will also work using an actual POSLynx and PIN pad
|
13
|
+
# if the IP address, port number, encryption, and client MAC
|
14
|
+
# values are changed appropriately.
|
15
|
+
|
16
|
+
SERVER_IP = '127.0.0.1'
|
17
|
+
SERVER_PORT = 3010
|
18
|
+
# :none for no encryption. :use_ssl for SSL encryption.
|
19
|
+
ENCRYPTION = :none
|
20
|
+
CLIENT_MAC = 'whatever'
|
21
|
+
|
22
|
+
|
23
|
+
gem 'client_for_poslynx'
|
24
|
+
require 'client_for_poslynx'
|
25
|
+
|
26
|
+
include ClientForPoslynx::Net
|
27
|
+
include ClientForPoslynx::Data
|
28
|
+
|
29
|
+
connector = EM_Connector.new(SERVER_IP, SERVER_PORT, encryption: ENCRYPTION)
|
30
|
+
|
31
|
+
EventMachine.run do
|
32
|
+
|
33
|
+
EM_Session.execute connector do |s|
|
34
|
+
begin
|
35
|
+
request_data = Requests::PinPadDisplayMessage.new.tap{ |r|
|
36
|
+
r.client_mac = CLIENT_MAC
|
37
|
+
r.line_count = 2
|
38
|
+
r.text_lines = ["How done", "do you want it"]
|
39
|
+
r.button_labels = %w(Rare Medium Well-done)
|
40
|
+
}
|
41
|
+
response = s.request( request_data )
|
42
|
+
unless response.error_code == '0000'
|
43
|
+
puts "Got failure response from 1st request"
|
44
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
request_data = Requests::PinPadDisplayMessage.new.tap{ |r|
|
49
|
+
r.client_mac = CLIENT_MAC
|
50
|
+
r.line_count = 2
|
51
|
+
r.text_lines = [
|
52
|
+
"Got result: #{response.result}",
|
53
|
+
"Got button response: #{response.button_response}"
|
54
|
+
]
|
55
|
+
}
|
56
|
+
s.request request_data
|
57
|
+
unless response.error_code == '0000'
|
58
|
+
puts "Got failure response from 2nd request"
|
59
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
63
|
+
s.sleep 4
|
64
|
+
|
65
|
+
request_data = Requests::PinPadReset.new.tap{ |r|
|
66
|
+
r.client_mac = CLIENT_MAC
|
67
|
+
}
|
68
|
+
s.request request_data
|
69
|
+
unless response.error_code == '0000'
|
70
|
+
puts "Got failure response from concluding reset request"
|
71
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
rescue EM_Session::RequestError => e
|
76
|
+
puts "An exception was encountered in the session"
|
77
|
+
puts e.message
|
78
|
+
|
79
|
+
ensure
|
80
|
+
EventMachine.stop_event_loop
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'client_for_poslynx'
|
2
|
+
|
3
|
+
# This example will also work using an actual POSLynx and PIN pad
|
4
|
+
# if the IP address, port number, encryption, and client MAC
|
5
|
+
# values are changed appropriately.
|
6
|
+
|
7
|
+
SERVER_IP = '127.0.0.1'
|
8
|
+
SERVER_PORT = 3010
|
9
|
+
# :none for no encryption. :use_ssl for SSL encryption.
|
10
|
+
ENCRYPTION = :none
|
11
|
+
CLIENT_MAC = 'whatever'
|
12
|
+
|
13
|
+
|
14
|
+
gem 'client_for_poslynx'
|
15
|
+
require 'client_for_poslynx'
|
16
|
+
|
17
|
+
include ClientForPoslynx::Net
|
18
|
+
include ClientForPoslynx::Data
|
19
|
+
|
20
|
+
connector = EM_Connector.new(SERVER_IP, SERVER_PORT, encryption: ENCRYPTION)
|
21
|
+
|
22
|
+
session_a = ->(s) {
|
23
|
+
begin
|
24
|
+
request_data = Requests::PinPadDisplayMessage.new.tap{ |r|
|
25
|
+
r.client_mac = CLIENT_MAC
|
26
|
+
r.line_count = 2
|
27
|
+
r.text_lines = ["How done", "do you want it"]
|
28
|
+
r.button_labels = %w(Rare Medium Well-done)
|
29
|
+
}
|
30
|
+
response = s.request( request_data )
|
31
|
+
unless response.error_code == '0000'
|
32
|
+
puts "Got failure response in session_a from 1st request"
|
33
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
request_data = Requests::PinPadDisplayMessage.new.tap{ |r|
|
38
|
+
r.client_mac = CLIENT_MAC
|
39
|
+
r.line_count = 2
|
40
|
+
r.text_lines = [
|
41
|
+
"Got result: #{response.result}",
|
42
|
+
"Got button response: #{response.button_response}"
|
43
|
+
]
|
44
|
+
}
|
45
|
+
s.request request_data
|
46
|
+
unless response.error_code == '0000'
|
47
|
+
puts "Got failure response in session_a from 2st request"
|
48
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
s.sleep 4
|
53
|
+
|
54
|
+
request_data = Requests::PinPadReset.new.tap{ |r|
|
55
|
+
r.client_mac = CLIENT_MAC
|
56
|
+
}
|
57
|
+
s.request request_data
|
58
|
+
unless response.error_code == '0000'
|
59
|
+
puts "Got failure response in session_b from concluding reset-request"
|
60
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
EventMachine.stop_event_loop
|
65
|
+
|
66
|
+
rescue EM_Session::RequestError => e
|
67
|
+
puts "An exception was encountered in session_a"
|
68
|
+
puts e.message
|
69
|
+
|
70
|
+
end
|
71
|
+
}
|
72
|
+
|
73
|
+
session_b = ->(s) {
|
74
|
+
begin
|
75
|
+
# Let session_a get underway before interrupting.
|
76
|
+
s.sleep 3
|
77
|
+
|
78
|
+
request_data = Requests::PinPadReset.new.tap{ |r|
|
79
|
+
r.client_mac = CLIENT_MAC
|
80
|
+
}
|
81
|
+
response = s.request( request_data )
|
82
|
+
unless response.error_code == '0000'
|
83
|
+
puts "Got failure response in session_b from 1st request"
|
84
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
request_data = Requests::PinPadDisplayMessage.new.tap{ |r|
|
89
|
+
r.client_mac = CLIENT_MAC
|
90
|
+
r.line_count = 1
|
91
|
+
r.text_lines = "Rudely interrupting..."
|
92
|
+
}
|
93
|
+
s.request request_data
|
94
|
+
unless response.error_code == '0000'
|
95
|
+
puts "Got failure response in session_b from 2nd request"
|
96
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
100
|
+
s.sleep 3
|
101
|
+
|
102
|
+
request_data = Requests::PinPadDisplayMessage.new.tap{ |r|
|
103
|
+
r.client_mac = CLIENT_MAC
|
104
|
+
r.line_count = 2
|
105
|
+
r.text_lines = ["How annoying was", "this interruption?"]
|
106
|
+
r.button_labels = %w(Very Not-much)
|
107
|
+
}
|
108
|
+
response = s.request( request_data )
|
109
|
+
unless response.error_code == '0000'
|
110
|
+
puts "Got failure response in session_b from 3rd request"
|
111
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
112
|
+
return
|
113
|
+
end
|
114
|
+
|
115
|
+
request_data = Requests::PinPadDisplayMessage.new.tap{ |r|
|
116
|
+
r.client_mac = CLIENT_MAC
|
117
|
+
r.line_count = 2
|
118
|
+
r.text_lines = [
|
119
|
+
"Got result: #{response.result}",
|
120
|
+
"Got button response: #{response.button_response}"
|
121
|
+
]
|
122
|
+
}
|
123
|
+
s.request request_data
|
124
|
+
unless response.error_code == '0000'
|
125
|
+
puts "Got failure response in session_b from 4th request"
|
126
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
127
|
+
return
|
128
|
+
end
|
129
|
+
|
130
|
+
s.sleep 4
|
131
|
+
|
132
|
+
request_data = Requests::PinPadReset.new.tap{ |r|
|
133
|
+
r.client_mac = CLIENT_MAC
|
134
|
+
}
|
135
|
+
s.request request_data
|
136
|
+
unless response.error_code == '0000'
|
137
|
+
puts "Got failure response in session_b from concluding reset-request"
|
138
|
+
puts "Error code: #{response.error_code}. Result text: #(response.result}"
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
rescue EM_Session::RequestError => e
|
143
|
+
puts "An exception was encountered in session_b"
|
144
|
+
puts e.message
|
145
|
+
|
146
|
+
ensure
|
147
|
+
EventMachine.stop_event_loop
|
148
|
+
|
149
|
+
end
|
150
|
+
}
|
151
|
+
|
152
|
+
EventMachine.run do
|
153
|
+
EM_Session.execute connector, &session_a
|
154
|
+
EM_Session.execute connector, &session_b
|
155
|
+
end
|
@@ -29,6 +29,12 @@ module ClientForPoslynx
|
|
29
29
|
visitor.public_send "visit_#{simple_class_name}", self
|
30
30
|
end
|
31
31
|
|
32
|
+
# True is the given object is of the right type to be a
|
33
|
+
# response to a request made using this request data.
|
34
|
+
def potential_response?(candidate)
|
35
|
+
self.class.response_class === candidate
|
36
|
+
end
|
37
|
+
|
32
38
|
end
|
33
39
|
|
34
40
|
end
|
@@ -0,0 +1,344 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module ClientForPoslynx
|
4
|
+
module Net
|
5
|
+
class EM_Connector ; end
|
6
|
+
|
7
|
+
# Convenient shorthand for EM_Connector for use within
|
8
|
+
# Net and its sub-nested classes and modules.
|
9
|
+
EMC = EM_Connector
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require_relative 'em_connector/connector_state'
|
14
|
+
require_relative 'em_connector/handles_connection'
|
15
|
+
require_relative 'em_connector/connection_handler'
|
16
|
+
require_relative 'em_connector/event_dispatcher'
|
17
|
+
require_relative 'em_connector/request_call'
|
18
|
+
require_relative 'em_connector/callback_map'
|
19
|
+
|
20
|
+
module ClientForPoslynx
|
21
|
+
module Net
|
22
|
+
|
23
|
+
# A <tt>ClientForPoslynx::Net::EM_Connector</tt> object is
|
24
|
+
# associated with a specific POSLynx host (lane) and provides
|
25
|
+
# a convenient means of connecting or re-connecting to the
|
26
|
+
# host any number of times within an Event Manager run loop.
|
27
|
+
#
|
28
|
+
# An instance of <tt>ClientForPoslynx::Net::EM_Connector</tt>
|
29
|
+
# may be created either inside or outside of a run loop, but
|
30
|
+
# it must be used from within a run loop to make or interact
|
31
|
+
# with connections since that's the only context in which
|
32
|
+
# Event Manager connections are applicable.
|
33
|
+
class EM_Connector
|
34
|
+
extend Forwardable
|
35
|
+
|
36
|
+
# Creates a new
|
37
|
+
# <tt>ClientForPoslynx::Net::EM_Connector</tt>
|
38
|
+
# instance.
|
39
|
+
#
|
40
|
+
# The <tt>server</tt> and <tt>port</tt> arguments are
|
41
|
+
# passed to Event Manager's methods for connecting or
|
42
|
+
# reconnecting to the poslynx lane as needed.
|
43
|
+
#
|
44
|
+
# ==== Options
|
45
|
+
# * <tt>:encryption<tt> - <tt>:use_ssl</tt> if SSL
|
46
|
+
# if the connection should be encrypted using SSL.
|
47
|
+
# <tt>:none</tt> if the connection should not be
|
48
|
+
# encrypted. Defaults to <tt>:none</tt>.
|
49
|
+
# * <tt>:handler<tt> - The class given as the handler
|
50
|
+
# argument to the <tt>::connect</tt> call to the Event
|
51
|
+
# Machine system (normally <tt>::EM</tt>).
|
52
|
+
# Defaults to
|
53
|
+
# <tt>ClientForPoslynx::Net::EM_Connector::ConnectionHandler</tt>
|
54
|
+
# This should generally be a subclass of
|
55
|
+
# <tt>ClientForPoslynx::Net::EM_Connector::ConnectionHandler</tt>
|
56
|
+
# or be a class that includes
|
57
|
+
# <tt>ClientforPoslynx::Net::EM_Connector::HandlesConnection</tt>
|
58
|
+
# and is a subclass of <tt>EM::Connection</tt>.
|
59
|
+
# When using a substitute for <tt>::EM</tt> as the Event
|
60
|
+
# Machine system, this might not need to be a subclass of
|
61
|
+
# <tt>EM::Connection</tt>, though it will need to supply
|
62
|
+
# substitute implementations of several of the
|
63
|
+
# <tt>EM::Connection</tt> methods in that case.
|
64
|
+
# * <tt>:em_system</tt> - The event machine system that
|
65
|
+
# will be called on for making connections. Defaults
|
66
|
+
# to <tt>::EM</tt>. Must implement the <tt>::connect</tt>
|
67
|
+
# method.
|
68
|
+
# This is used for dependency injection in unit tests and
|
69
|
+
# may be useful for inserting instrumentation arround the
|
70
|
+
# <tt>::connect</tt> call within an application.
|
71
|
+
def initialize(server, port, opts={})
|
72
|
+
@server = server
|
73
|
+
@port = port
|
74
|
+
state.encryption = opts.fetch( :encryption, :none )
|
75
|
+
@handler_class = opts.fetch( :handler, EMC::ConnectionHandler )
|
76
|
+
@em_system = opts.fetch( :em_system, ::EM )
|
77
|
+
state.connection_status ||= :initial
|
78
|
+
state.status_of_request ||= :initial
|
79
|
+
end
|
80
|
+
|
81
|
+
# The POSLynx server to be conected to.
|
82
|
+
attr_reader :server
|
83
|
+
|
84
|
+
# The server port through which to connected.
|
85
|
+
attr_reader :port
|
86
|
+
|
87
|
+
# The encryption mode <tt>:use_ssl</tt> or <tt>:none</tt>
|
88
|
+
def_delegator :state, :encryption
|
89
|
+
|
90
|
+
# The connection handler instance (connection) after the
|
91
|
+
# first call to <tt>#connect</tt>. It will be an instance
|
92
|
+
# of the connection handler class.
|
93
|
+
def connection
|
94
|
+
state.connection
|
95
|
+
end
|
96
|
+
|
97
|
+
# The current connection status. One of <tt>:initial</tt>,
|
98
|
+
# <tt>:connecting</tt>, <tt>:connected</tt>,
|
99
|
+
# <tt>:disconnecting</tt>, <tt>:disconnected</tt>.
|
100
|
+
def_delegator :state, :connection_status
|
101
|
+
|
102
|
+
# True when no connection attempt has been initiated yet.
|
103
|
+
def_delegator :state, :connection_initial?
|
104
|
+
|
105
|
+
# True when a connection attempt is in progress.
|
106
|
+
def_delegator :state, :connecting?
|
107
|
+
|
108
|
+
# True when currently connected.
|
109
|
+
def_delegator :state, :connected?
|
110
|
+
|
111
|
+
# True when disconnection is in progress.
|
112
|
+
def_delegator :state, :disconnecting?
|
113
|
+
|
114
|
+
# True when a opening a connection has been previously
|
115
|
+
# attempted or successful, and not currently connected.
|
116
|
+
def_delegator :state, :disconnected?
|
117
|
+
|
118
|
+
# An instance of <tt>EM_Connector::RequestCall</tt>
|
119
|
+
# containing the request data and result callbacks from the
|
120
|
+
# most recent <tt>#send_request</tt> or
|
121
|
+
# <tt>#get_response</tt> call.
|
122
|
+
attr_reader :latest_request
|
123
|
+
|
124
|
+
# The current <tt>#send_request</tt> status. One of
|
125
|
+
# <tt>:initial</tt>, <tt>:pending</tt>,
|
126
|
+
# <tt>:got_response</tt>, <tt>:failed</tt>.
|
127
|
+
def_delegator :state, :status_of_request
|
128
|
+
|
129
|
+
# True when no request-send has been initiated yet.
|
130
|
+
def_delegator :state, :request_initial?
|
131
|
+
|
132
|
+
# True when a request-send is in progress.
|
133
|
+
def_delegator :state, :request_pending?
|
134
|
+
|
135
|
+
# True when a response was receied from the most recently
|
136
|
+
# attempted request-send.
|
137
|
+
def_delegator :state, :got_response?
|
138
|
+
|
139
|
+
# True when the most recently attempted request-send
|
140
|
+
# resulted in failure.
|
141
|
+
def_delegator :state, :request_failed?
|
142
|
+
|
143
|
+
# The connection handler class to be passed as the handler
|
144
|
+
# argument to <tt>EM::connect</tt>.
|
145
|
+
attr_reader :handler_class
|
146
|
+
|
147
|
+
# The Event Manager system that will be called on for
|
148
|
+
# making connections.
|
149
|
+
attr_reader :em_system
|
150
|
+
|
151
|
+
# Asynchronously attempts to open an EventMachine
|
152
|
+
# connection to the POSLynx lane.
|
153
|
+
#
|
154
|
+
# The underlying connection instance is available via
|
155
|
+
# <tt>#connection</tt> immediately after the call returns,
|
156
|
+
# though it might not yet represent a completed, open
|
157
|
+
# connection.
|
158
|
+
#
|
159
|
+
# If the connector already has a currently open connection,
|
160
|
+
# then the call to <tt>#connect</tt> succeeds immediately
|
161
|
+
# and synchronously.
|
162
|
+
#
|
163
|
+
# Once the connection is completed or fails, the
|
164
|
+
# <tt>#call</tt> method of the apporopriate callback
|
165
|
+
# object (if supplied) is invoked with no arguments.
|
166
|
+
#
|
167
|
+
# If another <tt>#connect</tt> request is already pending,
|
168
|
+
# then the the new request is combined with the pending for
|
169
|
+
# request, and the appropriate callback will be invoked
|
170
|
+
# each of those when the connection attempt is subsequently
|
171
|
+
# concluded.
|
172
|
+
#
|
173
|
+
# ==== Result callback options
|
174
|
+
# * <tt>:on_success</tt> - An object to receive
|
175
|
+
# <tt>#call</tt> when the connection is successfully
|
176
|
+
# opened.
|
177
|
+
# * <tt>:on_failure</tt> - An object to receive
|
178
|
+
# <tt>#call</tt> when the connection attempt fails.
|
179
|
+
def connect(result_callbacks={})
|
180
|
+
result_callbacks = EMC.CallbackMap(result_callbacks)
|
181
|
+
if connection_initial?
|
182
|
+
make_initial_connection result_callbacks
|
183
|
+
elsif connected?
|
184
|
+
result_callbacks.call :on_success
|
185
|
+
elsif connecting?
|
186
|
+
piggyback_connect result_callbacks
|
187
|
+
else
|
188
|
+
reconnect result_callbacks
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# When called from within an EventManager event-handling
|
193
|
+
# loop, asynchronously attempts to disconnect the current
|
194
|
+
# EventMachine connection to the POSLynx lane.
|
195
|
+
#
|
196
|
+
# If there is no currently open connection, then the call
|
197
|
+
# to <tt>#disconnect</tt> succeeds immediately and
|
198
|
+
# synchronously.
|
199
|
+
#
|
200
|
+
# Note that you might never have reason to call this since
|
201
|
+
# Event Machine automatically closes connections when the
|
202
|
+
# run loop is stopped.
|
203
|
+
#
|
204
|
+
# ==== Result callback options
|
205
|
+
# * <tt>:on_completed</tt> - An object to receive
|
206
|
+
# <tt>#call</tt> when finished disconnecting.
|
207
|
+
def disconnect(result_callbacks={})
|
208
|
+
result_callbacks = EMC.CallbackMap( result_callbacks )
|
209
|
+
if connected?
|
210
|
+
connection.event_dispatcher =
|
211
|
+
EMC::EventDispatcher.for_disconnect( connection, result_callbacks )
|
212
|
+
state.connection_status = :disconnecting
|
213
|
+
connection.close_connection
|
214
|
+
else
|
215
|
+
result_callbacks.call :on_completed
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# When called with an open connection, asynchronously sends
|
220
|
+
# a request to the POSLynx with the given request data.
|
221
|
+
#
|
222
|
+
# When the response is received, the response data is
|
223
|
+
# passed to the <tt>#call</tt> method of the
|
224
|
+
# <tt>:on_response</tt> handler.
|
225
|
+
#
|
226
|
+
# If <tt>#send_request</tt> is called without an open
|
227
|
+
# connection or when the connection is lost before any
|
228
|
+
# response is received, the <tt>#call</tt> method of the
|
229
|
+
# <tt>:on_failure</tt> callback is invoked.
|
230
|
+
#
|
231
|
+
# * <tt>request_data</tt> - The request to be sent. Should
|
232
|
+
# be an instance of one of the
|
233
|
+
# <tt>ClientForPoslynx::Data::Requests::...</tt> classes.
|
234
|
+
# * <tt>result_callbacks</tt> - A hash map of objects, each
|
235
|
+
# of which handles a response condition when it receives
|
236
|
+
# <tt>#call</tt>
|
237
|
+
#
|
238
|
+
# ==== Result callback options
|
239
|
+
# * <tt>:on_response</tt> - An object to receive
|
240
|
+
# <tt>#call</tt> with the response data when the response
|
241
|
+
# is received.
|
242
|
+
# * <tt>:on_failure</tt> - An object to receive
|
243
|
+
# <tt>#call</tt> if there is no open connection or when
|
244
|
+
# the connection is lost.
|
245
|
+
# * Any other callback(s) that might need to be received
|
246
|
+
# from a collaborator via <tt>#latest_request</tt> data
|
247
|
+
# while the request is pending. <tt>EMSession</tt> may
|
248
|
+
# invoke an <tt>:on_detached</tt> callback, for example.
|
249
|
+
def send_request(request_data, result_callbacks={})
|
250
|
+
result_callbacks = EMC.CallbackMap( result_callbacks )
|
251
|
+
unless connected?
|
252
|
+
result_callbacks.call :on_failure
|
253
|
+
return
|
254
|
+
end
|
255
|
+
self.latest_request = EMC.RequestCall( request_data, result_callbacks )
|
256
|
+
state.status_of_request = :pending
|
257
|
+
connection.event_dispatcher = EMC::EventDispatcher.for_send_request(
|
258
|
+
connection, result_callbacks
|
259
|
+
)
|
260
|
+
connection.send_request request_data
|
261
|
+
end
|
262
|
+
|
263
|
+
# This methid exists to support 2 special-case scenarios
|
264
|
+
# that client code may need to handle.
|
265
|
+
#
|
266
|
+
# Scenario A:
|
267
|
+
#
|
268
|
+
# A request is in progress, and the original result
|
269
|
+
# handlers must be replaced with new handlers. For
|
270
|
+
# instance, a PIN Pad Reset may have been in progress to
|
271
|
+
# initate one workflow, and that workflow is being aborted
|
272
|
+
# to initiate a different workflow.
|
273
|
+
#
|
274
|
+
# Scenario B:
|
275
|
+
#
|
276
|
+
# An attempt was made to cancel a previous pending request
|
277
|
+
# by sending another request, but a response to the
|
278
|
+
# original request was subsequently received because it was
|
279
|
+
# already in transit. Now, we need to resume waiting for
|
280
|
+
# the response to the second request since it is actually
|
281
|
+
# still pending.
|
282
|
+
#
|
283
|
+
# The options for <tt>#get_response</tt> are the same as
|
284
|
+
# for <tt>#send_request</tt> and have the same meanings,
|
285
|
+
# but since no new request is to be made, there is no
|
286
|
+
# <tt>request_data</tt> argument.
|
287
|
+
#
|
288
|
+
# If the <tt>#status_of_request</tt> is
|
289
|
+
# <tt>:got_response</tt> before the invocation, then it is
|
290
|
+
# reverted to # <tt>:pending</tt>.
|
291
|
+
def get_response(result_callbacks={})
|
292
|
+
result_callbacks = EMC.CallbackMap( result_callbacks )
|
293
|
+
unless connected? && ( request_pending? || got_response? )
|
294
|
+
result_callbacks.call :on_failure
|
295
|
+
return
|
296
|
+
end
|
297
|
+
self.latest_request = EMC.RequestCall( latest_request.request_data, result_callbacks )
|
298
|
+
state.status_of_request = :pending
|
299
|
+
connection.event_dispatcher = EMC::EventDispatcher.for_send_request(
|
300
|
+
connection, result_callbacks
|
301
|
+
)
|
302
|
+
end
|
303
|
+
|
304
|
+
private
|
305
|
+
|
306
|
+
def state
|
307
|
+
@state ||= State.new
|
308
|
+
end
|
309
|
+
|
310
|
+
def latest_request=(value)
|
311
|
+
args = Array(value)
|
312
|
+
@latest_request = EMC.RequestCall( *args )
|
313
|
+
end
|
314
|
+
|
315
|
+
def make_initial_connection(result_callbacks)
|
316
|
+
state.connection_status = :connecting
|
317
|
+
em_system.connect \
|
318
|
+
server, port,
|
319
|
+
handler_class, state
|
320
|
+
connection.event_dispatcher = EMC::EventDispatcher.for_connect(
|
321
|
+
connection, result_callbacks
|
322
|
+
)
|
323
|
+
end
|
324
|
+
|
325
|
+
def reconnect(connect_event_dispatch_opts)
|
326
|
+
state.connection_status = :connecting
|
327
|
+
connection.event_dispatcher = EMC::EventDispatcher.for_connect(
|
328
|
+
connection, connect_event_dispatch_opts
|
329
|
+
)
|
330
|
+
connection.reconnect server, port
|
331
|
+
end
|
332
|
+
|
333
|
+
def piggyback_connect(response_callbacks)
|
334
|
+
response_callbacks = response_callbacks.merge(
|
335
|
+
original_dispatcher: connection.event_dispatcher
|
336
|
+
)
|
337
|
+
connection.event_dispatcher = EMC::EventDispatcher.for_connect(
|
338
|
+
connection, response_callbacks
|
339
|
+
)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
end
|
344
|
+
end
|