From 7d0e35e591948c837ebe5b5f24c2190557259b61 Mon Sep 17 00:00:00 2001 From: masayume <1936714878@qq.com> Date: Sun, 11 Jul 2021 16:19:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0RK3399=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdparty/cyusb/inc/CyAPI.h | 462 ++++++++++++++++ 3rdparty/cyusb/lib/windows/x64/CyAPI.lib | Bin 0 -> 179470 bytes 3rdparty/cyusb/lib/windows/x86/CyAPI.lib | Bin 0 -> 153730 bytes huagao/Device/CyUsbEx.cpp | 128 +++++ huagao/Device/CyUsbEx.h | 37 ++ huagao/Device/GScan.h | 18 +- huagao/Device/GScanO1003399.cpp | 652 +++++++++++++++++++++++ huagao/Device/GScanO1003399.h | 85 +++ huagao/Device/IConfig.h | 143 +++++ huagao/Device/IUsb.h | 2 +- huagao/Device/PublicFunc.h | 89 +++- huagao/Device/ThreadPool.h | 98 ++++ huagao/huagaods.cpp | 65 ++- huagao/stdafx.h | Bin 12328 -> 12726 bytes 14 files changed, 1726 insertions(+), 53 deletions(-) create mode 100644 3rdparty/cyusb/inc/CyAPI.h create mode 100644 3rdparty/cyusb/lib/windows/x64/CyAPI.lib create mode 100644 3rdparty/cyusb/lib/windows/x86/CyAPI.lib create mode 100644 huagao/Device/CyUsbEx.cpp create mode 100644 huagao/Device/CyUsbEx.h create mode 100644 huagao/Device/GScanO1003399.cpp create mode 100644 huagao/Device/GScanO1003399.h create mode 100644 huagao/Device/ThreadPool.h diff --git a/3rdparty/cyusb/inc/CyAPI.h b/3rdparty/cyusb/inc/CyAPI.h new file mode 100644 index 00000000..3f7959a5 --- /dev/null +++ b/3rdparty/cyusb/inc/CyAPI.h @@ -0,0 +1,462 @@ +//______________________________________________________________________________ +// +// Copyright (c) Cypress Semiconductor, 2003 +// All rights reserved. +// +//______________________________________________________________________________ + +#ifndef CyUSBH +#define CyUSBH + +#ifndef __USB200_H__ +#define __USB200_H__ +#include + +#pragma pack(push,1) /// + +typedef struct _USB_DEVICE_DESCRIPTOR { //设备描述符 + UCHAR bLength; //长度 + UCHAR bDescriptorType; //描述符类型 + USHORT bcdUSB; //USB + UCHAR bDeviceClass; //设备类 + UCHAR bDeviceSubClass; //设备派生类 + UCHAR bDeviceProtocol; //设备协议 + UCHAR bMaxPacketSize0; //最大数据包尺寸 + USHORT idVendor; //厂商ID + USHORT idProduct; //产品ID + USHORT bcdDevice; //设备 + UCHAR iManufacturer; //制造商 + UCHAR iProduct; //产品 + UCHAR iSerialNumber; //序列号 + UCHAR bNumConfigurations;//配置 +} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; + +typedef struct _USB_ENDPOINT_DESCRIPTOR { //端点描述符 + UCHAR bLength; //长度 + UCHAR bDescriptorType; //描述符类型 + UCHAR bEndpointAddress; //端点地址 + UCHAR bmAttributes; //端点属性 + USHORT wMaxPacketSize; //最大数据包尺寸 + UCHAR bInterval; //间隔 +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct _USB_CONFIGURATION_DESCRIPTOR { //配置描述符 + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct _USB_INTERFACE_DESCRIPTOR { //接口描述符 + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct _USB_STRING_DESCRIPTOR { //字符串描述符 + UCHAR bLength; + UCHAR bDescriptorType; + WCHAR bString[1]; +} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR; + +typedef struct _USB_COMMON_DESCRIPTOR { //USB串口描述符 + UCHAR bLength; + UCHAR bDescriptorType; +} USB_COMMON_DESCRIPTOR, *PUSB_COMMON_DESCRIPTOR; +#pragma pack(pop) +#endif +//______________________________________________________________________________ + +class CCyIsoPktInfo { //包信息 +public: + LONG Status; //包状态 + LONG Length; //包长度 +}; + +//______________________________________________________________________________ + + +// {AE18AA60-7F6A-11d4-97DD-00010229B959} +static GUID CYUSBDRV_GUID = {0xae18aa60, 0x7f6a, 0x11d4, 0x97, 0xdd, 0x0, 0x1, 0x2, 0x29, 0xb9, 0x59}; + +typedef enum {TGT_DEVICE, TGT_INTFC, TGT_ENDPT, TGT_OTHER } CTL_XFER_TGT_TYPE; +typedef enum {REQ_STD, REQ_CLASS, REQ_VENDOR } CTL_XFER_REQ_TYPE; +typedef enum {DIR_TO_DEVICE, DIR_FROM_DEVICE } CTL_XFER_DIR_TYPE; +typedef enum {XMODE_BUFFERED, XMODE_DIRECT } XFER_MODE_TYPE; + +const int MAX_ENDPTS = 16; +const int MAX_INTERFACES = 8; +const int USB_STRING_MAXLEN = 256; + + +//////////////////////////////////////////////////////////////////////////////// +// +// The CCyEndPoint ABSTRACT Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyUSBEndPoint +{ +protected: + bool WaitForIO(OVERLAPPED *ovLapStatus); + + virtual PUCHAR BeginDirectXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); //直接传输模式 + virtual PUCHAR BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); //缓冲传输模式 + + +public: + + CCyUSBEndPoint(void); + CCyUSBEndPoint(CCyUSBEndPoint& ept); + CCyUSBEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + HANDLE hDevice; + + // The fields of an EndPoint Descriptor + UCHAR DscLen; + UCHAR DscType; + UCHAR Address; + UCHAR Attributes; + USHORT MaxPktSize; + USHORT PktsPerFrame; + UCHAR Interval; + + // Other fields + ULONG TimeOut; + ULONG UsbdStatus; + ULONG NtStatus; + + DWORD bytesWritten; + DWORD LastError; + bool bIn; + + XFER_MODE_TYPE XferMode; + + bool XferData(PUCHAR buf, LONG &len, CCyIsoPktInfo* pktInfos = NULL); + bool XferData(PUCHAR buf, LONG &bufLen, CCyIsoPktInfo* pktInfos, bool pktMode); + virtual PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov) = 0; + virtual bool FinishDataXfer(PUCHAR buf, LONG &len, OVERLAPPED *ov, PUCHAR pXmitBuf, CCyIsoPktInfo* pktInfos = NULL); + bool WaitForXfer(OVERLAPPED *ov, ULONG tOut); + ULONG GetXferSize(void); + void SetXferSize(ULONG xfer); + + bool Reset(void); + bool Abort(void); + +private: + + + +}; + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Control Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyControlEndPoint : public CCyUSBEndPoint +{ +private: + +public: + CCyControlEndPoint(void); + CCyControlEndPoint(CCyControlEndPoint& ept); + CCyControlEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + CTL_XFER_TGT_TYPE Target; + CTL_XFER_REQ_TYPE ReqType; + CTL_XFER_DIR_TYPE Direction; + + UCHAR ReqCode; + WORD Value; + WORD Index; + + bool Read(PUCHAR buf, LONG &len); + bool Write(PUCHAR buf, LONG &len); + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Isoc Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyIsocEndPoint : public CCyUSBEndPoint +{ + +protected: + virtual PUCHAR BeginDirectXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); + virtual PUCHAR BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov); + +public: + CCyIsocEndPoint(void); + CCyIsocEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); + CCyIsoPktInfo* CreatePktInfos(LONG bufLen, int &packets); + +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Bulk Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyBulkEndPoint : public CCyUSBEndPoint +{ +public: + CCyBulkEndPoint(void); + CCyBulkEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Interrupt Endpoint Class +// +//////////////////////////////////////////////////////////////////////////////// +class CCyInterruptEndPoint : public CCyUSBEndPoint +{ +public: + CCyInterruptEndPoint(void); + CCyInterruptEndPoint(HANDLE h, PUSB_ENDPOINT_DESCRIPTOR pEndPtDescriptor); + + PUCHAR BeginDataXfer(PUCHAR buf, LONG len, OVERLAPPED *ov); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +// +// The Interface Class +// +//////////////////////////////////////////////////////////////////////////////// + +class CCyUSBInterface +{ +private: +protected: +public: + CCyUSBEndPoint *EndPoints[MAX_ENDPTS]; // Holds pointers to all the interface's endpoints, plus a pointer to the Control endpoint zero + + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; // Not counting the control endpoint + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; + + UCHAR bAltSettings; + USHORT wTotalLength; // Needed in case Intfc has additional (non-endpt) descriptors + + + CCyUSBInterface(HANDLE h, PUSB_INTERFACE_DESCRIPTOR pIntfcDescriptor); + CCyUSBInterface(CCyUSBInterface& ifc); // Copy Constructor + ~CCyUSBInterface(void); + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// The Config Class +// +//////////////////////////////////////////////////////////////////////////////// + +class CCyUSBConfig +{ +private: + +protected: +public: + CCyUSBInterface *Interfaces[MAX_INTERFACES]; + + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; + + UCHAR AltInterfaces; + + + CCyUSBConfig(void); + CCyUSBConfig(CCyUSBConfig& cfg); // Copy Constructor + CCyUSBConfig(HANDLE h, PUSB_CONFIGURATION_DESCRIPTOR pConfigDescr); + ~CCyUSBConfig(void); + + + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// The USB Device Class - This is the main class that contains members of all the +// other classes. +// +// To use the library, create an instance of this Class and call it's Open method +// +//////////////////////////////////////////////////////////////////////////////// + +class CCyUSBDevice +{ +// The public members are accessible (i.e. corruptible) by the user of the library +// Algorithms of the class don't rely on any public members. Instead, they use the +// private members of the class for their calculations. + +public: + + CCyUSBDevice(HANDLE hnd = NULL, GUID guid = CYUSBDRV_GUID, BOOL bOpen = true); + ~CCyUSBDevice(void); + + CCyUSBEndPoint **EndPoints; // Shortcut to USBCfgs[CfgNum]->Interfaces[IntfcIndex]->Endpoints + CCyUSBEndPoint *EndPointOf(UCHAR addr); + + CCyControlEndPoint *ControlEndPt; + CCyIsocEndPoint *IsocInEndPt; + CCyIsocEndPoint *IsocOutEndPt; + CCyBulkEndPoint *BulkInEndPt; + CCyBulkEndPoint *BulkOutEndPt; + CCyInterruptEndPoint *InterruptInEndPt; + CCyInterruptEndPoint *InterruptOutEndPt; + + USHORT StrLangID; + ULONG UsbdStatus; + ULONG NtStatus; + ULONG DriverVersion; + ULONG USBDIVersion; + char DeviceName[USB_STRING_MAXLEN]; + char FriendlyName[USB_STRING_MAXLEN]; + wchar_t Manufacturer[USB_STRING_MAXLEN]; + wchar_t Product[USB_STRING_MAXLEN]; + wchar_t SerialNumber[USB_STRING_MAXLEN]; + + CHAR DevPath[USB_STRING_MAXLEN]; + + USHORT BcdUSB; + USHORT VendorID; + USHORT ProductID; + UCHAR USBAddress; + UCHAR DevClass; + UCHAR DevSubClass; + UCHAR DevProtocol; + UCHAR MaxPacketSize; + USHORT BcdDevice; + + UCHAR ConfigValue; + UCHAR ConfigAttrib; + UCHAR MaxPower; + + UCHAR IntfcClass; + UCHAR IntfcSubClass; + UCHAR IntfcProtocol; + bool bHighSpeed; + + DWORD BytesXfered; + + + UCHAR DeviceCount(void); + UCHAR ConfigCount(void); + UCHAR IntfcCount(void); + UCHAR AltIntfcCount(void); + UCHAR EndPointCount(void); + + UCHAR Config(void) { return CfgNum; } // Normally 0 + void SetConfig(UCHAR cfg); + + UCHAR Interface(void) { return IntfcNum; } // Usually 0 + // No SetInterface method since only 1 intfc per device (per Windows) + + UCHAR AltIntfc(void); + bool SetAltIntfc(UCHAR alt); + + GUID DriverGUID(void) { return DrvGuid; } + HANDLE DeviceHandle(void) { return hDevice; } + void UsbdStatusString(ULONG stat, PCHAR s); + bool CreateHandle(UCHAR dev); + void DestroyHandle(); + + bool Open(UCHAR dev); + void Close(void); + bool Reset(void); + bool ReConnect(void); + bool Suspend(void); + bool Resume(void); + bool IsOpen(void) { return (hDevice != INVALID_HANDLE_VALUE); } + + UCHAR PowerState(void); + + + void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR descr); + void GetConfigDescriptor(PUSB_CONFIGURATION_DESCRIPTOR descr); + void GetIntfcDescriptor(PUSB_INTERFACE_DESCRIPTOR descr); + CCyUSBConfig GetUSBConfig(int index); + + +private: + + USB_DEVICE_DESCRIPTOR USBDeviceDescriptor; + PUSB_CONFIGURATION_DESCRIPTOR USBConfigDescriptors[2]; + + CCyUSBConfig *USBCfgs[2]; + + HANDLE hWnd; + HANDLE hDevice; + HANDLE hDevNotification; + HANDLE hHndNotification; + + GUID DrvGuid; + + UCHAR Devices; + UCHAR Interfaces; + UCHAR AltInterfaces; + UCHAR Configs; + + UCHAR DevNum; + UCHAR CfgNum; + UCHAR IntfcNum; // The current selected interface's bInterfaceNumber + UCHAR IntfcIndex; // The entry in the Config's interfaces table matching to IntfcNum and AltSetting + + void GetDevDescriptor(void); + void GetCfgDescriptor(int descIndex); + void GetString(wchar_t *s, UCHAR sIndex); + void SetStringDescrLanguage(void); + void SetAltIntfcParams(UCHAR alt); + bool IoControl(ULONG cmd, PUCHAR buf, ULONG len); + + void SetEndPointPtrs(void); + void GetDeviceName(void); + void GetFriendlyName(void); + void GetDriverVer(void); + void GetUSBDIVer(void); + void GetSpeed(void); + void GetUSBAddress(void); + //void CloseEndPtHandles(void); + + bool RegisterForPnpEvents(HANDLE h); +}; + +//--------------------------------------------------------------------------- +#endif diff --git a/3rdparty/cyusb/lib/windows/x64/CyAPI.lib b/3rdparty/cyusb/lib/windows/x64/CyAPI.lib new file mode 100644 index 0000000000000000000000000000000000000000..47939865796878b6e74ab9e822de9f11f61fc9d8 GIT binary patch literal 179470 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 literal 0 HcmV?d00001 diff --git a/3rdparty/cyusb/lib/windows/x86/CyAPI.lib b/3rdparty/cyusb/lib/windows/x86/CyAPI.lib new file mode 100644 index 0000000000000000000000000000000000000000..a888ef767beae0ed53ad0aeabf27a77b43a04d0e GIT binary patch literal 153730 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 literal 0 HcmV?d00001 diff --git a/huagao/Device/CyUsbEx.cpp b/huagao/Device/CyUsbEx.cpp new file mode 100644 index 00000000..a4e819a0 --- /dev/null +++ b/huagao/Device/CyUsbEx.cpp @@ -0,0 +1,128 @@ +#include "CyUsbEx.h" +#include "CyAPI.h" + +CyUsbEx::CyUsbEx(int index) +{ + m_index = index; + m_timeout = 100; +} + +bool CyUsbEx::open() +{ + if (is_open()) + return true; + + m_usb.reset(new CCyUSBDevice()); + m_usb->Open(m_index); + m_usb->Reset(); + if (m_usb->IsOpen()) + { + if (m_usb->BulkOutEndPt) + m_usb->BulkOutEndPt->TimeOut = m_timeout; + + if (m_usb->BulkInEndPt) + m_usb->BulkInEndPt->TimeOut = m_timeout; + + if (m_usb->InterruptInEndPt) + m_usb->InterruptInEndPt->TimeOut = 400; + } + + return is_open(); +} + +bool CyUsbEx::close() +{ + if (is_open()) + { + m_usb->Close(); + m_usb.reset(); + } + + return true; +} + +bool CyUsbEx::is_open() +{ + if (m_usb.get() != NULL) + return m_usb->IsOpen(); + + return false; +} + +bool CyUsbEx::is_connected() +{ + return is_open(); +} + +void CyUsbEx::set_timeout(int timeout) +{ + m_timeout = timeout; +} + +int CyUsbEx::read_bulk(void* data, int len) +{ + LONG llen = len; + m_usb->BulkInEndPt->XferData((UCHAR*)data, llen); + return llen; +} + + + +int CyUsbEx::write_bulk(void* data, int len) +{ + LONG llen = len; + m_usb->BulkOutEndPt->XferData((UCHAR*)data, llen); + return llen; +} + +int CyUsbEx::read_int(void* data, int len) +{ + LONG llen = len; + m_usb->InterruptInEndPt->XferData((UCHAR*)data, llen); + return llen; +} + +int CyUsbEx::control_msg(int rtype, int req, int value, int index, int len, void* data) +{ + if (!m_usb.get() || !m_usb->ControlEndPt) + return -1; + + m_usb->ControlEndPt->Index = index; + m_usb->ControlEndPt->Value = value; + m_usb->ControlEndPt->ReqType = (CTL_XFER_REQ_TYPE)((rtype >> 5) & 0x03); + m_usb->ControlEndPt->Direction = (CTL_XFER_DIR_TYPE)(rtype >> 7);// + m_usb->ControlEndPt->Target = (CTL_XFER_TGT_TYPE)(rtype & 0x3); + m_usb->ControlEndPt->ReqCode = req; + LONG llen = len; + m_usb->ControlEndPt->XferData((UCHAR*)data, llen); + return llen; +} + +void CyUsbEx::set_usbhotplug_callback(usbhotplug_callback callback, void* userdata) +{ + return; +} + +std::list> CyUsbList::find_all() +{ + std::list> usblists; + CCyUSBDevice usbDevice; + for (int i = 0; i < usbDevice.DeviceCount(); i++) + usblists.push_back(std::shared_ptr(new CyUsbEx(i))); + return usblists; +} + +std::list> CyUsbList::find_vid_pid(int vid, int pid) +{ + std::list> usblists; + CCyUSBDevice usbDevice; + for (int i = 0; i < usbDevice.DeviceCount(); i++) { + usbDevice.Open(i); + if (usbDevice.IsOpen() && (usbDevice.VendorID == vid) && (usbDevice.ProductID == pid)) { + usblists.push_back(std::shared_ptr(new CyUsbEx(i))); + usbDevice.Close(); + } + } + return usblists; +} + diff --git a/huagao/Device/CyUsbEx.h b/huagao/Device/CyUsbEx.h new file mode 100644 index 00000000..b775fbb9 --- /dev/null +++ b/huagao/Device/CyUsbEx.h @@ -0,0 +1,37 @@ +#pragma once +#include "IUsb.h" +#include +#include + + + +class CCyUSBDevice; + +class CyUsbList { +public: + static std::list> find_all(); + static std::list> find_vid_pid(int vid, int pid); +}; + +class CyUsbEx : + public IUsb +{ +public: + CyUsbEx(int index); + // 通过 IUsb 继承 + virtual bool open() override; + virtual bool close() override; + virtual bool is_open() override; + virtual bool is_connected() override; + virtual void set_timeout(int timeout) override; + virtual int read_bulk(void* data, int len) override; + virtual int write_bulk(void* data, int len) override; + virtual int read_int(void* data, int len) override; + virtual int control_msg(int rtype, int req, int value, int index, int len, void* data) override; + virtual void set_usbhotplug_callback(usbhotplug_callback callback = NULL, void* userdata = NULL) override; + +private: + std::shared_ptr m_usb; + int m_index; + int m_timeout; +}; diff --git a/huagao/Device/GScan.h b/huagao/Device/GScan.h index c5179481..26ac153c 100644 --- a/huagao/Device/GScan.h +++ b/huagao/Device/GScan.h @@ -72,15 +72,19 @@ typedef enum tagUsbSupported { // PC繁忙或出错 PC_SCAN_BUSY_or_ERROR = 73, //摺角 - DOG_EAR=74, + DOG_EAR = 74, //幅面检测错误 - SIZE_ERROR=75, + SIZE_ERROR = 75, //取图超时 - AQUIRE_IMAGE_TIMEOUT=76, + AQUIRE_IMAGE_TIMEOUT = 76, //获取图片与扫描张数不匹配 - LOSE_IMAGE=77, + LOSE_IMAGE = 77, //usb读取数据错误 - USB_BULK_ERROR=78, + USB_BULK_ERROR = 78, + //v4l2取图失败 + V4L2_AQULRE_ERROR = 79, + //扫描仪内部图片丢失 + V4L2_IMAGE_EMPTY = 80, //USB 未连接 USB_DISCONNECTED = 200, //用户点击停止 @@ -109,7 +113,9 @@ static map msgs = { {UsbSupported::SIZE_ERROR,"幅面检测异常!"}, {UsbSupported::AQUIRE_IMAGE_TIMEOUT,"取图超时!"}, {UsbSupported::LOSE_IMAGE,"上传图片与扫描张数不匹配!"}, - {UsbSupported::USB_BULK_ERROR,"USB数据读取错误!"} + {UsbSupported::USB_BULK_ERROR,"USB数据读取错误!"}, + {UsbSupported::V4L2_AQULRE_ERROR,"扫描仪取图失败!"}, + {UsbSupported::V4L2_IMAGE_EMPTY,"扫描仪图像处理异常!"}, }; enum tagEventIndex diff --git a/huagao/Device/GScanO1003399.cpp b/huagao/Device/GScanO1003399.cpp new file mode 100644 index 00000000..4f094ca8 --- /dev/null +++ b/huagao/Device/GScanO1003399.cpp @@ -0,0 +1,652 @@ +#include "GScanO1003399.h" +#include "ImageProcess/ImageApplyHeaders.h" +#include "ImageMultiOutput.h" +#include "scn_config.h" +#include "PaperSize.h" +#include "GScan.h" + +static std::mutex mx_ctrl; + +static int scanner_read_reg(std::shared_ptr& usb, int addr) +{ + int val = 0; + std::lock_guard lck(mx_ctrl); + usb->control_msg(0xc0, USB_REQ_GET_DEV_REGS, addr, 0, 4, &val); + return val; +} + +static void scanner_write_reg(std::shared_ptr& usb, int addr, int val) +{ + std::lock_guard lck(mx_ctrl); + usb->control_msg(0x40, USB_REQ_SET_DEV_REGS, addr, 0, 4, &val); +} + +static void scanner_cmd(std::shared_ptr& usb, int cmd) +{ + scanner_write_reg(usb, 0, cmd); +} + + + +GScanO1003399::GScanO1003399(): + m_imgthread(1) +{ + im_data.reset(new std::vector()); + open(0, 0); + + devState = DEV_STOP; +} + +GScanO1003399::~GScanO1003399() +{ + if (m_usbthread.get() && m_usbthread->joinable()) + { + b_usbthread = false; + m_usbthread->join(); + m_usbthread.reset(); + } + if (fu_rx.valid()) + fu_rx.wait(); +} + +void GScanO1003399::open(int vid, int pid) +{ + //auto lsusb=CyUsbList::find_vid_pid(vid, pid); + //if (!lsusb.empty()) + //{ + // m_usb = *lsusb.begin(); + // m_usb->open(); + //} + if (m_usb.get()&&m_usb->is_connected()) + return; + auto lsusb = CyUsbList::find_all(); + if (!lsusb.empty()) + { + m_usb = *lsusb.begin(); + m_usb->open(); + } +} + +void GScanO1003399::regist_deviceevent_callback(deviceevent_callback callback, void* usrdata) +{ +} + +void GScanO1003399::DogEar_callback(std::function fun) +{ +} +static int aquirenum = 0; +static int getimgnum = 0; +int GScanO1003399::aquire_bmpdata(std::vector& bmpdata) +{ + StopWatch sw; + while (true) + { + if ((m_imagedata.Size() < 1)&&(!is_runing())) + { + DoEvents(); + this_thread::sleep_for(chrono::milliseconds(1)); + if (!is_runing()) + { + if (devState == DEV_WRONG) + return get_ErrorCode(); + return -1; + } + if (sw.elapsed_s() > 30.0) + { + devState = DEV_STOP; + if (fu_rx.valid()) + fu_rx.wait(); + Stop_scan(); + ResetScanner(); + return AQUIRE_IMAGE_TIMEOUT; + } + } + else + { + if ((m_imagedata.Size() > 0)) + { + FileTools::writelog(log_INFO, "aquire_bmpdata num =" + to_string(aquirenum++)); + bmpdata = *(m_imagedata.Take()); + UpdateScanInfo(get_imgnReaded(), countNTransfered()); + return 0; + } + DoEvents(); + this_thread::sleep_for(chrono::milliseconds(2)); + } + + } + +} + +BOOL GScanO1003399::IsConnected() +{ + return m_usb.get()&&m_usb->is_connected(); +} + +std::string GScanO1003399::GetFWVersion() +{ + std::string fw; + if (m_usb.get() && m_usb->is_connected()) + { + scanner_write_reg(m_usb, SR_GET_FWVERSION, 0); + fw.resize(10); + read_data(&fw[0], fw.length(), 200); + } + return fw; +} + +std::string GScanO1003399::GetSerialNum() +{ + std::string sn; + if (m_usb.get() && m_usb->is_connected()) + { + scanner_write_reg(m_usb, SR_GET_SERIALNUM, 0); + sn.resize(14); + read_data(&sn[0], sn.length(), 200); + } + return sn; +} + +std::uint32_t GScanO1003399::GetMotorFPGA() +{ + return std::uint32_t(); +} + +std::uint32_t GScanO1003399::GetScanFPGA() +{ + return std::uint32_t(); +} + +bool GScanO1003399::is_scan() +{ + //return devState==DEV_ISRUNNING; + return is_runing() || (m_imagedata.Size() > 0); +} + +BOOL GScanO1003399::Get_Scanner_PaperOn() +{ + return scanner_read_reg(m_usb, SR_PAPER_READY); +} + +int GScanO1003399::Get_Roller_num() +{ + return 0; +} + +void GScanO1003399::config_params(GScanCap& param) +{ + m_param = param; + HGScanConfig cfg; + cfg = { 0 }; +#ifdef G200 + + PaperStatus ps = { param.papertype,param.paperAlign }; + cfg.g200params.paper = SupPaperTyps.count(ps)>0 ? SupPaperTyps[ps] : 0; + if (param.filter != 3 || param.enhance_color != 0 || param.hsvcorrect) + cfg.g200params.color = 1;//color + else + { + cfg.g200params.color = SupPixelTypes.count(param.pixtype) > 0 ? SupPixelTypes[param.pixtype] : 2; + + } + cfg.g200params.dpi = SupResolutions.count(param.resolution_native)>0 ? SupResolutions[param.resolution_native] : 1; + cfg.g200params.double_feed_enbale = (unsigned int)param.hardwarecaps.en_doublefeed; + cfg.g200params.stable_enbale = (unsigned int)param.hardwarecaps.en_stapledetect; + cfg.g200params.screw_detect_enable = (unsigned int)param.hardwarecaps.en_skrewdetect; + cfg.g200params.screw_detect_level = (unsigned int)cfg.g200params.screw_detect_enable ? secrewMaps[param.hardwarecaps.skrewdetectlevel] : 0; + + cfg.g200params.enable_sizecheck = param.en_sizecheck == 1 ? 1 : 0; + cfg.g200params.unused_one = cfg.g200params.sizeerror_errorratio = 0; + param.resolution_dst >= 240.0f ? 1 : 0; + +#else + + cfg.g400params.doubleFeeded = param.hardwarecaps.en_doublefeed == 0 ? 0 : 1; + cfg.g400params.dpi = G400_DPI::G400_D200;//gcap.resolution_dst <= 200.0f ? G400_DPI::G400_D200 : (gcap.resolution_dst <= 300.0f ? G400_DPI::G400_D300 : G400_DPI::G400_D600); + cfg.g400params.enableLed = 1; + if (param.filter != 3 || param.enhance_color) + cfg.g400params.isColor = 1; + else + cfg.g400params.isColor = SupPixelTypes[param.pixtype]; + cfg.g400params.enableStable = 0;//gcap.hardwarecaps.en_stapledetect==0?0:1; + cfg.g400params.isCorrect = 1;//1 机器校正 + PaperStatus ps = { param.papertype,param.paperAlign }; + cfg.g400params.pageSize = SupPaperTyps[ps]; + CSize size; +#ifdef G300 + size = Device::PaperSize().GetPaperSize(TwSS::A4, 200.0f, gcap.paperAlign);//G300 最大支持A4幅面 +#else + size = Device::PaperSize().GetPaperSize(param.papertype, 200.0f, param.paperAlign); +#endif// G300 + cfg.g400params.dstHeight = (int)((size.cy + 200) / 100); + cfg.g400params.reversed1 = cfg.g400params.reversed2 = 0; +#endif // G200 + config_scanparam(cfg); + config_imgprocparam({0}); + GScanCap_3399 param39{ 0 }; + param39.AutoCrop_threshold = param.AutoCrop_threshold; + param39.autodescrew = param.autodescrew; + param39.automaticcolor = param.automaticcolor; + param39.automaticcolortype = param39.automaticcolortype; + param39.brightness = param.brightness; + param39.contrast = param.contrast; + param39.detachnoise = param.detachnoise; + param39.discardblank_percent = param.discardblank_percent; + param39.en_fold = param.en_fold; + param39.en_sizecheck = param.en_sizecheck; + param39.enhance_color = param.enhance_color; + param39.fillbackground = param.fillbackground; + param39.fillhole = param.fillhole; + param39.filter = param.filter; + param39.gamma = param.gamma; + param39.hardwarecaps = param.hardwarecaps; + param39.hsvcorrect = param.hsvcorrect; + param39.imageRotateDegree = param.imageRotateDegree; + param39.indent = param.indent; + param39.is_autocontrast = param.is_autocontrast; + param39.is_autocrop = param.is_autocrop; + param39.is_autodiscradblank_normal = param.is_autodiscradblank_normal; + param39.is_autodiscradblank_vince = param.is_autodiscradblank_vince; + param39.is_autotext = param.is_autotext; + param39.is_backrotate180 = param.is_backrotate180; + param39.is_convex = param.is_convex; + param39.is_dogeardetection = param.is_dogeardetection; + param39.is_duplex = param.is_duplex; + param39.is_switchfrontback = param.is_switchfrontback; + param39.multi_output_red = param.multi_output_red; + param39.noise = param.noise; + param39.paperAlign = param.paperAlign; + param39.papertype = param.papertype; + param39.pixtype = param.pixtype; + param39.resolution_dst = param.resolution_dst; + param39.resolution_native = param.resolution_native; + param39.scannum = param.scannum; + param39.sharpen = param.sharpen; + param39.threshold = param.threshold; + m_usb->write_bulk(¶m39, sizeof(param39)); + +} + +void GScanO1003399::Scanner_StartScan(UINT16 count) +{ + + scanflag = true; + Set_ErrorCode(0); + getimgnum = 0; + aquirenum = 0; + + reset(); + devState = DEV_ISRUNNING; + if (scan_mode()) + { + devState = DEV_WRONG; + Set_ErrorCode(COUNT_MODE); + return; + } + //if (!Get_Scanner_PaperOn()) + //{ + // devState = DEV_WRONG; + // Set_ErrorCode(NO_FEED); + // return; + //} + + start(); + if (!m_usbthread.get()) { + b_usbthread = true; + m_usbthread.reset(new thread(&GScanO1003399::usb_run, this)); + } +} + +void GScanO1003399::Stop_scan() +{ + if (devState != DEV_ISRUNNING) + return; + stop(); + scanflag = false; + if (fu_rx.valid()) + fu_rx.wait(); + devState == DEV_ISRUNNING ? devState = DEV_STOP : devState = DEV_WRONG; +} + +int GScanO1003399::notifyscan() +{ + return 0; +} + +void GScanO1003399::ResetScanner() +{ + +} + +bool GScanO1003399::Get_IsImageQueueEmpty() +{ + return m_imagedata.Size() < 1; +} + +void GScanO1003399::reset() +{ + devState = DEV_STOP; + m_imagedata.Clear(); +} + +void GScanO1003399::clear_hwerror() +{ +} + +UINT32 GScanO1003399::get_ErrorCode() +{ + return Error_Code; +} + +void GScanO1003399::Set_ErrorCode(UINT32 value) +{ + Error_Code = value; +} + +int GScanO1003399::get_scanned_num() +{ + return 0; +} + +void GScanO1003399::set_sleep_time(int mode) +{ +} + +bool GScanO1003399::set_scannercode(std::string str) +{ + return false; +} + +std::string GScanO1003399::get_scannercode() +{ + return std::string(); +} + +void GScanO1003399::usb_run() +{ + unsigned char buff[64]; + while (b_usbthread) + { + if (!m_usb.get() || !m_usb->is_connected()) + { + this_thread::sleep_for(chrono::milliseconds(20)); + continue; + } + memset(buff, 0, sizeof(buff)); + auto length = m_usb->read_int(buff, sizeof(buff)); + if (((length) == sizeof(buff)) || im_dev_count()) + { + HGEIntInfo info = *(HGEIntInfo*)&buff; + switch (info.From) + { + case IMG: + if (is_runing()) + im_rx(); + break; + case MtBoard: + FileTools::writelog(log_ERROR, "Got MotorBoard error code = " + to_string(info.Code)); + break; + case FPGA: + FileTools::writelog(log_ERROR, "Got FPGA Error code = " + to_string(info.Code)); + break; + case V4L2: + FileTools::writelog(log_ERROR, "Got V4L2 Error code = " + to_string(info.Code)); + break; + default: + FileTools::writelog(log_ERROR, "Got Unkown error code ! From =" + to_string(info.From) + " Code = " + to_string(info.Code)); + break; + } + if ((codeconvter(info) != 0)) + { + scanflag = false; + devState = codeconvter(info) == -1 ? DEV_STOP : DEV_WRONG; + } + if (codeconvter(info) > 0) + { + Set_ErrorCode(codeconvter(info)); + stop(); + } + this_thread::sleep_for(chrono::microseconds(10)); + } + + } +} + +void GScanO1003399::start() +{ + scanner_cmd(m_usb, SC_START); +} + +void GScanO1003399::stop() +{ + scanner_cmd(m_usb, SC_STOP); +} + +bool GScanO1003399::is_runing() +{ + return scanner_read_reg(m_usb, SR_STATUS) & 0x01; +} + +int GScanO1003399::scan_mode() +{ + return scanner_read_reg(m_usb, SR_OS) & 0x01; +} + +int GScanO1003399::count() +{ + return scanner_read_reg(m_usb, SR_SCAN_COUNT); +} + +void GScanO1003399::abort_dev_tx() +{ + scanner_write_reg(m_usb, SR_IM_ABORT, 1); //!< to-list +} + +void GScanO1003399::config_scanparam(const HG_ScanConfig& cfg) +{ + scanner_write_reg(m_usb, SR_CONFIG_SCAN_PARAM, cfg.value); +} + +void GScanO1003399::config_imgprocparam(const HGImgProcParms& imgprocparams) +{ + scanner_write_reg(m_usb, SR_CONFIF_IMGPROCPARAM,imgprocparams.value); +} + +bool GScanO1003399::is_rx() +{ + return false; +} + +bool GScanO1003399::rx_cmd() +{ + scanner_write_reg(m_usb, 8, 1); + return true; +} + +bool GScanO1003399::is_dev_tx() +{ + return scanner_read_reg(m_usb, SR_IM_TXING); +} + +void GScanO1003399::im_rx() +{ + if (!is_rx() && !is_dev_tx()) + { + fu_rx = m_imgthread.enqueue([this] { + do + { + /* code */ + auto &buffi = im_data; + int count = front_datasize(); + buffi->resize(count); + rx_cmd(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + count = read_data(buffi->data(), count, count / (0.01 * 1024 * 1024)); + pop_dev_im(); + vector mats; + auto& buffs = G400Decode(buffi).getImageBuffs(); + UpdateScanInfo(countNReaded(), get_imgTransfered()); + imgproce(buffs); + } while (im_dev_count()); + }); + } +} + +int GScanO1003399::read_data(void* data, int length, int timeout) +{ + int readed = 0; + int reading = 0; + const int buffer_size = 2 * 1024 * 1024; + StopWatch sw; + while (readed < length && sw.elapsed_ms() < timeout) { + reading = std::max(0, std::min(length - readed, buffer_size)); + reading = m_usb->read_bulk((unsigned char*)data + readed, reading); + if (reading > 0) { + readed += reading; + } + } + return readed; +} + +void GScanO1003399::pop_dev_im() +{ + scanner_write_reg(m_usb, SR_IM_POP, 1); //!< to-list +} + +int GScanO1003399::front_datasize() +{ + return scanner_read_reg(m_usb, SR_IM_FRONT_SIZE); +} + +int GScanO1003399::im_dev_count() +{ + return scanner_read_reg(m_usb, SR_IM_COUNT); //!< to-list ; +} + +void GScanO1003399::imgproce(std::vector>>& buffs) +{ + vector mats; + for (auto& buf : buffs) { + cv::ImreadModes rmc; + //int rm; + if (m_param.filter != 3 || m_param.enhance_color || m_param.hsvcorrect) { + rmc = cv::IMREAD_COLOR; + //rm = 1; + } + else { + rmc = m_param.pixtype == 2 ? cv::IMREAD_COLOR : cv::IMREAD_GRAYSCALE; + //rm = scanParam.pixtype == 2 ? 1 : 6; + } + try + { + cv::Mat mat = cv::imdecode(*buf, rmc); + if (mat.empty()) { + FileTools::writelog(log_ERROR, "decode image data error"); + continue; + } + buf.reset(); + mats.push_back(mat); + FileTools::writelog(log_INFO, "push_back image num= " + to_string(getimgnum++)); + } + catch (const std::exception& e) + { + //writelog(e.what()); + FileTools::writelog(log_ERROR, e.what()); + } + } + buffs.clear(); + if (m_param.automaticcolor) + { + CImageApplyColorRecognition(m_param.automaticcolortype == 1 ? CImageApplyColorRecognition::ColorRecognitionMode::Color_Gray : CImageApplyColorRecognition::ColorRecognitionMode::Color_Mono).apply(mats,m_param.is_duplex); + + } + for (int i = 0; i < mats.size(); i++) { + //if (!m_param.is_duplex && i == 1) { + // mats[i].release(); + // break; + //} + if (!mats[i].empty()) { + IMat2Bmp idata; + if (m_param.pixtype == 1 && m_param.hsvcorrect) + if (mats[i].channels() == 3) + cvtColor(mats[i], mats[i], cv::COLOR_BGR2GRAY); + idata = (m_param.pixtype == 0 || (((m_param.automaticcolortype == 0) && (m_param.automaticcolor == true)) && (mats[i].channels() == 1))) ? (IMat2Bmp)Mat2BmpBw(mats[i], m_param.resolution_dst) : Mat2Bmp(mats[i], m_param.resolution_dst); + //if (!m_param.multi_output_red) + // mats[i].release(); + + m_imagedata.Put(idata.getBmpDataBuffer()); + } + } + + if (m_param.multi_output_red) { + for (int i = 0; i < mats.size(); i++) { + if (!mats[i].empty()) { + ImageMultiOutput m_mlt; + cv::Mat ret = m_mlt.GetMultiFilterMat(mats[i], 2); + mats[i].release(); + if (!ret.empty()) { + if (!m_param.is_duplex && i == 1) { + ret.release(); + break; + } + Mat2Bmp mb(ret, m_param.resolution_dst); + m_imagedata.Put(mb.getBmpDataBuffer()); + ret.release(); + } + } + } + } + mats.clear(); +} + +int GScanO1003399::codeconvter(HGEIntInfo code) +{ + if (code.From == HGType::FPGA) + { + switch (code.Code) + { + default: + break; + } + } + if (code.From == HGType::MtBoard) + { + switch (code.Code) + { + case 0x00002: + return NO_FEED; + case 0x00004: + return OPEN_COVER; + case 0x00008: + return FEED_IN_ERROR; + case 0x00010: + return PAPER_JAM; + case 0x00020: + return DETECT_DOUBLE_FEED; + case 0x00040: + return DETECT_STAPLE; + case 0x00080: + return PAPER_SKEW; + case 0x10000: + return AQUIRE_IMAGE_TIMEOUT; + case 0x20000: + return SIZE_ERROR; + default: + break; + } + } + if (code.From == HGType::V4L2) + { + switch (code.Code) + { + case 0: + return V4L2_AQULRE_ERROR; + case 1: + return V4L2_IMAGE_EMPTY; + default: + break; + } + } + + return 0; +} diff --git a/huagao/Device/GScanO1003399.h b/huagao/Device/GScanO1003399.h new file mode 100644 index 00000000..58941b8e --- /dev/null +++ b/huagao/Device/GScanO1003399.h @@ -0,0 +1,85 @@ +#pragma once +#include "stdafx.h" +#include "IGDevice.h" +#include "GScan.h" +#include "CyUsbEx.h" +#include "IConfig.h" +#include "G400ScanConfig.h" +#include +#include +#include +#include "ThreadPool.h" +#include "StopWatch.h" +#include "ImageMatQueue.h" +#include "opencv2/opencv.hpp" + + + +class GScanO1003399 :public IScanner +{ +public: + GScanO1003399(); + virtual ~GScanO1003399(); + virtual void open(int vid, int pid) override; + virtual void regist_deviceevent_callback(deviceevent_callback callback, void* usrdata = 0) override; + virtual void DogEar_callback(std::function fun) override; + virtual int aquire_bmpdata(std::vector& bmpdata) override; + virtual BOOL IsConnected() override; + virtual std::string GetFWVersion() override; + virtual std::string GetSerialNum() override; + virtual std::uint32_t GetMotorFPGA() override; + virtual std::uint32_t GetScanFPGA() override; + virtual bool is_scan() override; + virtual BOOL Get_Scanner_PaperOn() override; + virtual int Get_Roller_num() override; + virtual void config_params(GScanCap& param) override; + virtual void Scanner_StartScan(UINT16 count) override; + virtual void Stop_scan() override; + virtual int notifyscan() override; + virtual void ResetScanner() override; + virtual bool Get_IsImageQueueEmpty() override; + virtual void reset() override; + virtual void clear_hwerror() override; + virtual UINT32 get_ErrorCode() override; + virtual void Set_ErrorCode(UINT32 value) override; + virtual int get_scanned_num() override; + virtual void set_sleep_time(int mode) override; + virtual bool set_scannercode(std::string str) override; + virtual std::string get_scannercode() override; + +private: + void usb_run(); + void im_rx(); + void start(); + void stop(); + bool is_runing(); + int scan_mode(); + int count(); + void abort_dev_tx(); + void config_scanparam(const HG_ScanConfig& cfg); + void config_imgprocparam(const HGImgProcParms& imgprocparams); + bool is_rx(); + bool rx_cmd(); + bool is_dev_tx(); + int read_data(void* data, int length, int timeout); + void pop_dev_im(); + int front_datasize(); + int im_dev_count(); + void imgproce(std::vector>>& buffs); + int codeconvter(HGEIntInfo code); + +private: + volatile int devState; + volatile int Error_Code; + volatile bool scanflag; + GScanCap m_param; + std::shared_ptr m_usb; + volatile bool b_usbthread; + std::future fu_rx; + ThreadPool m_imgthread; + std::shared_ptr m_usbthread; + std::shared_ptr> im_data; + BlockingQueue>> m_imagedata; + +}; + diff --git a/huagao/Device/IConfig.h b/huagao/Device/IConfig.h index 90e019c2..3715fa70 100644 --- a/huagao/Device/IConfig.h +++ b/huagao/Device/IConfig.h @@ -6,6 +6,8 @@ #include "PaperSize.h" #define FOR_LANXUM +using u32 = unsigned int; + static std::map SupPaperTyps = { #ifdef G200 #ifdef FOR_LANXUM @@ -26,9 +28,15 @@ static std::map SupPaperTyps = { {{TwSS::USLedger,PaperAlign::Rot0},12}, {{TwSS::USLegal,PaperAlign::Rot0},13}, {{TwSS::None,PaperAlign::Rot0},0}, +#ifdef G1003399 + {{TwSS::USStatement,PaperAlign::Rot0},17}, + {{TwSS::MaxSize,PaperAlign::Rot0},19}, + {{TwSS::Trigeminy,PaperAlign::Rot0},17} +#else {{TwSS::USStatement,PaperAlign::Rot0},16}, {{TwSS::MaxSize,PaperAlign::Rot0},16}, {{TwSS::Trigeminy,PaperAlign::Rot0},16} +#endif #else {{TwSS::A3,PaperAlign::Rot0},0}, {{TwSS::A4,PaperAlign::Rot0},1}, @@ -102,6 +110,108 @@ static std::map secrewMaps = { {4,3}, {5,4} }; + + +enum HGType +{ + MtBoard = 1, + FPGA, + V4L2, + IMG, + STOPSCAN, +}; + +struct HGEIntInfo +{ + HGType From; + unsigned int Code; +}; + +#define USB_REQ_GET_FPGA_REGS 0x40 +#define USB_REQ_SET_FPGA_REGS 0x41 +#define USB_REQ_GET_MOTOR_REGS 0x42 +#define USB_REQ_SET_MOTOR_REGS 0x43 + +#define USB_REQ_GET_DEV_STATUS 0x60 +#define USB_REQ_GET_DEV_CONFIGURATION 0x61 +#define USB_REQ_SET_DEV_CONFIGURATION 0x62 +#define USB_REQ_GET_DEV_REGS 0x63 +#define USB_REQ_SET_DEV_REGS 0x64 + +enum Scanner_Reg_Defs +{ + SR_CMD, + SR_STATUS, + SR_SCAN_COUNT, + SR_OS, + SR_SENSORS, + SR_MOTOR, + SR_IM_TYPE, + SR_IM_COUNT, + SR_IM_TX, + SR_IM_FRONT_SIZE, + SR_IM_CLEAR, + SR_IM_TXING, + SR_IM_POP, + SR_IM_ABORT, + SR_COUNT, + SR_CONFIG_SCAN_PARAM, + SR_GET_FWVERSION, + SR_SET_FWERSION, + SR_GET_SERIALNUM, + SR_SET_SERIALNUM, + SR_CONFIF_IMGPROCPARAM, + SR_PAPER_READY, +}; + +enum Scanner_Cmd_Defs +{ + SC_START, + SC_STOP, + SC_CLEAR, + SC_COUNT +}; + +typedef union HG_ImgProcParms +{ + unsigned int value; + struct + { + u32 papertype : 5; + u32 scanside : 3; + u32 res : 10; + u32 rotate : 2; + u32 autodescrew : 1; + u32 fillbackground : 1; + u32 filter : 4; + u32 enhancecolor : 2; + u32 fillhole : 1; + u32 reversed : 3; + }imgprocparams; +}HGImgProcParms; + + +///用于CIS 自动校正参数保存 +typedef struct +{ + u32 gainF[6]; + u32 gainB[6]; + u32 offsetsF[6]; + u32 offsetsB[6]; + u32 expF[3]; + u32 expB[3]; + u32 sp; +}HGCISConfig; + + +typedef struct +{ + HGCISConfig colorCorrect; + HGCISConfig color; + HGCISConfig grayCorrect; + HGCISConfig gray; +}HGCorrectConfigs; + typedef union Config_Param { unsigned int value; struct { @@ -121,6 +231,39 @@ typedef union Config_Param { }; } ConfigParam; +typedef union HG_ScanConfig +{ + unsigned int value; + struct + { + unsigned int paper : 5; + unsigned int color : 1; + unsigned int dpi : 2; + unsigned int double_feed_enbale : 1; + unsigned int stable_enbale : 1; + unsigned int screw_detect_enable : 1; + unsigned int screw_detect_level : 3;//第十四位 + unsigned int unused_one : 6; + unsigned int pc_correct : 1; + unsigned int enable_sizecheck : 1; + unsigned int enabledsp_cache : 1; + unsigned int sizeerror_errorratio : 9; + }g200params; + struct + { + unsigned int pageSize : 5; + unsigned int isColor : 1; + unsigned int dpi : 2; + unsigned int doubleFeeded : 1; + unsigned int enableStable : 1; + unsigned int enableLed : 1; + unsigned int reversed1 : 6; + unsigned int isCorrect : 1; + unsigned int dstHeight : 8; + unsigned int reversed2 : 6; + }g400params; +}HGScanConfig; + class IConfig { public: diff --git a/huagao/Device/IUsb.h b/huagao/Device/IUsb.h index a286b467..91af68db 100644 --- a/huagao/Device/IUsb.h +++ b/huagao/Device/IUsb.h @@ -6,7 +6,7 @@ class IUsb public: virtual ~IUsb() {} virtual bool open() = 0; - virtual void set_usbhotplug_callback(usbhotplug_callback callback=NULL,void* userdata=NULL)=0; + virtual void set_usbhotplug_callback(usbhotplug_callback callback=0,void* userdata=0)=0; virtual bool close() = 0; virtual bool is_open() = 0; virtual bool is_connected() = 0; diff --git a/huagao/Device/PublicFunc.h b/huagao/Device/PublicFunc.h index d87c2891..4e2f315f 100644 --- a/huagao/Device/PublicFunc.h +++ b/huagao/Device/PublicFunc.h @@ -84,7 +84,8 @@ const std::string AUTOCROP_THRESHOLD = "AutoCrop_Threshold"; const std::string NOISE = "Noise"; const std::string LOWPOWERMODE = "ilowpowermode"; - +#pragma pack(push) +#pragma pack(4) /****************** **参数保存结构体** *******************/ @@ -186,7 +187,7 @@ typedef struct Scan_Rect { int y; }ScanRect; -enum PaperAlign :byte { +enum PaperAlign :uint8_t { Rot0 = 0, Rot270 = 3, AutoTextOrientation = 5 @@ -212,12 +213,12 @@ typedef enum SharpenBlur :short { struct GScanCap { - byte papertype; /**< the current paper source ADF or Flatbed*/ + uint8_t papertype; /**< the current paper source ADF or Flatbed*/ PaperAlign paperAlign; - byte en_sizecheck; /**< 尺寸检测*/ + uint8_t en_sizecheck; /**< 尺寸检测*/ float imageRotateDegree; - byte is_duplex; /**< True to use duplex false for simplex, ignored if flatbed*/ - byte en_fold; /**<对折*/ + uint8_t is_duplex; /**< True to use duplex false for simplex, ignored if flatbed*/ + uint8_t en_fold; /**<对折*/ int pixtype; /**< type of pixels to transfer image as */ int automaticcolor; /**<顔色自動識別*/ int automaticcolortype; /**<顔色自動識別后非彩色上傳類型*/ @@ -228,34 +229,79 @@ struct GScanCap float contrast; /**< Contrast */ float brightness; /**< Brightness */ float threshold; /**< Threshold */ - byte is_autocontrast; /**< 自动对比度*/ - byte is_autocrop; /**< 自动裁切*/ - byte is_autodiscradblank_normal; /**< 自动丢弃空白页通用*/ + uint8_t is_autocontrast; /**< 自动对比度*/ + uint8_t is_autocrop; /**< 自动裁切*/ + uint8_t is_autodiscradblank_normal; /**< 自动丢弃空白页通用*/ int discardblank_percent; /**<跳过空白页阀值*/ - byte is_autodiscradblank_vince;/**自动丢弃空白页发票*/ - byte is_switchfrontback; /**交换正反面*/ - byte autodescrew; /**< 自动纠偏*/ - byte multi_output_red; /*多流输出*/ - byte hsvcorrect; /**<答题卡除红*/ - byte filter; /**< 除色*/ - byte sharpen; - byte enhance_color; /**< 颜色增强*/ - byte fillbackground; /**< 填黑框*/ + uint8_t is_autodiscradblank_vince;/**自动丢弃空白页发票*/ + uint8_t is_switchfrontback; /**交换正反面*/ + uint8_t autodescrew; /**< 自动纠偏*/ + uint8_t multi_output_red; /*多流输出*/ + uint8_t hsvcorrect; /**<答题卡除红*/ + uint8_t filter; /**< 除色*/ + uint8_t sharpen; + uint8_t enhance_color; /**< 颜色增强*/ + uint8_t fillbackground; /**< 填黑框*/ bool is_convex; /**< 填黑框模式,true为凸多边形填充,false为凹多边形填充,默认true*/ int noise; /**< 除噪像素,能够消除noise宽度的背景竖条纹干扰,默认40*/ int indent; /**< 轮廓缩进,裁剪、纠偏或者黑底填充时,对探索到的纸张轮廓进行缩进indent像素,默认5*/ int AutoCrop_threshold; /**< 自动裁剪二值化阈值,取值范围(0, 255),默认40*/ unsigned short scannum; /**< 扫描张数*/ - byte is_backrotate180; /**< 背面旋转180*/ - byte is_dogeardetection; /**<折角检测*/ + uint8_t is_backrotate180; /**< 背面旋转180*/ + uint8_t is_dogeardetection; /**<折角检测*/ HardwareCaps hardwarecaps; /**< 硬件扫描参数*/ FillHole fillhole; DetachNoise detachnoise; /**< 黑白降噪*/ - byte is_autotext; /**< 自动文本方向识别*/ + uint8_t is_autotext; /**< 自动文本方向识别*/ std::string Caption; std::string SavePath; }; + +struct GScanCap_3399 +{ + uint8_t papertype; /**< the current paper source ADF or Flatbed*/ + PaperAlign paperAlign; + uint8_t en_sizecheck; /**< 尺寸检测*/ + float imageRotateDegree; + uint8_t is_duplex; /**< True to use duplex false for simplex, ignored if flatbed*/ + uint8_t en_fold; /**<对折*/ + int pixtype; /**< type of pixels to transfer image as */ + int automaticcolor; /**<顔色自動識別*/ + int automaticcolortype; /**<顔色自動識別后非彩色上傳類型*/ + //ScanRect scanrect; + float resolution_dst; /**< horizontal resolution */ + float resolution_native; + float gamma; /**< Gamma */ + float contrast; /**< Contrast */ + float brightness; /**< Brightness */ + float threshold; /**< Threshold */ + uint8_t is_autocontrast; /**< 自动对比度*/ + uint8_t is_autocrop; /**< 自动裁切*/ + uint8_t is_autodiscradblank_normal; /**< 自动丢弃空白页通用*/ + int discardblank_percent; /**<跳过空白页阀值*/ + uint8_t is_autodiscradblank_vince;/**自动丢弃空白页发票*/ + uint8_t is_switchfrontback; /**交换正反面*/ + uint8_t autodescrew; /**< 自动纠偏*/ + uint8_t multi_output_red; /*多流输出*/ + uint8_t hsvcorrect; /**<答题卡除红*/ + uint8_t filter; /**< 除色*/ + uint8_t sharpen; + uint8_t enhance_color; /**< 颜色增强*/ + uint8_t fillbackground; /**< 填黑框*/ + bool is_convex; /**< 填黑框模式,true为凸多边形填充,false为凹多边形填充,默认true*/ + int noise; /**< 除噪像素,能够消除noise宽度的背景竖条纹干扰,默认40*/ + int indent; /**< 轮廓缩进,裁剪、纠偏或者黑底填充时,对探索到的纸张轮廓进行缩进indent像素,默认5*/ + int AutoCrop_threshold; /**< 自动裁剪二值化阈值,取值范围(0, 255),默认40*/ + unsigned short scannum; /**< 扫描张数*/ + uint8_t is_backrotate180; /**< 背面旋转180*/ + uint8_t is_dogeardetection; /**<折角检测*/ + HardwareCaps hardwarecaps; /**< 硬件扫描参数*/ + FillHole fillhole; + DetachNoise detachnoise; /**< 黑白降噪*/ + uint8_t is_autotext; /**< 自动文本方向识别*/ +}; + typedef struct Paper_Status { Paper_Status(unsigned int paper, unsigned int orentate) :Paper(paper), Orentate(orentate) {} Paper_Status():Paper(0), Orentate(0){} @@ -296,6 +342,7 @@ enum G400_DPI { G400_D600 }; +#pragma pack(pop) #define CAPTION_LEN 256 #define TWAIN_IMAGE_FILE_LIST_NAME TEXT(".dat") diff --git a/huagao/Device/ThreadPool.h b/huagao/Device/ThreadPool.h new file mode 100644 index 00000000..41832030 --- /dev/null +++ b/huagao/Device/ThreadPool.h @@ -0,0 +1,98 @@ +#ifndef THREAD_POOL_H +#define THREAD_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ThreadPool { +public: + ThreadPool(size_t); + template + auto enqueue(F&& f, Args&&... args) + -> std::future::type>; + ~ThreadPool(); +private: + // need to keep track of threads so we can join them + std::vector< std::thread > workers; + // the task queue + std::queue< std::function > tasks; + + // synchronization + std::mutex queue_mutex; + std::condition_variable condition; + bool stop; +}; + +// the constructor just launches some amount of workers +inline ThreadPool::ThreadPool(size_t threads) + : stop(false) +{ + for(size_t i = 0;i task; + + { + std::unique_lock lock(this->queue_mutex); + this->condition.wait(lock, + [this]{ return this->stop || !this->tasks.empty(); }); + if(this->stop && this->tasks.empty()) + return; + task = std::move(this->tasks.front()); + this->tasks.pop(); + } + + task(); + } + } + ); +} + +// add new work item to the pool +template +auto ThreadPool::enqueue(F&& f, Args&&... args) + -> std::future::type> +{ + using return_type = typename std::result_of::type; + + auto task = std::make_shared< std::packaged_task >( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock(queue_mutex); + + // don't allow enqueueing after stopping the pool + if(stop) + throw std::runtime_error("enqueue on stopped ThreadPool"); + + tasks.emplace([task](){ (*task)(); }); + } + condition.notify_one(); + return res; +} + +// the destructor joins all threads +inline ThreadPool::~ThreadPool() +{ + { + std::unique_lock lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for(std::thread &worker: workers) + worker.join(); +} + +#endif diff --git a/huagao/huagaods.cpp b/huagao/huagaods.cpp index 3acf1c75..8f4f59dd 100644 --- a/huagao/huagaods.cpp +++ b/huagao/huagaods.cpp @@ -16,6 +16,7 @@ #include "Device/PublicFunc.h" #include "Device/GScanO200.h" #include "Device/GScanO400.h" +#include "Device/GScanO1003399.h" #include "Device/filetools.h" #include "Device/GScanVirtual.h" #include @@ -96,7 +97,7 @@ static constexpr const Identity srcIdent( #endif #ifdef G200 -#ifdef ISG100 +#if defined (ISG100) || defined (G1003399) #ifdef LANXUM "G62S Series", #else // ISG100 @@ -126,7 +127,7 @@ static constexpr const Identity srcIdent( #endif #ifdef G200 -#ifdef ISG100 +#if defined (ISG100) || defined (G1003399) #ifdef MAKEHUAGAO "HUAGOSCAN G100 TWAIN" #elif defined LANXUM //!LANXUM @@ -345,19 +346,24 @@ void HuagaoDs::showmsg(std::string caption, std::string text, int retcode) if (scanner.get()) { int losemun = scanner->get_lose_image_num(); int num = 0; + bool is_duplex = m_scanparam->is_duplex; +#ifdef G1003399 + is_duplex = false; +#endif // G1003399 + if ((retcode == 64 || retcode == 8 || retcode == 16) && losemun > 0)//64 ->SStop losemun--; if (!(m_scanparam->is_autodiscradblank_normal || m_scanparam->is_autodiscradblank_vince)) { - if (m_scanparam->is_duplex && m_scanparam->en_fold && m_scanparam->multi_output_red) + if (is_duplex && m_scanparam->en_fold && m_scanparam->multi_output_red) num = scanner->get_imgnReaded() * 2 - scanner->get_imgTransfered(); - else if (m_scanparam->is_duplex && m_scanparam->en_fold) + else if (is_duplex && m_scanparam->en_fold) num = scanner->get_imgnReaded() - scanner->get_imgTransfered(); - else if (m_scanparam->is_duplex && m_scanparam->multi_output_red) + else if (is_duplex && m_scanparam->multi_output_red) num = scanner->get_imgnReaded() * 4 - scanner->get_imgTransfered(); - else if ((!m_scanparam->is_duplex) && m_scanparam->multi_output_red) + else if ((!is_duplex) && m_scanparam->multi_output_red) num = scanner->get_imgnReaded() * 2 - scanner->get_imgTransfered(); - else if (!m_scanparam->is_duplex) + else if (!is_duplex) num = scanner->get_imgnReaded() - scanner->get_imgTransfered(); else num = scanner->get_imgnReaded() * 2 - scanner->get_imgTransfered(); @@ -770,6 +776,7 @@ Result HuagaoDs::identityOpenDs(const Identity&) { showmsg("警告", msgs[(UsbSupported)202]); return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } +#ifndef G1003399 auto usblist = UsbScan_List::find_all_usb(); if (!usblist.empty()) { @@ -790,16 +797,8 @@ Result HuagaoDs::identityOpenDs(const Identity&) { } } } - FileTools::writelog(log_INFO, "open ds find device vid=" + to_string(vid) + "\tpid=" + to_string(pid)); - m_haveError = false; - updataGscanCap(); - bmpData->resize(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)); - BITMAPINFOHEADER& bmInfo = *((BITMAPINFOHEADER*)header()); - bmInfo.biHeight = 2000; - bmInfo.biWidth = 2000; - bmInfo.biBitCount = m_scanparam->pixtype == 2 ? 24 : (m_scanparam->pixtype == 1 ? 8 : 1); - m_iBitdepth = m_scanparam->pixtype == 2 ? 24 : (m_scanparam->pixtype == 1 ? 8 : 1); + FileTools::writelog(log_INFO, "open ds find device vid=" + to_string(vid) + "\tpid=" + to_string(pid)); if (vid == 0 || pid == 0) { //ShellExecute(NULL, TEXT("open"), GetHidedlgPath(), CString("201"), NULL, SW_HIDE); @@ -811,6 +810,10 @@ Result HuagaoDs::identityOpenDs(const Identity&) { return checkDeviceOnline(); } scanner->open(vid, pid); +#else + scanner.reset(new GScanO1003399()); +#endif // G1003399 + if (!scanner->IsConnected()) { //ShellExecute(NULL, TEXT("open"), GetHidedlgPath(), CString("201"), NULL, SW_HIDE); @@ -826,8 +829,6 @@ Result HuagaoDs::identityOpenDs(const Identity&) { scanner->regist_deviceevent_callback(DeviceEvent_callback, this); auto dgcall = [&](int pagenum) { - CString text; - text.Format(_T("74 %d"), pagenum); //ShellExecute(NULL, TEXT("open"), GetHidedlgPath(), text, NULL, SW_HIDE); showmsg("警告", "在" + to_string(pagenum) + "检测到折角!"); scanner->Stop_scan(); @@ -837,6 +838,15 @@ Result HuagaoDs::identityOpenDs(const Identity&) { }; scanner->DogEar_callback(dgcall); } + + m_haveError = false; + updataGscanCap(); + bmpData->resize(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)); + BITMAPINFOHEADER& bmInfo = *((BITMAPINFOHEADER*)header()); + bmInfo.biHeight = 2000; + bmInfo.biWidth = 2000; + bmInfo.biBitCount = m_scanparam->pixtype == 2 ? 24 : (m_scanparam->pixtype == 1 ? 8 : 1); + m_iBitdepth = m_scanparam->pixtype == 2 ? 24 : (m_scanparam->pixtype == 1 ? 8 : 1); //MessageBox(NULL, L"2", L"", 0); // init caps // there are caps a minimal source must support @@ -1916,15 +1926,20 @@ Result HuagaoDs::pendingXfersEnd(const Identity&, PendingXfers& data) { if (!(m_scanparam->is_autodiscradblank_normal || m_scanparam->is_autodiscradblank_vince)) { int num = 0; - if (m_scanparam->is_duplex && m_scanparam->en_fold && m_scanparam->multi_output_red) + bool is_duplex = m_scanparam->is_duplex; +#ifdef G1003399 + is_duplex = false; +#endif // G1003399 + + if (is_duplex && m_scanparam->en_fold && m_scanparam->multi_output_red) num = scanner->get_imgnReaded() * 2 - scanner->get_imgTransfered(); - else if (m_scanparam->is_duplex && m_scanparam->en_fold) + else if (is_duplex && m_scanparam->en_fold) num = scanner->get_imgnReaded() - scanner->get_imgTransfered(); - else if (m_scanparam->is_duplex && m_scanparam->multi_output_red) + else if (is_duplex && m_scanparam->multi_output_red) num = scanner->get_imgnReaded() * 4 - scanner->get_imgTransfered(); - else if ((!m_scanparam->is_duplex) && m_scanparam->multi_output_red) + else if ((!is_duplex) && m_scanparam->multi_output_red) num = scanner->get_imgnReaded() * 2 - scanner->get_imgTransfered(); - else if (!m_scanparam->is_duplex) + else if (!is_duplex) num = scanner->get_imgnReaded() - scanner->get_imgTransfered(); else num = scanner->get_imgnReaded() * 2 - scanner->get_imgTransfered(); @@ -1991,13 +2006,13 @@ Result HuagaoDs::userInterfaceEnable(const Identity&, UserInterface& ui) { if (!ui.showUi()) { // this is an exception when we want to set state explicitly, notifyXferReady can be called only in enabled state // with hidden UI, the usual workflow DsState::Enabled -> notifyXferReady() -> DsState::XferReady is a single step -//#ifndef G200 +#ifndef G200 while (!scanner->Get_Scanner_PaperOn()) { if (MessageBox(NULL, L"检测到无纸,请添加纸张", L"提示", MB_YESNO | MB_SYSTEMMODAL) == IDNO) return seqError(); } - //#endif // !G200 +#endif // !G200 auto ret = startScan(); diff --git a/huagao/stdafx.h b/huagao/stdafx.h index e915f7854b742b5eff5e1e5d8da5ae6cf1ffda2f..04a82056a0f75925d77ad2ce7f54ba949298d49d 100644 GIT binary patch delta 211 zcmZ3Huq}B*7RTl!jxff_3EF)8?hJ+u20&;Ggq94JlQ(j{p8QKh21EQj=Sgm5hE#?e zhGHP8F!?-Z>}D?RIGM>}nnI!)44w?Z3^1cK859_57-}XPY6uHzz(rveOw>`H+@PVf Zd7pX*Gr5{4f6(Tb+@a&M`Gd9v3joj)FCG8@ delta 91 zcmdm%ydq&k7RTf+MuE*u9CnP8Z}9L;{=j)-aux^IWD8!N$uT-ylNI