- 包含库文件
- 设置宏和全局变量
- 设置私有函数
- 添加初始化函数
- 实现主函数
步骤 1:包含库文件
首先包含 SDK 所需的库文件。请参见下方代码块:C
#include <string.h>
#include "types.h"
#include "macros.h"
#include "cli/cli.h"
#include "msg_queue/msg_queue.h"
#include "vsnprintf/printk.h"
#include "cpu_platform.h"
#include "board/board.h"
#include "menu.h"
#include "vpos/vmc_link.h"
#include "vpos/vmc_vend.h"
/* Drivers */
#include "drv_usart.h"
步骤 2:设置宏和全局变量
下方代码定义了用于日志记录/调试的模块名称宏,并设置了用于存储配置和会话相关数据的全局变量。C
#define __MODULE__ "marshall-c-sdk-demo"
static vmc_config_t config;
static vend_item_t session_products[2];
static vend_session_t session;
static int current_session = -1;
static menu_t menu;
static int com_id = -1;
config:存储自动售货机配置。session_products:持有两个不同售卖商品的 array。session:存储会话详情。menu:存储菜单相关设置。com_id:表示通信 ID。
步骤 3:设置私有函数
实现用于调试、事件处理和交易处理的辅助函数。以下各部分提供了更多详情。调试输出函数
此函数用于确定调试信息的输出流。C
static void terminal_tx_str(char *str)
{
#if defined(__PLATFORM_X86) || defined(__PLATFORM_LINUX)
printf("%s", str);
#elif defined(__PLATFORM_STM32)
drv_usart_tx_data(&terminal_usart, (uint8_t*)str, strlen(str));
#endif
}
str:包含调试信息的 string。
CPU 定时器节拍
此函数由平台定时器定期调用(每 5-10 毫秒一次)。C
static __bool cpu_timer_tick(void *p)
{
vmc_link_background();
return __false;
}
p:指向可选参数的指针(此函数中未使用)。
VMC 链路事件处理程序
此函数处理链路事件并记录相关信息。C
static __bool vmc_link_event_handler_cb(int event_id, void *data)
{
__bool result = __false;
__log("link event_id: %d", event_id);
switch (event_id)
{
case vmc_link_event_on_ready:
if (__true)
{
vpos_config_t *config = (vpos_config_t*)data;
__log("link ok! decimal places: %d", config->decimal_place);
}
break;
case vmc_link_event_on_comm_error:
if (__true)
{
__log("lost comm");
}
break;
default:
break;
}
return result;
}
event_id:链接事件的类型。data:指向可选参数的指针(根据事件类型而变化)。
__false,因为未确定成功/失败。
多次贩售请求处理程序
此函数用于处理会话及其包含的项目。请参阅下方代码块:C
static void triggered_vend_request(void)
{
if (config.reader_always_on)
current_session = 2;
if (current_session != -1)
{
vend_session_t *session = &sessions[current_session];
/* a card has been detected. now we can ask the vpos to take money */
memset(session, 0, sizeof(vend_session_t));
session_products[0].code = 1;
session_products[0] .qty = 1;
session_products[0].price = 10;
session_products[0].unit = 1;
session_products[1].code = 2;
session_products[1].qty = 1;
session_products[1].price = 20;
session_products[1].unit = 1;
if (config.multi_vend_support)
{
session->products = session_products;
session->total_products = 2;
}
else
{
session->products = &session_products[current_session];
session->total_products = 1;
}
vmc_vend_vend_request(session);
if (config.mag_card_approved_by_vmc_support || config.mifare_approved_by_vmc_support || config.qr_approved_by_vmc_support)
{
vmc_vend_client_gateway_auth(__true);
}
}
}
session_products 0 和 1,每个都具有不同的商品代码和价格,并将它们添加到 “session” object 中。
多会话出货事件处理器
此函数用于处理出货事件并记录相关信息。C
static __bool vmc_vend_event_handler_cb(int event_id, void *data)
{
__bool result = __false;
static __bool vend_sent = __false;
__log("vend event_id: %d", event_id);
switch (event_id)
{
case vmc_vend_event_on_ready:
break;
case vmc_vend_event_on_session_begin:
if (__true)
{
triggered_vend_request();
}
break;
case vmc_vend_event_on_vend_approved:
/* the vpos approved the payment */
result = __true;
break;
case vmc_vend_event_on_vend_denied:
/* the vpos declined the payment */
break;
case vmc_vend_event_on_settlement:
break;
case vmc_vend_event_on_session_canceled:
if (__true)
{
__log("received cancel session");
}
break;
case vmc_vend_event_on_transaction_info:
if (__true)
{
__log("received transaction info");
vmc_vend_transfer_data_t *info = (vmc_vend_transfer_data_t*)data;
if (info->encode_bitmap & transfer_data_encode_transaction_id)
__log("transaction id: %lu", __ptr2uint64_t(info->transaction_id));
if (info->encode_bitmap & transfer_data_encode_card_bin)
__log("card bin: %.6s", info->card_bin);
if (info->encode_bitmap & transfer_data_encode_prop_card_uid)
__log("prop card uid: %lu", __ptr2uint64_t(info->prop_card_uid));
if (info->encode_bitmap & transfer_data_encode_card_last_4_digits)
__log("cc last 4 digits: %.4s", info->cc_last_4_digits);
}
break;
case vmc_vend_event_on_status:
if (__true)
{
uint32_t status = (uint32_t)data;
__log("received status: %d", status);
}
break;
case vmc_vend_event_on_sessions_rsp:
if (__true)
{
mdb_msg_sessions_status_t *sessions = (mdb_msg_sessions_status_t*)data;
__log("open sessions: %d\n", sessions->num_opened_sessions);
}
break;
case vmc_vend_event_on_reader_state_rsp:
if (__true)
{
mdb_msg_reader_status_t *state = (mdb_msg_reader_status_t*)data;
__log("reader state: %d\n", state);
}
break;
case vmc_vend_event_on_qr_code:
if (__true)
{
ereceipt_t *receipt = (ereceipt_t*)data;
__log("received ereceipt: %s\n", receipt->qr_data);
}
break;
case vmc_vend_event_on_session_timeout:
if (__true)
{
uint32_t *session_id = (uint32_t*)data;
__log("session timeout: %d\n", session_id);
}
break;
default:
break;
}
return result;
}
event_id:售卖事件的类型。data:指向可选参数的指针。根据事件类型而变化。
- 大多数情况下为
__false。 __true用于vmc_vend_event_on_vend_approved,表示出货批准成功。
步骤 4:设置初始化函数
添加辅助函数后,您需要实现用于初始化 VMC 配置和菜单处理程序的函数。请参见下方各部分了解更多详情。初始化 VMC
此函数用于初始化 VMC 配置并注册事件处理程序。请参见下方代码:C
static void vmc_init(void)
{
/* initialize sdk */
vmc_link_init();
/* configuring the sdk */
memset(&config, 0, sizeof(config));
__strcpy(config.model, "marshall-c-sdk-demo");
__strcpy(config.serial, "01234567");
__strcpy(config.sw_ver, "vmc app version");
__strcpy(config.vmc_hw_ver, "01234567");
__strcpy(config.vmc_manuf_code, "manuf");
config.multi_vend_support = __true;
config.multi_session_support = __false;
config.price_not_final_support = __false;
config.reader_always_on = __false;
config.always_idle = __false;
config.explicit_vend_success = __false;
config.vend_denied_policy = vend_denied_policy_persist_e;
config.mifare_approved_by_vmc_support = __false;
config.mag_card_approved_by_vmc_support = __false;
config.qr_approved_by_vmc_support = __false;
config.dump_packets_level = debug_level_dump_moderate;
config.debug = __true;
/* register to the link and vend events */
vmc_link_register_callback((vmc_link_event_handler_cb_t)vmc_link_event_handler_cb);
vmc_vend_register_callback((vmc_vend_event_handler_cb_t)vmc_vend_event_handler_cb);
}
config.multi_vend_support 被设置为__true。
初始化菜单
在初始化菜单之前,请添加必要的处理程序函数。请参见下方代码:C
static void handler_start_session(void *aux_data)
{
vmc_vend_session_start(vmc_vend_session_type_credit_e);
}
static void handler_cancel_session(void *aux_data)
{
vmc_vend_session_cancel();
}
static void handler_quit(void *aux_data)
{
menu_stop(&menu);
exit(0);
}
static void handler_link_stats(void *aux_data)
{
vmc_stats_t* stats = vmc_link_get_link_stats();
char buffer[50];
sprintf(buffer, "\n ------ Stats ------ \n");
menu_output(buffer);
sprintf(buffer, " crc error: %d\n", stats->crc_errors);
menu_output(buffer);
sprintf(buffer, " re-trans: %d\n", stats->retrans);
menu_output(buffer);
sprintf(buffer, " link loss: %d\n", stats->resets);
menu_output(buffer);
sprintf(buffer, " ------ Stats ------ \n");
menu_output(buffer);
}
static void handler_info_session(void *aux_data)
{
vmc_vend_session_start(vmc_vend_session_type_info_e);
}
static void handler_query_sessions(void *aux_data)
{
vmc_vend_get_session_status();
}
static void handler_reader_state(void *aux_data)
{
vmc_vend_get_reader_state();
}
C
static void menu_init(void)
{
menu.total_items = 0;
menu_add_item(&menu, "start session 0", &handler_start_session, 0);
menu_add_item(&menu, "close session 0", &handler_close_session, 0);
if (config.multi_session_support == __true)
{
menu_add_item(&menu, "start session 1", &handler_start_session, 1);
menu_add_item(&menu, "close session 1", &handler_close_session, 1);
}
menu_add_item(&menu, "info session", &handler_info_session, __null);
menu_add_item(&menu, "cancel session", &handler_cancel_session, __null);
menu_add_item(&menu, "query sessions", &handler_query_sessions, __null);
menu_add_item(&menu, "reader state", &handler_reader_state, __null);
menu_add_item(&menu, "link stats", &handler_link_stats, __null);
menu_add_item(&menu, "display control", &handler_display_control, __null);
menu_add_item(&menu, "quit", &handler_quit, __null);
menu.menu_running = __true;
menu_show(&menu);
}
步骤 5:实现主函数
在实现所有其他部分之后,您可以添加程序的主入口点,该入口点会初始化与外围设备的链路及菜单,然后启动一个运行菜单的无限循环。请参见下方代码:C
int mymain(int argc, char *argv[])
{
printk_register_puts(terminal_tx_str);
/* Modules initialization */
vmc_init();
set_command_line_arguments(argc, argv, &config, &com_id);
/* Board initialization */
board_init(com_id);
/* Register the timer tick handler */
cpu_platform_register_timer_tick_cb(2, (platform_timer_tick_cb_t)cpu_timer_tick);
/* Modules initialization */
#if UTILITIES_CLI_MODE==1
cli_init();
cli_register_io(terminal_tx_str);
printk("welcome to %s, version: %s, build: %s\n\r", __stringify(APP_NAME), __stringify(APP_VER), __stringify(APP_BUILD));
printk("build data: "__DATE__ " "__TIME__"\n\r");
/* Start modules and application */
cli_start();
#endif
vmc_link_start(&vmc_usart, &config);
menu_init();
while (1)
{
#if UTILITIES_CLI_MODE==1
if (drv_usart_is_rx_pending(&terminal_usart))
{
cli_process_char(drv_usart_rx_byte(&terminal_usart));
}
#endif
menu_run(&menu);
}
}