using MvCamCtrl.NET; using MvCameraControl; using MvCodeReaderSDKNet; using LB_VisionProcesses; using LB_VisionProcesses.Cameras; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Drawing.Imaging; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace LB_VisionProcesses.Cameras.HikCameras { public class HikCodeReader : BaseCamera { #region 变量 public MvCodeReader.MV_CODEREADER_RESULT_BCR_EX resultEx = new MvCodeReader.MV_CODEREADER_RESULT_BCR_EX(); public EventHandler TrigMonitor; public EventHandler CodeImageGrabbed; public MvCodeReader m_MyCamera = (MvCodeReader)null; public bool IsOpen = false; public ConcurrentQueue queueCode = new ConcurrentQueue(); byte[] m_BufForDriver = new byte[1024 * 1024 * 20]; private MvCodeReader.cbOutputEx2delegate m_cbImageOutput1; private bool m_hMonitorsThreadFlag1 = false; private Thread m_hMonitorsThread1 = (Thread)null; #endregion public HikCodeReader() : base() { Brand = CameraBrand.HikCodeReader; } public override bool AutoBalanceWhite() { throw new NotImplementedException(); } public override bool CloseDevice() { try { this.m_hMonitorsThreadFlag1 = false; if (this.m_MyCamera == null || !this.IsOpen && !this.isGrabbing) return true; if (!this.StopGrabbing()) return false; this.m_MyCamera.MV_CODEREADER_CloseDevice_NET(); this.m_MyCamera.MV_CODEREADER_DestroyHandle_NET(); this.SN = string.Empty; return true; } catch { return false; } } public override bool GetExpouseTime(out double value) { try { MvCodeReader.MV_CODEREADER_FLOATVALUE pFloatValue = default(MvCodeReader.MV_CODEREADER_FLOATVALUE); if (m_MyCamera.MV_CODEREADER_GetFloatValue_NET("ExposureTime", ref pFloatValue) != 0) { value = 0; return false; } value = Convert.ToDouble(pFloatValue.fCurValue); return true; } catch { value = 0; return false; } } public override bool GetGain(out double gain) { try { MvCodeReader.MV_CODEREADER_FLOATVALUE pFloatValue = default(MvCodeReader.MV_CODEREADER_FLOATVALUE); if (m_MyCamera.MV_CODEREADER_GetFloatValue_NET("Gain", ref pFloatValue) != 0) { gain = 0; return false; } gain = Convert.ToDouble(pFloatValue.fCurValue); return true; } catch { gain = 0; return false; } } public override bool GetLineStatus(IOLines line, out LineStatus lineStatus) { try { MvCodeReader.MV_CODEREADER_ENUMVALUE pEnumValue = default(MvCodeReader.MV_CODEREADER_ENUMVALUE); if (m_MyCamera.MV_CODEREADER_GetEnumValue_NET("LineSelector", ref pEnumValue) != 0) { lineStatus = LineStatus.Low; return false; } switch (pEnumValue.nCurValue) { default: lineStatus = LineStatus.Low; break; } return true; } catch { lineStatus = LineStatus.Low; return false; } } public override List GetListEnum() { try { MvCodeReader.MV_CODEREADER_DEVICE_INFO_LIST pstDevList = new MvCodeReader.MV_CODEREADER_DEVICE_INFO_LIST(); int nRet = MvCodeReader.MV_CODEREADER_EnumDevices_NET(ref pstDevList, MvCodeReader.MV_CODEREADER_GIGE_DEVICE); if (0 != nRet) return new List(); if (0 == pstDevList.nDeviceNum) return new List(); List list = new List(); string empty = string.Empty; // ch:在窗体列表中显示设备名 | en:Display stDevInfo name in the form list for (int index = 0; (long)index < (long)pstDevList.nDeviceNum; ++index) { MvCodeReader.MV_CODEREADER_DEVICE_INFO stDevInfo = (MvCodeReader.MV_CODEREADER_DEVICE_INFO)Marshal.PtrToStructure(pstDevList.pDeviceInfo[index], typeof(MvCodeReader.MV_CODEREADER_DEVICE_INFO)); if (stDevInfo.nTLayerType == MvCodeReader.MV_CODEREADER_GIGE_DEVICE) { IntPtr buffer = Marshal.UnsafeAddrOfPinnedArrayElement(stDevInfo.SpecialInfo.stGigEInfo, 0); MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO stGigEDeviceInfo = (MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO)); //if (stGigEDeviceInfo.chUserDefinedName != "") //{ // cbDeviceList.Items.Add("GEV: " + stGigEDeviceInfo.chUserDefinedName + " (" + stGigEDeviceInfo.chSerialNumber + ")"); //} //else //{ // cbDeviceList.Items.Add("GEV: " + stGigEDeviceInfo.chManufacturerName + " " + stGigEDeviceInfo.chModelName + " (" + stGigEDeviceInfo.chSerialNumber + ")"); //} if (!list.Contains(stGigEDeviceInfo.chSerialNumber)) list.Add(stGigEDeviceInfo.chSerialNumber); } } return list; } catch { return new List(); } } public override bool GetTriggerDelay(out double delay) { try { MvCodeReader.MV_CODEREADER_FLOATVALUE pFloatValue = default(MvCodeReader.MV_CODEREADER_FLOATVALUE); if (m_MyCamera.MV_CODEREADER_GetFloatValue_NET("TriggerDelay", ref pFloatValue) != 0) { delay = 0; return false; } delay = Convert.ToDouble(pFloatValue.fCurValue); return true; } catch { delay = 0; return false; } } public override bool GetTriggerFliter(out double flitertime) { try { MvCodeReader.MV_CODEREADER_FLOATVALUE pFloatValue = default(MvCodeReader.MV_CODEREADER_FLOATVALUE); if (m_MyCamera.MV_CODEREADER_GetFloatValue_NET("LineDebouncerTime", ref pFloatValue) != 0) { flitertime = 0; return false; } flitertime = Convert.ToDouble(pFloatValue.fCurValue); return true; } catch { flitertime = 0; return false; } } public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source) { try { MvCodeReader.MV_CODEREADER_ENUMVALUE pEnumValue = default(MvCodeReader.MV_CODEREADER_ENUMVALUE); if (m_MyCamera.MV_CODEREADER_GetEnumValue_NET("TriggerMode", ref pEnumValue) != 0) { mode = TriggerMode.Off; source = TriggerSource.Software; return false; } switch (pEnumValue.nCurValue) { case 0: mode = TriggerMode.Off; source = TriggerSource.Software; break; default: mode = TriggerMode.On; source = TriggerSource.Line3; break; } return true; } catch { mode = TriggerMode.Off; source = TriggerSource.Software; return false; } } public override bool GetTriggerPolarity(out TriggerPolarity polarity) { try { MvCodeReader.MV_CODEREADER_ENUMVALUE pEnumValue = default(MvCodeReader.MV_CODEREADER_ENUMVALUE); if (m_MyCamera.MV_CODEREADER_GetEnumValue_NET("LineOutHardTriggerActive", ref pEnumValue) != 0) { polarity = TriggerPolarity.RisingEdge; return false; } polarity = (TriggerPolarity)pEnumValue.nCurValue; return true; } catch { polarity = TriggerPolarity.RisingEdge; return false; } } public override bool InitDevice(string SN, object Handle = null) { try { m_MyCamera = new MvCodeReader(); IsOpen = false; MvCodeReader.MV_CODEREADER_DEVICE_INFO_LIST pstDevList = new MvCodeReader.MV_CODEREADER_DEVICE_INFO_LIST(); int nRet = MvCodeReader.MV_CODEREADER_EnumDevices_NET(ref pstDevList, MvCodeReader.MV_CODEREADER_GIGE_DEVICE); if (0 != nRet) return false; if (0 == pstDevList.nDeviceNum) return false; string empty = string.Empty; for (int index = 0; (long)index < (long)pstDevList.nDeviceNum; ++index) { // ch:获取选择的设备信息 | en:Get selected stDevInfo information MvCodeReader.MV_CODEREADER_DEVICE_INFO stDevInfo = (MvCodeReader.MV_CODEREADER_DEVICE_INFO)Marshal.PtrToStructure(pstDevList.pDeviceInfo[index], typeof(MvCodeReader.MV_CODEREADER_DEVICE_INFO)); if (stDevInfo.nTLayerType == MvCodeReader.MV_CODEREADER_GIGE_DEVICE) { IntPtr buffer = Marshal.UnsafeAddrOfPinnedArrayElement(stDevInfo.SpecialInfo.stGigEInfo, 0); MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO stGigEDeviceInfo = (MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO)); if (stGigEDeviceInfo.chSerialNumber == SN) { nRet = m_MyCamera.MV_CODEREADER_CreateHandle_NET(ref stDevInfo); if (MvCodeReader.MV_CODEREADER_OK != nRet) return false; nRet = m_MyCamera.MV_CODEREADER_OpenDevice_NET(); if (MvCodeReader.MV_CODEREADER_OK != nRet) { m_MyCamera.MV_CODEREADER_DestroyHandle_NET(); return false; } try { MvCodeReader.MV_CODEREADER_ENUMVALUE pEnumValue = default(MvCodeReader.MV_CODEREADER_ENUMVALUE); if (m_MyCamera.MV_CODEREADER_GetEnumValue_NET("RunningMode", ref pEnumValue) != 0) { m_MyCamera.MV_CODEREADER_DestroyHandle_NET(); return false; } switch (pEnumValue.nCurValue) { case 0: //工作模式:读码成功才会触发回调 nRet = m_MyCamera.MV_CODEREADER_SetEnumValue_NET("RunningMode", (uint)pEnumValue.nCurValue); if (MvCodeReader.MV_CODEREADER_OK != nRet) { m_MyCamera.MV_CODEREADER_DestroyHandle_NET(); return false; } break; case 2: //测试模式:取图完成就会触发回调 nRet = m_MyCamera.MV_CODEREADER_SetEnumValue_NET("RunningMode", (uint)pEnumValue.nCurValue); if (MvCodeReader.MV_CODEREADER_OK != nRet) { m_MyCamera.MV_CODEREADER_DestroyHandle_NET(); return false; } break; default: break; } } catch { } m_cbImageOutput1 = new MvCodeReader.cbOutputEx2delegate(ImageGrabbedCallBack); m_MyCamera.MV_CODEREADER_RegisterImageCallBackEx2_NET(m_cbImageOutput1, (IntPtr)0); IsOpen = true; this.SN = SN; Brand = CameraBrand.HikCodeReader; return true; } } } return false; } catch { IsOpen = false; return false; } } private void ImageGrabbedCallBack(IntPtr pData, IntPtr pstFrameInfoEx2, IntPtr pUser) { try { if (null == pstFrameInfoEx2 || null == pData) { return; } #region 屏蔽 //MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2 stFrameInfoEx2 // = (MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2)Marshal.PtrToStructure(pstFrameInfoEx2, typeof(MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2)); //if (0 >= stFrameInfoEx2.nFrameLen) //{ // return; //} //Marshal.Copy(pData, m_BufForDriver, 0, (int)stFrameInfoEx2.nFrameLen); //if (stFrameInfoEx2.enPixelType == MvCodeReader.MvCodeReaderGvspPixelType.PixelType_CodeReader_Gvsp_Mono8) //{ // IntPtr pImage = Marshal.UnsafeAddrOfPinnedArrayElement(m_BufForDriver, 0); // Bitmap bitmap = new Bitmap(stFrameInfoEx2.nWidth, stFrameInfoEx2.nHeight, stFrameInfoEx2.nWidth, PixelFormat.Format8bppIndexed, pImage); // ColorPalette cp = bitmap.Palette; // for (int i = 0; i < 256; i++) // { // cp.Entries[i] = Color.FromArgb(i, i, i); // } // bitmap.Palette = cp; // if (this.CodeImageGrabbed != null) // this.CodeImageGrabbed(this, new HikCodeReaderEventArgs(this.SN, bitmap, HikCodeReaderEventArgs.PixelType.Mono8)); //} //else if (stFrameInfoEx2.enPixelType == MvCodeReader.MvCodeReaderGvspPixelType.PixelType_CodeReader_Gvsp_Jpeg) //{ // //GC.Collect(); // using (MemoryStream ms = new MemoryStream()) // { // ms.Write(m_BufForDriver, 0, (int)stFrameInfoEx2.nFrameLen); // Image image = Image.FromStream(ms); // if (stFrameInfoEx2.bIsGetCode) // { // resultEx = (MvCodeReader.MV_CODEREADER_RESULT_BCR_EX)Marshal.PtrToStructure(stFrameInfoEx2.pstCodeListEx, typeof(MvCodeReader.MV_CODEREADER_RESULT_BCR_EX)); // for (int i = 0; i < resultEx.nCodeNum; i++) // { // string strchCode = Encoding.Default.GetString(resultEx.stBcrInfoEx[i].chCode).TrimEnd(default(char)); // if (!queueCode.Contains(strchCode) && !string.IsNullOrEmpty(strchCode)) // { // queueCode.Enqueue(strchCode); // Debug.WriteLine(SN + "读取到码:" + strchCode); // } // if (queueCode.Count > 25) // queueCode.TryDequeue(out string sn); // } // } // if (this.CodeImageGrabbed != null && image != null) // this.CodeImageGrabbed(this, new HikCodeReaderEventArgs(this.SN, image, HikCodeReaderEventArgs.PixelType.Jpeg, resultEx, (ulong)stFrameInfoEx2.nWidth, (ulong)stFrameInfoEx2.nHeight)); // } //} #endregion MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2 stFrameInfoEx2 = (MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2)Marshal.PtrToStructure(pstFrameInfoEx2, typeof(MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2)); // 绘制图像 Marshal.Copy(pData, m_BufForDriver, 0, (int)stFrameInfoEx2.nFrameLen); if (stFrameInfoEx2.enPixelType == MvCodeReader.MvCodeReaderGvspPixelType.PixelType_CodeReader_Gvsp_Mono8) { IntPtr pImage = Marshal.UnsafeAddrOfPinnedArrayElement(m_BufForDriver, 0); Bitmap bmp = new Bitmap(stFrameInfoEx2.nWidth, stFrameInfoEx2.nHeight, stFrameInfoEx2.nWidth, PixelFormat.Format8bppIndexed, pImage); ColorPalette cp = bmp.Palette; for (int i = 0; i < 256; i++) { cp.Entries[i] = Color.FromArgb(i, i, i); } bmp.Palette = cp; if (this.CodeImageGrabbed != null) this.CodeImageGrabbed(this, new HikCodeReaderEventArgs(this.SN, bmp, HikCodeReaderEventArgs.PixelType.Mono8)); } else if (stFrameInfoEx2.enPixelType == MvCodeReader.MvCodeReaderGvspPixelType.PixelType_CodeReader_Gvsp_Jpeg) { GC.Collect(); MemoryStream ms = new MemoryStream(); ms.Write(m_BufForDriver, 0, (int)stFrameInfoEx2.nFrameLen); resultEx = (MvCodeReader.MV_CODEREADER_RESULT_BCR_EX)Marshal.PtrToStructure(stFrameInfoEx2.UnparsedBcrList.pstCodeListEx2, typeof(MvCodeReader.MV_CODEREADER_RESULT_BCR_EX)); //for (int i = 0; i < resultEx.nCodeNum; i++) //{ // string strCode = System.Text.Encoding.Default.GetString(resultEx.stBcrInfoEx[i].chCode).TrimEnd(default(char)); // if (!string.IsNullOrEmpty(strCode)) // Debug.WriteLine("识别到码:" + strCode); //} if (this.CodeImageGrabbed != null) this.CodeImageGrabbed(this, new HikCodeReaderEventArgs(this.SN, Image.FromStream(ms), HikCodeReaderEventArgs.PixelType.Jpeg, resultEx, stFrameInfoEx2.nWidth, stFrameInfoEx2.nHeight)); } try { if (GetTriggerMode(out TriggerMode mode, out TriggerSource source)) { if (mode == TriggerMode.On && source != TriggerSource.Software) TriggerRunMessageReceived?.Invoke(SN, source.ToString()); // 触发运行事件 } } catch { } } catch { } } public override bool SetExpouseTime(double value) { int num = this.m_MyCamera.MV_CODEREADER_SetFloatValue_NET("ExposureTime", Convert.ToSingle(value)); if (num != 0) return false; return true; } public override bool SetGain(double gain) { int num = this.m_MyCamera.MV_CODEREADER_SetFloatValue_NET("Gain", Convert.ToSingle(gain)); if (num != 0) return false; return true; } public override bool SetLineMode(IOLines line = IOLines.Line0, LineMode mode = LineMode.Output) { int num = this.m_MyCamera.MV_CODEREADER_SetFloatValue_NET("LineSelector", (uint)line); if (num != 0) return false; return true; } public override bool SetLineStatus(IOLines line, LineStatus linestatus) { return true; //throw new NotImplementedException(); } public override bool SetTriggerDelay(double delay) { int num = this.m_MyCamera.MV_CODEREADER_SetFloatValue_NET("TriggerDelay", Convert.ToSingle(delay)); if (num != 0) return false; return true; } public override bool SetTriggerFliter(double flitertime) { int num = this.m_MyCamera.MV_CODEREADER_SetFloatValue_NET("LineDebouncerTime", Convert.ToSingle(flitertime)); if (num != 0) return false; return true; } public override bool SetTriggerMode(TriggerMode mode, TriggerSource triggerEnum = TriggerSource.None) { int num = this.m_MyCamera.MV_CODEREADER_SetEnumValue_NET("TriggerMode", (uint)mode); if (num != 0) return false; switch (triggerEnum) { // // 摘要: // 软触发 //MV_CODEREADER_TRIGGER_SOURCE_SOFTWARE = 7, case TriggerSource.Software: num = this.m_MyCamera.MV_CODEREADER_SetEnumValue_NET("TriggerSource", 7); break; case TriggerSource.Line0: num = this.m_MyCamera.MV_CODEREADER_SetEnumValue_NET("TriggerSource", 0); break; case TriggerSource.Line1: num = this.m_MyCamera.MV_CODEREADER_SetEnumValue_NET("TriggerSource", 1); break; case TriggerSource.Line2: num = this.m_MyCamera.MV_CODEREADER_SetEnumValue_NET("TriggerSource", 2); break; case TriggerSource.Line3: num = this.m_MyCamera.MV_CODEREADER_SetEnumValue_NET("TriggerSource", 3); break; default: break; } if (num != 0) return false; return true; } public override bool SetTriggerPolarity(TriggerPolarity polarity) { int num = this.m_MyCamera.MV_CODEREADER_SetEnumValue_NET("LineOutHardTriggerActive", (uint)polarity); if (num != 0) return false; return true; } public override bool SoftTrigger() { return false; } public override bool StartGrabbing() { if (this.m_MyCamera == null) return false; if (!this.IsOpen) return false; try { if (this.m_MyCamera.MV_CODEREADER_StartGrabbing_NET() != 0) { this.isGrabbing = false; return false; } this.isGrabbing = true; return true; } catch { this.isGrabbing = false; return false; } } public override bool StopGrabbing() { if (this.m_MyCamera == null) return true; if (!this.isGrabbing) return true; try { if (this.m_MyCamera.MV_CODEREADER_StopGrabbing_NET() != 0) return false; else { this.isGrabbing = false; this.m_hMonitorsThreadFlag1 = false; return true; } } catch { this.isGrabbing = false; return false; } } public override bool GetImage(out Bitmap bitmap, int outtime = 3000) { bitmap = null; try { // 设置超时时间 DateTime lastTime = DateTime.Now.AddMilliseconds(outtime); // 判断是否超时 while (lastTime > DateTime.Now)// 设置超时时间为 3 秒 { try { if (CallBackImg != null) { // 保存旧 Bitmap 并释放 bitmap = CallBackImg.Clone() as Bitmap; // 创建副本 // 释放旧资源 CallBackImg.Dispose(); CallBackImg = null; return true; } } catch { } Thread.Sleep(10); } return false; } catch { return bitmap == null ? false : true; } } public override bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000) { if (!isGrabbing) { StartGrabbing(); } GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource); if (triggerMode != TriggerMode.On && triggerSource != TriggerSource.Software) { SetTriggerMode(TriggerMode.On, TriggerSource.Software); } bitmap = null; CallBackImg = null; if (!SoftTrigger()) { return false; } // 开始时间 DateTime startTime = DateTime.Now; // 当前时间 // 判断是否超时 while (DateTime.Now < startTime.AddMilliseconds(outtime))// 设置超时时间为 3 秒 { GetImage(out bitmap, 50); if (bitmap != null) { break; } Thread.Sleep(10); } if (triggerMode != TriggerMode.On) { SetTriggerMode(TriggerMode.On, triggerSource); } return (bitmap != null); } public override void SetCamConfig(CameraConfig config) { if (Enum.TryParse(config.Params.Inputs["触发模式"].ToString(), out TriggerMode TriggerMode) && Enum.TryParse(config.Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource) && Enum.TryParse(config.Params.Inputs["触发极性"].ToString(), out TriggerPolarity TriggerPolarity) ) { SetTriggerMode(TriggerMode, TriggerSource); SetTriggerPolarity(TriggerPolarity); //SetTriggerFliter(Convert.ToDouble(config.Params.Inputs["触发消抖"].ToString())); //SetTriggerDelay(Convert.ToDouble(config.Params.Inputs["触发延时"].ToString())); SetExpouseTime(Convert.ToDouble(config.Params.Inputs["曝光时间"].ToString())); SetGain(Convert.ToDouble(config.Params.Inputs["增益"].ToString())); } } public override void GetCamConfig(out CameraConfig config) { GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource); GetTriggerPolarity(out TriggerPolarity triggerPolarity); //GetTriggerFliter(out double triggerfilter); //GetTriggerDelay(out double triggerdelay); GetExpouseTime(out double expouseTime); GetGain(out double gain); config = new CameraConfig(null); config.Params.Inputs.Add("触发模式", triggerMode); config.Params.Inputs.Add("触发方式", triggerSource); config.Params.Inputs.Add("触发极性", triggerPolarity); //config.Params.Inputs.Add("触发消抖", triggerfilter); //config.Params.Inputs.Add("触发延时", triggerdelay); config.Params.Inputs.Add("曝光时间", expouseTime); config.Params.Inputs.Add("增益", gain); } public override bool StartWith_SoftTriggerModel() { SetTriggerMode(TriggerMode.Off, TriggerSource.Software); return StartGrabbing(); } public override bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0) { if (hardtriggeritem == TriggerSource.Software) hardtriggeritem = TriggerSource.Line0; SetTriggerMode(TriggerMode.On, hardtriggeritem); return StartGrabbing(); } public override bool StartContinuousGrab() { throw new NotImplementedException(); } } }