Keyple Service C++ Library - 3.3.5
Component of the Keyple C++ middleware
ObservableLocalReaderAdapter.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/core/service/ObservableLocalReaderAdapter.hpp"
15
16#include <memory>
17#include <string>
18#include <vector>
19
20#include "keyple/core/plugin/CardIOException.hpp"
21#include "keyple/core/plugin/ReaderIOException.hpp"
22#include "keyple/core/service/ObservableReaderStateServiceAdapter.hpp"
23#include "keyple/core/service/ReaderEventAdapter.hpp"
24#include "keyple/core/service/ScheduledCardSelectionsResponseAdapter.hpp"
25#include "keyple/core/util/cpp/Arrays.hpp"
26#include "keyple/core/util/cpp/exception/Exception.hpp"
27#include "keypop/card/CardBrokenCommunicationException.hpp"
28#include "keypop/card/ReaderBrokenCommunicationException.hpp"
29#include "keypop/reader/ReaderCommunicationException.hpp"
30
31namespace keyple {
32namespace core {
33namespace service {
34
35using keyple::core::plugin::CardIOException;
36using keyple::core::plugin::ReaderIOException;
37using keyple::core::util::cpp::Arrays;
38using keyple::core::util::cpp::exception::Exception;
39using keypop::card::CardBrokenCommunicationException;
40using keypop::card::ReaderBrokenCommunicationException;
41using keypop::reader::ReaderCommunicationException;
42
43/* OBSERVABLE LOCAL READER ADAPTER JOB
44 * ---------------------------------------------------------- */
45
46ObservableLocalReaderAdapter::ObservableLocalReaderAdapterJob::
47 ObservableLocalReaderAdapterJob(
48 std::shared_ptr<CardReaderObserverSpi> observer,
49 const std::shared_ptr<CardReaderEvent> event,
50 ObservableLocalReaderAdapter* parent)
51: Job("ObservableLocalReaderAdapter")
52, mObserver(observer)
53, mEvent(event)
54, mParent(parent)
55{
56}
57
58void
59ObservableLocalReaderAdapter::ObservableLocalReaderAdapterJob::execute()
60{
61 mParent->notifyObserver(mObserver, mEvent);
62}
63
64/* OBSERVABLE LOCAL READER ADAPTER
65 * -------------------------------------------------------------- */
66
67const std::vector<uint8_t> ObservableLocalReaderAdapter::APDU_PING_CARD_PRESENCE
68 = {0x00, 0xC0, 0x00, 0x00, 0x00};
69const std::string ObservableLocalReaderAdapter::READER_MONITORING_ERROR
70 = "An error occurred while monitoring the reader.";
71
72ObservableLocalReaderAdapter::ObservableLocalReaderAdapter(
73 std::shared_ptr<ObservableReaderSpi> observableReaderSpi,
74 const std::string& pluginName)
75: LocalReaderAdapter(observableReaderSpi, pluginName)
76, mObservableReaderSpi(observableReaderSpi)
77, mStateService(std::make_shared<ObservableReaderStateServiceAdapter>(this))
78, mObservationManager(
79 std::make_shared<ObservationManagerAdapter<
80 CardReaderObserverSpi,
81 CardReaderObservationExceptionHandlerSpi>>(pluginName, getName()))
82{
83 auto asynchronousInsertion
84 = std::dynamic_pointer_cast<CardInsertionWaiterAsynchronousSpi>(
85 observableReaderSpi);
86 auto autonomousInsertion
87 = std::dynamic_pointer_cast<WaitForCardInsertionAutonomousSpi>(
88 observableReaderSpi);
89 if (asynchronousInsertion) {
90 asynchronousInsertion->setCallback(this);
91 } else if (autonomousInsertion) {
92 autonomousInsertion->connect(this);
93 }
94
95 auto asynchronousRemoval
96 = std::dynamic_pointer_cast<CardRemovalWaiterAsynchronousSpi>(
97 observableReaderSpi);
98 auto autonomousRemoval
99 = std::dynamic_pointer_cast<WaitForCardRemovalAutonomousSpi>(
100 observableReaderSpi);
101 if (asynchronousRemoval) {
102 asynchronousRemoval->setCallback(this);
103 } else if (autonomousRemoval) {
104 autonomousRemoval->connect(this);
105 }
106}
107
108std::shared_ptr<ObservableReaderSpi>
109ObservableLocalReaderAdapter::getObservableReaderSpi() const
110{
111 return mObservableReaderSpi;
112}
113
114std::shared_ptr<CardReaderObservationExceptionHandlerSpi>
115ObservableLocalReaderAdapter::getObservationExceptionHandler() const
116{
117 return mObservationManager->getObservationExceptionHandler();
118}
119
121ObservableLocalReaderAdapter::getDetectionMode() const
122{
123 return mDetectionMode;
124}
125
126MonitoringState
127ObservableLocalReaderAdapter::getCurrentMonitoringState() const
128{
129 return mStateService->getCurrentMonitoringState();
130}
131
132bool
133ObservableLocalReaderAdapter::isCardPresentPing()
134{
135 /* Transmits the APDU and checks for the IO exception */
136 try {
137 mObservableReaderSpi->transmitApdu(APDU_PING_CARD_PRESENCE);
138 } catch (const ReaderIOException& e) {
139 /* Notify the reader communication failure with the exception handler */
140 const auto rce = std::make_shared<ReaderCommunicationException>(
141 READER_MONITORING_ERROR, e);
142 getObservationExceptionHandler()->onReaderObservationError(
143 getPluginName(), getName(), rce);
144 return false;
145
146 } catch (const CardIOException&) {
147 return false;
148 }
149
150 return true;
151}
152
153std::shared_ptr<CardReaderEvent>
154ObservableLocalReaderAdapter::processCardInserted()
155{
156 /* RL-DET-INSNOTIF.1 */
157 mLogger->trace("Process inserted card\n");
158
159 mIsCardRemovedEventNotificationEnabled = true;
160
161 if (mCardSelectionScenario == nullptr) {
162 mLogger->trace(
163 "No card selection scenario defined. Notify "
164 "[CARD_INSERTED] event\n");
165
166 /* No default request is defined, just notify the card insertion */
167 return std::make_shared<ReaderEventAdapter>(
168 getPluginName(),
169 getName(),
170 CardReaderEvent::Type::CARD_INSERTED,
171 nullptr);
172 }
173
174 /*
175 * A card selection scenario is defined, send it and notify according to the
176 * notification mode and the selection status
177 */
178 try {
179 const std::vector<std::shared_ptr<CardSelectionResponseApi>>
180 cardSelectionResponses = transmitCardSelectionRequests(
181 mCardSelectionScenario->getCardSelectors(),
182 mCardSelectionScenario->getCardSelectionRequests(),
183 mCardSelectionScenario->getMultiSelectionProcessing(),
184 mCardSelectionScenario->getChannelControl());
185
186 if (hasACardMatched(cardSelectionResponses)) {
187 return std::make_shared<ReaderEventAdapter>(
188 getPluginName(),
189 getName(),
190 CardReaderEvent::Type::CARD_MATCHED,
191 std::make_shared<ScheduledCardSelectionsResponseAdapter>(
192 cardSelectionResponses));
193 }
194
195 if (mNotificationMode == NotificationMode::MATCHED_ONLY) {
196 /* Notify only if a card matched the selection, just ignore if not
197 */
198 mLogger->trace(
199 "Selection hasn't matched. Do not throw any event because of "
200 "[MATCHED_ONLY] flag\n");
201
202 return nullptr;
203 }
204
205 /* The card didn't match, notify an CARD_INSERTED event with the
206 * received response */
207 mLogger->trace(
208 "[%] none of % default selection matched\n",
209 getName(),
210 cardSelectionResponses.size());
211
212 return std::make_shared<ReaderEventAdapter>(
213 getPluginName(),
214 getName(),
215 CardReaderEvent::Type::CARD_INSERTED,
216 std::make_shared<ScheduledCardSelectionsResponseAdapter>(
217 cardSelectionResponses));
218
219 } catch (const ReaderBrokenCommunicationException& e) {
220 /* Notify the reader communication failure with the exception handler */
221 const auto rce = std::make_shared<ReaderCommunicationException>(
222 READER_MONITORING_ERROR, e);
223 getObservationExceptionHandler()->onReaderObservationError(
224 getPluginName(), getName(), rce);
225
226 } catch (const CardBrokenCommunicationException&) {
227 /* The last transmission failed, close the logical and physical channels
228 */
229 closeLogicalAndPhysicalChannelsSilently();
230
231 /*
232 * The card was removed or not read correctly, no exception raising or
233 * event notification, just log.
234 */
235 mLogger->warn(
236 "Error while processing card selection scenario: %\n",
237 ""); // e.getMessage());
238 }
239
240 /*
241 * Here we close the physical channel in case it was opened for a card
242 * excluded by the selection scenario.
243 */
244 try {
245 mObservableReaderSpi->closePhysicalChannel();
246
247 } catch (const ReaderIOException& e) {
248 /* Notify the reader communication failure with the exception handler */
249 const auto rce = std::make_shared<ReaderCommunicationException>(
250 READER_MONITORING_ERROR, e);
251 getObservationExceptionHandler()->onReaderObservationError(
252 getPluginName(), getName(), rce);
253 }
254
255 /* No event returned */
256 return nullptr;
257}
258
259bool
260ObservableLocalReaderAdapter::hasACardMatched(
261 const std::vector<std::shared_ptr<CardSelectionResponseApi>>&
262 cardSelectionResponses)
263{
264 if (std::any_of(
265 cardSelectionResponses.begin(),
266 cardSelectionResponses.end(),
267 [](const std::shared_ptr<CardSelectionResponseApi>&
268 cardSelectionResponse) {
269 return cardSelectionResponse != nullptr
270 && cardSelectionResponse->hasMatched();
271 })) {
272 mLogger->trace("A default selection case matched\n");
273 return true;
274 }
275
276 return false;
277}
278
279void
280ObservableLocalReaderAdapter::processCardRemoved()
281{
282 /* RL-DET-REMNOTIF.1 */
283 closeLogicalAndPhysicalChannelsSilently();
284 if (mIsCardRemovedEventNotificationEnabled) {
285 notifyObservers(
286 std::make_shared<ReaderEventAdapter>(
287 getPluginName(),
288 getName(),
289 CardReaderEvent::Type::CARD_REMOVED,
290 nullptr));
291 }
292}
293
294void
295ObservableLocalReaderAdapter::switchState(const MonitoringState stateId)
296{
297 mStateService->switchState(stateId);
298}
299
300void
301ObservableLocalReaderAdapter::notifyObservers(
302 const std::shared_ptr<CardReaderEvent> event)
303{
304 mLogger->debug(
305 "Reader [%] notifies event [%] to % observer(s)\n",
306 getName(),
307 event->getType(),
308 countObservers());
309
310 for (const auto& observer : mObservationManager->getObservers()) {
311 notifyObserver(observer, event);
312 }
313}
314
315void
316ObservableLocalReaderAdapter::notifyObserver(
317 std::shared_ptr<CardReaderObserverSpi> observer,
318 const std::shared_ptr<CardReaderEvent> event)
319{
320 try {
321 observer->onReaderEvent(event);
322 } catch (const Exception& e) {
323 try {
324 mObservationManager->getObservationExceptionHandler()
325 ->onReaderObservationError(
326 getPluginName(), getName(), std::make_shared<Exception>(e));
327 } catch (const Exception& e2) {
328 mLogger->error(
329 "Event notification error: % - %\n", e2.getMessage(), e2);
330 mLogger->error("Original cause: % - %\n", e.getMessage(), e);
331 }
332 } catch (const std::exception& e) {
333 try {
334 mObservationManager->getObservationExceptionHandler()
335 ->onReaderObservationError(
336 getPluginName(),
337 getName(),
338 std::make_shared<std::runtime_error>(e.what()));
339 } catch (const std::exception& e2) {
340 mLogger->error("Event notification error: %\n", e2.what());
341 mLogger->error("Original cause: %\n", e.what());
342 }
343 }
344}
345
346void
347ObservableLocalReaderAdapter::scheduleCardSelectionScenario(
348 std::shared_ptr<CardSelectionScenarioAdapter> cardSelectionScenario,
349 const NotificationMode notificationMode)
350{
351 mCardSelectionScenario = cardSelectionScenario;
352 mNotificationMode = notificationMode;
353}
354
355void
356ObservableLocalReaderAdapter::doUnregister()
357{
358 try {
359 stopCardDetection();
360 } catch (const Exception& e) {
361 mLogger->error(
362 "Error stopping card detection on reader [%] - %\n", getName(), e);
363 }
364
365 /* Finally */
366 mStateService->shutdown();
367
368 notifyObservers(
369 std::make_shared<ReaderEventAdapter>(
370 getPluginName(),
371 getName(),
372 CardReaderEvent::Type::UNAVAILABLE,
373 nullptr));
374 clearObservers();
375 LocalReaderAdapter::doUnregister();
376}
377
378bool
379ObservableLocalReaderAdapter::isCardPresent()
380{
381 checkStatus();
382
383 if (LocalReaderAdapter::isCardPresent()) {
384 return true;
385 } else {
386 /*
387 * If the card is no longer present but one of the channels is still
388 * open, then the card removal sequence is initiated.
389 */
390 if (isLogicalChannelOpen()
391 || mObservableReaderSpi->isPhysicalChannelOpen()) {
392 processCardRemoved();
393 }
394
395 return false;
396 }
397}
398
399void
400ObservableLocalReaderAdapter::addObserver(
401 std::shared_ptr<CardReaderObserverSpi> observer)
402{
403 checkStatus();
404 mObservationManager->addObserver(observer);
405}
406
407void
408ObservableLocalReaderAdapter::removeObserver(
409 std::shared_ptr<CardReaderObserverSpi> observer)
410{
411 Assert::getInstance().notNull(observer, "observer");
412
413 if (Arrays::contains(mObservationManager->getObservers(), observer)) {
414 mObservationManager->removeObserver(observer);
415 }
416}
417
418int
419ObservableLocalReaderAdapter::countObservers() const
420{
421 return mObservationManager->countObservers();
422}
423
424void
425ObservableLocalReaderAdapter::clearObservers()
426{
427 mObservationManager->clearObservers();
428}
429
430void
431ObservableLocalReaderAdapter::startCardDetection(
432 const DetectionMode detectionMode)
433{
434 /* RL-DET-REMCTRL.1 */
435 checkStatus();
436
437 mLogger->info(
438 "Reader [%] starts card detection with polling mode [%]\n",
439 getName(),
440 detectionMode);
441
442 mDetectionMode = detectionMode;
443 mStateService->onEvent(InternalEvent::START_DETECT);
444}
445
446void
447ObservableLocalReaderAdapter::stopCardDetection()
448{
449 /* RL-DET-REMCTRL.1 */
450 mLogger->info("Reader [%] stops card detection\n", getName());
451
452 mStateService->onEvent(InternalEvent::STOP_DETECT);
453}
454
455void
456ObservableLocalReaderAdapter::finalizeCardProcessing()
457{
458 mLogger->info("Reader [%] starts card removal sequence\n", getName());
459
460 mStateService->onEvent(InternalEvent::CARD_PROCESSED);
461}
462
463void
464ObservableLocalReaderAdapter::setReaderObservationExceptionHandler(
465 std::shared_ptr<CardReaderObservationExceptionHandlerSpi> exceptionHandler)
466{
467 checkStatus();
468 mObservationManager->setObservationExceptionHandler(exceptionHandler);
469}
470
471void
472ObservableLocalReaderAdapter::onCardInserted()
473{
474 mStateService->onEvent(InternalEvent::CARD_INSERTED);
475}
476
477void
478ObservableLocalReaderAdapter::onCardRemoved()
479{
480 mStateService->onEvent(InternalEvent::CARD_REMOVED);
481}
482
483} /* namespace service */
484} /* namespace core */
485} /* namespace keyple */
ObservableCardReader::DetectionMode DetectionMode