bnr_xfs/
device_handle.rs

1use std::sync::{
2    atomic::{AtomicBool, Ordering},
3    mpsc, Arc,
4};
5
6use time as datetime;
7
8use crate::capabilities::Capabilities;
9use crate::cash_unit::{CashUnit, LogicalCashUnitList, PhysicalCashUnitList};
10use crate::currency::{CashOrder, CurrencyCode};
11use crate::denominations::BillsetIdList;
12use crate::denominations::DenominationList;
13use crate::dispense::DispenseRequest;
14use crate::history::{
15    BillAcceptanceHistory, BillDispenseHistory, SystemFailureHistory, SystemRestartHistory,
16    SystemUseHistory,
17};
18use crate::status::CdrStatus;
19use crate::xfs;
20use crate::{Error, Result};
21
22mod inner;
23pub mod usb;
24
25use usb::UsbDeviceHandle;
26
27/// BNR USB Vendor ID.
28pub const BNR_VID: u16 = 0x0bed;
29/// BNR USB Product ID.
30pub const BNR_PID: u16 = 0x0a00;
31/// Length of the URB (USB Request Block) header length on Linux.
32pub const URB_LEN: u64 = 64;
33
34/// BNR USB endpoint for device-to-host [XfsMethodResponse](crate::xfs::method_response::XfsMethodResponse)s.
35///
36/// Sets the MSB to indicate an `IN` endpoint.
37pub const BNR_RESPONSE_EP: u8 = (1 << 7) | 1;
38/// BNR USB endpoint for host-to-device [XfsMethodCall](crate::xfs::method_call::XfsMethodCall)s.
39pub const BNR_CALL_EP: u8 = 2;
40/// BNR USB endpoint for device-to-host asynchronous callback calls.
41///
42/// Sets the MSB to indicate an `IN` endpoint.
43pub const BNR_CALLBACK_CALL_EP: u8 = (1 << 7) | 3;
44/// BNR USB endpoint for host-to-device asynchronous callback responses.
45pub const BNR_CALLBACK_RESPONSE_EP: u8 = 4;
46
47/// Trait for arguments to state change callbacks used by the XFS API.
48pub trait CallbackArg {
49    fn value(&self) -> i32;
50    fn is_null(&self) -> bool;
51    fn is_cash_order(&self) -> bool;
52    fn as_cash_order(&self) -> Result<&CashOrder>;
53    fn as_cash_order_mut(&mut self) -> Result<&mut CashOrder>;
54}
55
56impl CallbackArg for () {
57    fn value(&self) -> i32 {
58        0
59    }
60
61    fn is_null(&self) -> bool {
62        true
63    }
64
65    fn is_cash_order(&self) -> bool {
66        false
67    }
68
69    fn as_cash_order(&self) -> Result<&CashOrder> {
70        Err(Error::Xfs(
71            "Expected CashOrder CallbackArg, have: null".into(),
72        ))
73    }
74
75    fn as_cash_order_mut(&mut self) -> Result<&mut CashOrder> {
76        Err(Error::Xfs(
77            "Expected CashOrder CallbackArg, have: null".into(),
78        ))
79    }
80}
81
82/// Function signature for the `Operation Completed` callback used by the XFS API.
83///
84/// Handles device-sent messages indicating an asynchronous operation has completed.
85///
86/// # Parameters
87///
88/// - `call_id`: callback ID returned by the initial async call
89/// - `operation_id`: async operation ID to uniquely identify the type of call
90/// - `result`: the result status of the call
91/// - `extended_result`: the extended result of the call
92/// - `callback_arg`: callback call argument (may be the `unit` type if not supplied)
93pub type OperationCompletedFn = fn(i32, i32, i32, i32, &mut dyn CallbackArg);
94
95/// Function signature for the `Intermediate Occurred` callback used by the XFS API.
96///
97/// Handles an intermediate state transition occurred during an ongoing transaction.
98///
99/// # Parameters
100///
101/// - `call_id`: callback ID returned by the initial async call
102/// - `operation_id`: async operation ID to uniquely identify the type of call
103/// - `reason`: specifies the reason for the intermediate event
104/// - `callback_arg`: callback call argument (may be the `unit` type if not supplied)
105pub type IntermediateOccurredFn = fn(i32, i32, i32, &mut dyn CallbackArg);
106
107/// Function signature for the `Status Occured` callback used by the XFS API.
108///
109/// Handles a status event that occurred on the device.
110///
111/// # Parameters
112///
113/// - `status`: the status that occurred on the device (e.g. bill inserted)
114/// - `result`: the result of the status event
115/// - `extended_result`: the extended result of the status event
116/// - `callback_arg`: callback call argument (may be the `unit` type if not supplied)
117pub type StatusOccurredFn = fn(i32, i32, i32, &mut dyn CallbackArg);
118
119/// BNR XFS device handle for communication over USB.
120pub struct DeviceHandle {
121    usb: Arc<UsbDeviceHandle>,
122    stop_listener: Arc<AtomicBool>,
123    op_completed_callback: Option<OperationCompletedFn>,
124    intermediate_occurred_callback: Option<IntermediateOccurredFn>,
125    status_occurred_callback: Option<StatusOccurredFn>,
126    response_rx: mpsc::Receiver<xfs::method_call::XfsMethodCall>,
127}
128
129impl DeviceHandle {
130    /// Opens a new connection to the BNR XFS device.
131    ///
132    /// # Examples
133    ///
134    /// ```no_run
135    /// use bnr_xfs::{CallbackArg, DeviceHandle};
136    ///
137    /// // Callback handler for when an async call completes
138    /// //
139    /// // See OperationCompletedFn for details.
140    /// fn op_com(_call_id: i32, _op_id: i32, _res: i32, _ext_res: i32, _cb_arg: &mut dyn CallbackArg) {
141    ///     // process the completion event...
142    /// }
143    ///
144    /// // Callback handler for when an intermediate event occurs
145    /// //
146    /// // See IntermediateOccurredFn for details.
147    /// fn int_oc(_call_id: i32, _op_id: i32, _reason: i32, _cb_arg: &mut dyn CallbackArg) {
148    ///     // process the intermediate event...
149    /// }
150    ///
151    /// // Callback handler for when a status event occurs
152    /// //
153    /// // See StatusOccurredFn for details.
154    /// fn st_oc(_call_id: i32, _op_id: i32, _reason: i32, _cb_arg: &mut dyn CallbackArg) {
155    ///     // process the status event...
156    /// }
157    ///
158    /// let device_handle = DeviceHandle::open(Some(op_com), Some(int_oc), Some(st_oc)).unwrap();
159    ///
160    /// let _status = device_handle.get_status().unwrap();
161    /// ```
162    pub fn open(
163        op_completed_callback: Option<OperationCompletedFn>,
164        intermediate_occurred_callback: Option<IntermediateOccurredFn>,
165        status_occurred_callback: Option<StatusOccurredFn>,
166    ) -> Result<Self> {
167        Self::open_inner(
168            UsbDeviceHandle::find_usb()?,
169            op_completed_callback,
170            intermediate_occurred_callback,
171            status_occurred_callback,
172        )
173    }
174
175    /// Reconnects to the BNR XFS device
176    pub fn reconnect(&mut self) -> Result<()> {
177        self.stop_background_listener();
178        self.usb = Arc::new(UsbDeviceHandle::find_usb()?);
179        self.stop_listener.store(false, Ordering::SeqCst);
180        let (response_tx, response_rx) = mpsc::channel();
181
182        self.start_background_listener(response_tx, Arc::clone(&self.stop_listener))?;
183
184        self.response_rx = response_rx;
185
186        Ok(())
187    }
188
189    /// Resets the BNR device.
190    pub fn reset(&self) -> Result<()> {
191        self.reset_inner()
192    }
193
194    /// Sends the message to cancel any currently active transactions/commands.
195    pub fn cancel(&self) -> Result<()> {
196        self.cancel_inner()
197    }
198
199    /// Stops secured communication session if started, ends the communication with the BNR and terminates the thread that has been started by a previous `open` call.
200    pub fn close(&self) -> Result<()> {
201        self.close_inner()
202    }
203
204    /// Reboots the BNR. This call puts the BNR in the same state than a power cycle (power off/on).
205    pub fn reboot(&self) -> Result<()> {
206        self.reboot_inner()
207    }
208
209    /// Gets the ISO 8601 formatted date-time from the device.
210    pub fn get_date_time(&self) -> Result<datetime::OffsetDateTime> {
211        self.get_date_time_inner()
212    }
213
214    /// Sets the ISO 8601 formatted date-time on the device to the provided time.
215    ///
216    /// **NOTE** This setting is not persistent across reboots/power-cycles.
217    ///
218    /// The default device time will reset to `2001-01-01 00:00:00`.
219    pub fn set_date_time(&self, date_time: datetime::OffsetDateTime) -> Result<()> {
220        self.set_date_time_inner(date_time)
221    }
222
223    /// Sets the ISO 8601 formatted date-time on the device to the current time.
224    ///
225    /// **NOTE** This setting is not persistent across reboots/power-cycles.
226    ///
227    /// The default device time will reset to `2001-01-01 00:00:00`.
228    pub fn set_current_date_time(&self) -> Result<()> {
229        self.set_date_time_inner(datetime::OffsetDateTime::now_utc())
230    }
231
232    /// Gets the current status of the BNR device.
233    pub fn get_status(&self) -> Result<CdrStatus> {
234        self.get_status_inner()
235    }
236
237    /// "Parks" the device for maintenance, disabling all modules.
238    pub fn park(&self) -> Result<()> {
239        self.park_inner()
240    }
241
242    /// Gets the [Capabilities] of the BNR device.
243    pub fn get_capabilities(&self) -> Result<Capabilities> {
244        self.get_capabilities_inner()
245    }
246
247    /// Sets the [Capabilities] for the BNR device.
248    pub fn set_capabilities(&self, caps: &Capabilities) -> Result<Capabilities> {
249        self.set_capabilities_inner(caps)
250    }
251
252    /// Sends the initial message to start a `CashIn` transaction, and begin accepting notes.
253    pub fn cash_in_start(&self) -> Result<()> {
254        self.cash_in_start_inner()
255    }
256
257    /// Sends the follow-up message to start a `CashIn` transaction, and begin accepting notes.
258    ///
259    /// After successfully sending this message, the device is ready to accept notes.
260    ///
261    /// params:
262    ///
263    /// - `limit`: optional limit on the number of notes to accept.
264    ///   - `None` will tell the device to accept one note.
265    /// - `currency`: optional restriction on currency to accept.
266    ///   - `None` will tell the device to accept all currencies.
267    ///
268    /// The BNR API takes two mutable pointers for this call, the first for `amount` and the second
269    /// for an ISO currency string (4-bytes, null-terminated ASCII).
270    ///
271    /// From the BNR API docs:
272    ///
273    /// ```no_build, no_run
274    /// @param[in] amount Amount to accept with this operation. If this parameter is NULL, the BNR
275    /// will accept only one banknote. If the amount is 0, banknotes will be accepted until the
276    /// escrow is full, or a bnr_Cancel() command is called. If the amount is different from 0,
277    /// banknotes will be accepted until the amount is reached, or the escrow is full, or a
278    /// bnr_Cancel() command is called.
279    ///
280    /// @param[in] currencyCode Currency to accept during this operation. If this parameter is
281    /// NULL or the string is empty, any currency will be accepted by the BNR.
282    /// ```
283    pub fn cash_in(&self, limit: Option<u32>, currency: Option<CurrencyCode>) -> Result<()> {
284        self.cash_in_inner(limit, currency)
285    }
286
287    /// Sends the message to end a `CashIn` transaction.
288    ///
289    /// The caller will need to call [cash_in_start](Self::cash_in_start) and [cash_in](Self::cash_in) to begin accepting notes again.
290    pub fn cash_in_end(&self) -> Result<()> {
291        self.cash_in_end_inner()
292    }
293
294    /// Sends the message to rollback a `CashIn` transaction, returning any inserted notes to the
295    /// customer.
296    ///
297    /// The caller should first call the [cancel](crate::cancel) function to cancel the `CashIn`
298    /// transaction.
299    pub fn cash_in_rollback(&self) -> Result<()> {
300        self.cash_in_rollback_inner()
301    }
302
303    /// This command allows the application to force cash that has been presented to be ejected from the bezel.
304    pub fn eject(&self) -> Result<()> {
305        self.eject_inner()
306    }
307
308    /// Empties a recycler or loader cash unit in the cashbox.
309    ///
310    /// **Note** When calling this function for a loader, the `to_float` parameter is not taken into account and the loader is completely emptied.
311    ///
312    /// Params:
313    ///
314    /// - `pcu_name`: Name of the physical cash unit to empty.
315    /// - `to_float` If `true`, the command empties up to the low threshold of the Physical Cash Unit, otherwise to zero.
316    pub fn empty(&self, pcu_name: &str, to_float: bool) -> Result<()> {
317        self.empty_inner(pcu_name, to_float)
318    }
319
320    /// Activates the presentation of the cash.
321    ///
322    /// It can only be used following the [dispense] method.
323    ///
324    /// A #XFS_S_CDR_CASH_AVAILABLE status event is issued to report that the bills are presented at the outlet,
325    /// then a #XFS_S_CDR_CASH_TAKEN status event is issued to report that the user has removed the bills, and the command completes.
326    ///
327    /// After #XFS_S_CDR_CASH_AVAILABLE status event, if no #XFS_S_CDR_CASH_TAKEN status event is received within a reasonable time period,
328    /// the application should send a [cancel_waiting_cash_taken] to terminate the command, then send a [retract] to clear the bills from the outlet.
329    pub fn present(&self) -> Result<()> {
330        self.present_inner()
331    }
332
333    /// Asks the BNR to stop waiting for cash removal at the Bezel if any.
334    ///
335    /// If it can do so, an OperationCompleteEvent is sent with the result field containing #XFS_E_CANCELLED to indicate that the operation was cancelled.
336    /// Otherwise, the current operation’s messages will be sent as usual.
337    ///
338    /// This method is meant to be called after the BNR has sent a #XFS_S_CDR_CASH_AVAILABLE status event, and before #XFS_S_CDR_CASH_TAKEN status event.
339    /// If this method is called outside these conditions, then no operation will take place and no error will be returned.
340    /// If this method is called after cash has been removed but before the #XFS_S_CDR_CASH_TAKEN status event has been returned to the caller,
341    /// then no operation will take place and no error will be returned.
342    pub fn cancel_waiting_cash_taken(&self) -> Result<()> {
343        self.cancel_waiting_cash_taken_inner()
344    }
345
346    /// This command allows the application to force cash that has been presented to be retracted.
347    ///
348    /// Retracted bills will be moved to the intermediate stacker area and accounted in the Bundler LCU. The application can then present bills to the user, using [cash_in_rollback](Self::cash_in_rollback) or [present](Self::present)
349    /// (depending of the kind of the transaction) or clear the intermediate stacker area using the [reject](Self::reject) method.
350    ///
351    /// This method may only be called after bills have been presented at the outlet following a [dispense](Self::dispense) (if autoPresent mode is active), [cash_in_rollback](Self::cash_in_rollback) or [present](Self::present) method call,
352    /// and before the bills have been taken by the user.
353    ///
354    /// **Note** An asynchronous method must not be called before the preceding one is terminated (i.e. OperationComplete event has been received); typically before calling [retract],
355    /// the preceding command must be terminated by calling
356    /// [cancel_waiting_cash_taken](Self::cancel_waiting_cash_taken).
357    pub fn retract(&self) -> Result<()> {
358        self.retract_inner()
359    }
360
361    /// Gets the complete state of all physical and logical cash units in the BNR.
362    ///
363    /// Returns the [CashUnit] struct with details about the [PhysicalCashUnit]s and
364    /// [LogicalCashUnit]s on the BNR device.
365    pub fn query_cash_unit(&self) -> Result<CashUnit> {
366        self.query_cash_unit_inner()
367    }
368
369    /// Configures the BNR’s cash unit. This function is used to add or remove Logical and Physical Cash Unit in the BNR.
370    ///
371    /// Those settings are persistent over power cycles.
372    ///
373    /// Params:
374    ///
375    /// - `transport_count`: number of bills in the transport system.
376    /// - `lcu_list`: [LogicalCashUnitList] for configuring [LogicalCashUnit]s.
377    /// - `pcu_list`: [PhysicalCashUnitList] for configuring [PhysicalCashUnit]s.
378    pub fn configure_cash_unit(
379        &self,
380        transport_count: u32,
381        lcu_list: &LogicalCashUnitList,
382        pcu_list: &PhysicalCashUnitList,
383    ) -> Result<()> {
384        self.configure_cash_unit_inner(transport_count, lcu_list, pcu_list)
385    }
386
387    /// Updates the BNR’s cash unit. This function is used to change counts and thresholds of the BNR
388    /// [CashUnit]s.
389    ///
390    /// Those settings are persistent over power cycles.
391    ///
392    /// Params:
393    ///
394    /// - `transport_count`: number of bills in the transport system.
395    /// - `lcu_list`: [LogicalCashUnitList] for configuring [LogicalCashUnit]s.
396    /// - `pcu_list`: [PhysicalCashUnitList] for configuring [PhysicalCashUnit]s.
397    pub fn update_cash_unit(
398        &self,
399        transport_count: u32,
400        lcu_list: &LogicalCashUnitList,
401        pcu_list: &PhysicalCashUnitList,
402    ) -> Result<()> {
403        self.update_cash_unit_inner(transport_count, lcu_list, pcu_list)
404    }
405
406    /// BNR_CASH_OPERATIONS Determines if the amount requested by value or by bill list, is available for dispense.
407    ///
408    /// From the MEI/CPI documentation:
409    ///
410    /// Three methods are possible:
411    ///
412    /// - denominateRequest->mixNumber is #XFS_C_CDR_MXA_MIN_BILLS: The BNR chooses the banknotes to be distributed in order to obtain the total amount using the minimum number of banknotes. Two parameters must be correctly set:
413    ///   - denominateRequest->denomination.amount has to be expressed in MDUs
414    ///   - denominateRequest->currency.currencyCode is a string. See this page for a full list of the existing ISO currency codes: <http://www.iso.org/iso/home/standards/currency_codes.htm>
415    /// - denominateRequest->mixNumber is #BNRXFS_C_CDR_MXA_OPTIMUM_CHANGE: The BNR chooses the banknotes to be distributed in order to obtain the total amount in a way that slows down cashbox filling. As long as the low denomination Recyclers are not near to full, change is determined like with the MinBills algorithm. But when a Recycler becomes nearly full (5/6 of Full threshold), this algorithm will try to put more of this denomination in the change so that the Recycler doesn’t become full and this denomination doesn’t start to be cashed. Two parameters must be correctly set:
416    ///    - denominateRequest->denomination.amount has to be expressed in MDUs
417    ///    - denominateRequest->currency.currencyCode is a string. See this page for a full list of the existing ISO currency codes: <http://www.iso.org/iso/home/standards/currency_codes.htm>
418    /// - denominateRequest->mixNumber is #XFS_C_CDR_MIX_DENOM: The user chooses through a list of Logical Cash Units the banknotes to be distributed by the BNR in order to obtain the total amount. The following parameters must be correctly set:
419    ///   - denominateRequest->denomination.size gives the size of the items array
420    ///   - for each item of denominateRequest->denomination.items from 0 to (denominateRequest->denomination.size - 1):
421    ///     - denominateRequest->denomination.items[item].unit contains the number of a LCU from where banknotes must be distributed.
422    ///     - denominateRequest->denomination.items[item].count gives the number of notes to distribute from the LCU.
423    pub fn denominate(&self, request: &DispenseRequest) -> Result<()> {
424        self.denominate_inner(request)
425    }
426
427    /// Dispenses the amount requested by value or by bill list.
428    ///
429    /// From the MEI/CPI documentation:
430    ///
431    /// The BNR will make a bundle of notes and wait for the bnr_Present() command to give it to the customer.
432    ///
433    /// Three methods are possible:
434    ///
435    /// - `DispenseRequest::mix_number` is #XFS_C_CDR_MXA_MIN_BILLS: The BNR chooses the banknotes to be distributed in order to obtain the total amount using the minimum number of banknotes. Two parameters must be correctly set:
436    ///   - `DispenseRequest::denomination.amount` has to be expressed in MDUs
437    ///   - `DispenseRequest::currency.currency_code`
438    ///
439    /// - `DispenseRequest::mix_number` is #BNRXFS_C_CDR_MXA_OPTIMUM_CHANGE: The BNR chooses the banknotes to be distributed in order to obtain the total amount in a way that slows down cashbox filling. As long as the low denomination Recyclers are not near to full, change is determined like with the MinBills algorithm. But when a Recycler becomes nearly full (5/6 of Full threshold), this algorithm will try to put more of this denomination in the change so that the Recycler doesn’t become full and this denomination doesn’t start to be cashed. Two parameters must be correctly set:
440    ///   - `DispenseRequest::denomination.amount` has to be expressed in MDUs
441    ///   - `DispenseRequest::currency.currency_code`
442    ///
443    /// - `DispenseRequest::mix_number` is #XFS_C_CDR_MIX_DENOM: The user chooses through a list of Logical Cash Units the banknotes to be distributed by the BNR in order to obtain the total amount. The following parameters must be correctly set:
444    ///   - `DispenseRequest::denomination::size` gives the size of the items array
445    ///       for each item of [DispenseRequest::denomination::items] from 0 to `DispenseRequest::denomination::size - 1`:
446    ///      - `DispenseRequest::denomination::items[item]::unit` contains the number of a LCU from where banknotes must be distributed.
447    ///      - `DispenseRequest::denomination::items[item]::count` gives the number of banknotes to distribute from the LCU.
448    ///
449    /// - `DispenseRequest::currency.currency_code` is a string in the C library.
450    ///   - See [CurrencyCode] for a full list of the existing ISO currency codes, also: <http://www.iso.org/iso/home/standards/currency_codes.htm>
451    ///   - conversion from the enum to a string is handled internally, the user does not need to worry about this.
452    ///
453    /// Params:
454    ///
455    /// - `request`: Amount or bill list requested for dispense.
456    ///
457    /// Returns `Ok` If function call is successful. Otherwise, return is strictly negative and its absolute value contains the error code.
458    pub fn dispense(&self, request: &DispenseRequest) -> Result<()> {
459        self.dispense_inner(request)
460    }
461
462    /// Stops any active sessions on the BNR device.
463    pub fn stop_session(&self) -> Result<()> {
464        self.stop_session_inner()
465    }
466
467    /// Gets a list of denominations in the BNR.
468    ///
469    /// Returns:
470    ///
471    /// - Ok([DenominationList]): list of the denominations currently defined in the BNR.
472    /// - Error conditions: see [update_denominations](Self::update_denominations) for a list of error code descriptions.
473    pub fn query_denominations(&self) -> Result<DenominationList> {
474        self.query_denominations_inner()
475    }
476
477    /// Updates the settings for a list of denominations.
478    ///
479    /// For each [DenominationInfo](crate::denominations::DenominationInfo) element of the [DenominationList],
480    /// the application can update its validation settings.
481    ///
482    /// From the BNR API docs:
483    ///
484    /// ```no_build,no_run
485    /// Those settings are persistent over power cycles; please refer to DenominationInfo for more details about settable properties, and their default values.
486    ///
487    /// @param[in] DenominationList This list of denominations will be a modified version of the one obtained from query_denominations() call.
488    /// ```
489    ///
490    /// Returns:
491    ///
492    /// - Ok(()) on success
493    /// - Error conditions:
494    ///   - `#XFS_E_ILLEGAL` - A dispense command is already active on the BNR.
495    ///   - `#XFS_E_NOT_SUPPORTED` - operation not supported by the BNR firmware version.
496    ///   - `#XFS_E_PARAMETER_INVALID` - Invalid array size. The array size is bigger than expected.
497    ///   - `#XFS_E_CDR_CASHIN_ACTIVE` - A cashIn command has been issued and is already active.
498    ///   - `#XFS_E_FAILURE` - a command is already running on the BNR or an internal error occured.
499    pub fn update_denominations(&self, request: &DenominationList) -> Result<()> {
500        self.update_denominations_inner(request)
501    }
502
503    /// Queries the device for the configured [BillsetIdList].
504    ///
505    /// **NOTE** Firmware Compatibility: This function requires a BNR FW v1.12.0 or newer. With older FW versions, the return will be #XFS_E_NOT_SUPPORTED.
506    pub fn query_billset_ids(&self) -> Result<BillsetIdList> {
507        self.query_billset_ids_inner()
508    }
509
510    /// Gets the BNR [BillAcceptanceHistory].
511    pub fn get_bill_acceptance_history(&self) -> Result<BillAcceptanceHistory> {
512        self.get_bill_acceptance_history_inner()
513    }
514
515    /// Gets the BNR [BillDispenseHistory].
516    pub fn get_bill_dispense_history(&self) -> Result<BillDispenseHistory> {
517        self.get_bill_dispense_history_inner()
518    }
519
520    /// Gets the BNR [SystemFailureHistory].
521    pub fn get_failure_history(&self) -> Result<SystemFailureHistory> {
522        self.get_failure_history_inner()
523    }
524
525    /// Gets the BNR [SystemRestartHistory].
526    pub fn get_restart_history(&self) -> Result<SystemRestartHistory> {
527        self.get_restart_history_inner()
528    }
529
530    /// Gets the BNR [SystemUseHistory].
531    pub fn get_use_history(&self) -> Result<SystemUseHistory> {
532        self.get_use_history_inner()
533    }
534
535    /// Gets a reference to the [UsbDeviceHandle].
536    pub(crate) fn usb(&self) -> &UsbDeviceHandle {
537        self.usb.as_ref()
538    }
539
540    pub(crate) fn usb_clone(&self) -> Arc<UsbDeviceHandle> {
541        Arc::clone(&self.usb)
542    }
543
544    /// Gets the callback function for operation completed events.
545    pub fn op_completed_callback(&self) -> Option<OperationCompletedFn> {
546        self.op_completed_callback
547    }
548
549    /// Gets the callback function for intermediate events.
550    pub fn intermediate_occurred_callback(&self) -> Option<IntermediateOccurredFn> {
551        self.intermediate_occurred_callback
552    }
553
554    /// Gets the callback function for status events.
555    pub fn status_occurred_callback(&self) -> Option<StatusOccurredFn> {
556        self.status_occurred_callback
557    }
558}