client_for_poslynx 0.9.0 → 1.0.0.pre
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 +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
|