介绍
3CX 呼叫流程设计 (原名 VAD) 是一个可以快速而简单的创建语音应用,不需要会编程或者 SIP 技能的强大工具。创造出来的语音应用部署在 3CX 系统的服务端,最后实现为呼叫流程 App。通过管理界面,可以定义激活每个应用程序的条件,比如设置一个特定的呼入规则。
尽管使用 CFD 软件创建语音应用比编程简单很多,但人总有犯错的时候。在这种情况下,能正确诊断问题并解决它是非常有必要的。
修复项目编译错误
当 CFD 项目完成后,我们需要打包(build)它。打包过程分为两个阶段:
- 当你在打包项目的时候 CFD 会这么做:
- CFD 会为项目创建一个包含 C# 脚本文件和 WAV 文件的目录。
- 接着会生成一个上传到 3CX 的 ZIP 文件。
- 当生成输出后,你需要将它上传到 3CX 。当你这么做以后:
- 3CX 呼叫流程服务器会编译(compile)这些脚本。
- 如果编译通过,在 app 左侧会显示一个绿色的小点,意味着 app 已经可以使用了。如果不是这个现象,可以打开 app 检查是否存在编译错误。
下面我们会举例说明你可能会遇到的错误。
CFD 尝试生成 C# 脚本时出现的错误
这个错误可能是一些组件没有正确配置,或者没有设置组件的属性导致的,比如:
在使用 “Transfer” 组件的时候我们没有需要输入目的地(Destination)属性,会得到下面的错误:
当我们打包项目的时候可以在 “Error List” 面板看到错误信息。
双击错误,CFD 会自动选中有问题的组件。
编译 C# 脚本的时候出现的错误
当你成功打包 CFD app 后,需要将它上传到 3CX 。在上传时,由于各种原因(如组件配置表达式中的错误或缺少引用),可能会发生编译错误。
修复配置组件时出现的表达式错误
最常见的错误是在配置组件的时候使用了一个无效的表达式。
在实例当中,加入我们使用了一个 Transfer 模块,将目的地设置为一个无效的表达式(如上图),在脚本编译的时候产生错误。
在 3CX 管理控制台上传 app 的时候会显示这些错误,如上图所示。
我们可以看到,这个错误是由 C# 编译器报告的。通常来说,包含错误那一行会包含出现错误的组件名称,CFD 为程序的正常运行提供了良好的提示。
修复由嵌套命名空间引起的错误
在使用 “Launch External Script” 模块时,如果你的脚本声明了命名空间的话,生成的脚本不会由 3CX 编译。比如,如果我们的 CFD 项目中包含了下列脚本:
namespace MyNamespace { public class MyClass { public int DoSomething(int a, int b) { return a + b; } } }
在 Launch External Script 组件中使用脚本的话,向 3CX 上传项目的时候会出现下列错误。
“Invalid Script structure” 错误可以通过删除命名空间声明修复,去除后的 C# 脚本如下:
public class MyClass { public int DoSomething(int a, int b) { return a + b; } }
修复因缺少引用导致的错误
当我们使用 Launch External Script 组件时,我们需要提供 C# 代码执行。这个 C# 代码也许需要引用 3CX 服务器不包含的外部 DLL 引用。在这种情况下,当 3CX 打包 CFD app 脚本的时候,你会看到类似的错误:”Error CS0246: The type or namespace name ‘YourCustomLibrary’ could not be found (are you missing a using directive or an assembly reference?)“。
要解决此问题,您需要将缺少的引用及其依赖项添加到 3CX Call Flow Server 服务文件夹,以便在编译期间使用它并在运行时加载:
- Windows 的目录: “C:\Program Files\3CX Phone System\Bin”
- Linux 的目录: “/usr/lib/3cxpbx”
修复项目运行时错误
当你完成对 CFD 项目的打包以后,将它上传到 3CX,脚本编译通过后,语音 app 就可以使用了。
如果这个 app 没有按照我们的预期运行,我们需要查看日志调查 app 的操作。呼叫流程 APP 的日志记录在 “3CXCallFlow.log” 文件:
- Windows 的目录:“C:\ProgramData\3CX\Instance1\Data\Logs”
- Linux 的目录:“/var/lib/3cxpbx/Instance1/Data/Logs”
我们拿一个简单的应用举例。这个 app 会先播放 “Hello.wav” 文件,接着尝试执行 C# 代码(代码只会抛出一个异常),最后将通话转接到操作员分机(这步会失败因为在执行外部脚本的时候会报错)。这个流程如下图所示:
我们将 app 部署到 3CX,拨打它之后日志会记录如下信息:
1. 2019/04/04 16:59:07.306|26728|0009|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - Start executing main flow... 2. 2019/04/04 16:59:07.321|26728|0013|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - Start executing component 'playHello' 3. 2019/04/04 16:59:07.322|26728|0013|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - PromptQueue - Start playing file: C:\ProgramData\3CX\Instance1\Data\Ivr\Prompts\Callflows\mytestapp\Hello.wav 4. 2019/04/04 16:59:07.323|26728|0013|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-C:\ProgramData\3CX\Instance1\Data\Ivr\Prompts\Callflows\mytestapp\Hello.wav 5. 2019/04/04 16:59:11.600|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - OnPromptPlayed for component 'playHello' 6. 2019/04/04 16:59:11.601|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - CallFlow.CFD.PromptPlaybackComponent 'playHello' - No more files to play, moving to the next component... 7. 2019/04/04 16:59:11.601|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - Start executing component 'executeSomeCode' 8. 2019/04/04 16:59:11.603|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - _mytestapp.Main_59.Main+executeSomeCodeExternalCodeExecutionComponent 'executeSomeCode' - Start executing external script 9. 2019/04/04 16:59:11.833|26728|0003|Err|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - Error executing last component: System.Exception: This is an exception thrown from my code at _mytestapp.Main_59.Main.MyClass.DoSomething() at _mytestapp.Main_59.Main.executeSomeCodeExternalCodeExecutionComponent.ExecuteCode() at CallFlow.CFD.ExternalCodeExecutionComponent.ExecuteStart() at CallFlow.CFD.ExternalCodeExecutionComponent.Start(TimerManager timerManager, Dictionary`2 variableMap, TempWavFileManager tempWavFileManager, PromptQueue promptQueue) at _mytestapp.Main_59.Main.ProcessStart() 10. 2019/04/04 16:59:11.836|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - Start executing error handler flow... 11. 2019/04/04 16:59:11.836|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - Start executing component 'errorHandlerAutoAddedFinalDisconnectCall' 12. 2019/04/04 16:59:11.837|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - CallFlow.CFD.DisconnectCallComponent 'errorHandlerAutoAddedFinalDisconnectCall' - No more files to play, disconnecting call... 13. 2019/04/04 16:59:11.964|26728|0009|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: Main - Switching executing mode from Active to Wrapup 14. 2019/04/04 16:59:11.971|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - OnCallTerminated for component 'errorHandlerAutoAddedFinalDisconnectCall' 15. 2019/04/04 16:59:11.971|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - CallFlow.CFD.DisconnectCallComponent 'errorHandlerAutoAddedFinalDisconnectCall' - Processing OnCallTerminated 16. 2019/04/04 16:59:11.971|26728|0003|Trc|CallPair._mytestapp.Main_59.Main.915.[C:23.2]-From script: MyTestApp - CallID 00000C011AA560F5_23 - Callflow - Start executing disconnect handler flow…
注意下列事项:
- CFD app 的日志每一行都是以 “From script: ProjectName” 开始,在我们的例子当中是 “From script: MyTestApp”。
- 每一行都会有 “CallID”,用于标识每一通呼叫属于哪行日志。
- 在第二行中我们看到组件 “playHello” 被执行了。
- 在第七行,可以看到组件 “executeSomeCode” 被执行。
- 在第九行,可以看到执行组件 “executeSomeCode” 产生了错误。
- 在第十行中,我们看到错误处理程序流已执行,但由于它是空的,因此不会执行任何用户组件。
我们可以看到,在这个示例应用程序中,当我们尝试执行组件“executeSomeCode”时会发生错误,因此我们需要检查该组件做了什么。
迁移现有的脚本
将 v15.5 已经存在的脚本迁移到 v16, 需要下列步骤:
- 使用 v16 的新 CFD 打开 v15.5 的老项目。
- 如果项目中没有使用 “Launch External Script” 组件,跳到下一步。 如果你在使用这个组件,你需要使用文本编辑器打开脚本并移除命名空间的声明。 然后打开“Launch External Script”组件的配置对话框以应用这些更改:
- 在 Object Type 参数中移除命名空间。
- 如果调用的方法有返回值,开启选项 “The method returns a value”。
- 为脚本需要接收的参数设置合适的类型。
- 将项目打包生成一个 ZIP 文件。
- 登录 3CX 管理控制台 > 高级 > Call FLow Apps > 添加/升级,将生成的 CFD zip 文件上传。
- 转到呼出规则,在呼入规则 > 呼叫分配 的目的地设为上传了的 CFD app。