幽冥大陆(十六)纸币器BV20识别纸币——东方仙盟筑基期
纸币检测软件

编辑
#include "StdAfx.h"
#include "Validator.h"
CValidator::CValidator(void)
{
cmd = new SSP_COMMAND();
keys = new SSP_KEYS();
info = new SSP_COMMAND_INFO();
// Create ssp library functions
InitialiseLibrary();
m_NumberOfChannels = 0;
m_Type = 0;
m_NumStackedNotes = 0;
m_UnitData = 0;
}
CValidator::~CValidator(void)
{
if (m_UnitData)
delete[] m_UnitData;
delete info;
delete keys;
delete cmd;
}
bool CValidator::IsUnitValid()
{
if(GetUnitType() != 0x00)
{
return false;
}
return true;
}
bool CValidator::InitialiseLibrary()
{
// Load dll
CString strLib = _T("ITLSSPProc.dll");
HINSTANCE hInst = LoadLibrary(strLib);
if (hInst != NULL)
{
// Link function names
// Open the COM port
OpenPort = (LPFNDLLFUNC1)GetProcAddress(hInst, "OpenSSPComPortUSB");
if (OpenPort == NULL)
{
FreeLibrary(hInst);
return false;
}
// Close the COM port
ClosePort = (LPFNDLLFUNC2)GetProcAddress(hInst, "CloseSSPComPortUSB");
if (ClosePort == NULL)
{
FreeLibrary(hInst);
return false;
}
// Send a command to the unit
SSPSendCommand = (LPFNDLLFUNC3)GetProcAddress(hInst, "SSPSendCommand");
if (SSPSendCommand == NULL)
{
FreeLibrary(hInst);
return false;
}
// Create the generator and modulus prime numbers for the key encryption
InitiateSSPHostKeys = (LPFNDLLFUNC4)GetProcAddress(hInst, "InitiateSSPHostKeys");
if (InitiateSSPHostKeys == NULL)
{
FreeLibrary(hInst);
return false;
}
// Create the final encryption key
CreateSSPHostEncryptionKey = (LPFNDLLFUNC5)GetProcAddress(hInst, "CreateSSPHostEncryptionKey");
if (CreateSSPHostEncryptionKey == NULL)
{
FreeLibrary(hInst);
return false;
}
}
else
return false;
return true;
}
// The enable command allows the validator to accept all commands.
bool CValidator::EnableValidator()
{
cmd->CommandData[0] = SSP_CMD_ENABLE;
cmd->CommandDataLength = 1;
if (!SendCommand()) return false;
// check response
if (CheckGenericResponses())
{
}
return true;
}
// Disable command stops the validator accepting most commands.
bool CValidator::DisableValidator()
{
cmd->CommandData[0] = SSP_CMD_DISABLE;
cmd->CommandDataLength = 1;
if (!SendCommand()) return false;
// check response
if (CheckGenericResponses())
{
}
return true;
}
// The reset command instructs the validator to restart (same effect as switching on and off)
bool CValidator::ResetValidator()
{
cmd->CommandData[0] = SSP_CMD_RESET;
cmd->CommandDataLength = 1;
if (!SendCommand())
{
return false;
}
// check response
if(CheckGenericResponses())
{
}
return true;
}
// This function sets the protocol version in the validator to the version passed across. Whoever calls
// this needs to check the response to make sure the version is supported.
bool CValidator::SetProtocolVersion(char pVersion)
{
cmd->CommandData[0] = SSP_CMD_HOST_PROTOCOL_VERSION;
cmd->CommandData[1] = pVersion;
cmd->CommandDataLength = 2;
if (!SendCommand()) return false;
return true;
}
// This function sends the command LAST REJECT CODE which gives info about why a note has been rejected. It then
// outputs the info.
void CValidator::QueryRejection()
{
cmd->CommandData[0] = SSP_CMD_LAST_REJECT_CODE;
cmd->CommandDataLength = 1;
if (!SendCommand()) return;
if (CheckGenericResponses())
{
switch (cmd->ResponseData[1]) // Reason for rejection located in second byte of response
{
case 0x00:
//*m_Output << "Note accepted" << endl;
break;
case 0x01:
//*m_Output << "Note length incorrect" << endl;
break;
case 0x02:
//*m_Output << "Invalid note" << endl;
break;
case 0x03:
//*m_Output << "Invalid note" << endl;
break;
case 0x04:
//*m_Output << "Invalid note" << endl;
break;
case 0x05:
//*m_Output << "Invalid note" << endl;
break;
case 0x06:
//*m_Output << "Channel inhibited" << endl;
break;
case 0x07:
//*m_Output << "Second note inserted during read" << endl;
break;
case 0x08:
//*m_Output << "Host rejected note" << endl;
break;
case 0x09:
//*m_Output << "Invalid note" << endl;
break;
case 0x0A:
//*m_Output << "Invalid note read" << endl;
break;
case 0x0B:
//*m_Output << "Note too long" << endl;
break;
case 0x0C:
//*m_Output << "Validator disabled" << endl;
break;
case 0x0D:
//*m_Output << "Mechanism slow/stalled" << endl;
break;
case 0x0E:
//*m_Output << "Strim attempt" << endl;
break;
case 0x0F:
//*m_Output << "Fraud channel reject" << endl;
break;
case 0x10:
//*m_Output << "No notes inserted" << endl;
break;
case 0x11:
//*m_Output << "Invalid note read" << endl;
break;
case 0x12:
//*m_Output << "Twisted note detected" << endl;
break;
case 0x13:
//*m_Output << "Escrow time-out" << endl;
break;
case 0x14:
//*m_Output << "Bar code scan fail" << endl;
break;
case 0x15:
//*m_Output << "Invalid note read" << endl;
break;
case 0x16:
//*m_Output << "Invalid note read" << endl;
break;
case 0x17:
//*m_Output << "Invalid note read" << endl;
break;
case 0x18:
//*m_Output << "Invalid note read" << endl;
break;
case 0x19:
//*m_Output << "Incorrect note width" << endl;
break;
case 0x1A:
//*m_Output << "Note too short" << endl;
break;
}
}
}
// This function performs a number of commands in order to setup the encryption between the host and the validator.
bool CValidator::NegotiateKeys()
{
int i;
// make sure encryption is off
cmd->EncryptionStatus = false;
// send sync
cmd->CommandData[0] = SSP_CMD_SYNC;
cmd->CommandDataLength = 1;
if (!SendCommand()) return false;
InitiateSSPHostKeys(keys, cmd);
// send generator
cmd->CommandData[0] = SSP_CMD_SET_GENERATOR;
cmd->CommandDataLength = 9;
for (i = 0; i < 8; ++i)
{
cmd->CommandData[i + 1] = (char)(keys->Generator >> (8 * i));
}
if (!SendCommand()) return false;
// send modulus
cmd->CommandData[0] = SSP_CMD_SET_MODULUS;
cmd->CommandDataLength = 9;
for (i = 0; i < 8; ++i)
{
cmd->CommandData[i + 1] = (char)(keys->Modulus >> (8 * i));
}
if (!SendCommand()) return false;
// send key exchange
cmd->CommandData[0] = SSP_CMD_KEY_EXCHANGE;
cmd->CommandDataLength = 9;
for (i = 0; i < 8; ++i)
{
cmd->CommandData[i + 1] = (char)(keys->HostInter >> (8 * i));
}
if (!SendCommand()) return false;
keys->SlaveInterKey = 0;
for (i = 0; i < 8; ++i)
{
keys->SlaveInterKey += (ULONG)cmd->ResponseData[1 + i] << (8 * i);
}
CreateSSPHostEncryptionKey(keys);
// get full encryption key
cmd->Key.FixedKey = 0x0123456701234567;
cmd->Key.EncryptKey = keys->KeyHost;
cmd->EncryptionStatus = true; // turn on encrypting
//*m_Output << "Negotiated keys" << endl;
return true;
}
// This function uses the setup request command to get information about the validator. The response
// packet is a variable length due to the fact that the number of channels is not known beforehand.
bool CValidator::SetupRequest()
{
// send setup request
cmd->CommandData[0] = SSP_CMD_SETUP_REQUEST;
cmd->CommandDataLength = 1;
if (!SendCommand()) return false;
// check response
if (CheckGenericResponses())
{
// Output setup request data
// Unit type
//*m_Output << "Unit type: ";
m_Type = cmd->ResponseData[1];
switch (m_Type)
{
case 0x00:
//*m_Output << "Note Validator" << endl;
break;
case 0x03:
//*m_Output << "SMART Hopper" << endl;
break;
case 0x06:
//*m_Output << "SMART Payout" << endl;
break;
case 0x07:
//*m_Output << "NV11" << endl;
break;
}
// Firmware
//*m_Output << "Firmware: ";
//*m_Output << cmd->ResponseData[2] << cmd->ResponseData[3] << "." <<
// cmd->ResponseData[4] << cmd->ResponseData[5] << endl;
// Channel setup
// End of fixed data
int index = 12;
m_NumberOfChannels = cmd->ResponseData[index++];
m_UnitData = new SChannelData[m_NumberOfChannels];
index += m_NumberOfChannels; // Skip old channel values
index += m_NumberOfChannels; // Skip channel security levels
index += 3; // Skip value multiplier
// Protocol version
m_ProtocolVersion = cmd->ResponseData[index++];
//*m_Output << "Protocol Version: " << (int)m_ProtocolVersion << endl;
// Setup channel data
// Currencies
//*m_Output << "Channel Currencies: ";
for (int i = 0; i < m_NumberOfChannels; ++i)
{
m_UnitData[i].Channel = i + 1;
for (int j = 0; j < 3; ++j)
{
m_UnitData[i].Currency[j] = cmd->ResponseData[index + j];
//*m_Output << m_UnitData[i].Currency[j];
}
index += 3;
//*m_Output << " ";
}
//*m_Output << endl;
// Values
//*m_Output << "Channel Values: ";
int i;
for( i = 0; i < m_NumberOfChannels; ++i)
{
for (int j = 0; j < 4; ++j)
m_UnitData[i].Value += (int)cmd->ResponseData[index++] << (8*j);
//*m_Output << m_UnitData[i].Value << " ";
}
//*m_Output << endl;
}
return true;
}
// This function sends the set inhibits command to set the inhibits on the validator.
// The two bytes after the command byte represent two bit registers with each bit being
// a channel. 1-8 and 9-16 respectively. 0xFF = 11111111 in binary indicating all channels
// in this register are able to accept notes.
bool CValidator::SetInhibits()
{
// set inhibits
cmd->CommandData[0] = SSP_CMD_SET_INHIBITS;
cmd->CommandData[1] = 0xFF;
cmd->CommandData[2] = 0xFF;
cmd->CommandDataLength = 3;
if (!SendCommand()) return false;
// check response
if (CheckGenericResponses())
{
// *m_Output << "Inhibits set" << endl;
}
return true;
}
// 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.
bool CValidator::DoPoll(int &nRMB_value)
{
// send poll
cmd->CommandData[0] = SSP_CMD_POLL;
cmd->CommandDataLength = 1;
nRMB_value = 0;
if (!SendCommand())
return false;
CheckGenericResponses();
//parse poll response
int noteVal;
char *currency;
for (int i = 1; i < cmd->ResponseDataLength; ++i)
{
switch (cmd->ResponseData[i])
{
// The unit has been reset since the last time a poll was sent.
case SSP_POLL_RESET:
break;
// If the byte after is greater than 0 then the note is being held in
// escrow. If it is zero then the note is still being read.
case SSP_POLL_NOTE_READ:
if (cmd->ResponseData[i + 1] > 0)
{
noteVal = GetChannelValue(cmd->ResponseData[i + 1]);
currency = GetChannelCurrency(cmd->ResponseData[i + 1]);
}
else
{
}
++i;
break;
// The note has been accepted and credit given, the following byte will
// contain the channel number of the credited note.
case SSP_POLL_CREDIT:
noteVal = GetChannelValue(cmd->ResponseData[i + 1]);
nRMB_value = noteVal;
currency = GetChannelCurrency(cmd->ResponseData[i + 1]);
++m_NumStackedNotes;
++i;
break;
// Validator is in the process of rejecting a note.
case SSP_POLL_REJECTING:
break;
// A note has been rejected from the validator.
case SSP_POLL_REJECTED:
QueryRejection(); // This will output info about the reason for the rejection
break;
// The validator is stacking a note.
case SSP_POLL_STACKING:
break;
// The validator has completed stacking a note.
case SSP_POLL_STACKED:
break;
// A note has become jammed in a place where it cannot be recovered.
case SSP_POLL_SAFE_JAM:
break;
// A note has become jammed in a place where it may possibly be recovered.
case SSP_POLL_UNSAFE_JAM:
break;
// The unit is disabled and will not accept notes.
case SSP_POLL_DISABLED:
break;
// The validator has detected a fraud attempt.
case SSP_POLL_FRAUD_ATTEMPT:
noteVal = GetChannelValue(cmd->ResponseData[i + 1]);
currency = GetChannelCurrency(cmd->ResponseData[i + 1]);
++i;
break;
// The cashbox of the unit is full and cannot stack any more notes.
case SSP_POLL_STACKER_FULL:
break;
// A note has been rejected from the front of the validator on startup. This
// may happen in the case of power loss while a note is reading.
case SSP_POLL_NOTE_CLEARED_FROM_FRONT:
noteVal = GetChannelValue(cmd->ResponseData[i + 1]);
currency = GetChannelCurrency(cmd->ResponseData[i + 1]);
++i;
break;
// A note has been stacked to the cashbox of the validator on startup. This
// may happen in the case of power loss while a note is stacking.
case SSP_POLL_NOTE_CLEARED_TO_CASHBOX:
noteVal = GetChannelValue(cmd->ResponseData[i + 1]);
currency = GetChannelCurrency(cmd->ResponseData[i + 1]);
++i;
break;
// The cashbox of the unit has been removed.
case SSP_POLL_CASHBOX_REMOVED:
break;
// The cashbox of the unit has been replaced.
case SSP_POLL_CASHBOX_REPLACED:
break;
case SSP_POLL_BARCODE_TICKET_VALIDATED:
break;
case SSP_POLL_BARCODE_TICKET_ACK:
break;
// The unit's top section has been opened (only applies to NV9/NV11 and NV200).
// Protocol >= 6.
case SSP_POLL_NOTE_PATH_OPEN:
break;
// This poll response indicates the unit is disabled as all the channels are
// inhibited. Protocol >= 7.
case SSP_POLL_CHANNEL_DISABLE:
break;
// If a poll response is detected and is not in this list
default:
break;
}
}
return true;
}
/* Non-Command functions */
// This function calls the open com port function of the SSP library and sets up
// the command structure.
bool CValidator::OpenComPort(char portNum)
{
if (OpenPort(cmd) == 0)
return false;
return true;
}
/* Exception and Error Handling */
// This is used for generic response error catching, it outputs the info in a
// meaningful way.
bool CValidator::CheckGenericResponses()
{
if (cmd->ResponseData[0] == SSP_RESPONSE_CMD_OK)
return true;
else
{
switch (cmd->ResponseData[0])
{
case SSP_RESPONSE_CMD_CANNOT_PROCESS:
{
//*m_Output << "Command response is CANNOT PROCESS COMMAND";
if (cmd->ResponseDataLength > 1)
{
// *m_Output << ", error code - 0x" << cmd->ResponseData[1];
}
//*m_Output << endl;
return false;
}
case SSP_RESPONSE_CMD_FAIL:
{
//*m_Output << "Command response is FAIL" << endl;
return false;
}
case SSP_RESPONSE_CMD_KEY_NOT_SET:
{
//*m_Output << "Command response is KEY NOT SET, renegotiate keys" << endl;
return false;
}
case SSP_RESPONSE_CMD_PARAM_OUT_OF_RANGE:
{
//*m_Output << "Command response is PARAM OUT OF RANGE" << endl;
return false;
}
case SSP_RESPONSE_CMD_SOFTWARE_ERROR:
{
//*m_Output << "Command response is SOFTWARE ERROR" << endl;
return false;
}
case SSP_RESPONSE_CMD_UNKNOWN:
{
//*m_Output << "Command response is UNKNOWN" << endl;
return false;
}
case SSP_RESPONSE_CMD_WRONG_PARAMS:
{
//*m_Output << "Command response is WRONG PARAMETERS" << endl;
return false;
}
default:
return false;
}
}
}
// Takes a byte and converts it to a command name, returns a char string
char* CValidator::GetCommandName(char commandByte)
{
switch (commandByte)
{
case 0x4A: return "SET GENERATOR";
case 0x4B: return "SET MODULUS";
case 0x4C: return "REQUEST KEY EXCHANGE";
case 0x01: return "RESET";
case 0x02: return "SET INHIBITS";
case 0x03: return "DISPLAY ON";
case 0x04: return "DISPLAY OFF";
case 0x05: return "SETUP REQUEST";
case 0x06: return "HOST PROTOCOL VERSION";
case 0x07: return "POLL";
case 0x08: return "REJECT";
case 0x09: return "DISABLE";
case 0x0A: return "ENABLE";
case 0x0B: return "PROGRAM FIRMWARE";
case 0x0C: return "GET SERIAL NUMBER";
case 0x0D: return "UNIT DATA";
case 0x0E: return "CHANNEL VALUE DATA";
case 0x0F: return "CHANNEL SECURITY DATA";
case 0x10: return "CHANNEL RETEACH DATA";
case 0x11: return "SYNC";
case 0x12: return "UPDATE COIN ROUTE";
case 0x13: return "DISPENSE";
case 0x14: return "HOST SERIAL NUMBER REQUEST";
case 0x15: return "SETUP REQUEST";
case 0x17: return "LAST REJECT CODE";
case 0x18: return "HOLD";
case 0x19: return "ENABLE PROTOCOL VERSION EVENTS";
case 0x23: return "GET BAR CODE READER CONFIGURATION";
case 0x24: return "SET BAR CODE READER CONFIGURATION";
case 0x25: return "GET BAR CODE INHIBIT";
case 0x26: return "SET BAR CODE INHIBIT";
case 0x27: return "GET BAR CODE DATA";
case 0x54: return "CONFIGURE BEZEL";
case 0x56: return "POLL WITH ACK";
case 0x57: return "EVENT ACK";
case 0x3B: return "SET ROUTING";
case 0x3C: return "GET ROUTING";
case 0x33: return "PAYOUT AMOUNT";
case 0x35: return "GET NOTE/COIN AMOUNT";
case 0x34: return "SET NOTE/COIN AMOUNT";
case 0x38: return "HALT PAYOUT";
case 0x3D: return "FLOAT AMOUNT";
case 0x3E: return "GET MINIMUM PAYOUT";
case 0x40: return "SET COIN MECH INHIBITS";
case 0x46: return "PAYOUT BY DENOMINATION";
case 0x44: return "FLOAT BY DENOMINATION";
case 0x47: return "SET COMMAND CALIBRATION";
case 0x48: return "RUN COMMAND CALIBRATION";
case 0x3F: return "EMPTY ALL";
case 0x50: return "SET OPTIONS";
case 0x51: return "GET OPTIONS";
case 0x49: return "COIN MECH GLOBAL INHIBIT";
case 0x52: return "SMART EMPTY";
case 0x53: return "CASHBOX PAYOUT OPERATION DATA";
case 0x5C: return "ENABLE PAYOUT DEVICE";
case 0x5B: return "DISABLE PAYOUT DEVICE";
case 0x58: return "GET NOTE COUNTERS";
case 0x59: return "RESET NOTE COUNTERS";
case 0x30: return "SET REFILL MODE";
case 0x41: return "GET NOTE POSITIONS";
case 0x42: return "PAYOUT NOTE";
case 0x43: return "STACK NOTE";
case 0x45: return "SET VALUE REPORTING TYPE";
default: return "COMMAND NOT FOUND";
}
}
bool CValidator::SendCommand()
{
// set the command name
info->CommandName = (unsigned char*)GetCommandName(cmd->CommandData[0]);
// attempt to send the command using the library
if (SSPSendCommand(cmd, info) == 0)
{
// If the command fails
ClosePort(); // close the com port
//*m_Output << "Failed to send command, port status: ";
//*m_Output << (int)cmd->ResponseStatus << endl;
return false;
}
return true;
}
// This function sends a series of commands to the validator to initialise it for use.
// It opens the com port, negotiates keys for encryption, sets the protocol version,
// sets the inhibits and calls the setup request. After this function the validator is
// ready to be enabled and used.
bool CValidator::ConnectToValidator(const SSP_COMMAND& command, int protocolVersion, int attempts)
{
// Set command structure data to the copy passed across
// cmd is the internal command structure used by this class instance
cmd->BaudRate = command.BaudRate;
cmd->RetryLevel = command.RetryLevel;
cmd->Timeout = command.Timeout;
cmd->PortNumber = command.PortNumber;
cmd->SSPAddress = command.SSPAddress;
cmd->IgnoreError = command.IgnoreError;
for (int i = 0; i < attempts; ++i)
{
// Close port in case it was left open
ClosePort();
// Open the com port
if (!OpenComPort(cmd->PortNumber))
{
//*m_Output << "Failed to open port " << (int)cmd->PortNumber << endl;
continue;
}
// Negotiate keys for encryption
if (!NegotiateKeys())
{
//*m_Output << "Failed on key negotiation..." << endl;
continue;
}
// Set the protocol version to the value passed to this function
if (!SetProtocolVersion(protocolVersion))
{
//*m_Output << "Failed on setting protocol version..." << endl;
continue;
}
// Set the inhibits (all uninhibited in this SDK)
if (!SetInhibits())
{
//*m_Output << "Failed on setting inhibits..." << endl;
continue;
}
// Call setup request
if (!SetupRequest())
{
//*m_Output << "Failed on setup request..." << endl;
continue;
}
return true;
}
return false;
}
东方仙盟筑基期之灵钞鉴真秘术指南
在东方仙盟的筑基期,修仙者们为确保灵钞交易的公正与安全,探寻出一套借助 “鉴真灵匣(CValidator)” 施展灵钞鉴真及相关操控的秘术。此秘术依托 “灵库妙法(SSP)” 灵诀库,涵盖灵匣初始化、灵钞鉴真指令发送、加密密钥协商、设备信息获取等诸多关键环节。
鉴真灵匣构建
cpp
鉴真灵匣::鉴真灵匣(void)
{
灵令 = new 灵匣指令();
灵钥 = new 灵匣密钥();
灵讯 = new 灵匣指令灵讯();
// 凝炼灵库妙法
凝炼灵库妙法();
灵匣通道数量 = 0;
灵匣类型 = 0;
堆叠灵钞数量 = 0;
灵匣单元数据 = 0;
}
修仙者凝化 “鉴真灵匣” 时,会同时生成 “灵令”“灵钥”“灵讯” 等灵具,并启动 “凝炼灵库妙法”,初始化相关参数。
灵库妙法凝炼
cpp
bool 鉴真灵匣::凝炼灵库妙法()
{
// 唤出灵库宝册
灵库宝册名 = _T("ITLSSPProc.dll");
灵库宝册实例 = 唤出灵库宝册(灵库宝册名);
if (灵库宝册实例 != NULL)
{
// 勾连灵诀之名
// 开启灵窍通联
开启灵窍通联 = (灵诀指针1)获取灵诀指针(灵库宝册实例, "OpenSSPComPortUSB");
if (开启灵窍通联 == NULL)
{
归还灵库宝册(灵库宝册实例);
return false;
}
// 闭合灵窍通联
闭合灵窍通联 = (灵诀指针2)获取灵诀指针(灵库宝册实例, "CloseSSPComPortUSB");
if (闭合灵窍通联 == NULL)
{
归还灵库宝册(灵库宝册实例);
return false;
}
// 发送灵令至灵匣
发送灵令 = (灵诀指针3)获取灵诀指针(灵库宝册实例, "SSPSendCommand");
if (发送灵令 == NULL)
{
归还灵库宝册(灵库宝册实例);
return false;
}
// 凝化密钥灵晶
凝化密钥灵晶 = (灵诀指针4)获取灵诀指针(灵库宝册实例, "InitiateSSPHostKeys");
if (凝化密钥灵晶 == NULL)
{
归还灵库宝册(灵库宝册实例);
return false;
}
// 铸就加密灵钥
铸就加密灵钥 = (灵诀指针5)获取灵诀指针(灵库宝册实例, "CreateSSPHostEncryptionKey");
if (铸就加密灵钥 == NULL)
{
归还灵库宝册(灵库宝册实例);
return false;
}
}
else
return false;
return true;
}
此过程通过 “唤出灵库宝册” 加载灵库宝册,勾连各关键灵诀,若任一环节失败,则终止并 “归还灵库宝册”。
灵匣启用与停用
cpp
// 启用灵匣,接纳诸般灵令
bool 鉴真灵匣::启用鉴真灵匣()
{
灵令->灵令数据[0] = 启用灵匣灵令;
灵令->灵令数据长度 = 1;
if (!发送灵令至灵匣()) return false;
// 核验灵讯回应
if (核验通用灵讯回应())
{
}
return true;
}
// 停用灵匣,阻绝多数灵令
bool 鉴真灵匣::停用鉴真灵匣()
{
灵令->灵令数据[0] = 停用灵匣灵令;
灵令->灵令数据长度 = 1;
if (!发送灵令至灵匣()) return false;
// 核验灵讯回应
if (核验通用灵讯回应())
{
}
return true;
}
“启用鉴真灵匣” 与 “停用鉴真灵匣” 灵诀分别设置对应灵令,发送并核验回应,决定灵匣对灵令的接纳状态。
灵匣重置
cpp
// 重置灵匣,仿若重启
bool 鉴真灵匣::重置鉴真灵匣()
{
灵令->灵令数据[0] = 重置灵匣灵令;
灵令->灵令数据长度 = 1;
if (!发送灵令至灵匣())
{
return false;
}
// 核验灵讯回应
if(核验通用灵讯回应())
{
}
return true;
}
“重置鉴真灵匣” 灵诀设定重置灵令,发送并核验回应,实现灵匣重启效果。
协议版本设定
cpp
// 设定灵匣协议版本
bool 鉴真灵匣::设定协议版本(灵纹字符 版本灵纹)
{
灵令->灵令数据[0] = 设定协议版本灵令;
灵令->灵令数据[1] = 版本灵纹;
灵令->灵令数据长度 = 2;
if (!发送灵令至灵匣()) return false;
return true;
}
“设定协议版本” 灵诀依传入的 “版本灵纹” 设置协议版本灵令并发送。
灵钞拒钞缘由探查
cpp
// 探查灵钞拒钞缘由并输出灵讯
void 鉴真灵匣::探查拒钞缘由()
{
灵令->灵令数据[0] = 探查拒钞缘由灵令;
灵令->灵令数据长度 = 1;
if (!发送灵令至灵匣()) return;
if (核验通用灵讯回应())
{
switch (灵令->回应数据[1])
{
case 0x00:
//*输出灵讯 << "灵钞接纳" << 灵境换行符;
break;
case 0x01:
//*输出灵讯 << "灵钞长度有误" << 灵境换行符;
break;
case 0x02:
//*输出灵讯 << "灵钞无效" << 灵境换行符;
break;
case 0x03:
//*输出灵讯 << "灵钞无效" << 灵境换行符;
break;
case 0x04:
//*输出灵讯 << "灵钞无效" << 灵境换行符;
break;
case 0x05:
//*输出灵讯 << "灵钞无效" << 灵境换行符;
break;
case 0x06:
//*输出灵讯 << "灵匣通道禁制" << 灵境换行符;
break;
case 0x07:
//*输出灵讯 << "读钞时插入第二张灵钞" << 灵境换行符;
break;
case 0x08:
//*输出灵讯 << "仙盟拒钞" << 灵境换行符;
break;
case 0x09:
//*输出灵讯 << "灵钞无效" << 灵境换行符;
break;
case 0x0A:
//*输出灵讯 << "灵钞读取无效" << 灵境换行符;
break;
case 0x0B:
//*输出灵讯 << "灵钞过长" << 灵境换行符;
break;
case 0x0C:
//*输出灵讯 << "鉴真灵匣停用" << 灵境换行符;
break;
case 0x0D:
//*输出灵讯 << "灵匣机制迟缓/停滞" << 灵境换行符;
break;
case 0x0E:
//*输出灵讯 << "剪钞企图" << 灵境换行符;
break;
case 0x0F:
//*输出灵讯 << "欺诈通道拒钞" << 灵境换行符;
break;
case 0x10:
//*输出灵讯 << "无灵钞插入" << 灵境换行符;
break;
case 0x11:
//*输出灵讯 << "灵钞读取无效" << 灵境换行符;
break;
case 0x12:
//*输出灵讯 << "灵钞扭曲检测" << 灵境换行符;
break;
case 0x13:
//*输出灵讯 << "暂存超时" << 灵境换行符;
break;
case 0x14:
//*输出灵讯 << "条码扫描失败" << 灵境换行符;
break;
case 0x15:
//*输出灵讯 << "灵钞读取无效" << 灵境换行符;
break;
case 0x16:
//*输出灵讯 << "灵钞读取无效" << 灵境换行符;
break;
case 0x17:
//*输出灵讯 << "灵钞读取无效" << 灵境换行符;
break;
case 0x18:
//*输出灵讯 << "灵钞读取无效" << 灵境换行符;
break;
case 0x19:
//*输出灵讯 << "灵钞宽度有误" << 灵境换行符;
break;
case 0x1A:
//*输出灵讯 << "灵钞过短" << 灵境换行符;
break;
}
}
}
“探查拒钞缘由” 灵诀发送探查灵令,依回应数据解析拒钞缘由并输出灵讯。
加密密钥协商
cpp
// 协商灵匣与仙盟主机间的加密密钥
bool 鉴真灵匣::协商加密密钥()
{
整数值;
// 确保加密关闭
灵令->加密状态 = 假;
// 发送同步灵令
灵令->灵令数据[0] = 同步灵令;
灵令->灵令数据长度 = 1;
if (!发送灵令至灵匣()) return false;
凝化密钥灵晶(灵钥, 灵令);
// 发送生成器灵令
灵令->灵令数据[0] = 发送生成器灵令;
灵令->灵令数据长度 = 9;
for (整数值 = 0; 整数值 < 8; ++整数值)
{
灵令->灵令数据[整数值 + 1] = (灵纹字符)(灵钥->生成器 >> (8 * 整数值));
}
if (!发送灵令至灵匣()) return false;
// 发送模数灵令
灵令->灵令数据[0] = 发送模数灵令;
灵令->灵令数据长度 = 9;
for (整数值 = 0; 整数值 < 8; ++整数值)
{
灵令->灵令数据[整数值 + 1] = (灵纹字符)(灵钥->模数 >> (8 * 整数值));
}
if (!发送灵令至灵匣()) return false;
// 发送密钥交换灵令
灵令->灵令数据[0] = 发送密钥交换灵令;
灵令->灵令数据长度 = 9;
for (整数值 = 0; 整数值 < 8; ++整数值)
{
灵令->灵令数据[整数值 + 1] = (灵纹字符)(灵钥->主机交互值 >> (8 * 整数值));
}
if (!发送灵令至灵匣()) return false;
灵钥->从机交互密钥 = 0;
for (整数值 = 0; 整数值 < 8; ++整数值)
{
灵钥->从机交互密钥 += (无符号长整数)灵令->回应数据[1 + 整数值] << (8 * 整数值);
}
铸就加密灵钥(灵钥);
// 获取完整加密密钥
灵令->密钥.固定密钥 = 0x0123456701234567;
灵令->密钥.加密密钥 = 灵钥->主机密钥;
灵令->加密状态 = 真; // 开启加密
//*输出灵讯 << "协商密钥完成" << 灵境换行符;
return true;
}
此灵诀按序发送同步、生成器、模数、密钥交换等灵令,借助 “凝化密钥灵晶” 和 “铸就加密灵钥” 灵诀完成加密密钥协商。
灵匣信息获取
cpp
// 获取灵匣信息
bool 鉴真灵匣::获取灵匣信息()
{
// 发送获取信息灵令
灵令->灵令数据[0] = 获取灵匣信息灵令;
灵令->灵令数据长度 = 1;
if (!发送灵令至灵匣()) return false;
// 核验灵讯回应
if (核验通用灵讯回应())
{
// 输出获取信息灵讯
// 灵匣类型
//*输出灵讯 << "灵匣类型: ";
灵匣类型 = 灵令->回应数据[1];
switch (灵匣类型)
{
case 0x00:
//*输出灵讯 << "灵钞鉴真匣" << 灵境换行符;
break;
case 0x03:
//*输出灵讯 << "灵晶储纳器" << 灵境换行符;
break;
case 0x06:
//*输出灵讯 << "灵晶发放器" << 灵境换行符;
break;
case 0x07:
//*输出灵讯 << "NV11 型灵匣" << 灵境换行符;
break;
}
// 固件版本
//*输出灵讯 << "固件版本: ";
//*输出灵讯 << 灵令->回应数据[2] << 灵令->回应数据[3] << "." <<
// 灵令->回应数据[4] << 灵令->回应数据[5] << 灵境换行符;
// 通道设置
// 固定数据结束
索引值 = 12;
灵匣通道数量 = 灵令->回应数据[索引值++];
灵匣单元数据 = new 通道数据[灵匣通道数量];
索引值 += 灵匣通道数量; // 跳过旧通道值
索引值 += 灵匣通道数量; // 跳过通道安全级别
索引值 += 3; // 跳过值乘数
// 协议版本
协议版本 = 灵令->回应数据[索引值++];
//*输出灵讯 << "协议版本: " << (整数)协议版本 << 灵境换行符;
// 设置通道数据
// 灵钞币种
//*输出灵讯 << "通道币种: ";
for (整数值 = 0; 整数值 < 灵匣通道数量; ++整数值)
{
灵匣单元数据[整数值].通道 = 整数值 + 1;
for (整数值2 = 0; 整数值2 < 3; ++整数值2)
{
灵匣单元数据[整数值].币种[整数值2] = 灵令->回应数据[索引值 + 整数值2];
//*输出灵讯 << 灵匣单元数据[整数值].币种[整数值2];
}
索引值 += 3;
//*输出灵讯 << " ";
}
//*输出灵讯 << 灵境换行符;
// 灵钞面值
//*输出灵讯 << "通道面值: ";
整数值;
for(整数值 = 0; 整数值 < 灵匣通道数量; ++整数值)
{
for (整数值2 = 0; 整数值2 < 4; ++整数值2)
灵匣单元数据[整数值].面值 += (整数)灵令->回应数据[索引值++] << (8*整数值2);
//*输出灵讯 << 灵匣单元数据[整数值].面值 << " ";
}
//*输出灵讯 << 灵境换行符;
}
return true;
}
阿雪技术观
在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目维护改善,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基生命,为科技进步添砖加瓦。
Hey folks, in this wild tech – driven world, why not dive headfirst into the whole tech – sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're tossing out your code snippets, hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open – source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome place where we all come together. We're gonna team up and explore the whole silicon – based life thing, and in the process, we'll be fueling the growth of technology


