private void recycleBoxPayout_CheckedChange(object sender, EventArgs e) { CheckBox chkbox = sender as CheckBox; try { ChannelData d = new ChannelData(); Payout.GetDataByChannel(Int32.Parse(chkbox.Name), ref d); if (chkbox.Checked) { Payout.ChangeNoteRoute(d.Value, d.Currency, false, textBox1); } else { Payout.ChangeNoteRoute(d.Value, d.Currency, true, textBox1); } // Ensure payout ability is enabled in the validator Payout.EnablePayout(); } catch (Exception ex) { MessageBox.Show(ex.ToString()); return; } }
// This is a one off function that is called the first time the MainLoop() // function runs, it just sets up a few of the UI elements that only need // updating once. private void SetupFormLayout() { // need validator class instance if (Payout == null) { MessageBox.Show("Validator class is null.", "ERROR"); return; } // Positioning int x = 600, y = 40; // Add label Label lbl = new Label(); lbl.Location = new Point(x, y - 10); lbl.Size = new Size(80, 30); lbl.Text = "Recycle\nChannels:"; Controls.Add(lbl); for (int i = 1; i <= Payout.NumberOfChannels; i++) { CheckBox c = new CheckBox(); c.Location = new Point(x, y + (i * 20)); c.Name = i.ToString(); ChannelData d = new ChannelData(); Payout.GetDataByChannel(i, ref d); c.Text = CHelpers.FormatToCurrency(d.Value) + " " + new String(d.Currency); c.Checked = d.Recycling; c.CheckedChanged += new EventHandler(recycleBox_CheckedChange); Controls.Add(c); } }
// This function sends the set coin mech inhibits command to set which coins are accepted on the Hopper. // Please note: The response data of this command if it is sent with no coin mech attached will be // WRONG PARAMETERS. public void SetInhibits(TextBox log = null) { // set inhibits on each coin for (int i = 0; i < m_NumberOfChannels; i++) { m_cmd.CommandData[0] = CCommands.Hopper.SSP_CMD_SET_COIN_MECH_INHIBITS; m_cmd.CommandData[1] = 0x01; // coin accepted // convert values to byte array and set command data byte[] b = BitConverter.GetBytes(GetChannelValue(i + 1)); m_cmd.CommandData[2] = b[0]; m_cmd.CommandData[3] = b[1]; // currency ChannelData d = m_UnitDataList[i]; m_cmd.CommandData[4] = (byte)d.Currency[0]; m_cmd.CommandData[5] = (byte)d.Currency[1]; m_cmd.CommandData[6] = (byte)d.Currency[2]; m_cmd.CommandDataLength = 7; if (!SendCommand(log)) { return; } if (CheckGenericResponses(log) && log != null) { log.AppendText("Inhibits set on channel " + (i + 1).ToString() + "\r\n"); } } }
private void recycleBox_CheckedChange(object sender, EventArgs e) { try { // Get the sending object as a checkbox CheckBox c = sender as CheckBox; // Get the data from the payout ChannelData d = new ChannelData(); Payout.GetDataByChannel(Int32.Parse(c.Name), ref d); if (c.Checked) { Payout.ChangeNoteRoute(d.Value, d.Currency, false, textBox1); } else { Payout.ChangeNoteRoute(d.Value, d.Currency, true, textBox1); } } catch (Exception ex) { MessageBox.Show(ex.ToString(), "EXCEPTION"); return; } }
// This returns the currency of a specified channel. public char[] GetChannelCurrency(int channel) { if (channel > 0 && channel <= m_NumberOfChannels) { ChannelData d = m_UnitDataList[channel - 1]; return(d.Currency); } return(null); }
// This returns the level of a specified channel. public int GetChannelLevel(int channel) { if (channel > 0 && channel <= m_NumberOfChannels) { ChannelData d = m_UnitDataList[channel - 1]; return(d.Level); } return(-1); }
private void PayoutBtn_Click(object sender, EventArgs e) { bool payoutRequired = false; byte[] data = new byte[9 * Hopper.NumberOfChannels]; // create to size of maximum possible byte length = 0; int dataIndex = 0; byte denomsToPayout = 0; // For each denomination for (int i = 0; i < Hopper.NumberOfChannels; i++) { try { // Check if there is input in the box if (AmountToPayout[i].Text != "" && AmountToPayout[i].Text != "0") { // If textbox isn't blank then this denom is being paid out denomsToPayout++; length += 9; // 9 bytes per denom to payout (2 amount, 4 value, 3 currency) payoutRequired = true; // need to do a payout as there is now > 0 denoms // Number of this denomination to payout UInt16 numToPayout = UInt16.Parse(AmountToPayout[i].Text); byte[] b = CHelpers.ConvertInt16ToBytes((short)numToPayout); data[dataIndex++] = b[0]; data[dataIndex++] = b[1]; // Value of this denomination ChannelData d = Hopper.UnitDataList[i]; b = CHelpers.ConvertInt32ToBytes(d.Value); data[dataIndex++] = b[0]; data[dataIndex++] = b[1]; data[dataIndex++] = b[2]; data[dataIndex++] = b[3]; // Currency of this denomination data[dataIndex++] = (Byte)d.Currency[0]; data[dataIndex++] = (Byte)d.Currency[1]; data[dataIndex++] = (Byte)d.Currency[2]; } } catch (Exception ex) { MessageBox.Show(ex.ToString()); payoutRequired = false; // don't payout on exception } } if (payoutRequired) { // Send payout command and shut this form Hopper.PayoutByDenomination(denomsToPayout, data, length, Output); base.Dispose(); } }
// This allows the caller to access all the data stored with a channel. An empty ChannelData struct is passed // over which gets filled with info. public void GetDataByChannel(int channel, ref ChannelData d) { // Iterate through each foreach (ChannelData dList in m_UnitDataList) { if (dList.Channel == channel) // Compare channel { d = dList; // Copy data from list to param break; } } }
private void frmPayoutByDenom_Shown(object sender, EventArgs e) { // Create textbox array based on number of channels AmountToPayout = new TextBox[Hopper.NumberOfChannels]; // Add description labels Label l = new Label(); l.Size = new Size(80, 45); l.Location = new Point(100, 10); l.Text = "Number to\nPayout"; Controls.Add(l); // Iterate through the channels for (int i = 0; i < Hopper.NumberOfChannels; i++) { // Create an entry box and label for each denomination (channel) l = new Label(); l.Size = new Size(80, 25); l.Location = new Point(10, 55 + i * 25); ChannelData d = Hopper.UnitDataList[i]; l.Text = d.Value / 100f + " " + new String(d.Currency); Controls.Add(l); AmountToPayout[i] = new TextBox(); AmountToPayout[i].Size = new Size(60, 25); AmountToPayout[i].Location = new Point(100, 55 + i * 25); Controls.Add(AmountToPayout[i]); } // Add button to payout Button b = new Button(); b.Size = new Size(140, 25); Point p = AmountToPayout[Hopper.NumberOfChannels - 1].Location; p.Y += 25; p.X -= 90; b.Location = p; b.Text = "Payout"; b.Click += new EventHandler(PayoutBtn_Click); Controls.Add(b); }
private void recycleBox_CheckedChange(object sender, EventArgs e) { try { // Get the sending object as a checkbox CheckBox c = sender as CheckBox; // Get the data from the payout ChannelData d = new ChannelData(); Payout.GetDataByChannel(Int32.Parse(c.Name), ref d); if (c.Checked) Payout.ChangeNoteRoute(d.Value, d.Currency, false, textBox1); else Payout.ChangeNoteRoute(d.Value, d.Currency, true, textBox1); } catch (Exception ex) { MessageBox.Show (ex.ToString (), "EXCEPTION"); return; } }
private void frmPayoutByDenom_Shown(object sender, EventArgs e) { // Create textbox arrays based on number of channels AmountToPayout_Hopper = new TextBox[Hopper.NumberOfChannels]; AmountToPayout_Payout = new TextBox[Payout.NumberOfChannels]; bool payoutHighest = true; if (Hopper.NumberOfChannels > Payout.NumberOfChannels) { payoutHighest = false; } System.OperatingSystem osInfo = System.Environment.OSVersion; int titleXSize = 0; // XP, 2000, Server 2003 if (osInfo.Platform == PlatformID.Win32NT && osInfo.Version.Major == 5) { titleXSize = 80; } // Vista, 7 else if (osInfo.Platform == PlatformID.Win32NT && osInfo.Version.Major == 6) { titleXSize = 120; } // Add description labels for Hopper Label l = new Label(); l.Size = new Size(titleXSize, 45); l.Location = new Point(10, 10); l.Text = "Number to\nPayout (Hopper)"; Controls.Add(l); // Iterate through the channels in the Hopper for (int i = 0; i < Hopper.NumberOfChannels; i++) { // Create an entry box and label for each denomination (channel) l = new Label(); l.Size = new Size(80, 25); l.Location = new Point(10, 55 + i * 25); ChannelData d = Hopper.UnitDataList[i]; l.Text = d.Value / 100f + " " + new String(d.Currency); Controls.Add(l); AmountToPayout_Hopper[i] = new TextBox(); AmountToPayout_Hopper[i].Size = new Size(60, 25); AmountToPayout_Hopper[i].Location = new Point(100, 55 + i * 25); Controls.Add(AmountToPayout_Hopper[i]); } // Add description labels for Payout l = new Label(); l.Size = new Size(titleXSize, 45); l.Location = new Point(200, 10); l.Text = "Number to\nPayout (Payout)"; Controls.Add(l); // Iterate through the channels in the Payout for (int i = 0; i < Payout.NumberOfChannels; i++) { // Create an entry box and label for each denomination (channel) l = new Label(); l.Size = new Size(80, 25); l.Location = new Point(200, 55 + i * 25); ChannelData d = Payout.UnitDataList[i]; l.Text = d.Value / 100 + " " + new String(d.Currency); Controls.Add(l); AmountToPayout_Payout[i] = new TextBox(); AmountToPayout_Payout[i].Size = new Size(60, 25); AmountToPayout_Payout[i].Location = new Point(300, 55 + i * 25); Controls.Add(AmountToPayout_Payout[i]); } // Add button to payout Button b = new Button(); b.Size = new Size(360, 25); Point p; if (payoutHighest) { p = AmountToPayout_Payout[Payout.NumberOfChannels - 1].Location; } else { p = AmountToPayout_Hopper[Hopper.NumberOfChannels - 1].Location; } p.Y += 25; p.X = 10; b.Location = p; b.Text = "Payout"; b.Click += new EventHandler(PayoutBtn_Click); Controls.Add(b); }
// 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); } if (cmd.ResponseData[0] == 0xFA) { return(false); } // store response locally so data can't get corrupted by other use of the cmd variable byte[] response = new byte[255]; cmd.ResponseData.CopyTo(response, 0); byte responseLength = cmd.ResponseDataLength; //parse poll response ChannelData data = new ChannelData(); for (i = 1; i < responseLength; ++i) { switch (response[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.SMARTPayout.SSP_POLL_RESET: log.AppendText("Unit reset\r\n"); UpdateData(); break; // This response indicates the unit is disabled. case CCommands.SMARTPayout.SSP_POLL_DISABLED: log.AppendText("Unit disabled...\r\n"); 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.SMARTPayout.SSP_POLL_NOTE_READ: if (cmd.ResponseData[i + 1] > 0) { GetDataByChannel(response[i + 1], ref data); log.AppendText("Note in escrow, amount: " + CHelpers.FormatToCurrency(data.Value) + "\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.SMARTPayout.SSP_POLL_CREDIT: GetDataByChannel(response[i + 1], ref data); log.AppendText("Credit " + CHelpers.FormatToCurrency(data.Value) + "\r\n"); UpdateData(); i++; break; // A note is being rejected from the validator. This will carry on polling while the note is in transit. case CCommands.SMARTPayout.SSP_POLL_REJECTING: log.AppendText("Rejecting note\r\n"); break; // A note has been rejected from the validator, the note will be resting in the bezel. This response only // appears once. case CCommands.SMARTPayout.SSP_POLL_REJECTED: log.AppendText("Note rejected\r\n"); QueryRejection(log); break; // A note is in transit to the cashbox. case CCommands.SMARTPayout.SSP_POLL_STACKING: log.AppendText("Stacking note\r\n"); break; // The payout device is 'floating' a specified amount of notes. It will transfer some to the cashbox and // leave the specified amount in the payout device. case CCommands.SMARTPayout.SSP_POLL_FLOATING: log.AppendText("Floating notes\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 floated (1 byte), then a 4 byte value // and 3 byte currency code for each country. i += (byte)((response[i + 1] * 7) + 1); break; // A note has reached the cashbox. case CCommands.SMARTPayout.SSP_POLL_STACKED: log.AppendText("Note stacked\r\n"); break; // The float operation has been completed. case CCommands.SMARTPayout.SSP_POLL_FLOATED: log.AppendText("Completed floating\r\n"); UpdateData(); EnableValidator(); i += (byte)((response[i + 1] * 7) + 1); break; // A note has been stored in the payout device to be paid out instead of going into the cashbox. case CCommands.SMARTPayout.SSP_POLL_NOTE_STORED: log.AppendText("Note stored\r\n"); UpdateData(); 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.SMARTPayout.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.SMARTPayout.SSP_POLL_UNSAFE_JAM: log.AppendText("Unsafe jam\r\n"); break; // An error has been detected by the payout unit. case CCommands.SMARTPayout.SSP_POLL_PAYOUT_ERROR: // Note: Will be reported only when Protocol version >= 7 log.AppendText("Detected error with payout device\r\n"); i += (byte)((response[i + 1] * 7) + 2); break; // A fraud attempt has been detected. case CCommands.SMARTPayout.SSP_POLL_FRAUD_ATTEMPT: log.AppendText("Fraud attempt\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // The stacker (cashbox) is full. case CCommands.SMARTPayout.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.SMARTPayout.SSP_POLL_NOTE_CLEARED_FROM_FRONT: log.AppendText("Note cleared from front of validator\r\n"); i++; break; // A note was detected somewhere inside the validator on startup and was cleared into the cashbox. case CCommands.SMARTPayout.SSP_POLL_NOTE_CLEARED_TO_CASHBOX: log.AppendText("Note cleared to cashbox\r\n"); i++; break; // A note has been detected in the validator on startup and moved to the payout device case CCommands.SMARTPayout.SSP_POLL_NOTE_PAID_INTO_STORE_ON_POWERUP: log.AppendText("Note paid into payout device on startup\r\n"); i += 7; break; // A note has been detected in the validator on startup and moved to the cashbox case CCommands.SMARTPayout.SSP_POLL_NOTE_PAID_INTO_STACKER_ON_POWERUP: log.AppendText("Note paid into cashbox on startup\r\n"); i += 7; break; // The cashbox has been removed from the unit. This will continue to poll until the cashbox is replaced. case CCommands.SMARTPayout.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.SMARTPayout.SSP_POLL_CASHBOX_REPLACED: log.AppendText("Cashbox replaced\r\n"); 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.SMARTPayout.SSP_POLL_NOTE_DISPENSING: log.AppendText("Dispensing note(s)\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // The note has been dispensed and removed from the bezel by the user. case CCommands.SMARTPayout.SSP_POLL_NOTE_DISPENSED: log.AppendText("Dispensed note(s)\r\n"); UpdateData(); EnableValidator(); i += (byte)((response[i + 1] * 7) + 1); break; // The payout device is in the process of emptying all its stored notes to the cashbox. This // will continue to poll until the device is empty. case CCommands.SMARTPayout.SSP_POLL_EMPTYING: log.AppendText("Emptying\r\n"); break; // This single poll response indicates that the payout device has finished emptying. case CCommands.SMARTPayout.SSP_POLL_EMPTIED: log.AppendText("Emptied\r\n"); UpdateData(); EnableValidator(); break; // The payout device is in the process of SMART emptying all its stored notes to the cashbox, keeping // a count of the notes emptied. This will continue to poll until the device is empty. case CCommands.SMARTPayout.SSP_POLL_SMART_EMPTYING: log.AppendText("SMART Emptying...\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // The payout device has finished SMART emptying, the information of what was emptied can now be displayed // using the CASHBOX PAYOUT OPERATION DATA command. case CCommands.SMARTPayout.SSP_POLL_SMART_EMPTIED: log.AppendText("SMART Emptied, getting info...\r\n"); UpdateData(); GetCashboxPayoutOpData(log); EnableValidator(); i += (byte)((response[i + 1] * 7) + 1); break; // The payout device has encountered a jam. This will not clear until the jam has been removed and the unit // reset. case CCommands.SMARTPayout.SSP_POLL_JAMMED: log.AppendText("Unit jammed...\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // This is reported when the payout has been halted by a host command. This will report the value of // currency dispensed upto the point it was halted. case CCommands.SMARTPayout.SSP_POLL_HALTED: log.AppendText("Halted...\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // This is reported when the payout was powered down during a payout operation. It reports the original amount // requested and the amount paid out up to this point for each currency. case CCommands.SMARTPayout.SSP_POLL_INCOMPLETE_PAYOUT: log.AppendText("Incomplete payout\r\n"); i += (byte)((response[i + 1] * 11) + 1); break; // This is reported when the payout was powered down during a float operation. It reports the original amount // requested and the amount paid out up to this point for each currency. case CCommands.SMARTPayout.SSP_POLL_INCOMPLETE_FLOAT: log.AppendText("Incomplete float\r\n"); i += (byte)((response[i + 1] * 11) + 1); break; // A note has been transferred from the payout unit to the stacker. case CCommands.SMARTPayout.SSP_POLL_NOTE_TRANSFERRED_TO_STACKER: log.AppendText("Note transferred to stacker\r\n"); i += 7; break; // A note is resting in the bezel waiting to be removed by the user. case CCommands.SMARTPayout.SSP_POLL_NOTE_HELD_IN_BEZEL: log.AppendText("Note in bezel...\r\n"); i += 7; break; // The payout has gone out of service, the host can attempt to re-enable the payout by sending the enable payout // command. case CCommands.SMARTPayout.SSP_POLL_PAYOUT_OUT_OF_SERVICE: log.AppendText("Payout out of service...\r\n"); break; // The unit has timed out while searching for a note to payout. It reports the value dispensed before the timeout // event. case CCommands.SMARTPayout.SSP_POLL_TIMEOUT: log.AppendText("Timed out searching for a note\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; default: log.AppendText("Unsupported poll response received: " + (int)cmd.ResponseData[i] + "\r\n"); break; } } return(true); }
// This is a one off function that is called the first time the MainLoop() // function runs, it just sets up a few of the UI elements that only need // updating once. private void SetupFormLayout() { // Get channel levels in hopper tbCoinLevels.Text = Hopper.GetChannelLevelInfo(); // setup list of recyclable channel tick boxes based on OS type System.OperatingSystem osInfo = System.Environment.OSVersion; int x1 = 0, y1 = 0, x2 = 0, y2 = 0; // XP, 2000, Server 2003 if (osInfo.Platform == PlatformID.Win32NT && osInfo.Version.Major == 5) { x1 = this.Location.X + 455; y1 = this.Location.Y + 10; x2 = this.Location.X + 780; y2 = this.Location.Y + 10; } // Vista, 7 else if (osInfo.Platform == PlatformID.Win32NT && osInfo.Version.Major == 6) { x1 = this.Location.X + 458; y1 = this.Location.Y + 12; x2 = this.Location.X + 780; y2 = this.Location.Y + 12; } GroupBox g1 = new GroupBox(); g1.Size = new Size(100, 380); g1.Location = new Point(x1, y1); g1.Text = "Hopper Recycling"; GroupBox g2 = new GroupBox(); g2.Size = new Size(100, 380); g2.Location = new Point(x2, y2); g2.Text = "Payout Recycling"; // Hopper checkboxes for (int i = 1; i <= Hopper.NumberOfChannels; i++) { CheckBox c = new CheckBox(); c.Location = new Point(5, 20 + (i * 20)); c.Name = i.ToString(); c.Text = CHelpers.FormatToCurrency(Hopper.GetChannelValue(i)) + " " + new String(Hopper.GetChannelCurrency(i)); c.Checked = Hopper.IsChannelRecycling(i); c.CheckedChanged += new EventHandler(recycleBoxHopper_CheckedChange); g1.Controls.Add(c); } // Payout checkboxes for (int i = 1; i <= Payout.NumberOfChannels; i++) { CheckBox c = new CheckBox(); c.Location = new Point(5, 20 + (i * 20)); c.Name = i.ToString(); ChannelData d = new ChannelData(); Payout.GetDataByChannel(i, ref d); c.Text = CHelpers.FormatToCurrency(d.Value) + " " + new String(d.Currency); c.Checked = d.Recycling; c.CheckedChanged += new EventHandler(recycleBoxPayout_CheckedChange); g2.Controls.Add(c); } Controls.Add(g1); Controls.Add(g2); }
// This function uses the setup request command to get all the information about the validator. It can optionally // output to a specified textbox. public void SetupRequest(TextBox outputBox = null) { // send setup request m_cmd.CommandData[0] = CCommands.Generic.SSP_CMD_SETUP_REQUEST; m_cmd.CommandDataLength = 1; if (!SendCommand(outputBox) || !CheckGenericResponses(outputBox)) return; // display setup request // unit type string displayString = "Unit Type: "; int index = 1; m_UnitType = (char)m_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; } // firmware displayString += "\r\nFirmware: "; for (int i = index; i < index + 4; i++) { displayString += (char)m_cmd.ResponseData[i]; if (i == 3) displayString += "."; } index += 4; // country code // legacy code for old protocol versions, protocol 6+ allows each channel to have a currency // for multi currency datasets index += 3; // +3 to skip country code // protocol version m_ProtocolVersion = m_cmd.ResponseData[index++]; displayString += "\r\nProtocol Version: " + m_ProtocolVersion; // number of coin values m_NumberOfChannels = m_cmd.ResponseData[index++]; displayString += "\r\nNumber of Coin Values: " + m_NumberOfChannels; // coin values int[] channelValuesTemp = new int[m_NumberOfChannels + 1]; int counter = 1; displayString += "\r\nCoin Values: "; for (int i = index; i < index + (m_NumberOfChannels * 2) - 1; i += 2, counter++) { short x = BitConverter.ToInt16(m_cmd.ResponseData, i); channelValuesTemp[counter] = x; // update array displayString += " (" + x + ") "; } index += (m_NumberOfChannels * 2); // country codes for values (protocol 6+ only) char[] channelCurrencyTemp = new char[(m_NumberOfChannels + 1) * 3]; counter = 3; displayString += "\r\nCoin Currencies: "; for (int i = index; i < index + (3 * m_NumberOfChannels); i+=3) { channelCurrencyTemp[counter] = (char)m_cmd.ResponseData[i]; displayString += channelCurrencyTemp[counter++]; channelCurrencyTemp[counter] = (char)m_cmd.ResponseData[i+1]; displayString += channelCurrencyTemp[counter++]; channelCurrencyTemp[counter] = (char)m_cmd.ResponseData[i+2]; displayString += channelCurrencyTemp[counter++] + " "; } index += (3 * m_NumberOfChannels); // create list entry for each channel m_UnitDataList.Clear(); // clear old table for (byte i = 1; i <= m_NumberOfChannels; i++) { ChannelData d = new ChannelData(); d.Channel = i; d.Value = channelValuesTemp[i]; d.Currency[0] = channelCurrencyTemp[0 + (i * 3)]; d.Currency[1] = channelCurrencyTemp[1 + (i * 3)]; d.Currency[2] = channelCurrencyTemp[2 + (i * 3)]; d.Level = CheckCoinLevel(d.Value, d.Currency); IsCoinRecycling(d.Value, d.Currency, ref d.Recycling); m_UnitDataList.Add(d); } // Sort the list of data by the value. m_UnitDataList.Sort(delegate(ChannelData d1, ChannelData d2) { return d1.Channel.CompareTo(d2.Channel); }); displayString += "\r\n\r\n"; if (outputBox != null) outputBox.AppendText(displayString); }
// 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) || !CheckGenericResponses(log)) return; // display setup request string displayString = "Unit 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++; 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 = CheckNoteLevel(d.Value, d.Currency, log); bool b = false; IsNoteRecycling(d.Value, d.Currency, ref b, log); d.Recycling = b; m_UnitDataList.Add(d); } // Sort the list of data by the channel. m_UnitDataList.Sort(delegate(ChannelData d1, ChannelData d2) { return d1.Channel.CompareTo(d2.Channel); }); 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; if (cmd.ResponseData[0] == 0xFA) return false; // store response locally so data can't get corrupted by other use of the cmd variable byte[] response = new byte[255]; cmd.ResponseData.CopyTo(response, 0); byte responseLength = cmd.ResponseDataLength; //parse poll response ChannelData data = new ChannelData(); for (i = 1; i < responseLength; ++i) { switch (response[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.SMARTPayout.SSP_POLL_RESET: log.AppendText("Unit reset\r\n"); UpdateData(); break; // This response indicates the unit is disabled. case CCommands.SMARTPayout.SSP_POLL_DISABLED: log.AppendText("Unit disabled...\r\n"); 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.SMARTPayout.SSP_POLL_NOTE_READ: if (cmd.ResponseData[i + 1] > 0) { GetDataByChannel(response[i + 1], ref data); log.AppendText("Note in escrow, amount: " + CHelpers.FormatToCurrency(data.Value) + "\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.SMARTPayout.SSP_POLL_CREDIT: GetDataByChannel(response[i + 1], ref data); log.AppendText("Credit " + CHelpers.FormatToCurrency(data.Value) + "\r\n"); UpdateData(); i++; break; // A note is being rejected from the validator. This will carry on polling while the note is in transit. case CCommands.SMARTPayout.SSP_POLL_REJECTING: log.AppendText("Rejecting note\r\n"); break; // A note has been rejected from the validator, the note will be resting in the bezel. This response only // appears once. case CCommands.SMARTPayout.SSP_POLL_REJECTED: log.AppendText("Note rejected\r\n"); QueryRejection(log); break; // A note is in transit to the cashbox. case CCommands.SMARTPayout.SSP_POLL_STACKING: log.AppendText("Stacking note\r\n"); break; // The payout device is 'floating' a specified amount of notes. It will transfer some to the cashbox and // leave the specified amount in the payout device. case CCommands.SMARTPayout.SSP_POLL_FLOATING: log.AppendText("Floating notes\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 floated (1 byte), then a 4 byte value // and 3 byte currency code for each country. i += (byte)((response[i + 1] * 7) + 1); break; // A note has reached the cashbox. case CCommands.SMARTPayout.SSP_POLL_STACKED: log.AppendText("Note stacked\r\n"); break; // The float operation has been completed. case CCommands.SMARTPayout.SSP_POLL_FLOATED: log.AppendText("Completed floating\r\n"); UpdateData(); EnableValidator(); i += (byte)((response[i + 1] * 7) + 1); break; // A note has been stored in the payout device to be paid out instead of going into the cashbox. case CCommands.SMARTPayout.SSP_POLL_NOTE_STORED: log.AppendText("Note stored\r\n"); UpdateData(); 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.SMARTPayout.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.SMARTPayout.SSP_POLL_UNSAFE_JAM: log.AppendText("Unsafe jam\r\n"); break; // An error has been detected by the payout unit. case CCommands.SMARTPayout.SSP_POLL_PAYOUT_ERROR: // Note: Will be reported only when Protocol version >= 7 log.AppendText("Detected error with payout device\r\n"); i += (byte)((response[i + 1] * 7) + 2); break; // A fraud attempt has been detected. case CCommands.SMARTPayout.SSP_POLL_FRAUD_ATTEMPT: log.AppendText("Fraud attempt\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // The stacker (cashbox) is full. case CCommands.SMARTPayout.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.SMARTPayout.SSP_POLL_NOTE_CLEARED_FROM_FRONT: log.AppendText("Note cleared from front of validator\r\n"); i++; break; // A note was detected somewhere inside the validator on startup and was cleared into the cashbox. case CCommands.SMARTPayout.SSP_POLL_NOTE_CLEARED_TO_CASHBOX: log.AppendText("Note cleared to cashbox\r\n"); i++; break; // A note has been detected in the validator on startup and moved to the payout device case CCommands.SMARTPayout.SSP_POLL_NOTE_PAID_INTO_STORE_ON_POWERUP: log.AppendText("Note paid into payout device on startup\r\n"); i += 7; break; // A note has been detected in the validator on startup and moved to the cashbox case CCommands.SMARTPayout.SSP_POLL_NOTE_PAID_INTO_STACKER_ON_POWERUP: log.AppendText("Note paid into cashbox on startup\r\n"); i += 7; break; // The cashbox has been removed from the unit. This will continue to poll until the cashbox is replaced. case CCommands.SMARTPayout.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.SMARTPayout.SSP_POLL_CASHBOX_REPLACED: log.AppendText("Cashbox replaced\r\n"); 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.SMARTPayout.SSP_POLL_NOTE_DISPENSING: log.AppendText("Dispensing note(s)\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // The note has been dispensed and removed from the bezel by the user. case CCommands.SMARTPayout.SSP_POLL_NOTE_DISPENSED: log.AppendText("Dispensed note(s)\r\n"); UpdateData(); EnableValidator(); i += (byte)((response[i + 1] * 7) + 1); break; // The payout device is in the process of emptying all its stored notes to the cashbox. This // will continue to poll until the device is empty. case CCommands.SMARTPayout.SSP_POLL_EMPTYING: log.AppendText("Emptying\r\n"); break; // This single poll response indicates that the payout device has finished emptying. case CCommands.SMARTPayout.SSP_POLL_EMPTIED: log.AppendText("Emptied\r\n"); UpdateData(); EnableValidator(); break; // The payout device is in the process of SMART emptying all its stored notes to the cashbox, keeping // a count of the notes emptied. This will continue to poll until the device is empty. case CCommands.SMARTPayout.SSP_POLL_SMART_EMPTYING: log.AppendText("SMART Emptying...\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // The payout device has finished SMART emptying, the information of what was emptied can now be displayed // using the CASHBOX PAYOUT OPERATION DATA command. case CCommands.SMARTPayout.SSP_POLL_SMART_EMPTIED: log.AppendText("SMART Emptied, getting info...\r\n"); UpdateData(); GetCashboxPayoutOpData(log); EnableValidator(); i += (byte)((response[i + 1] * 7) + 1); break; // The payout device has encountered a jam. This will not clear until the jam has been removed and the unit // reset. case CCommands.SMARTPayout.SSP_POLL_JAMMED: log.AppendText("Unit jammed...\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // This is reported when the payout has been halted by a host command. This will report the value of // currency dispensed upto the point it was halted. case CCommands.SMARTPayout.SSP_POLL_HALTED: log.AppendText("Halted...\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; // This is reported when the payout was powered down during a payout operation. It reports the original amount // requested and the amount paid out up to this point for each currency. case CCommands.SMARTPayout.SSP_POLL_INCOMPLETE_PAYOUT: log.AppendText("Incomplete payout\r\n"); i += (byte)((response[i + 1] * 11) + 1); break; // This is reported when the payout was powered down during a float operation. It reports the original amount // requested and the amount paid out up to this point for each currency. case CCommands.SMARTPayout.SSP_POLL_INCOMPLETE_FLOAT: log.AppendText("Incomplete float\r\n"); i += (byte)((response[i + 1] * 11) + 1); break; // A note has been transferred from the payout unit to the stacker. case CCommands.SMARTPayout.SSP_POLL_NOTE_TRANSFERRED_TO_STACKER: log.AppendText("Note transferred to stacker\r\n"); i += 7; break; // A note is resting in the bezel waiting to be removed by the user. case CCommands.SMARTPayout.SSP_POLL_NOTE_HELD_IN_BEZEL: log.AppendText("Note in bezel...\r\n"); i += 7; break; // The payout has gone out of service, the host can attempt to re-enable the payout by sending the enable payout // command. case CCommands.SMARTPayout.SSP_POLL_PAYOUT_OUT_OF_SERVICE: log.AppendText("Payout out of service...\r\n"); break; // The unit has timed out while searching for a note to payout. It reports the value dispensed before the timeout // event. case CCommands.SMARTPayout.SSP_POLL_TIMEOUT: log.AppendText("Timed out searching for a note\r\n"); i += (byte)((response[i + 1] * 7) + 1); break; default: log.AppendText("Unsupported poll response received: " + (int)cmd.ResponseData[i] + "\r\n"); break; } } return true; }
// This function uses the setup request command to get all the information about the validator. It can optionally // output to a specified textbox. public void SetupRequest(TextBox outputBox = null) { // send setup request m_cmd.CommandData[0] = CCommands.Generic.SSP_CMD_SETUP_REQUEST; m_cmd.CommandDataLength = 1; if (!SendCommand(outputBox)) { return; } // display setup request // unit type string displayString = "Unit Type: "; int index = 1; m_UnitType = (char)m_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; } // firmware displayString += "\r\nFirmware: "; for (int i = index; i < index + 4; i++) { displayString += (char)m_cmd.ResponseData[i]; if (i == 3) { displayString += "."; } } index += 4; // country code // legacy code for old protocol versions, protocol 6+ allows each channel to have a currency // for multi currency datasets index += 3; // +3 to skip country code // protocol version m_ProtocolVersion = m_cmd.ResponseData[index++]; displayString += "\r\nProtocol Version: " + m_ProtocolVersion; // number of coin values m_NumberOfChannels = m_cmd.ResponseData[index++]; displayString += "\r\nNumber of Coin Values: " + m_NumberOfChannels; // coin values int[] channelValuesTemp = new int[m_NumberOfChannels + 1]; int counter = 1; displayString += "\r\nCoin Values: "; for (int i = index; i < index + (m_NumberOfChannels * 2) - 1; i += 2, counter++) { short x = BitConverter.ToInt16(m_cmd.ResponseData, i); channelValuesTemp[counter] = x; // update array displayString += " (" + x + ") "; } index += (m_NumberOfChannels * 2); // country codes for values (protocol 6+ only) char[] channelCurrencyTemp = new char[(m_NumberOfChannels + 1) * 3]; counter = 3; displayString += "\r\nCoin Currencies: "; for (int i = index; i < index + (3 * m_NumberOfChannels); i += 3) { channelCurrencyTemp[counter] = (char)m_cmd.ResponseData[i]; displayString += channelCurrencyTemp[counter++]; channelCurrencyTemp[counter] = (char)m_cmd.ResponseData[i + 1]; displayString += channelCurrencyTemp[counter++]; channelCurrencyTemp[counter] = (char)m_cmd.ResponseData[i + 2]; displayString += channelCurrencyTemp[counter++] + " "; } index += (3 * m_NumberOfChannels); // create list entry for each channel m_UnitDataList.Clear(); // clear old table for (byte i = 1; i <= m_NumberOfChannels; i++) { ChannelData d = new ChannelData(); d.Channel = i; d.Value = channelValuesTemp[i]; d.Currency[0] = channelCurrencyTemp[0 + (i * 3)]; d.Currency[1] = channelCurrencyTemp[1 + (i * 3)]; d.Currency[2] = channelCurrencyTemp[2 + (i * 3)]; d.Level = CheckCoinLevel(d.Value, d.Currency); IsCoinRecycling(d.Value, d.Currency, ref d.Recycling); 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)); }); displayString += "\r\n\r\n"; if (outputBox != null) { outputBox.AppendText(displayString); } }
// This function shows a simple example of calculating a payout split between the SMART Payout and the // SMART Hopper. It works on a highest value split, first the notes are looked at, then any remainder // that can't be paid out with a note is paid from the SMART Hopper. private void CalculatePayout(string amount, char[] currency) { float payoutAmount; try { // Parse it to a number payoutAmount = float.Parse(amount) * 100; } catch (Exception ex) { MessageBox.Show(ex.Message); return; } int payoutList = 0; // Obtain the list of sorted channels from the SMART Payout, this is sorted by channel value // - lowest first List <ChannelData> reverseList = new List <ChannelData>(Payout.UnitDataList); reverseList.Reverse(); // Reverse the list so the highest value is first // Iterate through each foreach (ChannelData d in reverseList) { ChannelData temp = d; // Don't overwrite real values // Keep testing to see whether we need to payout this note or the next note while (true) { // If the amount to payout is greater than the value of the current note and there is // some of that note available and it is the correct currency if (payoutAmount >= temp.Value && temp.Level > 0 && String.Equals(new String(temp.Currency), new String(currency))) { payoutList += temp.Value; // Add to the list of notes to payout from the SMART Payout payoutAmount -= temp.Value; // Minus from the total payout amount temp.Level--; // Take one from the level } else { break; // Don't need any more of this note } } } // Test the proposed payout values if (payoutList > 0) { // First test SP Payout.PayoutAmount(payoutList, currency, true); if (Payout.CommandStructure.ResponseData[0] != 0xF0) { DialogResult res = MessageBox.Show("Smart Payout unable to pay requested amount, attempt to pay all from Hopper?", "Error with Payout", MessageBoxButtons.YesNo); if (res == System.Windows.Forms.DialogResult.No) { return; } else { payoutAmount += payoutList; } } // SP is ok to pay Payout.PayoutAmount(payoutList, currency, false, textBox1); } // Now if there is any left over, request from Hopper if (payoutAmount > 0) { // Test Hopper first Hopper.PayoutAmount((int)payoutAmount, currency, true); if (Hopper.CommandStructure.ResponseData[0] != 0xF0) { MessageBox.Show("Unable to pay requested amount!"); return; } // Hopper is ok to pay Hopper.PayoutAmount((int)payoutAmount, currency, false, textBox1); } }