前言:今年暑假的时候导师接到的一个活,一家制造企业再将产品存库过程中需要人工扫描运输皮带上产品的二维码,二维码信息中前7位数字决定了这个产品是否是这一批的,老板决定做的智能化一些,我们在查阅了资料后,决定使用海康工业读码器来实现,我的任务就是修改代码,做出控制界面,但现在简简单单有了眉目,记录一下。
一、项目实现目的
实现读码器扫码后存入数据库,同时需要控制电机启停和蜂鸣器。
二、实现过程
1、购买了海康读码器
我的读码器购买型号:【反光型】U口-130万像素红光MV-ID2013EM-05-RBP-U
我们项目中,有2条运输皮带,每个运输皮带上计划是2到3个读码器。
2、下载了idmvs客户端,此客户端为官方的
在这里可以选择链接读码器,设置读码器属性等操作,详细可以在官网找到该软件操作文档。
但我们需要实时的存入数据库,并根据二维码内容来判断是否启停电机和开启报警灯,这个官方软件不太合适。
3、发现玄机
后经死皮赖脸询问客服,客服回答在IDMVS\Development\Modules路径下,有SDK文件,可以根据该文件编程来实现我需要的功能。这个文件打开长这样:
这我哪里能看的懂?(实际经过一段时间后看懂了)
随后,我在这个文件最下面找到了官方给的示例代码。
也就是下面的代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MvCodeReaderSDKNet;
using System.Runtime.InteropServices;
using System.IO;
namespace GrabImage
{
class GrabImage
{
// 判断字符编码
public static bool IsTextUTF8(byte[] inputStream)
{
int encodingBytesCount = 0;
bool allTextsAreASCIIChars = true;
for (int i = 0; i < inputStream.Length; i++)
{
byte current = inputStream[i];
if ((current & 0x80) == 0x80)
{
allTextsAreASCIIChars = false;
}
// First byte
if (encodingBytesCount == 0)
{
if ((current & 0x80) == 0)
{
// ASCII chars, from 0x00-0x7F
continue;
}
if ((current & 0xC0) == 0xC0)
{
encodingBytesCount = 1;
current <<= 2;
// More than two bytes used to encoding a unicode char.
// Calculate the real length.
while ((current & 0x80) == 0x80)
{
current <<= 1;
encodingBytesCount++;
}
}
else
{
// Invalid bits structure for UTF8 encoding rule.
return false;
}
}
else
{
// Following bytes, must start with 10.
if ((current & 0xC0) == 0x80)
{
encodingBytesCount--;
}
else
{
// Invalid bits structure for UTF8 encoding rule.
return false;
}
}
}
if (encodingBytesCount != 0)
{
// Invalid bits structure for UTF8 encoding rule.
// Wrong following bytes count.
return false;
}
// Although UTF8 supports encoding for ASCII chars, we regard as a input stream, whose contents are all ASCII as default encoding.
return !allTextsAreASCIIChars;
}
static void Main(string[] args)
{
int nRet = MvCodeReader.MV_CODEREADER_OK;
MvCodeReader device = new MvCodeReader();
do
{
// 枚举设备
MvCodeReader.MV_CODEREADER_DEVICE_INFO_LIST stDevList = new MvCodeReader.MV_CODEREADER_DEVICE_INFO_LIST();
nRet = MvCodeReader.MV_CODEREADER_EnumDevices_NET(ref stDevList, MvCodeReader.MV_CODEREADER_GIGE_DEVICE);
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Enum device failed:{0:x8}", nRet);
break;
}
Console.WriteLine("Enum device count : " + Convert.ToString(stDevList.nDeviceNum));
if (0 == stDevList.nDeviceNum)
{
break;
}
MvCodeReader.MV_CODEREADER_DEVICE_INFO stDevInfo; // 通用设备信息
// 打印设备信息
for (Int32 i = 0; i < stDevList.nDeviceNum; i++)
{
stDevInfo = (MvCodeReader.MV_CODEREADER_DEVICE_INFO)Marshal.PtrToStructure(stDevList.pDeviceInfo[i], typeof(MvCodeReader.MV_CODEREADER_DEVICE_INFO));
if (MvCodeReader.MV_CODEREADER_GIGE_DEVICE == stDevInfo.nTLayerType)
{
MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO stGigEDeviceInfo = (MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO)MvCodeReader.ByteToStruct(stDevInfo.SpecialInfo.stGigEInfo, typeof(MvCodeReader.MV_CODEREADER_GIGE_DEVICE_INFO));
uint nIp1 = ((stGigEDeviceInfo.nCurrentIp & 0xff000000) >> 24);
uint nIp2 = ((stGigEDeviceInfo.nCurrentIp & 0x00ff0000) >> 16);
uint nIp3 = ((stGigEDeviceInfo.nCurrentIp & 0x0000ff00) >> 8);
uint nIp4 = (stGigEDeviceInfo.nCurrentIp & 0x000000ff);
Console.WriteLine("\n" + i.ToString() + ": [GigE] User Define Name : " + stGigEDeviceInfo.chUserDefinedName);
Console.WriteLine("device IP :" + nIp1 + "." + nIp2 + "." + nIp3 + "." + nIp4);
}
else if (MvCodeReader.MV_CODEREADER_USB_DEVICE == stDevInfo.nTLayerType)
{
MvCodeReader.MV_CODEREADER_USB3_DEVICE_INFO stUsb3DeviceInfo = (MvCodeReader.MV_CODEREADER_USB3_DEVICE_INFO)MvCodeReader.ByteToStruct(stDevInfo.SpecialInfo.stUsb3VInfo, typeof(MvCodeReader.MV_CODEREADER_USB3_DEVICE_INFO));
Console.WriteLine("\n" + i.ToString() + ": [U3V] User Define Name : " + stUsb3DeviceInfo.chUserDefinedName);
Console.WriteLine("\n Serial Number : " + stUsb3DeviceInfo.chSerialNumber);
Console.WriteLine("\n Device Number : " + stUsb3DeviceInfo.nDeviceNumber);
}
}
Int32 nDevIndex = 0;
Console.Write("\nPlease input index (0 -- {0:d}) : ", stDevList.nDeviceNum - 1);
try
{
nDevIndex = Convert.ToInt32(Console.ReadLine());
}
catch
{
Console.Write("Invalid Input!\n");
break;
}
if (nDevIndex > stDevList.nDeviceNum - 1 || nDevIndex < 0)
{
Console.Write("Input Error!\n");
break;
}
stDevInfo = (MvCodeReader.MV_CODEREADER_DEVICE_INFO)Marshal.PtrToStructure(stDevList.pDeviceInfo[nDevIndex], typeof(MvCodeReader.MV_CODEREADER_DEVICE_INFO));
// 创建设备
nRet = device.MV_CODEREADER_CreateHandle_NET(ref stDevInfo);
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Create device failed:{0:x8}", nRet);
break;
}
// 打开设备
nRet = device.MV_CODEREADER_OpenDevice_NET();
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Open device failed:{0:x8}", nRet);
break;
}
// 设置触发模式为off
if (MvCodeReader.MV_CODEREADER_OK != device.MV_CODEREADER_SetEnumValue_NET("TriggerMode", 0))
{
Console.WriteLine("Set TriggerMode failed!");
break;
}
// 开启抓图
nRet = device.MV_CODEREADER_StartGrabbing_NET();
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Start grabbing failed:{0:x8}", nRet);
break;
}
int nCount = 0;
IntPtr pBufForDriver = IntPtr.Zero;
MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2 stFrameInfo = new MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2();
IntPtr pstFrameInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2)));
Marshal.StructureToPtr(stFrameInfo, pstFrameInfo, false);
while (nCount++ != 10)
{
nRet = device.MV_CODEREADER_GetOneFrameTimeoutEx2_NET(ref pBufForDriver, pstFrameInfo, 1000);
// 获取一帧图像
if (MvCodeReader.MV_CODEREADER_OK == nRet)
{
stFrameInfo = (MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2)Marshal.PtrToStructure(pstFrameInfo, typeof(MvCodeReader.MV_CODEREADER_IMAGE_OUT_INFO_EX2));
Console.WriteLine("Get One Frame:" + "nChannelID[" + Convert.ToString(stFrameInfo.nChannelID) + "] , Width[" + Convert.ToString(stFrameInfo.nWidth) + "], Height[" + Convert.ToString(stFrameInfo.nHeight)
+ "] , FrameNum[" + Convert.ToString(stFrameInfo.nFrameNum) + "]");
// 分配条码内存空间
MvCodeReader.MV_CODEREADER_RESULT_BCR_EX2 stBcrResult = (MvCodeReader.MV_CODEREADER_RESULT_BCR_EX2)Marshal.PtrToStructure(stFrameInfo.pstCodeListEx2, typeof(MvCodeReader.MV_CODEREADER_RESULT_BCR_EX2));
Console.WriteLine("Get CodeNum:" + "CodeNum[" + Convert.ToString(stBcrResult.nCodeNum) + "]");
for (int i = 0; i < stBcrResult.nCodeNum; ++i)
{
bool bIsValidUTF8 = IsTextUTF8(stBcrResult.stBcrInfoEx2[i].chCode);
if (bIsValidUTF8)
{
string strCode = Encoding.UTF8.GetString(stBcrResult.stBcrInfoEx2[i].chCode);
Console.WriteLine("Get CodeNum: " + "CodeNum[" + i.ToString() + "], CodeString[" + strCode.Trim().TrimEnd('\0') + "]");
}
else
{
string strCode = Encoding.GetEncoding("GB2312").GetString(stBcrResult.stBcrInfoEx2[i].chCode);
Console.WriteLine("Get CodeNum: " + "CodeNum[" + i.ToString() + "], CodeString[" + strCode.Trim().TrimEnd('\0') + "]");
}
}
continue;
}
else
{
Console.WriteLine("No data:{0:x8}", nRet);
}
}
// 停止抓图
nRet = device.MV_CODEREADER_StopGrabbing_NET();
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Stop grabbing failed{0:x8}", nRet);
break;
}
// 关闭设备
nRet = device.MV_CODEREADER_CloseDevice_NET();
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Close device failed{0:x8}", nRet);
break;
}
// 销毁设备
nRet = device.MV_CODEREADER_DestroyHandle_NET();
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Destroy device failed:{0:x8}", nRet);
break;
}
} while (false);
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
// 销毁设备
nRet = device.MV_CODEREADER_DestroyHandle_NET();
if (MvCodeReader.MV_CODEREADER_OK != nRet)
{
Console.WriteLine("Destroy device failed:{0:x8}", nRet);
}
}
Console.WriteLine("Press enter to exit");
Console.ReadKey();
}
}
}
OK了
4、悟道
全部看懂之后,就可以随心所欲的进行修改了,我设置了上位机界面如下
运行时,界面发生变化如下:
成功识别到二维码信息,打算左边2个读码器,右边2个读码器,并且可以对二维码信息进行比对,
这是存入数据库:
可以实时的存入 数据库。
这是上位机通过s7.net控制plc进而控制电机和报警灯的电路
到此项目完成。
三、总结
在做这个项目过程中,最难受的时候就是拿着读码器,看着IDMVS客户端界面手足无措的时候,无从下手的感觉最要命,当然那时候我也知道可以从头写个程序来实现功能,但我哪里会啊,我只可以在原来的基础上删减修改,从0做起难如登天。还好最后找到了示例代码,并自己慢慢改成功了。