关于play_and_get_digits开发日志

play_and_get_digits

       关于通过UUID播放play_and_get_digits获取按键,这个事吐槽下,弄到了大晚上才弄明白,前后经历了很多挫折,最终还是看了上海老李写的文档发现了解决办法。

       最近一直想利用官方自大的IVRmenu菜单功能实现自己的ivr定制需求,但是发现了几个问题,fs自带的ivr,无法判断空闲坐席,无法判断登录坐席,通过看案例可以通过dialplan方式获取判断上下班时间,折腾了整整一天没有找到合适资料,查找了各种资料甚少,或者说我是小白,如果哪位网页有方法可以告诉我,相互交流下。幸好自己多年前写过板卡模式呼叫中心系统的代码经验,加之今年疫情期间,已经基本迁移到现有的C#ESL控制模式里边,新冠疫情期间也熬了几次夜才做的差不多,没有完全验证,今天一个热力公司客服系统有需求,所以趁客户的需求赶紧完善下我们基于fs开发的yhhpbx业务能力,也算是需求驱动我的研发动力。

       好久不写程序,感觉有点生疏,最近自己的小团队突破20人了,没有精力直接参与开发了,管理也是大头,总之一切为了生存,经营一个大家庭不容易。

 

言归正传,

play_and_get_digits 不管是用app,还是api都不能马上获取到按键,最终只能通过自己程序阻塞获取按键,直到时间超时,按键长度超时目前不知道怎么用,也没有找的合适的方法,直接亮出我们的方法C# esl ,有参考老李的的作品。

/// <summary>
/// 播放语音文件并且接受DTMF
/// 2020.11.6升级
/// </summary>
/// <param name="t_eslConnection"></param>
/// <param name="arg_uuid"></param>
/// <param name="arg_VoiceFile"></param>
/// <param name="destption">调用来源</param>
/// <returns></returns>
public static string AskPlayBackfrom_DigitsSelect(ESLconnection t_eslConnection, string arg_uuid, string arg_VoiceFile, string destption)
{
try
{
//ESLconnection eslConnection = new ESLconnection(Global.g_fsServerIP, Global.g_fsServerProt, "ClueCon");

if (t_eslConnection.Connected() != ESL_SUCCESS)
{
DebugView.DebugViewPrint("AskPlayBackfrom_DigitsSelect()", "Error connecting to FreeSwitch");
return "";
}

string comSelectVoice = arg_VoiceFile;
comSelectVoice = comSelectVoice.Replace(@"\\", "/");
comSelectVoice = comSelectVoice.Replace(@"\", "/");
comSelectVoice = comSelectVoice.Replace(@"//", "/");

if (!File.Exists(comSelectVoice))
{
comSelectVoice = Application.StartupPath + "/Voice/" + arg_VoiceFile;
comSelectVoice = comSelectVoice.Replace(@"\\", "/");
comSelectVoice = comSelectVoice.Replace(@"\", "/");
comSelectVoice = comSelectVoice.Replace(@"//", "/");
}

if (!File.Exists(comSelectVoice))
{
DebugView.DebugViewPrint("EslMainFun.AskPlayBackfrom_DigitsSelect", "[" + comSelectVoice + "]不存在...");
comSelectVoice = "D:/Voice/" + arg_VoiceFile;
comSelectVoice = comSelectVoice.Replace(@"\\", "/");
comSelectVoice = comSelectVoice.Replace(@"\", "/");
comSelectVoice = comSelectVoice.Replace(@"//", "/");
}

//play_and_get_digits <min> <max> <tries> <timeout> <terminators> <file> <invalid_file> [<var_name> [<regexp> [<digit_timeout> [<transfer_on_failure>]]]]

//sprintf(cmd_tmp,"1 1 1 10 a %s",filename);//设置让 play_get_dtmf 函数里面的按键处理条 件无效, ivr check_dtmf_event 函数自己处理按键判断
//string filename = "F:/MornRay/Project/Esl/Voice/we.wav";
//<min最小按键个数> <max最大按键个数> <tries提示音乐播放次数> <timeout ms按键超时时间> <terminators结束字符> <file放音文件>

int MaxTimer = 30;
int MaxDtmf = 1;
string EndDtmf = "#";
string cmd_tmp = String.Format("1 " + MaxDtmf + " 2 " + MaxTimer * 1000 + " '' {0} ''", comSelectVoice);
ESLevent retEvent = t_eslConnection.Execute("play_and_get_digits", cmd_tmp, arg_uuid);
string DtmfKeys = Check_dtmf_event(t_eslConnection, "", "", EndDtmf, MaxDtmf, MaxTimer, 0);
return DtmfKeys;
}
catch (Exception ex)
{
LogP.LogError("AskPlaybackfrom_digits(string arg_uuid, string arg_VoiceFile),Exception:" + ex.Message, "eeror.fscmd.log");
LogP.LogError("app=>play_and_get_digits ,uuid=" + arg_uuid + ", from:" + destption, "eeror.fscmd.log");
return "";
}
}

/// <summary>
/// 播音取按键
/// </summary>
/// <param name="eslCon">连接</param>
/// <param name="filename">filename:语音文件名称,多个文件以分号";"隔开,文件名称可以带.wav,扩展名,假如没有,默认是.pcm扩展名.可指定路径,假如没有,默认是语音程序的./data/system目录下</param>
/// <param name="invalidfile"></param>
/// <param name="EndDtmf">EndDtmf:按键结束条件,比如"#"表示按#号结束输入,""表示没有结束按键条件//支持最大3个结束按键 比如 EndDtmf="*0#" 表示按 0,* 或者#都可以结束</param>
/// <param name="MaxDtmf">MaxDtmf:最大按键个数结束条件,0表示没有按键个数结束条件</param>
/// <param name="MaxTimer">MaxTimer:按键最大时间结束条件,单位秒,0表示没有最大时间结束条件</param>
/// <param name="TwoDtmfTimer">TwoDtmfTimer:两个按键间的间隔时间结束条件,单位秒,0表示没有两个按键间的间隔时间结束条件</param>
/// <returns>返回的字符串</returns>
public static string Check_dtmf_event(ESLconnection eslCon, string filename, string invalidfile, string EndDtmf, int MaxDtmf, int MaxTimer, int TwoDtmfTimer)
{

long t_stime = TimeStampTran.GetTimeStamps(); // 开始监视代码运行时间
string DtmfKeys = "";
try
{
while (true)
{
long t_etime = TimeStampTran.GetTimeStamps();
long spantime = t_etime - t_stime;
if (spantime > MaxTimer * 1000)
{
break;
}
ESLevent eslEvent = eslCon.RecvEvent();
if (eslEvent != null && eslEvent.GetHeader("Event-Name", -1) == "DTMF")
{

string currDtmf = eslEvent.GetHeader("DTMF-Digit", -1);

if (currDtmf == EndDtmf)
{
break;
}

DtmfKeys = DtmfKeys + currDtmf;


if (DtmfKeys.Length >= MaxDtmf)
{
break;
}

}
Thread.Sleep(50);
}
return DtmfKeys == null ? "" : DtmfKeys;

}
catch (Exception ex)
{
return "";
}

}

 

说明: Check_dtmf_event()是我写的阻塞检测按键的方法.

我的微信:langant,我们可以提供关于fresswitch的开发经验,定制其他软件需求定制。

#include "esp_system.h" #include "Game_Main.h" #include "driver/gpio.h" #include "esptim.h" #include "esp_random.h" #include "esp_mesh.h" #include "../Fun/videoplay.h" #include "../Fun/draw.h" #include "../Fun/RGBPanel.h" #include "../Fun/LanGame.h" #include "../Fun/GameDevice.h" #include "../Fun/JC24BDevice.h" #include "../Fun/video_control.h" #include "../Fun/LanWifiMesh.h" #include "../Fun/Button.h" #include "../Fun/countdown_timer.h" #include "../config.h" uint8_t GMAIN_GameState; static uint8_t GMAIN_TestColorIndex; static TIM_timing_t GMAIN_TimeObj1; static uint8_t GMAIN_BootClickCount = 0; static TIM_timing_t GMAIN_BootTimeObj; static uint8_t GMAIN_testTime = 3; CountdownTimer game_timer; // 全局倒计时器 static TickType_t last_update = 0; // 全局显示更新时间戳 // 显示区域定义 #define DISPLAY_WIDTH 256 // 屏幕宽度 #define DIGIT_WIDTH 45 // 单数字宽度 #define POINT_WIDTH 22 // 小数点宽度 // 总宽度计算:3整数位 + 小数点 + 2小数位 #define TOTAL_WIDTH (3*DIGIT_WIDTH + POINT_WIDTH + 2*DIGIT_WIDTH) #define START_X ((DISPLAY_WIDTH - TOTAL_WIDTH) / 2) // 居中显示 // 格式化数字为固定位数字符串(自动补零) void format_fixed_digits(int value, int digits, char* output) { for (int i = digits - 1; i >= 0; i--) { output[i] = (value % 10) + '0'; // 取最后一位 value /= 10; } output[digits] = '\0'; // 字符串终止符 } // void GMAIN_ChangeState(uint8_t state) // { // // 状态切换前处理当前状态 // switch (GMAIN_GameState) { // case GAME_M_TIME: // // 如果当前是倒计时状态,暂停计时器 // if (timer_get_state(&game_timer) == TIMER_RUNNING) { // timer_pause(&game_timer); // } // break; // } // GMAIN_GameState = state; // 更新状态 // switch (GMAIN_GameState) // { // case 0: // video_control_play(VIDEO_LOGO, VIDEO_PLAY_LOOP, 0); // break; // case 1: // video_control_play(VIDEO_WIN, VIDEO_PLAY_LOOP, esp_mesh_is_root() ? 10 : 12); // break; // case 2: // video_control_play(VIDEO_WIN_ASSIST, VIDEO_PLAY_LOOP, esp_mesh_is_root() ? 10 : 15); // break; // case 3: // video_control_play(VIDEO_WWJ_STATE_COIN_IN, VIDEO_PLAY_SINGLE, 0); // break; // case 4: // video_control_play(VIDEO_WWJ_STATE_COIN_OK, VIDEO_PLAY_LOOP, 0); // break; // case 5: // video_control_play(VIDEO_WWJ_STATE_GAMMING, VIDEO_PLAY_LOOP, 0); // break; // case GAME_M_WWJ_STATE_STOP: // video_control_play(VIDEO_WWJ_STATE_STOP, VIDEO_PLAY_SINGLE, 0); // break; // case GAME_M_TIME: // // 初始化倒计时器(如果未初始化) // if (timer_get_state(&game_timer) == TIMER_STOPPED) { // timer_init(&game_timer, 10.0f); // 10秒倒计时 // } // // 启动倒计时 // timer_start(&game_timer); // // 初始化显示相关变量 // static TickType_t last_update = 0; // last_update = xTaskGetTickCount(); // 重置更新时间 // break; // case GAME_M_SELF_TEST: // video_stop(); // GMAIN_TestColorIndex = 0; // TIM_TIMING_SET_1MS(GMAIN_TimeObj1, 0); // break; // } // } // void GMAIN_Run(void) // { // switch (GMAIN_GameState) // { // case GAME_M_IDLE: // break; // case GAME_M_WIN: // break; // case GAME_M_WIN_ASSIST: // break; // case GAME_M_WWJ_STATE_COIN_IN: // case GAME_M_WWJ_STATE_COIN_OK: // break; // case GAME_M_WWJ_STATE_GAMMING: // break; // case GAME_M_WWJ_STATE_STOP: // break; // case GAME_M_TIME: // 倒计时状态处理 // // 获取剩余时间 // int integer_seconds, centiseconds; // timer_get_remaining_split(&game_timer, &integer_seconds, &centiseconds); // TickType_t now = xTaskGetTickCount(); // if (now - last_update >= pdMS_TO_TICKS(10)) { // RGBPanel_FillScreen(0x0000); // RGBPanel_ShowValue(10, 0, integer_seconds, // RGBPanel_IMAGE_WWJ_STATE_RED_NUM0, // RGBPanel_NO_IMAGE); // RGBPanel_drawImageById(40, 0, RGBPanel_IMAGE_WWJ_STATE_RED_Point); // RGBPanel_ShowValue(50, 0, centiseconds, // RGBPanel_IMAGE_WWJ_STATE_RED_NUM0, // RGBPanel_NO_IMAGE); // RGBPanel_Update(); // last_update = now; // } // // 检查是否超时 // if (timer_is_expired(&game_timer)) { // // 倒计时结束处理 // GMAIN_ChangeState(GAME_M_WIN); // 切换到胜利状态 // } // break; // case GAME_M_SELF_TEST: // if (TIM_TIMING_RUN(GMAIN_TimeObj1)) // { // if (GMAIN_TestColorIndex >= 8) // { // JC24BDevice_SendLogoPlay(); // LanWifiMesh_SendLogoPlay(); // GMAIN_ChangeState(GAME_M_IDLE); // return; // } // uint8_t r = 0; // uint8_t g = 0; // uint8_t b = 0; // if ((GMAIN_TestColorIndex & 0x01) > 0) // { // r = 0xFF; // } // if ((GMAIN_TestColorIndex & 0x02) > 0) // { // g = 0xFF; // } // if ((GMAIN_TestColorIndex & 0x04) > 0) // { // b = 0xFF; // } // RGBPanel_FillScreenRGB888(r, g, b); // RGBPanel_Update(); // GMAIN_TestColorIndex++; // TIM_TIMING_SET_100MS(GMAIN_TimeObj1, GMAIN_testTime); // } // break; // } // if (IO_KeyPressed(KEY_BUTTON_TEST)) // { // GMAIN_ChangeState(GAME_M_WIN); // JC24BDevice_BroadcastWin(); // LanWifiMesh_BroadcastWin(); // } // if (TIM_TIMING_RUN(GMAIN_BootTimeObj)) // { // TIM_TIMING_SET_1S(GMAIN_BootTimeObj, 10000); // GMAIN_BootClickCount = 0; // } // if (IO_KeyPressed(KEY_BUTTON_BOOT) && g_config.meshType == CONFIG_MESH_TYPE_ROOT) // { // GMAIN_BootClickCount++; // if (GMAIN_BootClickCount >= 5) // { // config_set_mesh_type(CONFIG_MESH_TYPE_CHILD); // esp_restart(); // } // TIM_TIMING_SET_1S(GMAIN_BootTimeObj, 2); // } // } extern int g_wifi_rssi; static void GMAIN_ShowRootFlag(void); void GMAIN_FrameCall(uint32_t frameIndex) { switch (g_config.matrix_chain) { case 2: { switch (GMAIN_GameState) { case GAME_M_WWJ_STATE_COIN_IN: RGBPanel_ShowValue(54, 18, g_GameDeviceData.coinInCount, RGBPanel_IMAGE_WWJ_STATE_WHITE_NUM0, RGBPanel_NO_IMAGE); RGBPanel_ShowValue(92, 18, g_GameDeviceData.coinPlayCount, RGBPanel_IMAGE_WWJ_STATE_YELLOW_NUM0, RGBPanel_NO_IMAGE); break; } } break; case 3: { switch (GMAIN_GameState) { case GAME_M_WWJ_STATE_COIN_IN: RGBPanel_ShowValue(102, 18, g_GameDeviceData.coinInCount, RGBPanel_IMAGE_WWJ_STATE_WHITE_NUM0, RGBPanel_NO_IMAGE); RGBPanel_ShowValue(144, 18, g_GameDeviceData.coinPlayCount, RGBPanel_IMAGE_WWJ_STATE_YELLOW_NUM0, RGBPanel_NO_IMAGE); break; } } break; case 4: { switch (GMAIN_GameState) { case GAME_M_WWJ_STATE_COIN_IN: case GAME_M_WWJ_STATE_COIN_OK: RGBPanel_ShowValue(168, 18, g_GameDeviceData.coinInCount, RGBPanel_IMAGE_WWJ_STATE_WHITE_NUM0, RGBPanel_NO_IMAGE); RGBPanel_ShowValue(208, 18, g_GameDeviceData.coinPlayCount, RGBPanel_IMAGE_WWJ_STATE_YELLOW_NUM0, RGBPanel_NO_IMAGE); break; } } break; default: break; } if (esp_mesh_is_root()) { GMAIN_ShowRootFlag(); } else if (g_wifi_rssi <= -60 || g_wifi_rssi == 0) { RGBPanel_ShowWifiRssi(g_wifi_rssi); } } // 6*6 static const uint8_t GMAIN_mesh_root_flag[] = { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, }; #define COLOR_YELLOW 0xFFE0 #define GRADIENT_STEPS (128) // 渐变的步数 static uint16_t gradientProgress = 0; typedef struct { uint8_t colorR; uint8_t colorG; uint8_t colorB; } color_t; static color_t startColor = { 31, 31, 31}; static color_t endColor = { 31, 0, 0}; static uint8_t colorIndex = 0; const uint8_t colorIndexBuff[7] = { 0x7, 0x05, 0x01, 0x03, 0x02, 0x06, 0x04}; uint16_t getGradientColor(void) { // 计算每个通道的渐变步长 uint16_t r = startColor.colorR + (endColor.colorR - startColor.colorR) * gradientProgress / (GRADIENT_STEPS - 1); uint16_t g = startColor.colorG + (endColor.colorG - startColor.colorG) * gradientProgress / (GRADIENT_STEPS - 1); uint16_t b = startColor.colorB + (endColor.colorB - startColor.colorB) * gradientProgress / (GRADIENT_STEPS - 1); // 增加渐变进度 if (++gradientProgress == (GRADIENT_STEPS - 1)) { gradientProgress = 0; startColor = endColor; colorIndex = (colorIndex + 1) % 7; endColor.colorR = ((colorIndexBuff[colorIndex] & 0x04) > 0 ? 31 : 0); endColor.colorG = ((colorIndexBuff[colorIndex] & 0x02) > 0 ? 31 : 0); endColor.colorB = ((colorIndexBuff[colorIndex] & 0x01) > 0 ? 31 : 0); } uint16_t color = (r << 11) | (g << 5) | b; // 返回RGB565格式的颜色值 return color; } static void GMAIN_ShowRootFlag(void) { uint16_t color = getGradientColor(); int16_t startX = 58; startX += (g_config.matrix_chain - 1) * 64; for (uint8_t y = 0; y < 6; y++) { for (uint8_t x = 0; x < 6; x++) { RGBPanel_drawPixel(startX + x, y, (GMAIN_mesh_root_flag[x + y * 6] ? color : 0)); } } } void GMAIN_VideoEndCall(void) { switch (GMAIN_GameState) { case 0: break; case 1: case 2: case 3: case 4: case 5: case GAME_M_SELF_TEST: break; } } void GMAIN_Init(void) { timer_init(&game_timer, 0); // 初始化为0秒 GMAIN_count_down(GAME_M_SELF_TEST); GMAIN_BootClickCount = 0; TIM_TIMING_SET_1S(GMAIN_BootTimeObj, 10000); } void GMAIN_count_down(uint8_t state) { // 状态切换前处理当前状态 switch (GMAIN_GameState) { case 2: // 如果当前是倒计时状态,暂停计时器 if (timer_get_state(&game_timer) == TIMER_RUNNING) { timer_stop(&game_timer); } break; } GMAIN_GameState = state; // 更新状态 switch (GMAIN_GameState) { case 0: video_control_play(VIDEO_LOGO, VIDEO_PLAY_LOOP, 0); break; case 1: video_control_play(VIDEO_COUNTDOWN_READY, VIDEO_PLAY_LOOP, 0); break; case 2: video_stop(); // 初始化倒计时器(如果未初始化) if (timer_get_state(&game_timer) == TIMER_STOPPED) { timer_init(&game_timer, g_GameDeviceData.settingtime_value); } // 启动倒计时 timer_start(&game_timer); // 初始化显示相关变量 static TickType_t last_update = 0; last_update = xTaskGetTickCount(); // 重置更新时间 break; case 3: timer_pause(&game_timer); break; case 4: video_control_play(VIDEO_WIN , VIDEO_PLAY_LOOP, 0); break; case 5: video_control_play(VIDEO_LOSE , VIDEO_PLAY_LOOP, 0); break; case GAME_M_SELF_TEST: video_stop(); GMAIN_TestColorIndex = 0; TIM_TIMING_SET_1MS(GMAIN_TimeObj1, 0); break; } } void GMAIN_count_down_Run(void) { switch (GMAIN_GameState) { case 0: break; case 1: break; case 2: // 正计时状态处理 // 获取已耗时 int integer_seconds, centiseconds; timer_get_elapsed_split(&game_timer, &integer_seconds, &centiseconds); TickType_t now = xTaskGetTickCount(); if (now - last_update >= pdMS_TO_TICKS(10)) { RGBPanel_FillScreen(0x0000); char int_str[4]; char frac_str[3]; format_fixed_digits(integer_seconds, 3, int_str); format_fixed_digits(centiseconds, 2, frac_str); int current_x = START_X; int current_y = 5; for (int i = 0; i < 3; i++) { uint8_t digit = int_str[i] - '0'; RGBPanel_drawImageById(current_x, current_y, RGBPanel_IMAGE_WWJ_STATE_RED_NUM0 + digit); current_x += DIGIT_WIDTH; } RGBPanel_drawImageById(current_x, current_y+13, RGBPanel_IMAGE_WWJ_STATE_RED_Point); current_x += POINT_WIDTH; for (int i = 0; i < 2; i++) { uint8_t digit = frac_str[i] - '0'; RGBPanel_drawImageById(current_x, current_y, RGBPanel_IMAGE_WWJ_STATE_RED_NUM0 + digit); current_x += DIGIT_WIDTH; } } last_update = now; // 超时判断 if (timer_is_expired(&game_timer)) { vTaskDelay(pdMS_TO_TICKS(20)); // 等待20ms确保显示完成 GMAIN_count_down(3); break; } RGBPanel_Update(); break; case 3: break; case 4: break; case 5: break; case GAME_M_SELF_TEST: if (TIM_TIMING_RUN(GMAIN_TimeObj1)) { if (GMAIN_TestColorIndex >= 8) { JC24BDevice_SendLogoPlay(); LanWifiMesh_SendLogoPlay(); GMAIN_count_down(GAME_M_IDLE); return; } uint8_t r = 0; uint8_t g = 0; uint8_t b = 0; if ((GMAIN_TestColorIndex & 0x01) > 0) { r = 0xFF; } if ((GMAIN_TestColorIndex & 0x02) > 0) { g = 0xFF; } if ((GMAIN_TestColorIndex & 0x04) > 0) { b = 0xFF; } RGBPanel_FillScreenRGB888(r, g, b); RGBPanel_Update(); GMAIN_TestColorIndex++; TIM_TIMING_SET_100MS(GMAIN_TimeObj1, GMAIN_testTime); } break; } // if (IO_KeyPressed(KEY_BUTTON_TEST)) // { // GMAIN_count_down(GAME_M_WIN); // JC24BDevice_BroadcastWin(); // LanWifiMesh_BroadcastWin(); // } if (TIM_TIMING_RUN(GMAIN_BootTimeObj)) { TIM_TIMING_SET_1S(GMAIN_BootTimeObj, 10000); GMAIN_BootClickCount = 0; } if (IO_KeyPressed(KEY_BUTTON_BOOT) && g_config.meshType == CONFIG_MESH_TYPE_ROOT) { GMAIN_BootClickCount++; if (GMAIN_BootClickCount >= 5) { config_set_mesh_type(CONFIG_MESH_TYPE_CHILD); esp_restart(); } TIM_TIMING_SET_1S(GMAIN_BootTimeObj, 2); } } 还是停留在9.98,我觉得是显示问题,帮我看看
最新发布
07-10
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值