// This function gets the CASHBOX PAYOUT OPERATION DATA from the validator and returns it as a string public void GetCashboxPayoutOpData(TextBox log = null) { // first send the command m_cmd.CommandData[0] = CCommands.Hopper.SSP_CMD_CASHBOX_PAYOUT_OP_DATA; m_cmd.CommandDataLength = 1; if (!SendCommand(log)) { return; } // now deal with the response data if (CheckGenericResponses(log)) { // number of different coins int n = m_cmd.ResponseData[1]; string displayString = "Number of Total Coins: " + n.ToString() + "\r\n\r\n"; int i = 0; for (i = 2; i < (9 * n); i += 9) { displayString += "Moved " + CHelpers.ConvertBytesToInt16(m_cmd.ResponseData, i) + " x " + CHelpers.FormatToCurrency(CHelpers.ConvertBytesToInt32(m_cmd.ResponseData, i + 2)) + " " + (char)m_cmd.ResponseData[i + 6] + (char)m_cmd.ResponseData[i + 7] + (char)m_cmd.ResponseData[i + 8] + " to cashbox\r\n"; } displayString += CHelpers.ConvertBytesToInt32(m_cmd.ResponseData, i) + " coins not recognised\r\n"; if (log != null) { log.AppendText(displayString); } } }
// The get note positions command instructs the validator to return in the second byte the number of // notes stored and then in the following bytes, the values/channel (see SetValueReportingType()) of the stored // notes. The length of the response will vary based on the number of stored notes. public void CheckForStoredNotes(TextBox log = null) { cmd.CommandData[0] = CCommands.SSP_CMD_GET_NOTE_POSITIONS; cmd.CommandDataLength = 1; if (!SendCommand(log)) { return; } if (CheckGenericResponses(log)) { int counter = 0; Array.Clear(m_NotePositionValues, 0, m_NotePositionValues.Length); // Work backwards for a more accurate display (LIFO) for (int i = (cmd.ResponseData[1] * 4) + 1; i >= 2; i -= 4, counter++) { m_NotePositionValues[counter] = CHelpers.ConvertBytesToInt32(cmd.ResponseData, i - 3); } } }
// This function is called repeatedly to poll the validator about what events are happening. It // can optionally output these events to a textbox. public bool DoPoll(TextBox log) { byte i; //send poll m_cmd.CommandData[0] = CCommands.Generic.SSP_CMD_POLL; m_cmd.CommandDataLength = 1; if (!SendCommand(log)) { return(false); } // Check unit hasn't lost key (could be due to power loss or reset) if (m_cmd.ResponseData[0] == 0xFA) { return(false); } // isolate poll response to allow reuse of the SSP_COMMAND structure m_CurrentPollResponseLength = m_cmd.ResponseDataLength; m_cmd.ResponseData.CopyTo(m_CurrentPollResponse, 0); //parse poll response int coin = 0; string currency = ""; for (i = 1; i < m_CurrentPollResponseLength; i++) { switch (m_CurrentPollResponse[i]) { // This response indicates that the unit was reset and this is the first time a poll // has been called since the reset. case CCommands.Hopper.SSP_POLL_RESET: UpdateData(); break; // This response is given when the unit is disabled. case CCommands.Hopper.SSP_POLL_DISABLED: log.AppendText("Unit disabled...\r\n"); break; // The unit is in the process of paying out a coin or series of coins, this will continue to poll // until the coins have been fully dispensed case CCommands.Hopper.SSP_POLL_DISPENSING: log.AppendText("Dispensing coin(s)...\r\n"); // Now the index needs to be moved on to skip over the data provided by this response so it // is not parsed as a normal poll response. // In this response, the data includes the number of countries being dispensed (1 byte), then a 4 byte value // and 3 byte currency code for each country. i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // This is polled when a unit has finished a dispense operation. The following 4 bytes give the // value of the coin(s) dispensed. case CCommands.Hopper.SSP_POLL_DISPENSED: for (int j = 0; j < m_CurrentPollResponse[i + 1] * 7; j += 7) { coin = CHelpers.ConvertBytesToInt32(m_cmd.ResponseData, i + j + 2); // get coin data from response // get currency from response currency = ""; currency += (char)m_CurrentPollResponse[i + j + 6]; currency += (char)m_CurrentPollResponse[i + j + 7]; currency += (char)m_CurrentPollResponse[i + j + 8]; log.AppendText(CHelpers.FormatToCurrency(coin) + " " + currency + " coin(s) dispensed\r\n"); } UpdateData(); EnableValidator(); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // The coins being recycled inside the unit are running low. case CCommands.Hopper.SSP_POLL_COINS_LOW: log.AppendText("Coins low\r\n"); break; // There are no coins left in the unit. case CCommands.Hopper.SSP_POLL_EMPTY: log.AppendText("Unit empty\r\n"); break; // The mechanism inside the unit is jammed. case CCommands.Hopper.SSP_POLL_JAMMED: log.AppendText("Jammed\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // A dispense, SMART empty or float operation has been halted. case CCommands.Hopper.SSP_POLL_HALTED: i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // The device is 'floating' a specified amount of coins. It will transfer some to the cashbox and // leave the specified amount in the device. case CCommands.Hopper.SSP_POLL_FLOATING: i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // The float operation has completed. case CCommands.Hopper.SSP_POLL_FLOATED: UpdateData(); EnableValidator(); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // This poll appears when the SMART Hopper has been searching for a coin but cannot find it within // the timeout period. case CCommands.Hopper.SSP_POLL_TIME_OUT: log.AppendText("Search for suitable coins failed\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // A payout was interrupted in some way. The amount paid out does not match what was requested. The value // of the dispensed and requested amount is contained in the response. case CCommands.Hopper.SSP_POLL_INCOMPLETE_PAYOUT: log.AppendText("Incomplete payout detected...\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 11) + 1); break; // A float was interrupted in some way. The amount floated does not match what was requested. The value // of the dispensed and requested amount is contained in the response. case CCommands.Hopper.SSP_POLL_INCOMPLETE_FLOAT: log.AppendText("Incomplete float detected...\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 11) + 1); break; // This poll appears when coins have been dropped to the cashbox whilst making a payout. The value of // coins and the currency is reported in the response. case CCommands.Hopper.SSP_POLL_CASHBOX_PAID: log.AppendText("Cashbox paid\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // A credit event has been detected, this is when the coin mech has accepted a coin as legal currency. case CCommands.Hopper.SSP_POLL_COIN_CREDIT: coin = CHelpers.ConvertBytesToInt16(m_cmd.ResponseData, i + 1); currency = ""; currency += (char)m_CurrentPollResponse[i + 5]; currency += (char)m_CurrentPollResponse[i + 6]; currency += (char)m_CurrentPollResponse[i + 7]; log.AppendText(CHelpers.FormatToCurrency(coin) + " " + currency + " credited\r\n"); UpdateData(); i += 7; break; // The coin mech has become jammed. case CCommands.Hopper.SSP_POLL_COIN_MECH_JAMMED: log.AppendText("Coin mech jammed\r\n"); break; // The return button on the coin mech has been pressed. case CCommands.Hopper.SSP_POLL_COIN_MECH_RETURN_PRESSED: log.AppendText("Return button pressed\r\n"); break; // The unit is in the process of dumping all the coins stored inside it into the cashbox. case CCommands.Hopper.SSP_POLL_EMPTYING: log.AppendText("Emptying...\r\n"); break; // The unit has finished dumping coins to the cashbox. case CCommands.Hopper.SSP_POLL_EMPTIED: log.AppendText("Emptied\r\n"); UpdateData(); EnableValidator(); break; // A fraud attempt has been detected. case CCommands.Hopper.SSP_POLL_FRAUD_ATTEMPT: log.AppendText("Fraud attempted\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // The unit is in the process of dumping all the coins stored inside it into the cashbox. // This poll means that the unit is keeping track of what it empties. case CCommands.Hopper.SSP_POLL_SMART_EMPTYING: log.AppendText("SMART emptying...\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // The unit has finished SMART emptying. The info on what has been dumped can be obtained // by sending the CASHBOX PAYOUT OPERATION DATA command. case CCommands.Hopper.SSP_POLL_SMART_EMPTIED: GetCashboxPayoutOpData(log); UpdateData(); EnableValidator(); log.AppendText("SMART emptied\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // A coin has had its routing changed to either cashbox or recycling. case CCommands.Hopper.SSP_POLL_COIN_ROUTED: log.AppendText("Routed coin\r\n"); UpdateData(); break; default: log.AppendText("Unsupported poll response received: " + (int)m_CurrentPollResponse[i] + "\r\n"); break; } } return(true); }
// This function uses the setup request command to get all the information about the validator. public void SetupRequest(TextBox log = null) { // send setup request cmd.CommandData[0] = CCommands.Generic.SSP_CMD_SETUP_REQUEST; cmd.CommandDataLength = 1; if (!SendCommand(log)) { return; } // display setup request string displayString = "Validator Type: "; int index = 1; // unit type (table 0-1) m_UnitType = (char)cmd.ResponseData[index++]; switch (m_UnitType) { case (char)0x00: displayString += "Validator"; break; case (char)0x03: displayString += "SMART Hopper"; break; case (char)0x06: displayString += "SMART Payout"; break; case (char)0x07: displayString += "NV11"; break; default: displayString += "Unknown Type"; break; } displayString += "\r\nFirmware: "; // firmware (table 2-5) while (index <= 5) { displayString += (char)cmd.ResponseData[index++]; if (index == 4) { displayString += "."; } } // country code (table 6-8) // this is legacy code, in protocol version 6+ each channel has a seperate currency index = 9; // to skip country code // value multiplier (table 9-11) // also legacy code, a real value multiplier appears later in the response index = 12; // to skip value multiplier displayString += "\r\nNumber of Channels: "; int numChannels = cmd.ResponseData[index++]; m_NumberOfChannels = numChannels; displayString += numChannels + "\r\n"; // channel values (table 13 to 13+n) // the channel values located here in the table are legacy, protocol 6+ provides a set of expanded // channel values. index = 13 + m_NumberOfChannels; // Skip channel values // channel security (table 13+n to 13+(n*2)) // channel security values are also legacy code index = 13 + (m_NumberOfChannels * 2); // Skip channel security displayString += "Real Value Multiplier: "; // real value multiplier (table 13+(n*2) to 15+(n*2)) // (big endian) m_ValueMultiplier = cmd.ResponseData[index + 2]; m_ValueMultiplier += cmd.ResponseData[index + 1] << 8; m_ValueMultiplier += cmd.ResponseData[index] << 16; displayString += m_ValueMultiplier + "\r\nProtocol Version: "; index += 3; // protocol version (table 16+(n*2)) index = 16 + (m_NumberOfChannels * 2); int protocol = cmd.ResponseData[index++]; displayString += protocol + "\r\n"; // protocol 6+ only // channel currency country code (table 17+(n*2) to 17+(n*5)) index = 17 + (m_NumberOfChannels * 2); int sectionEnd = 17 + (m_NumberOfChannels * 5); int count = 0; byte[] channelCurrencyTemp = new byte[3 * m_NumberOfChannels]; while (index < sectionEnd) { displayString += "Channel " + ((count / 3) + 1) + ", currency: "; channelCurrencyTemp[count] = cmd.ResponseData[index++]; displayString += (char)channelCurrencyTemp[count++]; channelCurrencyTemp[count] = cmd.ResponseData[index++]; displayString += (char)channelCurrencyTemp[count++]; channelCurrencyTemp[count] = cmd.ResponseData[index++]; displayString += (char)channelCurrencyTemp[count++]; displayString += "\r\n"; } // expanded channel values (table 17+(n*5) to 17+(n*9)) index = sectionEnd; displayString += "Expanded channel values:\r\n"; sectionEnd = 17 + (m_NumberOfChannels * 9); int n = 0; count = 0; int[] channelValuesTemp = new int[m_NumberOfChannels]; while (index < sectionEnd) { n = CHelpers.ConvertBytesToInt32(cmd.ResponseData, index); channelValuesTemp[count] = n; index += 4; displayString += "Channel " + ++count + ", value = " + n + "\r\n"; } // Create list entry for each channel m_UnitDataList.Clear(); // clear old table for (byte i = 0; i < m_NumberOfChannels; i++) { ChannelData d = new ChannelData(); d.Channel = i; d.Channel++; // Offset from array index by 1 d.Value = channelValuesTemp[i] * Multiplier; d.Currency[0] = (char)channelCurrencyTemp[0 + (i * 3)]; d.Currency[1] = (char)channelCurrencyTemp[1 + (i * 3)]; d.Currency[2] = (char)channelCurrencyTemp[2 + (i * 3)]; d.Level = 0; // Can't store notes d.Recycling = false; // Can't recycle notes m_UnitDataList.Add(d); } // Sort the list of data by the value. m_UnitDataList.Sort(delegate(ChannelData d1, ChannelData d2) { return(d1.Value.CompareTo(d2.Value)); }); if (log != null) { log.AppendText(displayString); } }
// The poll function is called repeatedly to poll to validator for information, it returns as // a response in the command structure what events are currently happening. public bool DoPoll(TextBox log) { byte i; //send poll cmd.CommandData[0] = CCommands.Generic.SSP_CMD_POLL; cmd.CommandDataLength = 1; if (!SendCommand(log)) { return(false); } // Check unit hasn't lost key (could be due to power loss or reset) if (cmd.ResponseData[0] == 0xFA) { return(false); } // Store poll response to avoid corruption if the cmd structure is accessed whilst polling cmd.ResponseData.CopyTo(m_CurrentPollResponse, 0); m_CurrentPollResponseLength = cmd.ResponseDataLength; //parse poll m_CurrentPollResponse int noteVal = 0; for (i = 1; i < m_CurrentPollResponseLength; i++) { switch (m_CurrentPollResponse[i]) { // This m_CurrentPollResponse indicates that the unit was reset and this is the first time a poll // has been called since the reset. case CCommands.NV11.SSP_POLL_RESET: log.AppendText("Unit reset\r\n"); UpdateData(); break; // A note is currently being read by the validator sensors. The second byte of this response // is zero until the note's type has been determined, it then changes to the channel of the // scanned note. case CCommands.NV11.SSP_POLL_NOTE_READ: if (m_CurrentPollResponse[i + 1] > 0) { noteVal = GetChannelValue(m_CurrentPollResponse[i + 1]); log.AppendText("Note in escrow, amount: " + CHelpers.FormatToCurrency(noteVal) + "\r\n"); } else { log.AppendText("Reading note\r\n"); } i++; break; // A credit event has been detected, this is when the validator has accepted a note as legal currency. case CCommands.NV11.SSP_POLL_CREDIT: noteVal = GetChannelValue(m_CurrentPollResponse[i + 1]); log.AppendText("Credit " + CHelpers.FormatToCurrency(noteVal) + "\r\n"); NotesAccepted++; UpdateData(); i++; break; // A note is being rejected from the validator. This will carry on polling while the note is in transit. case CCommands.NV11.SSP_POLL_REJECTING: break; // A note has been rejected from the validator. This response only appears once. case CCommands.NV11.SSP_POLL_REJECTED: log.AppendText("Note rejected\r\n"); QueryRejection(log); UpdateData(); break; // A note is in transit to the cashbox. case CCommands.NV11.SSP_POLL_STACKING: log.AppendText("Stacking note\r\n"); break; // A note has reached the cashbox. case CCommands.NV11.SSP_POLL_STACKED: log.AppendText("Note stacked\r\n"); break; // A safe jam has been detected. This is where the user has inserted a note and the note // is jammed somewhere that the user cannot reach. case CCommands.NV11.SSP_POLL_SAFE_JAM: log.AppendText("Safe jam\r\n"); break; // An unsafe jam has been detected. This is where a user has inserted a note and the note // is jammed somewhere that the user can potentially recover the note from. case CCommands.NV11.SSP_POLL_UNSAFE_JAM: log.AppendText("Unsafe jam\r\n"); break; // The validator is disabled, it will not execute any commands or do any actions until enabled. case CCommands.NV11.SSP_POLL_DISABLED: log.AppendText("Unit disabled...\r\n"); break; // A fraud attempt has been detected. The second byte indicates the channel of the note that a fraud // has been attempted on. case CCommands.NV11.SSP_POLL_FRAUD_ATTEMPT: log.AppendText("Fraud attempt, note type: " + GetChannelValue(m_CurrentPollResponse[i + 1]) + "\r\n"); i++; break; // The stacker (cashbox) is full. case CCommands.NV11.SSP_POLL_STACKER_FULL: log.AppendText("Stacker full\r\n"); break; // A note was detected somewhere inside the validator on startup and was rejected from the front of the // unit. case CCommands.NV11.SSP_POLL_NOTE_CLEARED_FROM_FRONT: log.AppendText(GetChannelValue(m_CurrentPollResponse[i + 1]) + " note cleared from front at reset." + "\r\n"); i++; break; // A note was detected somewhere inside the validator on startup and was cleared into the cashbox. case CCommands.NV11.SSP_POLL_NOTE_CLEARED_TO_CASHBOX: log.AppendText(GetChannelValue(m_CurrentPollResponse[i + 1]) + " note cleared to stacker at reset." + "\r\n"); i++; break; // The cashbox has been removed from the unit. This will continue to poll until the cashbox is replaced. case CCommands.NV11.SSP_POLL_CASHBOX_REMOVED: log.AppendText("Cashbox removed\r\n"); break; // The cashbox has been replaced, this will only display on a poll once. case CCommands.NV11.SSP_POLL_CASHBOX_REPLACED: log.AppendText("Cashbox replaced\r\n"); break; // A note has been stored in the payout device to be paid out instead of going into the cashbox. case CCommands.NV11.SSP_POLL_NOTE_STORED: log.AppendText("Note stored\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); UpdateData(); break; // The validator is in the process of paying out a note, this will continue to poll until the note has // been fully dispensed and removed from the front of the validator by the user. case CCommands.NV11.SSP_POLL_NOTE_DISPENSING: log.AppendText("Dispensing note\r\n"); i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; // The note has been dispensed and removed from the bezel by the user. case CCommands.NV11.SSP_POLL_NOTE_DISPENSED: for (int j = 0; j < m_CurrentPollResponse[i + 1]; j += 7) { log.AppendText("Dispensed " + (CHelpers.ConvertBytesToInt32(m_CurrentPollResponse, j + i + 2) / 100).ToString() + " " + (char)m_CurrentPollResponse[j + i + 6] + (char)m_CurrentPollResponse[j + i + 7] + (char)m_CurrentPollResponse[j + i + 8] + "\r\n"); } i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); NotesDispensed++; UpdateData(); EnableValidator(log); break; // A note has been transferred from the payout storage to the cashbox case CCommands.NV11.SSP_POLL_NOTE_TRANSFERRED_TO_STACKER: log.AppendText("Note stacked\r\n"); UpdateData(); EnableValidator(log); break; // This single poll response indicates that the payout device has finished emptying. case CCommands.NV11.SSP_POLL_EMPTIED: log.AppendText("Device emptied\r\n"); UpdateData(); EnableValidator(log); break; // This response indicates a note is being dispensed and is resting in the bezel waiting to be removed // before the validator can continue case CCommands.NV11.SSP_POLL_NOTE_HELD_IN_BEZEL: for (int j = 0; j < m_CurrentPollResponse[i + 1]; j += 7) { log.AppendText((CHelpers.ConvertBytesToInt32(m_CurrentPollResponse, j + i + 2) / 100).ToString() + " " + (char)m_CurrentPollResponse[j + i + 6] + (char)m_CurrentPollResponse[j + i + 7] + (char)m_CurrentPollResponse[j + i + 8] + " held in bezel...\r\n"); } i += (byte)((m_CurrentPollResponse[i + 1] * 7) + 1); break; default: break; } } return(true); }