由云台协议DLL浅谈插件程序的编写(上)【原创】
前言:本文通过一个简单的工程描述了一种插件设计的思想。复杂设计和模块化分解能力是衡量一个程序员水平的重要标志,欢迎大家和我探讨。
1.我们的目的是在Plug-Ins目录里面查找“ptz*.dll”格式的云台插件的动态链接库。每个库可支持多个协议,通过查找该目录下的合法插件,有此插件创建出某个协议的实例,达到通过该协议操作云台的目的。
2.结构体定义:ComParam为串口通信参数:(定义在include\ComManager\ComParam.h文件中)
class COMPARAMEXT_API ComParam
{
public:
ComParam( int nBaudRate, // 波特率
int nByteSize = 8, // 数据位
int nParity = 0, // 校验位 0 - NOPARITY
int nStopBits = 0); // 停止位 0 - ONESTOPBIT(参考winbase.h中的定义)
};
3.为了达到使用dll的目的,先封装几个简单的类:
CDllLoad类:src\PTZ_Test\controls\DllLoad.h(这个类仅在src\PTZ_Test\controls\PTZDll.h)
// DllLoad.h
#pragma once
class CDllLoad
{
public:
CDllLoad()
{
}
virtual ~CDllLoad(void)
{
Close();
}
void Open(LPCTSTR lpszName) // 加载DLL
{
if (m_hModule)
{
Close();
}
m_hModule = ::LoadLibrary(lpszName);
}
void Close()
{
::FreeLibrary(m_hModule); // 卸载DLL
m_hModule = NULL;
}
operator HMODULE() { return m_hModule;}
PROC GetProcAddress(LPCSTR lpProcName)
{
return ::GetProcAddress(m_hModule, lpProcName); // 取得函数入口地址
}
protected:
HMODULE m_hModule;
};
CPTZDll类:src\PTZ_Test\controls\DllLoad.h(主要功能:根据插件产生出该DLL的支持的协议对象,然后由该对象的函数调用需要的功能,由于函数调用时实际使用的是dll的类的成员函数,所以对象存在时必须也有一份CPTZDll的实例而且是打开状态的。)
// PTZDll.h
#pragma once
#include "DllLoad.h"
#include "PTZInterface.h"
class CPTZDll
{
typedef CPTZInterface * (__stdcall* CREATE_PTZ_INSTANCE)(const char * pszProtocolsName);
public:
CPTZDll() // 成员初始化
: m_pDllLoad(0)
, m_pfnCreatePTZInstance(0)
, m_ppProtocolsName(0)
{
}
~CPTZDll()
{
Close();
delete m_pDllLoad;
m_pDllLoad =0;
}
BOOL OpenPlugIn(LPCTSTR lpszName) // 打开插件,如果是有效的插件返回TRUE,否则FALSE
{
if (m_pDllLoad)
Close();
else
{
m_pDllLoad = new CDllLoad();
m_pDllLoad->Open(lpszName);
}
m_pfnCreatePTZInstance = (CREATE_PTZ_INSTANCE)m_pDllLoad->GetProcAddress("CreatePTZInstance");
m_ppProtocolsName = (const char **)m_pDllLoad->GetProcAddress("ProtocolNames");
return (m_ppProtocolsName && m_ppProtocolsName[0]);
}
void Close()
{
m_pfnCreatePTZInstance = 0;
m_ppProtocolsName = 0;
if (m_pDllLoad)
m_pDllLoad->Close();
}
const char ** GetSupportProtocols()
{
return m_ppProtocolsName;
}
CPTZInterface * CreatePTZInstance(LPCTSTR lpszProtolName) // 根据该插件内的某个协议产生出一个对象
{
if (m_pfnCreatePTZInstance)
{
return m_pfnCreatePTZInstance(lpszProtolName);
}
return 0;
}
private:
CDllLoad * m_pDllLoad;
CREATE_PTZ_INSTANCE m_pfnCreatePTZInstance;
const char ** m_ppProtocolsName;
};
CPlugInInfo类:src\PTZ_Test\controls\PTZProtocolFind.h和src\PTZ_Test\controls\ptzprotocolfind.cpp
//=========================================================
//
// Copyright (c) 2000-2004 iWise Technologies,Co. Ltd.
// All Rights Reserved.
//
// Product: iW988
// File: PTZProtocolFind.h
// Created: 天衣有缝
// Data :2004.12.22 PM
// Description:
// ValueAdded main program for iW988.
// Contact:
//
//=========================================================
#pragma once
class CPlugInInfo // 插件信息类
{
public:
string szFileName ; // 文件名
vector<string> Protocols ; // 协议列表
string szFullPath ; // 文件全路径
public:
CPlugInInfo(void)
{
}
~CPlugInInfo(void)
{
}
};
class CPTZProtocolFind
{
public:
CPTZProtocolFind(void);
~CPTZProtocolFind(void);
public:
void EnumPlugIns(vector<CPlugInInfo>& PlugInList); // 枚举Plug-Ins目录的所以ptz文件
};
//=========================================================
//
// Copyright (c) 2000-2004 iWise Technologies,Co. Ltd.
// All Rights Reserved.
//
// Product: iW988
// File: PTZProtocolFind.cpp
// Created: 天衣有缝
// Data :2004.12.22 PM
// Description:
// ValueAdded main program for iW988.
// Contact:
//
//=========================================================
#include "StdAfx.h"
#include "ptzprotocolfind.h"
#include "PTZDll.h"
CPTZProtocolFind::CPTZProtocolFind(void)
{
}
CPTZProtocolFind::~CPTZProtocolFind(void)
{
}
// 枚举“Plug-Ins”目录下的所有有效插件
void CPTZProtocolFind::EnumPlugIns(vector<CPlugInInfo>& PlugInList)
{
char szFilePath[MAX_PATH];
::GetModuleFileName(NULL, szFilePath, MAX_PATH);
char * pFind = strrchr(szFilePath, '\\');
*pFind = '\0';
strcat(szFilePath, "\\Plug-Ins\\ptz*.dll"); // 查找的匹配字符串
CFileFind find;
BOOL bFind;
bFind = find.FindFile(szFilePath);
while (bFind)
{
CPTZDll dll;
bFind = find.FindNextFile();
if (find.IsArchived()) // 文件而非文件夹
{
// 输出这个文件:
XTRACE("路径:%s\n", find.GetFilePath());
if ( dll.OpenPlugIn(find.GetFilePath()) ) // 如果是云台插件,则枚举出它的协议
{
CPlugInInfo info;
const char ** ppProtocols = dll.GetSupportProtocols();
if(!ppProtocols)
{
continue;
}
while(0 != *ppProtocols)
{
string _string = *ppProtocols;
info.Protocols.push_back(_string);
ppProtocols ++;
}
info.szFullPath = find.GetFilePath();
info.szFileName = find.GetFileName();
PlugInList.push_back(info);
}
}
dll.Close();
}
find.Close();
// 下面是调试代码
XTRACE("插件数量:%d\n",PlugInList.size());
vector<CPlugInInfo>::iterator it = PlugInList.begin();
for(; it != PlugInList.end(); it++)
{
XTRACE("文件名:%s----》文件路径:%s\n", (*it).szFileName.c_str(), (*it).szFullPath.c_str());
}
}
4.在对话框工程的窗体中加入两个成员变量:
CPTZDll m_PTZdll ; // dll加载对象
CPTZInterface* m_PTZInterface ; // 正在使用的插件协议对象
用户选择一个插件:BOOL bReturn = m_PTZdll.OpenPlugIn(m_strProtocolPath);
然后根据该插件选择一个协议:m_PTZInterface = m_PTZdll.CreatePTZInstance(m_strProtocolName); // 参数是一个表示协议的字符串
这时我们可以利用这个m_PTZInterface对象来执行各种操作。
说明:m_PTZInterface的类“CPTZInterface”是一个抽象类,由此派生出各种各样的协议。
其定义如下:
//PTZInterface.h
//iWise DVR PTZ Interface define header file.
#pragma once
#include "PTZDef.h"
#include "ComParam.h"
class CPTZInterface
{
public:
virtual void SetPTZSettings(int nComPort, int nAddrID, const ComParam & param) = 0;
virtual void Move(MOVEMENT_DIR nDirection) = 0;
virtual void Action(int nActionID, int param) = 0;
virtual int SetSpeed(int nSpeedTrgID, int nSpeed) = 0;
virtual void DeviceSwitch(int nDeviceID, bool bSwitchOn) = 0;
virtual int SetPreset(int nIndex) = 0;
virtual int CallPreset(int nIndex) = 0;
virtual void Reset() = 0;
virtual int SelfCheck() = 0;
virtual int GetCaps(int nCapID, void * lParam) = 0;
virtual int ExtendOp(int nExOpID, void * lParam) = 0;
virtual void Destroy() = 0;
};
参考文档:
云台控制协议插件开发文档
本文档描述有关iWise-DVR云台控制协议插件的程序结构、设计原理、插件接口等重要信息,iWisePTZTest为插件的测试程序(插件也可以直接放到iWise-DVR环境中测试)。本文档以下出现的“DVR”均指“iWise-DVR”,本文档适用于使用串口的云台控制设备的基于DVR的云台控制协议插件的开发。
插件工作原理
云台控制有多种不同的控制协议,DVR提供统一的插件接口以支持不同的云台控制协议,通过协议插件来进行对云台的控制操作。DVR启动时会扫描Plug-Ins目录里面的所有文件名以“ptz”开头的动态连接库(DLL),并判断这些DLL是否为有效的云台控制协议插件。DVR定义了一套云台控制的基本操作,插件必须提供这些操作的具体实现,当用户要对云台进行控制时,DRV最终将调用插件的方法来控制云台,插件需要根据协议特征产生相应的指令数据并向串口设备发送这些指令。同时DVR提供向串口发送数据的统一的方法。
云台控制协议插件接口
DVR定义class CPTZInterface作为云台控制协议接口,该类是接口的核心,插件必须使用CPTZInterface派生一个类并实现所有的方法。CPTZInterface类详细定义如下:
class CPTZInterface
{
public:
virtual void SetPTZSettings(int nComPort, int nAddrID,
const ComParam & param) = 0;
virtual void Move(MOVEMENT_DIR nDirection) = 0;
virtual void Action(int nActionID, int param) = 0;
virtual int SetSpeed(int nSpeedTrgID, int nSpeed) = 0;
virtual void DeviceSwitch(int nDeviceID, bool bSwitchOn) = 0;
virtual int SetPreset(int nIndex) = 0;
virtual int CallPreset(int nIndex) = 0;
virtual void Reset() = 0;
virtual int SelfCheck() = 0;
virtual int GetCaps(int nCapID, void * lParam) = 0;
virtual int ExtendOp(int nExOpID, void * lParam) = 0;
virtual void Destroy() = 0;
};
SetPTZSettings函数设置云台设备所在的串口号、地址码及通信参数。
nComPort - 云台设备所在串口号
nAddrID - 设备地址码
param - 通信参数,ComParam结构的引用(请参考《iWise-DVR环境串口操作接口使用说明》)。
Move函数控制云台在各方向上的运动。
nDirection–运动方向,由MOVEMENT_DIR定义
enum MOVEMENT_DIR
{
MD_STOP, // 停止运动
MD_LEFT, // 向左
MD_RIGHT, // 向右
MD_UP, // 向上
MD_DOWN, // 向下
MD_LEFT_UP, // 左上
MD_LEFT_DOWN, // 左下
MD_RIGHT_UP, // 右上
MD_RIGHT_DOWN, // 右下
};
Action函数控制云台摄像机的动作。
nActionID–动作定义
ACTION_ZOOM 缩放,param = 0时为缩小 param = 1时为放大
ACTION_ZOOM_STOP 停止缩放
ACTION_FOCUS 聚焦,param = 0时为往近处聚焦 1为往远处
ACTION_FOCUS_STOP 停止聚焦
ACTION_AUTO_FOCUS 自动聚焦
ACTION_AUTO_FOCUS_STOP 停止自动聚焦
ACTION_AUTO_SCAN 自动扫描
ACTION_AUTO_SCAN_STOP 停止自动扫描
SetSpeed设置云台运动或摄像机动作的速度。
nSpeedTrgID–欲设置速度的目标
SPEED_TRG_PAN 水平方向运动
SPEED_TRG_TILT 垂直方向运动
ACTION_AUTO_SCAN 自动扫描
ACTION_AUTO_SCAN_STOP 停止自动扫描
nSpeed –速度值 (0x00-0x40)
如果成功,该函数返回设置后的速度,否则返回0。
DeviceSwitch函数提供打开、关闭某设备的功能。
nDeviceID–设备ID定义
DEVICE_CAMERA照相机
DEVICE_LIGHT灯光
DEVICE_RAINBRUSH 雨刷
DEVICE_IRIS 镜头光圈
bSwitchOn–true为打开设备,false为关闭设备
SetPreset函数设置预置点。
nIndex–预置点索引号,从1开始,使用GetCaps取得设备可以支持的预置点总数。
返回0为成功,否则失败
CallPreset函数调用预置点,将云台摄象机移动到指定预置点位置。
nIndex–预置点索引号,对使用SetPreset设置成功的索引号有效。
返回0为成功,否则失败
Reset函数使云台摄象机设备恢复到初始状态。
SelfCheck函数使云台摄象机设备自检。
返回0为成功,否则失败
GetCaps函数可以取得设备所能提供的能力。
nCapID–欲取得的能力的ID定义,请参考以下值
GC_BASAL 取得基本能力, lParam为指向PTZBasalCaps结构的指针。
lParam–指向特定能力结构的指针。
返回0为成功,否则失败
ExtendOp函数对CPTZInterface接口提供扩展功能。
nExOpID–扩展功能ID定义。
lParam–根据不同的nExOpID有不同的意义。
该函数目前未使用
Destroy函数负责销毁自身对象。
销毁方式必须和CreatePTZInstance的创建方式一致。
设备基本能力结构定义:
struct PTZBasalCaps
{
int nSize; // PTZBasalCaps结构的大小
int nPresetCount; // 支持预置点的个数
int nMinSpeed; // 云台移动速度最小值
int nMaxSpeed; // 云台移动速度最大值
bool bAutoScanSupported; // 是否支持自动扫描
}
插件DLL接口约定
插件DLL在实现了CPTZInterface的具体实现方法之后,必须提供一致的构建和销毁CPTZInterface对象的方法,因此必须导出如下函数:
• CPTZInterface * __declspec(dllexport) CreatePTZInstance(
const char * pszProtocolsName);
pszProtocolsName – 协议索名称,该名称由GetSupportProtocols函数取得。
该函数构造一个协议接口对象并返回其指针,该函数创建的对象必须由CPTZInterface::Destroy()函数自销毁。为安全起见,插件在卸载时也可以销毁使用者没有销毁的所有对象。
• const char ** __declspec(dllexport) GetSupportProtocols();
nProtocols – 支持的协议数目
该函数返回插件所支持的协议名称列表指针
GetSupportProtocols代码示例:
char * szProtocol[] = {
"PELCO-D",
"PELCO-P",
0 // 必须以0表示结束
};
const char ** __declspec(dllexport) GetSupportProtocols()
{
return (const char **)szProtocol;
}
以上代码表示该插件支持"PELCO-D"、"PELCO-P"两种协议。
class CPTZInterface声明在ptzInterface.h中。
有关串口指令的发送请参考《iWise-DVR环境串口操作接口使用说明》。