使用CUSDK做二次开发串口透传数据的功能说明 CUSDK在网盘SDK目录下可下载: 我们所有的软件,目前都通过QQ网盘提供下载: (包含PC端接收浏览视频的客户端软件:smarteye client以及流媒体服务器smarteye server,SDK,手机拍传软件APP-MPU,手机客户端软件APP-MCU,局域网搜索配置工具、产品手册、文档、固件) 这个网盘链接永久有效,请保留收藏。 1.工程的切入点:1.先分析MFCDemo工程中打开串口/gps的方案,再结合WinFormDemo的实际情况处理。 2.透明串口和gps功能很接近,可以仿照,从WinFormDemo中Dialog.cs打开gps切入。 2.具体分析:概述:整个 OnNMDblclkChannelList 过程需要获取 PU 列表,打开 GPS_TSP 通道(打开通道的时候要对数据进行设定解码);除此以外,其他过程还要负责 GPS 信息框内显示。 如何获取 PU 列表?TSP 通道并不是由服务器泵上去的,因此我们通过人工添加“假通道”来实现服务器泵上去的功能,让他出现在每个 PU列表中的最后方,也就是每当 gps 通道添加完成之后,手动添加 tsp 通道,代码位于 MDataList.cpp 中的 CMDataList::InsertPu: /* 假 通道, 用于测试 串口 */ strcpy(onePu->pChannel.szName,"TSP"); onePu->pChannel.iChannelIndex= BVCU_SUBDEV_INDEXMAJOR_MIN_TSP; onePu->pChannel.iMediaDir =BVCU_MEDIADIR_DATARECV; onePu->iChannelCount ++; /* 假通道 */ 注:InsertPu 具体由谁调用,以及如何改动不作具体分析,这不是本文档探讨的重点。 打开GPS_TSP 通道的实现。在 MFCDemoDlg.cpp 中,我们通过 OpenGPS_TSP 打开 GPS/TSP 通道,OpenGPS_TSP 定义于 MLibBVCU.cpp 中,具体定义形式为CMLibBVCU::OpenGPS_TSP,这个函数需要处理四个数据(实际上就是参数的设置),由于 m_session 已经被定义为类 CMLibBVCU 的静态成员,因此只需传递三个参数。GPS/TSP 的区别在于解码的不同;此外显示GPS/TSP 通道里面数据的方法也不一样,这个不是重点,但是在后面的 C# 工程中体现了这一点,因此提一下。 /* 略过部分请看具体工程源码,以后不再赘述 */ int CMLibBVCU::OpenGPS_TSP(BVCU_HDialog* phDialog,constchar* puId,intchannel) { …… /* 参数的设置略过,串口和 gps 很接近,因此可以共用一套方法,区别之一是解码的不同 */ if(BVCU_SUBDEV_INDEXMAJOR_MIN_GPS <= channel && channel <=BVCU_SUBDEV_INDEXMAJOR_MAX_GPS) dlgParam.afterDecode =afterDecode_GPS; else dlgParam.afterDecode =afterDecode_TSP; /* 以下省略的部分不重要,用于设置其他参数的,略过 */ …… returnBVCU_Dialog_Open(phDialog,&dlgParam,&dlgCtrlParam); } return BVCU_RESULT_E_FAILED; } /* 这是具体的解码过程,用到了 m_OnRecvTSPData*/ BVCU_ResultCMLibBVCU::afterDecode_TSP(BVCU_HDialog hDialog, SAVCodec_Context* pCodec,SAV_Frame* pFrame) { if (hDialog && pFrame) { if(pFrame->ppData[0]) { if(m_OnRecvTSPData) m_OnRecvTSPData(hDialog, (char*)pFrame->ppData[0], pFrame->iDataSize[0]); } } return BVCU_RESULT_S_OK; } 注:具体改动请比较该工程和以前 MLibBVCU.* 区别,“*” 包含“.h”“.cpp”,显示部分不再分析,不是重点略过,至此 MFCDemo 工程的重点分析完毕。 现在我们看 WinFormDemo 工程! 概述:C# 工程因为不能直接调用 C++ 工程代码,因此必须把C++ 工程封装为 dll 调用,而提供 ManagedLayer.dll 的封装层是 ManagedLayer 这个 C++ 工程。在 MFCDemo 中,我们可以共用方法 OpenGPS_TSP 源于两个前提,1.MFCDemo 是 C++ 工程,可以直接调用位于 include 文件中的底层函数BVCU_dialog_open 2.MFCDemo 是C++ 工程,很多参数可以方便的共享和传入,所以共用方法OpenGPS_TSP 只需传递三个参数(实际设置了四个参数,额外的参数是 m_session),显示 TSP 和 GPS 信息的函数可以共享,也可以单独设立,只需直接调用即可。对比 C# 工程会发现,1.C# 工程无法调用位于 include 文件中的底层函数 BVCU_dialog_open,除非他调用 ManagedLayer 提供的接口,从而间接的调用它 2.C# 工程无法与 C++ 工程直接共享和传递参数,因此打开 GPS 的函数必须由 BVCU.cs 中的 DllImport 载入,并且传递完整的 7 个参数,如同该例子: [DllImport("ManagedLayer.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int ManagedLayer_CuOpenGpsDialog(IntPtr handle, refIntPtr dialog, IntPtrsession, Byte[] puId, int channelNo, EventHandler.BVCU_GpsDialog_OnEvent onDlgEvent, EventHandler.BVCU_GpsDialog_OnDataonDlgData); [DllImport("ManagedLayer.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int ManagedLayer_CuOpenTspDialog(IntPtr handle, refIntPtr dialog, IntPtrsession, Byte[] puId, int channelNo, EventHandler.BVCU_TspDialog_OnEvent onDlgEvent, EventHandler.BVCU_TspDialog_OnDataonDlgData); 起初,我也尝试了一个上午的时间,试图通过修改 C# 的委托函数 EventHandler.BVCU_GpsDialog_OnEvent , EventHandler.BVCU_GpsDialog_OnData 和 ManagedLayer 工程中的相关函数以达到 GPS 兼容 TSP 的打开,使得 GPS/TSP 共用一套方法。然而,因为 GPS 解码委托的传入参数和TSP 解码委托传入参数有本质的不同,最终宣告失败。 切入思路如法炮制:“假通道”的添加,分别打开 GPS 或 TSP,分别显示 GPS 或 TSP,但是在显示上我们共用GPS 的显示框,所以 TSP 输出内容为:经度框里面显示的实际是接收的字符串,纬度框里面显示的实际是该字符串的长度。 2.1添加“假通道” 概述:完成一些列初始化登陆之后,EventHandle 会被触发从而使用委托的方式,完成一系列的动作。对于委托,可以这样理解(见下面的标注): EventHandle.cs 的代码,以下均是: public EventHandler() { //dialog_OnDialogEvent,我 EventHandle 把下述职责委托于你,使得你负责处理 BVCU_Dialog_OnDialogEvent的所有事件,处理好了向某个人交代。某个人指的是,真正需要处理该事件的方法,处理好了只有在运行时才能决定。 ……略过原有代码 //该处是我添加的用于处理串口事件、接收数据的两个委托 BVCU_TspDialog_OnEvent(TspDialog_OnEvent); tspDialog_OnData = new BVCU_TspDialog_OnData(TspDialog_OnData); ……略过原有代码 //该处是原有代码,但是在委托给 Cmd_OnGetPuList之后,被委托方使用 Cmd_OnGetPuList(注意区分大小写)的过程中是我修改的代码,添加了“假通道” cmd_OnGetPuList = new BVCU_Cmd_OnGetPuList(Cmd_OnGetPuList); ……略过原有代码 } Cmd_OnGetPuList 如何添加“假通道”的(因为我只判断了小写的“gps”,所以 PU 列表中只有包含小写的“gps”通道才会被添加“TSP”通道): /* * Get Pu List */ public BVCU_Cmd_OnGetPuList cmd_OnGetPuList; voidCmd_OnGetPuList(IntPtr session, IntPtr ptPuId, IntPtrptPuName, int iStatus, refBVCU_PUOneChannelInfo channel, int iFinished) { stringpuId = Marshal.PtrToStringAnsi(ptPuId, BVCU.BVCU_MAX_ID_LEN + 1).Split('\0')[0]; stringpuName = Marshal.PtrToStringAnsi(ptPuName, BVCU.BVCU_MAX_NAME_LEN + 1).Split('\0')[0]; if(BVCU.TRUE(iFinished)) { m_session.OnGetPuListFinished(); } Session.Channel chnl = newSession.Channel(); if(iStatus == BVCU.BVCU_ONLINE_STATUS_OFFLINE) { chnl.online = false; } else { chnl.online = true; } getChannel(chnl, channel); m_session.OnGetPu(puName, puId,chnl); if(channel.szName.Equals("gps")) { // 假通道,用于串口测试,这里我只判断了小写的 gps,因为 PU_2740 底下的 gps 是小写的,我所要测试的透明串口就是 PU_2740,如果有变更需要,只需手动修改 if 条件即可 chnl = newSession.Channel(); channel.szName = "TSP"; channel.iPTZIndex = 15; channel.iMediaDir = 32; channel.iChannelIndex = 65792; if(iStatus == BVCU.BVCU_ONLINE_STATUS_OFFLINE) { chnl.online = false; } else { chnl.online = true; } getChannel(chnl, channel); m_session.OnGetPu(puName, puId,chnl); } } 2.2分别打开 GPS 或TSP该代码位于Form1.cs中,其中 m_sdkOperator.Dialog.openTspDialog(pu,channelNo) 语句用到的 openTspDialog里面调用了 BVCU.cs 中的 DllImport 的 ManagedLayer_CuOpenTspDialog,参见BVCU.cs: [DllImport("ManagedLayer.dll", CallingConvention = CallingConvention.Cdecl)]publicstatic extern int ManagedLayer_CuOpenTspDialog(IntPtr handle, refIntPtr dialog, IntPtrsession, Byte[] puId, int channelNo, EventHandler.BVCU_TspDialog_OnEvent onDlgEvent, EventHandler.BVCU_TspDialog_OnDataonDlgData); 以下代码位于Form1.cs中: /* * 双击界面上的资源树打开对话 */ const int TREE_LEVEL_SESSION = 0; const int TREE_LEVEL_PU = 1; const int TREE_LEVEL_CHANNEL = 2; privatevoid treeViewResList_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgse) { ……略过已有代码,else if 是修改的代码,只修改了 if 条件 elseif (channelNo >= BVCU.BVCU_SUBDEV_INDEXMAJOR_MIN_GPS&& channelNo < BVCU.BVCU_SUBDEV_INDEXMAJOR_MAX_GPS) { ……未修改 } //此处为添加的代码 else { if(m_sdkOperator.Dialog.openTspDialog(pu, channelNo) == 0) { ListViewItem item = new ListViewItem(); item.Text = pu.id; item.Tag = channelNo; ListViewItem.ListViewSubItemTspData = new ListViewItem.ListViewSubItem(); ListViewItem.ListViewSubItemlen = new ListViewItem.ListViewSubItem(); item.SubItems.AddRange(new ListViewItem.ListViewSubItem[] { TspData, len }); listViewGPSData.Items.Add(item); } } } } openTspDialog 位于 Dialog.cs 中: ArrayList m_tspDialogs; public int openTspDialog(Session.Pu pu, intchannelNo) { foreach(OneDialog dlg inm_tspDialogs) { if(dlg.pu.id.Equals(pu.id, StringComparison.CurrentCultureIgnoreCase) && dlg.channelNo ==channelNo) { return-1; } } IntPtrdialog = IntPtr.Zero; //此处为调用的 ManagedLayer_CuOpenTspDialog if(BVCU.ManagedLayer_CuOpenTspDialog(m_bvsdkHandle,ref dialog, m_session.Handle, Encoding.UTF8.GetBytes(pu.id), channelNo,m_bvsdkEventHandler.tspDialog_OnEvent, m_bvsdkEventHandler.tspDialog_OnData) == BVCU.BVCU_RESULT_S_OK) { OneDialogdlg = new OneDialog(); dlg.dialogHandle = dialog; dlg.channelNo = channelNo; dlg.pu = pu; m_tspDialogs.Add(dlg); return0; } return-1; } 2.3 m_bvsdkEventHandler.tspDialog_OnEvent/ tspDialog_OnData回调 EventHandle.cs中: public delegate void BVCU_TspDialog_OnEvent(IntPtrdialog, int eventCode, Int32errorCode); public delegate void BVCU_TspDialog_OnData(IntPtrdialog, string pTspData, int len); public BVCU_TspDialog_OnData tspDialog_OnData; public BVCU_TspDialog_OnEvent tspDialog_OnEvent; voidTspDialog_OnEvent(IntPtr dialog, int eventCode, Int32errorCode) { if(errorCode != BVCU.BVCU_RESULT_S_OK) { m_dialog.onTspDialogOpenFailed(dialog); } } voidTspDialog_OnData(IntPtr dialog, string pTspData, intlen) { //以下在 Dialog.cs 中 m_dialog.onTspData(dialog,pTspData, len); } Dialog.cs 中: delegate void OnGetTspData(IntPtr dialog, stringpTspData, int len); OnGetTspDatadeleGetTspData; public void onTspData(IntPtrdialog, string pTspData, int len) { if(null == deleGetTspData) { deleGetTspData = new OnGetTspData(procGetTspData); } m_mainForm.BeginInvoke(deleGetTspData, newobject[] { dialog, pTspData, len }); } voidprocGetTspData(IntPtr dialog, string pTspData, intlen) { foreach (OneDialog dlg inm_tspDialogs) { if(dlg.dialogHandle == dialog) { m_mainForm.onGetTspData(dlg.pu.id, pTspData, len); return; } } } 2.4显示 TSPForm1.cs中: public void onGetTspData(string puId, string pTspData, int len) { foreach(ListViewItem item inlistViewGPSData.Items) { if(item.Text.Equals(puId, StringComparison.CurrentCultureIgnoreCase)) { ListViewItem.ListViewSubItem TspData = newListViewItem.ListViewSubItem(); TspData.Text = pTspData; item.SubItems[1] = TspData; ListViewItem.ListViewSubItem length = newListViewItem.ListViewSubItem(); length.Text =len.ToString(); item.SubItems[2] = length; return; } } } 2.5底层支持载入的 ManagedLayer_CuOpenTspDialog 位于工程 ManagedLayer.cpp 中,我在里面添加了这个函数,具体请参考工程代码,ManagedLayer_CuOpenTspDialog 调用了 CBVCU.cpp 中的 openTspDialog,openTspDialog 指定了 dlgParam.OnEvent = tspDialog_OnEvent; 和 dlgParam.afterDecode = tspDialog_OnData; 作为 Tsp 的事件回调和解码回调,这两个回调定义于 CBVCU.cpp 中。 intCBVCU:penTspDialog(BVCU_HDialog* dlg, BVCU_HSession session, char* puId, intchannelNo, BVCU_TspDialog_OnEventonDlgEvent, BVCU_TspDialog_OnData onDlgData) { BVCU_DialogParamdlgParam; BVCU_DialogControlParamdlgCtlParam; memset(&dlgParam,0, sizeof(dlgParam)); memset(&dlgCtlParam,0, sizeof(dlgCtlParam)); dlgParam.iSize= sizeof(dlgParam); dlgCtlParam.iSize= sizeof(dlgCtlParam); dlgParam.hSession= session; BVCU_DialogTargettarget; strncpy_s(target.szID,BVCU_MAX_ID_LEN+1,puId,_TRUNCATE); // memset(&target,0, sizeof(BVCU_DialogTarget)); target.iIndexMajor= channelNo; // strcpy_s(target.szID,BVCU_MAX_ID_LEN + 1, puId); target.iIndexMinor= -1; dlgParam.iTargetCount= 1; dlgParam.pTarget= ⌖ dlgParam.iAVStreamDir= BVCU_MEDIADIR_DATARECV; dlgParam.OnEvent= tspDialog_OnEvent; dlgParam.afterDecode= tspDialog_OnData; if (NULL == m_procGpsDialogEvent) { m_procTspDialogEvent= onDlgEvent; } if (NULL == m_procGpsDialogOnData) { m_procTspDialogOnData= onDlgData; } // <TODO>: 参考MLibBVCU.cpp return BVCU_Dialog_Open(dlg, &dlgParam,&dlgCtlParam); } 总结:有了MFCDemo例程,其他的都不是很难,只需仿照就行了。
|