单位的项目需要测温,同事买了个海康威视的人体测温机芯,型号位:TB-4117-3/S,给了一份pdf的说明书。

按说明书把设备连接设置好,从官网下载了sdk,我的个乖乖,压缩包就有70多M,把他家的所有东西都给了我,有各种Demo,就是没有测温的,晕死,差点想打退堂鼓不玩了。

最后,最后得到如下成果:

一、所需的DLL

  1. HCCore.dll
  2. HCCoreDevCfg.dll 这玩意必须在运行目录
  3. HCGeneralCfgMgr.dll 这鬼也必须在运行目录
  4. HCNetSDK.dll
  5. libeay32.dll
  6. ssleay32.dll
    以上6个文件必须的,干啥用俺也不知道,反正缺一不可。

二、委托、dll封装、数据结果


        public delegate void LOGINRESULTCALLBACK(int lUserID, int dwResult, IntPtr lpDeviceInfo, IntPtr pUser);
        public delegate void UpdateTextStatusCallback(string strLogStatus, IntPtr lpDeviceInfo);
        public delegate void RemoteConfigCallback(uint dwType, IntPtr lpBuffer, uint dwBufLen, IntPtr pUserData);


        #region DllWrapper
        const string DllFileName = "HCNetSDK.dll";
        [DllImport(DllFileName)]
        public static extern bool NET_DVR_Init();
        [DllImport(DllFileName)]
        public static extern int NET_DVR_Login_V40(ref NET_DVR_USER_LOGIN_INFO pLoginInfo, ref NET_DVR_DEVICEINFO_V40 lpDeviceInfo);
        [DllImportAttribute(DllFileName)]
        public static extern int NET_DVR_StartRemoteConfig(int lUserID, int dwCommand, IntPtr lpInBuffer, Int32 dwInBufferLen, RemoteConfigCallback cbStateCallback, IntPtr pUserData);
        [DllImport(DllFileName)]
        public static extern bool NET_DVR_Logout(int iUserID);
        [DllImport(DllFileName)]
        public static extern bool NET_DVR_Cleanup();
        [DllImport(DllFileName)]
        public static extern uint NET_DVR_GetLastError();
        #endregion

        #region Data

        [StructLayout(LayoutKind.Sequential)]
        public struct NET_DVR_THERMOMETRY_UPLOAD
        {
            public uint dwSize;
            public uint dwRelativeTime;     // 相对时标
            public uint dwAbsTime;            // 绝对时标
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32, ArraySubType = UnmanagedType.I1)]
            public byte[] szRuleName;//规则名称
            public byte byRuleID;//规则ID号
            public byte byRuleCalibType;//规则标定类型 0-点,1-框,2-线
            public ushort wPresetNo; //预置点号
            [MarshalAs(UnmanagedType.Struct)]
            public NET_DVR_POINT_THERM_CFG struPointThermCfg;
            [MarshalAs(UnmanagedType.Struct)]

            public NET_DVR_LINEPOLYGON_THERM_CFG struLinePolygonThermCfg;
            public byte byThermometryUnit;//测温单位: 0-摄氏度(℃),1-华氏度(℉),2-开尔文(K)
            public byte byDataType;//数据状态类型:0-检测中,1-开始,2-结束
            public byte byRes1;
            /*
            bit0-中心点测温:0-不支持,1-支持;
            bit1-最高点测温:0-不支持,1-支持;
            bit2-最低点测温:0-不支持,1-支持;
            */
            public byte bySpecialPointThermType;// 是否支持特殊点测温
            public float fCenterPointTemperature;//中心点温度,精确到小数点后一位(-40-1500),(浮点数+100)*10 (由bySpecialPointThermType判断是否支持中心点)
            public float fHighestPointTemperature;//最高点温度,精确到小数点后一位(-40-1500),(浮点数+100)*10(由bySpecialPointThermType判断是否支持最高点)
            public float fLowestPointTemperature;//最低点温度,精确到小数点后一位(-40-1500),(浮点数+100)*10(由bySpecialPointThermType判断是否支持最低点)
            [MarshalAs(UnmanagedType.Struct)]
            public NET_VCA_POINT struHighestPoint;//线、框测温最高温度位置坐标(当规则标定类型为线、框的时候生效)
            [MarshalAs(UnmanagedType.Struct)]
            public NET_VCA_POINT struLowestPoint;//线、框测温最低温度位置坐标(当规则标定类型为线、框的时候生效)
            public byte byIsFreezedata;//是否数据冻结 0-否 1-是
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes2;
            public uint dwChan; //通道号,查询条件中通道号为0xffffffff时该字段生效
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 88, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct NET_DVR_POINT_THERM_CFG
        {
            public float fTemperature;
            public NET_VCA_POINT struPoint;
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 120, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes;

        }

        [StructLayout(LayoutKind.Sequential)]
        public struct NET_DVR_LINEPOLYGON_THERM_CFG
        {
            public float fMaxTemperature;
            public float fMinTemperature;
            public float fAverageTemperature;
            public float fTemperatureDiff;
            public NET_VCA_POLYGON struRegion;
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes;
        }
        public const int VCA_MAX_POLYGON_POINT_NUM = 10;//检测区域最多支持10个点的多边形

        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct NET_VCA_POLYGON
        {
            /// DWORD->unsigned int
            public uint dwPointNum;
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = VCA_MAX_POLYGON_POINT_NUM, ArraySubType = UnmanagedType.Struct)]
            public NET_VCA_POINT[] struPos;
        }
        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct NET_VCA_POINT
        {
            public float fX;// X轴坐标, 0.001~1
            public float fY;//Y轴坐标, 0.001~1
        }



        [StructLayout(LayoutKind.Sequential)]
        public struct NET_DVR_REALTIME_THERMOMETRY_COND
        {
            public uint dwSize;
            public uint dwChan;//通道号,从1开始,0xffffffff代表获取全部通道
            public byte byRuleID; //规则ID 0-代表获取全部规则,具体规则ID从1开始 
            /*
            1-定时模式:设备每隔一秒上传各个规则测温数据的最高温、最低温和平均温度值、温差
            2-温差模式:若上一秒与下一秒的最高温或者最低温或者平均温或者温差值的温差大于等于2摄氏度,则上传最高温、最低温和平均温度值。若大于等于一个小时温差值均小于2摄氏度,则上传最高温、最低温、平均温和温差值
            */
            public byte byMode; //长连接模式, 0-保留(为兼容老设备),1-定时模式,2-温差模式
            public ushort wInterval; //上传间隔,仅温差模式支持,1~3600S,填0则默认3600S上传一次
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 60, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes; //保留
        }
        public const int NET_DVR_DEV_ADDRESS_MAX_LEN = 129;
        public const int NET_DVR_LOGIN_USERNAME_MAX_LEN = 64;
        public const int NET_DVR_LOGIN_PASSWD_MAX_LEN = 64;

        [StructLayout(LayoutKind.Sequential)]
        public struct NET_DVR_USER_LOGIN_INFO
        {
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = NET_DVR_DEV_ADDRESS_MAX_LEN, ArraySubType = UnmanagedType.I1)]
            public byte[] sDeviceAddress;
            public byte byUseTransport;
            public ushort wPort;
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = NET_DVR_LOGIN_USERNAME_MAX_LEN, ArraySubType = UnmanagedType.I1)]
            public byte[] sUserName;
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = NET_DVR_LOGIN_PASSWD_MAX_LEN, ArraySubType = UnmanagedType.I1)]
            public byte[] sPassword;
            public LOGINRESULTCALLBACK cbLoginResult;
            public IntPtr pUser;
            public bool bUseAsynLogin;
            public byte byProxyType; //0:不使用代理,1:使用标准代理,2:使用EHome代理
            public byte byUseUTCTime;    //0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换
            public byte byLoginMode; //0-Private, 1-ISAPI, 2-自适应
            public byte byHttps;    //0-不适用tls,1-使用tls 2-自适应
            public int iProxyID;    //代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值
            public byte byVerifyMode;  //认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效;    
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 119, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes3;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct NET_DVR_DEVICEINFO_V40
        {
            public NET_DVR_DEVICEINFO_V30 struDeviceV30;
            public byte bySupportLock;        //设备支持锁定功能,该字段由SDK根据设备返回值来赋值的。bySupportLock为1时,dwSurplusLockTime和byRetryLoginTime有效
            public byte byRetryLoginTime;	    //剩余可尝试登陆的次数,用户名,密码错误时,此参数有效
            public byte byPasswordLevel;      //admin密码安全等级0-无效,1-默认密码,2-有效密码,3-风险较高的密码。当用户的密码为出厂默认密码(12345)或者风险较高的密码时,上层客户端需要提示用户更改密码。      
            public byte byProxyType;//代理类型,0-不使用代理, 1-使用socks5代理, 2-使用EHome代理
            public uint dwSurplusLockTime;	//剩余时间,单位秒,用户锁定时,此参数有效
            public byte byCharEncodeType;     //字符编码类型
            public byte bySupportDev5;//支持v50版本的设备参数获取,设备名称和设备类型名称长度扩展为64字节
            public byte bySupport;  //能力集扩展,位与结果:0- 不支持,1- 支持
            // bySupport & 0x1:  保留
            // bySupport & 0x2:  0-不支持变化上报 1-支持变化上报
            public byte byLoginMode; //登录模式 0-Private登录 1-ISAPI登录
            public int dwOEMCode;
            public int iResidualValidity;   //该用户密码剩余有效天数,单位:天,返回负值,表示密码已经超期使用,例如“-3表示密码已经超期使用3天”
            public byte byResidualValidity; // iResidualValidity字段是否有效,0-无效,1-有效
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 243, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes2;
        }

        public const int SERIALNO_LEN = 48;//序列号长度

        //NET_DVR_Login_V30()参数结构
        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct NET_DVR_DEVICEINFO_V30
        {
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = SERIALNO_LEN, ArraySubType = UnmanagedType.I1)]
            public byte[] sSerialNumber;  //序列号
            public byte byAlarmInPortNum;		        //报警输入个数
            public byte byAlarmOutPortNum;		        //报警输出个数
            public byte byDiskNum;				    //硬盘个数
            public byte byDVRType;				    //设备类型, 1:DVR 2:ATM DVR 3:DVS ......
            public byte byChanNum;				    //模拟通道个数
            public byte byStartChan;			        //起始通道号,例如DVS-1,DVR - 1
            public byte byAudioChanNum;                //语音通道数
            public byte byIPChanNum;					//最大数字通道个数,低位  
            public byte byZeroChanNum;			//零通道编码个数 //2010-01-16
            public byte byMainProto;			//主码流传输协议类型 0-private, 1-rtsp,2-同时支持private和rtsp
            public byte bySubProto;				//子码流传输协议类型0-private, 1-rtsp,2-同时支持private和rtsp
            public byte bySupport;        //能力,位与结果为0表示不支持,1表示支持,
                                          //bySupport & 0x1, 表示是否支持智能搜索
                                          //bySupport & 0x2, 表示是否支持备份
                                          //bySupport & 0x4, 表示是否支持压缩参数能力获取
                                          //bySupport & 0x8, 表示是否支持多网卡
                                          //bySupport & 0x10, 表示支持远程SADP
                                          //bySupport & 0x20, 表示支持Raid卡功能
                                          //bySupport & 0x40, 表示支持IPSAN 目录查找
                                          //bySupport & 0x80, 表示支持rtp over rtsp
            public byte bySupport1;        // 能力集扩充,位与结果为0表示不支持,1表示支持
                                           //bySupport1 & 0x1, 表示是否支持snmp v30
                                           //bySupport1 & 0x2, 支持区分回放和下载
                                           //bySupport1 & 0x4, 是否支持布防优先级	
                                           //bySupport1 & 0x8, 智能设备是否支持布防时间段扩展
                                           //bySupport1 & 0x10, 表示是否支持多磁盘数(超过33个)
                                           //bySupport1 & 0x20, 表示是否支持rtsp over http	
                                           //bySupport1 & 0x80, 表示是否支持车牌新报警信息2012-9-28, 且还表示是否支持NET_DVR_IPPARACFG_V40结构体
            public byte bySupport2; /*能力,位与结果为0表示不支持,非0表示支持							
							bySupport2 & 0x1, 表示解码器是否支持通过URL取流解码
							bySupport2 & 0x2,  表示支持FTPV40
							bySupport2 & 0x4,  表示支持ANR
							bySupport2 & 0x8,  表示支持CCD的通道参数配置
							bySupport2 & 0x10,  表示支持布防报警回传信息(仅支持抓拍机报警 新老报警结构)
							bySupport2 & 0x20,  表示是否支持单独获取设备状态子项
							bySupport2 & 0x40,  表示是否是码流加密设备*/
            public ushort wDevType;              //设备型号
            public byte bySupport3; //能力集扩展,位与结果为0表示不支持,1表示支持
                                    //bySupport3 & 0x1, 表示是否多码流
                                    // bySupport3 & 0x4 表示支持按组配置, 具体包含 通道图像参数、报警输入参数、IP报警输入、输出接入参数、
                                    // 用户参数、设备工作状态、JPEG抓图、定时和时间抓图、硬盘盘组管理 
                                    //bySupport3 & 0x8为1 表示支持使用TCP预览、UDP预览、多播预览中的"延时预览"字段来请求延时预览(后续都将使用这种方式请求延时预览)。而当bySupport3 & 0x8为0时,将使用 "私有延时预览"协议。
                                    //bySupport3 & 0x10 表示支持"获取报警主机主要状态(V40)"。
                                    //bySupport3 & 0x20 表示是否支持通过DDNS域名解析取流

            public byte byMultiStreamProto;//是否支持多码流,按位表示,0-不支持,1-支持,bit1-码流3,bit2-码流4,bit7-主码流,bit-8子码流
            public byte byStartDChan;		//起始数字通道号,0表示无效
            public byte byStartDTalkChan;	//起始数字对讲通道号,区别于模拟对讲通道号,0表示无效
            public byte byHighDChanNum;		//数字通道个数,高位
            public byte bySupport4;
            public byte byLanguageType;// 支持语种能力,按位表示,每一位0-不支持,1-支持  
                                       //  byLanguageType 等于0 表示 老设备
                                       //  byLanguageType & 0x1表示支持中文
                                       //  byLanguageType & 0x2表示支持英文
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 9, ArraySubType = UnmanagedType.I1)]
            public byte[] byRes2;		//保留
        }

        #endregion

三、测温

 const string IPAddress = "192.168.1.64";
        const string UserName = "admin";
        const string Password = "13245678";
        const ushort PortNo = 8000;
        static void Main(string[] args)
        {
            //初始化
            if (!NET_DVR_Init())
            {
                Console.WriteLine("NET_DVR_Init error!");
                return;
            }
            //登陆
            var struLogInfo = new NET_DVR_USER_LOGIN_INFO();

            //设备IP地址或者域名
            byte[] byIP = System.Text.Encoding.Default.GetBytes(IPAddress);
            struLogInfo.sDeviceAddress = new byte[129];
            byIP.CopyTo(struLogInfo.sDeviceAddress, 0);

            //设备用户名
            byte[] byUserName = System.Text.Encoding.Default.GetBytes(UserName);
            struLogInfo.sUserName = new byte[64];
            byUserName.CopyTo(struLogInfo.sUserName, 0);

            //设备密码
            byte[] byPassword = System.Text.Encoding.Default.GetBytes(Password);
            struLogInfo.sPassword = new byte[64];
            byPassword.CopyTo(struLogInfo.sPassword, 0);

            struLogInfo.wPort = PortNo;//设备服务端口号

            struLogInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是 

            var DeviceInfo = new NET_DVR_DEVICEINFO_V40();

            //登录设备 Login the device
            var m_lUserID = NET_DVR_Login_V40(ref struLogInfo, ref DeviceInfo);
            if (m_lUserID < 0)
            {
                Console.WriteLine("登陆失败,错误代码:" + NET_DVR_GetLastError()); //登录失败,输出错误号
                return;
            }


            //配置测温
            var size = Marshal.SizeOf(typeof(NET_DVR_REALTIME_THERMOMETRY_COND));

            NET_DVR_REALTIME_THERMOMETRY_COND struThermCond = new NET_DVR_REALTIME_THERMOMETRY_COND();
            struThermCond.dwSize = (uint)size;
            struThermCond.byRuleID = 0;       //规则ID,0代表获取全部规则,具体规则ID从1开始
            struThermCond.dwChan = 1;// dwChannel; //从1开始,0xffffffff代表获取全部通道

            IntPtr pCond = Marshal.AllocCoTaskMem(size);
            Marshal.StructureToPtr(struThermCond, pCond, false);
            var ret = NET_DVR_StartRemoteConfig(m_lUserID, 3629, pCond, size, Callback, IntPtr.Zero);
            if (ret < 0)
            {
                Console.WriteLine("配置测温失败, 错误代码:" + NET_DVR_GetLastError()); //登录失败,输出错误号
                return;
            }

            Console.WriteLine("开始测温...");
            Console.ReadKey();

            //退出登录
            NET_DVR_Logout(m_lUserID);

            NET_DVR_Cleanup();


        }
        /// <summary>
        /// 只显示最高温度
        /// </summary>
        /// <param name="dwType"></param>
        /// <param name="lpBuffer"></param>
        /// <param name="dwBufLen"></param>
        /// <param name="pUserData"></param>
        static void Callback(uint dwType, IntPtr lpBuffer, uint dwBufLen, IntPtr pUserData)
        {
            var ret = Marshal.PtrToStructure<NET_DVR_THERMOMETRY_UPLOAD>(lpBuffer);
            Console.WriteLine(ret.struLinePolygonThermCfg.fMaxTemperature);
        }

哎,就这么点事,弄得很复杂。