#include "takephoto.h" // 模块名称 static const char MODULE_NAME[] = "TakePhoto"; // 保存照片 static int SavePhoto(HANDLE hCam, MV_FRAME_OUT *pFrame, EImgType imgType, const char *imgFile); // 拍照回调 typedef struct { HANDLE hCam; EImgType saveImgType; const char *saveImgFilename; HANDLE hESig; // 任务结束的通知 int rCode; // 任务结束返回值 } PthotoProcCtx; static int PhotoProc(unsigned long wParam, unsigned long lParam) // 线程回调函数,执行一次拍照任务 { PthotoProcCtx *ctx = (PthotoProcCtx *)wParam; MV_FRAME_OUT frame = { 0 }; // 1, 触发一次拍照 int ret = MV_CC_SetCommandValue(ctx->hCam, "TriggerSoftware"); // 2, 等待获取图像 if(MV_OK == ret) { memset(&frame, 0, sizeof(MV_FRAME_OUT)); ret = MV_CC_GetImageBuffer(ctx->hCam, &frame, 1000); } // 3, 导出图像文件 if(MV_OK == ret) { sw_log_debug("[%s] +++GetOneFrame+++, Width[%d], Height[%d], FrameNum[%d], FrameLen[%d], PixelType[0x%08X]", MODULE_NAME, \ frame.stFrameInfo.nWidth, frame.stFrameInfo.nHeight, frame.stFrameInfo.nFrameNum, frame.stFrameInfo.nFrameLen, \ (unsigned int)frame.stFrameInfo.enPixelType); ret = SavePhoto(ctx->hCam, &frame, ctx->saveImgType, ctx->saveImgFilename); MV_CC_FreeImageBuffer(ctx->hCam, &frame); } // 4, 控制线程退出 switch(ret) { case MV_E_NODATA: // 无数据时, 线程继续运行 ret = 1; break; default: // 成功或发生其他错误,线程结束运行 ctx->rCode = ret; sw_signal_give(ctx->hESig); ret = -1; break; } return ret; } // 单次执行相机拍照, 并保存到文件, 成功返回: 0值, 失败返回:非0值 // "imgType" - 获取图像类型 // "saveImgFilename" - 保存的文件名 // "timeout" - 等待超时时间, 单位:秒: < 0 表示无超时; // 无论超时怎么设置, 首次尝试拍照一定会执行 // , 但时间不确定; 拍照成功、超时或发生错误 // 时会自动结束任务(相机持续无数据的时间). // "pImgMark" - 输出本次拍照图像的水印信息, 可以设置NULL int TakePhoto(EImgType imgType, const char *saveImgFilename, int timeout, SImgMark *pImgMark) { int fd; char runDir[MAX_PATH_CHARS] = { 0 }, lockFile[MAX_PATH_CHARS+32] = { 0 }; int ret; SImgMark imgMark = { 0 }; HANDLE hCam = NULL; PthotoProcCtx ctx = { 0 }; MV_CC_DEVICE_INFO_LIST devList = { 0 }; MV_CC_DEVICE_INFO *pDevInfo = NULL; // 1, 占用锁定, 避免同时间拍照 xGetSelfRunningInfo(runDir, NULL); if(runDir[strlen(runDir)-1] == '/') sprintf(lockFile, "%s%s", runDir, "status/"); else sprintf(lockFile, "%s/%s", runDir, "status/"); if(!sw_dir_exists(lockFile)) sw_dir_create(lockFile); strcat(lockFile, "takephoto.lock"); fd = open(lockFile, O_CREAT | O_RDWR | __O_CLOEXEC, 0666); if(-1 == fd) { return -1; } if(-1 == flock(fd, LOCK_EX | LOCK_NB)) { close(fd); return -2; } // 2, 查找相机, 获取其设备信息 ret = MV_CC_Initialize(); if(MV_OK != ret) { sw_log_error("[%s] 相机SDK初始化失败, errCode=0x%x!!", MODULE_NAME, ret); goto end_p; } ret = MV_CC_EnumDevices(MV_USB_DEVICE, &devList); // 目前只枚举USB连接的相机 if(MV_OK != ret) { sw_log_error("[%s] USB口枚举相机失败, errCode=0x%x!!", MODULE_NAME, ret); goto end_p; } if(devList.nDeviceNum != 1) { ret = -3; sw_log_error("[%s] USB口相机数量错误, deviceNum=%u!!", MODULE_NAME, devList.nDeviceNum); goto end_p; } pDevInfo = devList.pDeviceInfo[0]; if(!pDevInfo) { ret = -4; sw_log_error("[%s] unexpected internal error, unable to obtain detailed information about the industrial camera!!", MODULE_NAME); goto end_p; } strcpy(imgMark.camModelName, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chModelName); strcpy(imgMark.camSerialNum, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber); // 3, 打开相机, 设置其触发模式 ret = MV_CC_CreateHandleWithoutLog(&hCam, pDevInfo); if(MV_OK == ret) ret = MV_CC_OpenDevice(hCam, MV_ACCESS_Exclusive, 0); if(MV_OK == ret) ret = MV_USB_SetTransferWays(hCam, 1); // 设置传输通道个数(RTU硬件资源有限, 需要降低通道数) if(MV_OK != ret) { sw_log_error("[%s] USB口相机打开失败, errCode=0x%x!!", MODULE_NAME, ret); goto end_p; } ret = MV_CC_SetEnumValue(hCam, "TriggerMode", 1); // 允许触发模式 if(MV_OK == ret) ret = MV_CC_SetEnumValue(hCam, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE); // 设置软件触发 if(MV_OK != ret) { sw_log_error("[%s] USB口相机设置失败, errCode=0x%x!!", MODULE_NAME, ret); goto end_p; } // 4, 开始拍照, 等待完成后输出 ret = MV_CC_StartGrabbing(hCam); if(MV_OK != ret) { sw_log_error("[%s] USB口相机取流失败, errCode=0x%x!!", MODULE_NAME, ret); goto end_p; } ctx.hCam = hCam; ctx.saveImgType = imgType; ctx.saveImgFilename = saveImgFilename; ctx.hESig = sw_signal_create(); if(!ctx.hESig) { ret = -5; sw_log_error("[%s] SIG信号量创建失败!!", MODULE_NAME); goto end_p; } ctx.rCode = MV_OK; HANDLE hThrd = sw_thrd_create("PhotoProc", THREAD_DEFAULT_PRIORITY, THREAD_DEFAULT_STACK_SIZE, PhotoProc, (unsigned long)&ctx, 0); if(!hThrd) { ret = -6; sw_signal_destroy(ctx.hESig); sw_log_error("[%s] 拍照线程-创建失败!!", MODULE_NAME); goto end_p; } sw_thrd_resume(hThrd); ret = sw_signal_wait(ctx.hESig, timeout*1000); // 阻塞等待拍照任务结束或超时 if(0 == ret) ret = ctx.rCode; else { ret = -7; sw_log_error("[%s] 拍照过程-等待超时!!", MODULE_NAME); } sw_thrd_destroy(hThrd, WAITTHRD_SAFEEXIT_TIMEOUT); sw_signal_destroy(ctx.hESig); // 5, 成功拍照, 输出相机的信息 if(MV_OK == ret) { MVCC_FLOATVALUE fv = { 0 }; MV_CC_GetFloatValue(hCam, "ExposureTime", &fv); imgMark.imgExposureTime = fv.fCurValue; if(pImgMark) memcpy(pImgMark, &imgMark, sizeof(SImgMark)); } // 6, 任务结束, 释放相关的资源 end_p: if(hCam) { MV_CC_StopGrabbing(hCam); MV_CC_CloseDevice(hCam); MV_CC_DestroyHandle(hCam); } MV_CC_Finalize(); flock(fd, LOCK_UN); close(fd); sw_file_delete(lockFile); return ret; } // 保存照片 static int SavePhoto(HANDLE hCam, MV_FRAME_OUT *pFrame, EImgType imgType, const char *imgFile) { MV_SAVE_IMAGE_PARAM_EX3 saveParams = { 0 }; MVCC_INTVALUE_EX iv; char *filename1 = (char *)imgFile, *filename2 = NULL; int ret; char ext[5]; if(!hCam || !pFrame || !filename1 || strlen(filename1) <= 0) return -8; switch(imgType) { case IMG_TYPE_BMP: // 保存为.bmp格式 imgType = MV_Image_Bmp; strcpy(ext, ".bmp"); break; case IMG_TYPE_JPG: // 保存为.jpg格式 imgType = MV_Image_Jpeg; strcpy(ext, ".jpg"); break; default: return -9; } ret = MV_CC_GetIntValueEx(hCam, "PayloadSize", &iv); if(MV_OK != ret) return ret; saveParams.pData = pFrame->pBufAddr; saveParams.nDataLen = pFrame->stFrameInfo.nFrameLen; saveParams.enPixelType = pFrame->stFrameInfo.enPixelType; saveParams.nHeight = pFrame->stFrameInfo.nHeight; saveParams.nWidth = pFrame->stFrameInfo.nWidth; saveParams.enImageType = imgType; saveParams.nJpgQuality = 90; // JPG编码质量(50-99], 其它格式无效, 影响JPG图像文件大小 saveParams.iMethodValue = 1; if(imgType == IMG_TYPE_JPG) saveParams.nBufferSize = (unsigned int)(iv.nCurValue * 1); else saveParams.nBufferSize = (unsigned int)(iv.nCurValue * 4); saveParams.pImageBuffer = (unsigned char *)sw_heap_malloc(saveParams.nBufferSize); if(!saveParams.pImageBuffer) return -10; ret = MV_CC_SaveImageEx3(hCam, &saveParams); if(MV_OK != ret) goto end_p; if(!xstrcasestr(filename1, "right", ext)) { filename2 = (char *)sw_heap_malloc(strlen(filename1) +sizeof(ext)); if(filename2) sprintf(filename2, "%s%s", filename1, ext); else { ret = -11; goto end_p; } ret = sw_file_update(filename2, "wb", (const char *)saveParams.pImageBuffer, saveParams.nImageLen); sw_heap_free(filename2); } else { ret = sw_file_update(filename1, "wb", (const char *)saveParams.pImageBuffer, saveParams.nImageLen); } if(ret == saveParams.nImageLen) ret = MV_OK; else ret = -12; end_p: sw_heap_free(saveParams.pImageBuffer); return ret; } // 获取系统当前USBFS内存大小(MB), 成功返回: >=0, 失败返回: -1值 int GetSysUsbfsMemCurrentSize() { const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb"; char buf[MAX_LINE_CHARS]; int val = -1; FILE *fp = fopen(usbfsFile, "r"); if(fp) { if(fgets(buf, sizeof(buf), fp)) sscanf(buf, "%d", &val); fclose(fp); } return val; } // 设置系统新的USBFS内存大小(MB), 成功返回: 0值, 失败返回: -1值 int SetSysUsbfsMemSize(int val) { const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb"; char buf[MAX_LINE_CHARS]; sprintf(buf, "%d\n", val); if(val >= 0 && sw_file_update(usbfsFile, "w", buf, strlen(buf)) == strlen(buf)) return 0; else return -1; }