65.9K
CodeProject 正在变化。 阅读更多。
Home

Plantronics Legend UC 耳机 – 呼叫中心示例

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2013年2月28日

CPOL

9分钟阅读

viewsIcon

34592

downloadIcon

154

Plantronics Legend UC 耳机 – 呼叫中心示例

当有人问我是否可以试用一款新的 Plantronics 耳机时,我立刻想起了我曾担任电话客服代表,负责接听客户支持请求的那段时光。我们团队使用着看起来像是要指挥飞机起降的电话耳机。一整天,我们都坐在办公桌前,盯着屏幕,屏幕上显示着来电客户的信息。我们可以搜索在线知识库中的信息,但大部分时间,我们都在盯着屏幕发呆。

我们的呼叫中心有一个自动化的打卡系统,它根据你接听客户电话的可用时间来计时。当你到达时,你需要通过电话键盘输入 PIN 码来打卡。一旦你输入了 PIN 码,你就上线可以接听电话了。只要你的电话已通过身份验证,电话就会转接到你这里。当你起身离开座位去休息、吃午饭或下班时,你需要挂断电话。这个过程会停止将呼叫转接到你的分机,并自动下班打卡。

在这个示例中,我将使用 Plantronics Legend UC 耳机来重现这个场景。虽然这款耳机并非呼叫中心常见的耳机,但它是今天进行技术演示的绝佳平台,预示着未来将会有配备传感器的呼叫中心耳机。这款耳机支持蓝牙功能,并可连接多种不同的软电话,包括 Lync、Skype 和 Avaya。此外,它还内置了传感器,可以检测与蓝牙接收器的距离。还有传感器可以检测耳机是否戴在头上。这使得耳机在您将耳机戴到耳上时,能够自动接听电话。我认为这两个传感器对于本文的范畴非常有趣,我将重点关注利用它们来自动化我们的上下班打卡流程。

初始开发设置

为了让工作站能够与 Plantronics 耳机的所有传感器进行交互,必须安装一个名为 Spokes 的客户端软件包。对于开发者而言,有一个软件开发工具包 (SDK),您可以下载并使用它来替代客户端软件。在本文接下来的部分,我将假设您已经下载、安装并运行了 Spokes 软件服务。

我将从 Visual Studio 2012 开始开发,创建一个带有 ASP.Net 4.5 的空白 Web Forms 应用程序。我们希望将通知呈现给呼叫中心代表,我将构建一个简单的工时表网格。对于这些用户界面元素,我将添加 Telerik 的 ASP.Net AJAX 控件的试用版。控件安装完成后,我将右键单击解决方案资源管理器中的 Web 项目,并将其转换为 RadControls Web Project,使用 Windows 7 主题。

要从浏览器连接到耳机,我需要添加一个来自 Plantronics SDK 的 JavaScript 文件,名为 spokes.js。安装 SDK 后,您可以在以下路径找到它:c:\Program Files (x86)\ Plantronics\Plantronics SDK\Samples\RestJsClient。我将该文件的副本放在了 Web 项目的 Scripts 文件夹中。我希望将连接耳机和打卡操作的所有逻辑隔离到一个 JavaScript 文件中,该文件将被应用程序的所有页面引用。为此,我创建了一个名为 callcenter.js 的 JavaScript 文件并将其添加到 Scripts 文件夹中。为了实现耳机在应用程序中的这种通用使用,我在将由应用程序所有页面使用的 site.master 文件中添加了对 spokes.jscallcenter.js 的引用。

           <%--Site scripts--%>
            <asp:ScriptReference Path="~/Scripts/spokes.js" />
            <asp:ScriptReference Path="~/Scripts/callcenter.js" />
        </Scripts>
    </asp:ScriptManager>
列表 1 – 添加到 Site.Master 中用于控制耳机的脚本引用

通知将显示在 Telerik Notification 控件内部。此控件将显示为屏幕角落的一个滑动窗口,类似于您从 Windows 任务栏收到的其他“Toast 通知”。该控件的标记如下:

       <telerik:RadNotification ID="statusNotification" runat="server" Animation="Slide" Title="Headset Status" OffsetX="-10" OffsetY="-10" ShowCloseButton="False" ViewStateMode="Disabled" Height="125" Width="250">
            <ContentTemplate>
                <table style="width: 250px; height: 75px;">
                    <tr>
                        <td valign="center">
                            <img id="Img1" runat="server" src="~/Images/headset_40.png" />
                        </td>
                        <td valign="center">
                            <span id="statusText"></span>
                        </td>
                    </tr>
                </table>
            </ContentTemplate>
        </telerik:RadNotification>
列表 2 – RadNotification 控件标记

构建耳机呼叫中心

callcenter.js 文件是一个独立的组件,它隔离了与耳机的所有交互以及将耳机通知发送给用户的过程。我已开始构建此脚本,使用一个自执行函数来隔离私有方法,并仅公开应用程序其余部分所需的方法。

var CallCenter = (function() {
    var statusCtl;
    var spokes;
    var initFunction = function () {

        spokes = new Spokes("http://127.0.0.1:32001/Spokes");
        statusCtl = $find(controls.statusNotification.ClientID);

        ConnectSpokes();
    };
    return {
        Init: initFunction,
        Spokes : function() {
            return spokes;
        },
    };

})();

$().ready(function() {
    CallCenter.Init();
});
列表 3 – CallCenter JavaScript 对象的 JavaScript 基础

在此代码列表中,我分配了一个私有变量来存储 Spokes 服务的引用。此外,我还捕获了 Notification 控件对象的引用,以便稍后可以向用户发布消息。spokes 对象由运行在本地机器上并在 32001 端口上响应的服务构建。一旦我们获得了 spokes 对象的引用,我们就需要使用 connectSpokes() 方法连接到耳机。此方法将允许我们开始监听 Spokes 服务使用具有发布/订阅机制的队列为我们发布的事件状态更改。

   var ConnectSpokes = function() {
    	
        spokes.Device.deviceList(function (result) {
            if (result.isError) {
                ShowMessage("Unable to connect to headset");
            } else if (result.Result[0] == null) {
                ShowMessage("Error - Is there a headset connected?");
            } else {
                // attach to the device
                spokes.Device.attach(result.Result[0].Uid, ControlInterface);
                PollDeviceEvents();
            }
        });

    };

    //Creates a control interface
    function ControlInterface(session) {
        if (session.isError || !spokes.Device.isAttached) {
            ShowMessage("Session Registration Error");
        }
        else {
            spokes.Device.deviceInfo(function (result) {

                if (result.Result.RemoteFirmwareVersion == null) {
                    // no headset attached
                    ShowMessage("No headset attached to adapter");
                    clockedInState = false;
                } else {
                    ShowMessage("Connected to headset successfully");
                }

                console.log(result);
            });
        }
    }
列表 4 – 使用 REST 连接到 Spokes 服务

这些方法中有几个测试值得注意:

  • 首先,我们检查获取设备列表的请求是否没有返回错误。如果从设备列表中返回了错误,则表示 Plantronics 适配器和耳机未正确连接和通电。
  • 如果设备列表为空,则也是一个错误。我们将显示一条消息,指示用户应检查耳机连接。
  • controlInterface 方法中,有一个针对 RemoteFirmwareVersion 的测试。这是耳机设备本身公开的唯一一个我们可以检查的属性。所有其他公开的状态属性都是 USB 适配器的属性。我们必须检查此属性是否有值,以验证耳机是否已正确连接到我们的工作站。

图 1 – 耳机服务连接成功的通知

如果这些测试通过,我们将通知用户,连接到耳机,并在 pollDeviceEvents 方法中开始监听事件。作为参考,以下是 sendMessage 方法的主体,该方法利用通知控件:

    function ShowMessage(msg) {
    	
        $("#statusText").html(msg);
        statusCtl.show();
    }
列表 5 – ShowMessage 方法,通知我们的浏览器用户

pollDeviceEvents 方法是一系列检查和逻辑判断,用于确定如何最好地响应耳机引发的事件。其来源如下:

    function PollDeviceEvents() {
    	
        setInterval(function() {

            spokes.Device.events(function (result) {
                if (!result.isError) {

            	    for (var i = 0; i < result.Result.length; i++) {

                        var eventName = result.Result[i].Event_Name;

                        if (eventName == "Doff" || eventName == "Don" || eventName == "OutofRange" || eventName=="InRange") {

                            if (eventName == "Doff" || eventName == "OutofRange") {
                                BeginClockout();
                            }
                            else if (eventName == "Don" || eventName == "InRange") {

                                window.clearInterval(clockOutInterval);
                                statusCtl.set_autoCloseDelay(3000);

                                    LogTimeEvent(true);
                                    ShowMessage("Going on the clock");

                            }

                        }

                        console.log(result.Result[i].Event_Name);
                    }
                }
            })

        }, 500)


    };
列表 6 – PollDeviceEvents 方法 – 处理耳机引发的事件

此方法仅对耳机引发的四种事件感兴趣: Don(戴上)、Doff(摘下)、InRange(范围内)和 OutofRange(范围外)。前两个事件描述了设备被戴上或从用户头上取下。后两个事件描述了耳机设备与工作站的相对距离。在我的场景中,当用户摘下耳机或离开工作站时,我们将使用 BeginClockout 方法,给予 30 秒的时间让他们回来或将被自动打下班卡。

    var secondsDelay = 30;
    var secondsUntilClockOut;
    function BeginClockout() {
    	
        // Don't set the clockout interval twice
        if (secondsUntilClockOut > 0)
            return;

        secondsUntilClockOut = secondsDelay;
        statusCtl.set_autoCloseDelay(secondsDelay * 1000);
        ShowMessage("You have removed your headset and will be clocked out in <span id='countDown'>" + secondsDelay +  "</span> seconds");

        clockOutInterval = window.setInterval(function () {

            if (secondsUntilClockOut == 0) {
                window.clearInterval(clockOutInterval);
                LogTimeEvent(false);
            }

            secondsUntilClockOut--;
            $("#countDown").html(secondsUntilClockOut);
        }, 1000);

    }
列表 7 – BeginClockout 方法 – 指示用户将被自动打下班卡

BeginClockout 方法中,我们设置一个计时器,每秒计数,直到指定的 secondsDelay 值耗尽。在此期间,我们将通过 ShowMessage 方法使用我们的通知控件,来说明距离打卡操作发生还剩多少时间。

图 2 – 即将进行打卡操作的通知

打卡操作将在 LogTimeEvent 方法中进行,并传入新的状态指示器。我将扩展 CallCenter 对象,提供一种识别员工和打卡服务位置的方法,以便在 LogTimeEvent 方法中,我可以使用标准的基于 jQuery 的 ajax 调用来调用 WebAPI 服务,并提供所有必要的信息来执行打卡操作。

    var timeClockUrl = "";
    var employeeId = -1;

    // Enhanced return statement for CallCenter
    return {
        Init: initFunction,
        Spokes : function() {
            return spokes;
        },
        set_TimeClockUrl: function(url) {
            timeClockUrl = url;
            return this;
        },
        set_EmployeeId: function(id) {
            employeeId = id;
            return this;
        }
    };


    function LogTimeEvent(beginWork) {
        // If beginWork is true, it is a ClockIn event, if false, it is a ClockOut event

        clockInTimeout = window.setTimeout(function () {
            // Do AJAX clock-in
            $.ajax(timeClockUrl, {
                async: false,
                cache: false,
                type: "POST",
                dataType: "json",
                contentType: "application/json",
                data: JSON.stringify({
                    EmployeeId: employeeId,
                    StartClock: beginWork
                })
            });

            clockedInState = beginWork;
            scheduledClockIn = false;

        }, 1000);

        scheduledClockIn = true;

    }
列表 8 – LogTimeEvent 方法 – 将耳机事件连接到打卡事件的传输层

此 jQuery post 的负载包含 employeeId 和新的打卡状态指示器。此方法配置为延迟一秒传输此信息。此短暂延迟使我们能够捕获耳机重复事件,并且只向打卡服务发送一次调用。

配置打卡服务

在此示例中,我选择使用 ASP.Net WebAPI 作为我们打卡服务的框架。我在项目中创建了一个 api 文件夹,并在该文件夹中使用“添加新项”菜单添加一个名为 ClockController 的新 Web API Controller 类。在此类中,我将构建 Post 方法,以接受我们 LogTimeEvent 方法中的 jQuery ajax 调用所发送的负载。

        public static readonly List<TimeClockEvent> TimeClockEvents = new List<TimeClockEvent>();


        public HttpResponseMessage Post([FromBody]ClockModel model)
        {

            if (model.StartClock)
            {
                var evt = new TimeClockEvent()
                {
                    Id = TimeClockEvents.Count + 1,
                    EmployeeId = model.EmployeeId,
                    ClockIn = DateTime.Now
                };
                TimeClockEvents.Add(evt);
            }
            else
            {
                var evt = TimeClockEvents.OrderByDescending(e => e.ClockIn).First(e => e.EmployeeId == model.EmployeeId);
                evt.ClockOut = DateTime.Now;
            }

            return new HttpResponseMessage(HttpStatusCode.Created);

        }


        public class ClockModel
        {
            public int EmployeeId;
            public bool StartClock;
        }

public class TimeClockEvent
{

    public int Id { get; set; }
    public int EmployeeId { get; set; }
    public DateTime ClockIn { get; set; }
    public DateTime ClockOut { get; set; }
    public TimeSpan Elapsed
    {
        get { return ClockOut - ClockIn; }
    }
}
列表 9 – ClockController 和支持模型

我使用一个静态列表作为内存中的打卡事件存储。在实际应用程序中,您需要使用其他形式的持久化存储,例如数据库,来存储您的打卡记录。此 POST 方法将我们的负载作为 ClockModel 对象接收。根据打卡记录的期望状态,该方法将创建或更新 TimeClockEvents 列表中的记录。

有了这些数据,我们就可以创建一个简单的工时表,其中包含一个绑定到数据源的网格控件,以演示我们确实正在记录打卡记录。

图 3 – 一个显示两条先前输入的记录和一条由耳机生成的第三条记录的工时表

摘要

在本文中,我们构建了一个 JavaScript 对象,该对象连接到本地机器上的 RESTful 服务,以与 Plantronics 耳机通信。我们的对象会监听耳机内部传感器触发的事件,并将适当的消息发送到我们使用 WebAPI 构建的 Web 服务。此项目的示例代码可在此处 下载。请注意,示例代码包含更健壮的耳机事件连接和错误处理。您可以访问 http://developer.plantronics.com 上的 Plantronics 开发者论坛和 SDK。Telerik 控件可从 http://www.telerik.com/products/aspnet-ajax/download.aspx 下载。

© . All rights reserved.