1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
use crate::{impl_default, make_list, std::fmt};
/// Represents the device's response status byte.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum ResponseStatus {
// Events
/// Note cleared from front bezel.
///
/// During the device power up sequence a bill was detected as being in the note path. This bill
/// is then rejected from the device via the bezel and this event is issued. If the bill value is
/// known then the channel number is given in the data byte, otherwise the data byte will be
/// zero value.
NoteClearedFromFront = 0xe1,
/// Note cleared into the cashbox.
///
/// During the device power up sequence a bill was detected as being in the stack path. This bill
/// is then moved into the device cashbox and this event is issued. If the bill value is known
/// then the channel number is given in the data byte, otherwise the data byte will be zero
/// value.
NoteClearedIntoCashbox = 0xe2,
/// Cashbox removed from device.
///
/// The system has detected that the cashbox unit has been removed from it's working position.
///
/// The system will remain disabled for bill entry until the cashbox unit is replaced into it's
/// working position.
CashboxRemoved = 0xe3,
/// Cashbox replaced into the device.
///
/// The device cashbox box unit has been detected as replaced into it's working position.
///
/// The validator will re-enable if it has not already been disabled by the host system.
CashboxReplaced = 0xe4,
/// An attempt to defraud the device ocurred.
///
/// The validator system has detected an attempt to mauipulate the coin/banknote in order to
/// fool the system to register credits with no monies added.
FraudAttempt = 0xe6,
/// Stacker unit is full.
///
/// Event in response to poll given when the device has detected that the stacker unit has
/// stacked it's full limit of banknotes.
StackerFull = 0xe7,
/// Device is in a disabled state.
///
/// A disabled event is given in response to a poll command when a device has been disabled by
/// the host or by some other internal function of the device.
Disabled = 0xe8,
/// A bill has been detected as jammed during it's transport through the validator.
///
/// An unsafe jam indicates that this bill may be in a position where the user could retrieve it from the
/// validator bezel.
UnsafeJam = 0xe9,
/// A bill has been transported trough the banknote validator and is in it's stacked position.
Stacked = 0xeb,
/// The bill is currently being transported to and through the device stacker.
Stacking = 0xcc,
/// A bill has been rejected back to the user by the Banknote Validator.
Rejected = 0xec,
/// A bill is in the process of being rejected back to the user by the Banknte Validator.
Rejecting = 0xed,
/// Add note credit from escrow to storage.
///
/// This event is generated when the banknote has been moved from the escrow position to a
/// safe position within the validator system where the banknote cannot be retreived by the user.
///
/// At this point, it is safe for the host to use this event as it's 'Credt' point.
NoteCredit = 0xee,
/// An event given when the BNV is reading a banknote.
///
/// If the event data byte is zero, then the note is in the process of being scanned and validated.
///
/// If the data byte value changes from zero to a value greater then zero, this indicates a valid banknote is
/// now held in the escrow position. The byte value shows the channel of the banknote that has been
/// validated.
///
/// A poll command after this value has been given will cause the banknote to be accepted from
/// the escrow position. The host can also issue a reject command at this point to reject the banknote
/// back to the user.
Read = 0xef,
/// OK is the first byte returned in the response to a successful command. It does not indicate
/// that the command has completed, just that it has been received and understood.
Ok = 0xf0,
/// Device reset
DeviceReset = 0xf1,
/// Returned when an invalid command is received by a peripheral. Check the firmware is up to
/// date and the protocol level is set correctly.
CommandNotKnown = 0xf2,
/// Indicates the command was received by the device but the parameters provided with the
/// command did not match what the device was expecting.
///
/// Check the specification to ensure the arguments provided with the command were valid
/// and that the correct protocol version is being used
WrongNumberParameters = 0xf3,
/// Indicates the command was received by the device but the parameters provided with the
/// command were out of available range. Examples of this are providing a non-prime number
/// to set generator command (0x4A).
ParameterOutOfRange = 0xf4,
/// A command sent could not be processed at that time.
///
/// This response can have an additional byte giving the reason the command cannot be processed.
/// Check individual device command details for details An example of this is asking a Hopper to
/// payout whilst it is already dispensing coins.
///
/// Check the poll response for the state of the device and retry the command when the device
/// is enabled and not busy.
CommandCannotBeProcessed = 0xf5,
/// Failure response
///
/// Used if none of the other error conditions are applicable or as detailed in command
/// documentation. An example is setting protocol version to a number greater than that
/// supported by the device.
Fail = 0xf8,
/// The device is in encrypted communication mode, but the encryption keys have not been
/// negotiated.
KeyNotSet = 0xfa,
/// The device has had all its note channels inhibited, and has become disabled for note insertion.
ChannelDisable = 0xb5,
/// Reserved for future use
Reserved(u8),
}
impl ResponseStatus {
/// Creates a new [ResponseStatus].
pub const fn new() -> Self {
Self::Reserved(0xff)
}
/// Gets whether the [ResponseStatus] is [ResponseStatus::Ok].
pub fn is_ok(&self) -> bool {
*self == Self::Ok
}
/// Converts a `u8` into a [ResponseStatus].
pub const fn from_u8(val: u8) -> Self {
match val {
0xe1 => Self::NoteClearedFromFront,
0xe2 => Self::NoteClearedIntoCashbox,
0xe3 => Self::CashboxRemoved,
0xe4 => Self::CashboxReplaced,
0xe6 => Self::FraudAttempt,
0xe7 => Self::StackerFull,
0xe8 => Self::Disabled,
0xe9 => Self::UnsafeJam,
0xeb => Self::Stacked,
0xcc => Self::Stacking,
0xec => Self::Rejected,
0xed => Self::Rejecting,
0xee => Self::NoteCredit,
0xef => Self::Read,
0xf0 => Self::Ok,
0xf1 => Self::DeviceReset,
0xf2 => Self::CommandNotKnown,
0xf3 => Self::WrongNumberParameters,
0xf4 => Self::ParameterOutOfRange,
0xf5 => Self::CommandCannotBeProcessed,
0xf8 => Self::Fail,
0xfa => Self::KeyNotSet,
0xb5 => Self::ChannelDisable,
res => Self::Reserved(res),
}
}
/// Converts the [ResponseStatus] to a `u8`.
pub const fn to_u8(&self) -> u8 {
match self {
Self::NoteClearedFromFront => 0xe1,
Self::NoteClearedIntoCashbox => 0xe2,
Self::CashboxRemoved => 0xe3,
Self::CashboxReplaced => 0xe4,
Self::FraudAttempt => 0xe6,
Self::StackerFull => 0xe7,
Self::Disabled => 0xe8,
Self::UnsafeJam => 0xe9,
Self::Stacked => 0xeb,
Self::Stacking => 0xcc,
Self::Rejected => 0xec,
Self::Rejecting => 0xed,
Self::NoteCredit => 0xee,
Self::Read => 0xef,
Self::Ok => 0xf0,
Self::DeviceReset => 0xf1,
Self::CommandNotKnown => 0xf2,
Self::WrongNumberParameters => 0xf3,
Self::ParameterOutOfRange => 0xf4,
Self::CommandCannotBeProcessed => 0xf5,
Self::Fail => 0xf8,
Self::KeyNotSet => 0xfa,
Self::ChannelDisable => 0xb5,
Self::Reserved(res) => *res,
}
}
}
impl From<u8> for ResponseStatus {
fn from(val: u8) -> Self {
Self::from_u8(val)
}
}
impl From<ResponseStatus> for u8 {
fn from(val: ResponseStatus) -> Self {
val.to_u8()
}
}
impl From<&ResponseStatus> for u8 {
fn from(val: &ResponseStatus) -> Self {
(*val).into()
}
}
impl From<ResponseStatus> for &'static str {
fn from(val: ResponseStatus) -> Self {
match val {
ResponseStatus::NoteClearedFromFront => "NoteClearedFromFront",
ResponseStatus::NoteClearedIntoCashbox => "NoteClearedIntoCashbox",
ResponseStatus::CashboxRemoved => "CashboxRemoved",
ResponseStatus::CashboxReplaced => "CashboxReplaced",
ResponseStatus::FraudAttempt => "FraudAttempt",
ResponseStatus::StackerFull => "StackerFull",
ResponseStatus::Disabled => "Disabled",
ResponseStatus::UnsafeJam => "UnsafeJam",
ResponseStatus::Stacked => "Stacked",
ResponseStatus::Stacking => "Stacking",
ResponseStatus::Rejected => "Rejected",
ResponseStatus::Rejecting => "Rejecting",
ResponseStatus::NoteCredit => "NoteCredit",
ResponseStatus::Read => "Read",
ResponseStatus::Ok => "OK",
ResponseStatus::DeviceReset => "DeviceReset",
ResponseStatus::CommandNotKnown => "CommandNotKnown",
ResponseStatus::WrongNumberParameters => "WrongNumberParameters",
ResponseStatus::ParameterOutOfRange => "ParameterOutOfRange",
ResponseStatus::CommandCannotBeProcessed => "CommandCannotBeProcessed",
ResponseStatus::Fail => "Fail",
ResponseStatus::KeyNotSet => "KeyNotSet",
ResponseStatus::ChannelDisable => "ChannelDisable",
ResponseStatus::Reserved(_) => "Reserved",
}
}
}
impl From<&ResponseStatus> for &'static str {
fn from(r: &ResponseStatus) -> Self {
(*r).into()
}
}
impl fmt::Display for ResponseStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Reserved(s) => write!(f, "Reserved(0x{s:02x})"),
_ => write!(f, "{}", <&str>::from(self)),
}
}
}
impl_default!(ResponseStatus);
make_list!(
ResponseStatusList,
ResponseStatus,
"A list container for [ResponseStatus]s."
);