From bbd891b2438440a1a7dfae48afad94f68ad8ec17 Mon Sep 17 00:00:00 2001 From: bobpan Date: Fri, 16 Oct 2020 16:41:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9ElpCoretrl=E3=80=81lpmain?= =?UTF-8?q?=E7=AD=89=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=88=9D=E6=AD=A5=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E5=8E=9F=E6=9C=89=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E8=B0=83=E9=80=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lpBase/SystemStateInfo.h | 19 + src/lpBase/baseClass.h | 35 + src/lpBase/baseConstant.h | 20 + src/lpBase/baseDefine.h | 72 ++ src/lpBase/baseFunction.h | 31 + src/lpBase/baseInclude.h | 20 + src/lpBase/baseStruct.h | 474 +++++++++ src/lpBase/iAlgorithm.h | 185 ++++ src/lpBase/iCoreCtrl.h | 244 +++++ src/lpBase/iImgProc.h | 46 + src/lpBase/iIoCtrl.h | 63 ++ src/lpBase/icamera.h | 105 ++ src/lpBase/lpalgorithm.h | 50 + src/lpBase/lpbMetaTypeDefine.h | 389 +++++++ src/lpBase/lpbdefine.h | 184 ++++ src/lpBase/lpbengine.h | Bin 0 -> 41322 bytes src/lpBase/lpdesigner.h | 41 + src/lpBase/lpdsgdefine.h | 9 + src/lpBase/sysInclude.h | 26 + src/lpCoreCtrl/AlgorithmResult.cpp | 48 + src/lpCoreCtrl/AlgorithmResult.h | 22 + src/lpCoreCtrl/AlgorithmShared.cpp | 45 + src/lpCoreCtrl/AlgorithmShared.h | 29 + src/lpCoreCtrl/CoreCtrl.cpp | 479 +++++++++ src/lpCoreCtrl/CoreCtrl.h | 100 ++ src/lpCoreCtrl/GlobalDataBase.cpp | 53 + src/lpCoreCtrl/GlobalDataBase.h | 20 + src/lpCoreCtrl/ImageObject.cpp | 249 +++++ src/lpCoreCtrl/ImageObject.h | 51 + src/lpCoreCtrl/LoadModule.cpp | 55 + src/lpCoreCtrl/LoadModule.h | 32 + src/lpCoreCtrl/ModulesManager.cpp | 109 ++ src/lpCoreCtrl/ModulesManager.h | 38 + src/lpCoreCtrl/QZkShowImage.cpp | 40 + src/lpCoreCtrl/QZkShowImage.h | 31 + src/lpCoreCtrl/ShowWindow.cpp | 332 ++++++ src/lpCoreCtrl/ShowWindow.h | 63 ++ src/lpCoreCtrl/XmlConfigParser.cpp | 31 + src/lpCoreCtrl/XmlConfigParser.h | 25 + src/lpCoreCtrl/ZkCameraImage.cpp | 338 +++++++ src/lpCoreCtrl/ZkCameraImage.h | 148 +++ src/lpCoreCtrl/ZkImage.cpp | 74 ++ src/lpCoreCtrl/ZkImage.h | 42 + src/lpCoreCtrl/globalCoreCtrl.cpp | 36 + src/lpCoreCtrl/globalCoreCtrl.h | 73 ++ src/lpCoreCtrl/iCameraObject.cpp | 11 + src/lpCoreCtrl/iCameraObject.h | 313 ++++++ src/lpCoreCtrl/qtpthreadbase.cpp | 56 ++ src/lpCoreCtrl/qtpthreadbase.h | 33 + src/lpCoreCtrl/qtpthreadimage.cpp | 77 ++ src/lpCoreCtrl/qtpthreadimage.h | 30 + src/lpCoreCtrl/tadpoleGuiHeader.h | 57 ++ src/lpCoreCtrl/tpCamera/CameraPool.cpp | 883 ++++++++++++++++ src/lpCoreCtrl/tpCamera/CameraPool.h | 150 +++ src/lpCoreCtrl/tpCamera/LibCameraes.cpp | 203 ++++ src/lpCoreCtrl/tpCamera/LibCameraes.h | 54 + src/lpCoreCtrl/tpCamera/globalCamera.cpp | 460 +++++++++ src/lpCoreCtrl/tpCamera/globalCamera.h | 97 ++ src/lpCoreCtrl/tpCamera/tpCamera.cpp | 60 ++ src/lpCoreCtrl/tpCamera/triggerthread.cpp | 61 ++ src/lpCoreCtrl/tpCamera/triggerthread.h | 37 + src/lpCoreCtrl/tpCoreCtrl.cpp | 75 ++ src/lpCoreCtrl/tpImgProc/AlgorithmOption.cpp | 178 ++++ src/lpCoreCtrl/tpImgProc/AlgorithmOption.h | 47 + src/lpCoreCtrl/tpImgProc/IImageAlgorithm.cpp | 28 + src/lpCoreCtrl/tpImgProc/IImageAlgorithm.h | 30 + src/lpCoreCtrl/tpImgProc/ImageAlgorithm.cpp | 23 + src/lpCoreCtrl/tpImgProc/ImageAlgorithm.h | 34 + src/lpCoreCtrl/tpImgProc/ImgProc.cpp | 217 ++++ src/lpCoreCtrl/tpImgProc/ImgProc.h | 56 ++ src/lpCoreCtrl/tpImgProc/LoadAlgorithm.cpp | 72 ++ src/lpCoreCtrl/tpImgProc/LoadAlgorithm.h | 35 + src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.cpp | 95 ++ src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.h | 21 + src/lpCoreCtrl/tpImgProc/tpImgProc.cpp | 54 + src/lpCoreCtrl/tpJsonConfig.h | 29 + src/lpMain/AutoTrigger.cpp | 29 + src/lpMain/AutoTrigger.h | 21 + src/lpMain/CoreCtrl/CDllCoreCtrl.cpp | 79 ++ src/lpMain/CoreCtrl/CDllCoreCtrl.h | 21 + src/lpMain/CoreCtrl/CDllDetectorEngine.cpp | 79 ++ src/lpMain/CoreCtrl/CDllDetectorEngine.h | 19 + src/lpMain/CoreCtrl/QDetectorDesignerMgr.cpp | 64 ++ src/lpMain/CoreCtrl/QDetectorDesignerMgr.h | 31 + src/lpMain/IStation.h | 56 ++ src/lpMain/IWfCtrl.h | 36 + src/lpMain/ModelTable.cpp | 122 +++ src/lpMain/ModelTable.h | 37 + .../QDiskCleanThread/QDiskCleanThread.cpp | 184 ++++ .../QDiskCleanThread/QDiskCleanThread.h | 50 + src/lpMain/QDiskCleanThread/WorkChecker.cpp | 51 + src/lpMain/QDiskCleanThread/WorkChecker.h | 23 + src/lpMain/QDiskCleanThread/solarCellHelper.h | 90 ++ src/lpMain/Resource/app.png | Bin 0 -> 5841 bytes src/lpMain/Resource/app2.png | Bin 0 -> 48804 bytes src/lpMain/Resource/toolBar/administrator.png | Bin 0 -> 3030 bytes src/lpMain/Resource/toolBar/background.png | Bin 0 -> 93764 bytes src/lpMain/Resource/toolBar/cali.png | Bin 0 -> 17226 bytes src/lpMain/Resource/toolBar/education24.png | Bin 0 -> 17539 bytes src/lpMain/Resource/toolBar/help.png | Bin 0 -> 13342 bytes src/lpMain/Resource/toolBar/messenger.png | Bin 0 -> 2987 bytes src/lpMain/Resource/toolBar/model.png | Bin 0 -> 1757 bytes src/lpMain/Resource/toolBar/save.png | Bin 0 -> 18714 bytes src/lpMain/Resource/toolBar/setting.png | Bin 0 -> 8022 bytes src/lpMain/Resource/toolBar/test.png | Bin 0 -> 16087 bytes src/lpMain/Resource/wanfeng.png | Bin 0 -> 21164 bytes src/lpMain/Resource/wanfeng2.png | Bin 0 -> 7586 bytes src/lpMain/Station.cpp | 601 +++++++++++ src/lpMain/Station.h | 96 ++ src/lpMain/TrigDetector.cpp | 184 ++++ src/lpMain/TrigDetector.h | 69 ++ src/lpMain/WfColossus.cpp | 380 +++++++ src/lpMain/WfColossus.h | 46 + src/lpMain/WfCtrl.cpp | 395 ++++++++ src/lpMain/WfCtrl.h | 69 ++ src/lpMain/WfModel.h | 16 + src/lpMain/algela/QTipWidget.cpp | 118 +++ src/lpMain/algela/QTipWidget.h | 42 + src/lpMain/algela/RoiImgViewer.cpp | 670 ++++++++++++ src/lpMain/algela/RoiImgViewer.h | 109 ++ src/lpMain/algela/lpImgViewer.cpp | 235 +++++ src/lpMain/algela/lpImgViewer.h | 81 ++ src/lpMain/lpMain.qrc | 22 + src/lpMain/sqliteDB/DetectDataDB.cpp | 399 ++++++++ src/lpMain/sqliteDB/DetectDataDB.h | 34 + src/lpMain/sqliteDB/InfoFile.h | 53 + src/lpMain/sqliteDB/QSqliteGeneral.cpp | 133 +++ src/lpMain/sqliteDB/QSqliteGeneral.h | 18 + src/lpMain/sqliteDB/QSqliteWheelHubWf.cpp | 343 +++++++ src/lpMain/sqliteDB/QSqliteWheelHubWf.h | 64 ++ src/lpMain/sqliteDB/databasesql.cpp | 165 +++ src/lpMain/sqliteDB/databasesql.h | 73 ++ src/lpMain/sqliteDB/gensql.cpp | 116 +++ src/lpMain/sqliteDB/gensql.h | 15 + src/lpMain/sqliteDB/qcheckdatadlg.cpp | 372 +++++++ src/lpMain/sqliteDB/qcheckdatadlg.h | 58 ++ src/lpMain/sqliteDB/qcheckdatadlg.ui | 386 +++++++ src/lpMain/sqliteDB/stationdb.cpp | 94 ++ src/lpMain/sqliteDB/stationdb.h | 35 + tpvs17/Enchanter/Enchanter.qrc | 4 + tpvs17/Enchanter/Enchanter.vcxproj | 139 +++ tpvs17/Enchanter/Enchanter.vcxproj.filters | 47 + tpvs17/Enchanter/Enchanter.vcxproj.user | 15 + tpvs17/Enchanter/IMainWidget.h | 20 + tpvs17/Enchanter/main.cpp | 35 + tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj | 141 +++ tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.filters | 212 ++++ tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.user | 12 + tpvs17/lpMain/CMainWin.cpp | 951 ++++++++++++++++++ tpvs17/lpMain/CMainWin.h | 135 +++ tpvs17/lpMain/CMainWin.ui | 386 +++++++ tpvs17/lpMain/IMainWidget.h | 20 + tpvs17/lpMain/QAboutUI.cpp | 12 + tpvs17/lpMain/QAboutUI.h | 19 + tpvs17/lpMain/QAboutUI.ui | 34 + tpvs17/lpMain/QModelMangerUI.cpp | 203 ++++ tpvs17/lpMain/QModelMangerUI.h | 34 + tpvs17/lpMain/QModelMangerUI.ui | 199 ++++ tpvs17/lpMain/QPLCIndexUI.cpp | 40 + tpvs17/lpMain/QPLCIndexUI.h | 23 + tpvs17/lpMain/QPLCIndexUI.ui | 109 ++ tpvs17/lpMain/QTestModeWid.cpp | 33 + tpvs17/lpMain/QTestModeWid.h | 28 + tpvs17/lpMain/QTestModeWid.ui | 110 ++ tpvs17/lpMain/lpMain.cpp | 29 + tpvs17/lpMain/lpMain.h | 18 + tpvs17/lpMain/lpMain.vcxproj | 326 ++++++ tpvs17/lpMain/lpMain.vcxproj.filters | 300 ++++++ tpvs17/lpMain/lpMain.vcxproj.user | 15 + tpvs17/tpMain/toolBar/messenger.png | Bin 0 -> 2987 bytes 170 files changed, 18284 insertions(+) create mode 100644 src/lpBase/SystemStateInfo.h create mode 100644 src/lpBase/baseClass.h create mode 100644 src/lpBase/baseConstant.h create mode 100644 src/lpBase/baseDefine.h create mode 100644 src/lpBase/baseFunction.h create mode 100644 src/lpBase/baseInclude.h create mode 100644 src/lpBase/baseStruct.h create mode 100644 src/lpBase/iAlgorithm.h create mode 100644 src/lpBase/iCoreCtrl.h create mode 100644 src/lpBase/iImgProc.h create mode 100644 src/lpBase/iIoCtrl.h create mode 100644 src/lpBase/icamera.h create mode 100644 src/lpBase/lpalgorithm.h create mode 100644 src/lpBase/lpbMetaTypeDefine.h create mode 100644 src/lpBase/lpbdefine.h create mode 100644 src/lpBase/lpbengine.h create mode 100644 src/lpBase/lpdesigner.h create mode 100644 src/lpBase/lpdsgdefine.h create mode 100644 src/lpBase/sysInclude.h create mode 100644 src/lpCoreCtrl/AlgorithmResult.cpp create mode 100644 src/lpCoreCtrl/AlgorithmResult.h create mode 100644 src/lpCoreCtrl/AlgorithmShared.cpp create mode 100644 src/lpCoreCtrl/AlgorithmShared.h create mode 100644 src/lpCoreCtrl/CoreCtrl.cpp create mode 100644 src/lpCoreCtrl/CoreCtrl.h create mode 100644 src/lpCoreCtrl/GlobalDataBase.cpp create mode 100644 src/lpCoreCtrl/GlobalDataBase.h create mode 100644 src/lpCoreCtrl/ImageObject.cpp create mode 100644 src/lpCoreCtrl/ImageObject.h create mode 100644 src/lpCoreCtrl/LoadModule.cpp create mode 100644 src/lpCoreCtrl/LoadModule.h create mode 100644 src/lpCoreCtrl/ModulesManager.cpp create mode 100644 src/lpCoreCtrl/ModulesManager.h create mode 100644 src/lpCoreCtrl/QZkShowImage.cpp create mode 100644 src/lpCoreCtrl/QZkShowImage.h create mode 100644 src/lpCoreCtrl/ShowWindow.cpp create mode 100644 src/lpCoreCtrl/ShowWindow.h create mode 100644 src/lpCoreCtrl/XmlConfigParser.cpp create mode 100644 src/lpCoreCtrl/XmlConfigParser.h create mode 100644 src/lpCoreCtrl/ZkCameraImage.cpp create mode 100644 src/lpCoreCtrl/ZkCameraImage.h create mode 100644 src/lpCoreCtrl/ZkImage.cpp create mode 100644 src/lpCoreCtrl/ZkImage.h create mode 100644 src/lpCoreCtrl/globalCoreCtrl.cpp create mode 100644 src/lpCoreCtrl/globalCoreCtrl.h create mode 100644 src/lpCoreCtrl/iCameraObject.cpp create mode 100644 src/lpCoreCtrl/iCameraObject.h create mode 100644 src/lpCoreCtrl/qtpthreadbase.cpp create mode 100644 src/lpCoreCtrl/qtpthreadbase.h create mode 100644 src/lpCoreCtrl/qtpthreadimage.cpp create mode 100644 src/lpCoreCtrl/qtpthreadimage.h create mode 100644 src/lpCoreCtrl/tadpoleGuiHeader.h create mode 100644 src/lpCoreCtrl/tpCamera/CameraPool.cpp create mode 100644 src/lpCoreCtrl/tpCamera/CameraPool.h create mode 100644 src/lpCoreCtrl/tpCamera/LibCameraes.cpp create mode 100644 src/lpCoreCtrl/tpCamera/LibCameraes.h create mode 100644 src/lpCoreCtrl/tpCamera/globalCamera.cpp create mode 100644 src/lpCoreCtrl/tpCamera/globalCamera.h create mode 100644 src/lpCoreCtrl/tpCamera/tpCamera.cpp create mode 100644 src/lpCoreCtrl/tpCamera/triggerthread.cpp create mode 100644 src/lpCoreCtrl/tpCamera/triggerthread.h create mode 100644 src/lpCoreCtrl/tpCoreCtrl.cpp create mode 100644 src/lpCoreCtrl/tpImgProc/AlgorithmOption.cpp create mode 100644 src/lpCoreCtrl/tpImgProc/AlgorithmOption.h create mode 100644 src/lpCoreCtrl/tpImgProc/IImageAlgorithm.cpp create mode 100644 src/lpCoreCtrl/tpImgProc/IImageAlgorithm.h create mode 100644 src/lpCoreCtrl/tpImgProc/ImageAlgorithm.cpp create mode 100644 src/lpCoreCtrl/tpImgProc/ImageAlgorithm.h create mode 100644 src/lpCoreCtrl/tpImgProc/ImgProc.cpp create mode 100644 src/lpCoreCtrl/tpImgProc/ImgProc.h create mode 100644 src/lpCoreCtrl/tpImgProc/LoadAlgorithm.cpp create mode 100644 src/lpCoreCtrl/tpImgProc/LoadAlgorithm.h create mode 100644 src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.cpp create mode 100644 src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.h create mode 100644 src/lpCoreCtrl/tpImgProc/tpImgProc.cpp create mode 100644 src/lpCoreCtrl/tpJsonConfig.h create mode 100644 src/lpMain/AutoTrigger.cpp create mode 100644 src/lpMain/AutoTrigger.h create mode 100644 src/lpMain/CoreCtrl/CDllCoreCtrl.cpp create mode 100644 src/lpMain/CoreCtrl/CDllCoreCtrl.h create mode 100644 src/lpMain/CoreCtrl/CDllDetectorEngine.cpp create mode 100644 src/lpMain/CoreCtrl/CDllDetectorEngine.h create mode 100644 src/lpMain/CoreCtrl/QDetectorDesignerMgr.cpp create mode 100644 src/lpMain/CoreCtrl/QDetectorDesignerMgr.h create mode 100644 src/lpMain/IStation.h create mode 100644 src/lpMain/IWfCtrl.h create mode 100644 src/lpMain/ModelTable.cpp create mode 100644 src/lpMain/ModelTable.h create mode 100644 src/lpMain/QDiskCleanThread/QDiskCleanThread.cpp create mode 100644 src/lpMain/QDiskCleanThread/QDiskCleanThread.h create mode 100644 src/lpMain/QDiskCleanThread/WorkChecker.cpp create mode 100644 src/lpMain/QDiskCleanThread/WorkChecker.h create mode 100644 src/lpMain/QDiskCleanThread/solarCellHelper.h create mode 100644 src/lpMain/Resource/app.png create mode 100644 src/lpMain/Resource/app2.png create mode 100644 src/lpMain/Resource/toolBar/administrator.png create mode 100644 src/lpMain/Resource/toolBar/background.png create mode 100644 src/lpMain/Resource/toolBar/cali.png create mode 100644 src/lpMain/Resource/toolBar/education24.png create mode 100644 src/lpMain/Resource/toolBar/help.png create mode 100644 src/lpMain/Resource/toolBar/messenger.png create mode 100644 src/lpMain/Resource/toolBar/model.png create mode 100644 src/lpMain/Resource/toolBar/save.png create mode 100644 src/lpMain/Resource/toolBar/setting.png create mode 100644 src/lpMain/Resource/toolBar/test.png create mode 100644 src/lpMain/Resource/wanfeng.png create mode 100644 src/lpMain/Resource/wanfeng2.png create mode 100644 src/lpMain/Station.cpp create mode 100644 src/lpMain/Station.h create mode 100644 src/lpMain/TrigDetector.cpp create mode 100644 src/lpMain/TrigDetector.h create mode 100644 src/lpMain/WfColossus.cpp create mode 100644 src/lpMain/WfColossus.h create mode 100644 src/lpMain/WfCtrl.cpp create mode 100644 src/lpMain/WfCtrl.h create mode 100644 src/lpMain/WfModel.h create mode 100644 src/lpMain/algela/QTipWidget.cpp create mode 100644 src/lpMain/algela/QTipWidget.h create mode 100644 src/lpMain/algela/RoiImgViewer.cpp create mode 100644 src/lpMain/algela/RoiImgViewer.h create mode 100644 src/lpMain/algela/lpImgViewer.cpp create mode 100644 src/lpMain/algela/lpImgViewer.h create mode 100644 src/lpMain/lpMain.qrc create mode 100644 src/lpMain/sqliteDB/DetectDataDB.cpp create mode 100644 src/lpMain/sqliteDB/DetectDataDB.h create mode 100644 src/lpMain/sqliteDB/InfoFile.h create mode 100644 src/lpMain/sqliteDB/QSqliteGeneral.cpp create mode 100644 src/lpMain/sqliteDB/QSqliteGeneral.h create mode 100644 src/lpMain/sqliteDB/QSqliteWheelHubWf.cpp create mode 100644 src/lpMain/sqliteDB/QSqliteWheelHubWf.h create mode 100644 src/lpMain/sqliteDB/databasesql.cpp create mode 100644 src/lpMain/sqliteDB/databasesql.h create mode 100644 src/lpMain/sqliteDB/gensql.cpp create mode 100644 src/lpMain/sqliteDB/gensql.h create mode 100644 src/lpMain/sqliteDB/qcheckdatadlg.cpp create mode 100644 src/lpMain/sqliteDB/qcheckdatadlg.h create mode 100644 src/lpMain/sqliteDB/qcheckdatadlg.ui create mode 100644 src/lpMain/sqliteDB/stationdb.cpp create mode 100644 src/lpMain/sqliteDB/stationdb.h create mode 100644 tpvs17/Enchanter/Enchanter.qrc create mode 100644 tpvs17/Enchanter/Enchanter.vcxproj create mode 100644 tpvs17/Enchanter/Enchanter.vcxproj.filters create mode 100644 tpvs17/Enchanter/Enchanter.vcxproj.user create mode 100644 tpvs17/Enchanter/IMainWidget.h create mode 100644 tpvs17/Enchanter/main.cpp create mode 100644 tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj create mode 100644 tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.filters create mode 100644 tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.user create mode 100644 tpvs17/lpMain/CMainWin.cpp create mode 100644 tpvs17/lpMain/CMainWin.h create mode 100644 tpvs17/lpMain/CMainWin.ui create mode 100644 tpvs17/lpMain/IMainWidget.h create mode 100644 tpvs17/lpMain/QAboutUI.cpp create mode 100644 tpvs17/lpMain/QAboutUI.h create mode 100644 tpvs17/lpMain/QAboutUI.ui create mode 100644 tpvs17/lpMain/QModelMangerUI.cpp create mode 100644 tpvs17/lpMain/QModelMangerUI.h create mode 100644 tpvs17/lpMain/QModelMangerUI.ui create mode 100644 tpvs17/lpMain/QPLCIndexUI.cpp create mode 100644 tpvs17/lpMain/QPLCIndexUI.h create mode 100644 tpvs17/lpMain/QPLCIndexUI.ui create mode 100644 tpvs17/lpMain/QTestModeWid.cpp create mode 100644 tpvs17/lpMain/QTestModeWid.h create mode 100644 tpvs17/lpMain/QTestModeWid.ui create mode 100644 tpvs17/lpMain/lpMain.cpp create mode 100644 tpvs17/lpMain/lpMain.h create mode 100644 tpvs17/lpMain/lpMain.vcxproj create mode 100644 tpvs17/lpMain/lpMain.vcxproj.filters create mode 100644 tpvs17/lpMain/lpMain.vcxproj.user create mode 100644 tpvs17/tpMain/toolBar/messenger.png diff --git a/src/lpBase/SystemStateInfo.h b/src/lpBase/SystemStateInfo.h new file mode 100644 index 0000000..69755ec --- /dev/null +++ b/src/lpBase/SystemStateInfo.h @@ -0,0 +1,19 @@ +#ifndef SYSTEMSTATEINFO +#define SYSTEMSTATEINFO + +#include +#include "lpdsgdefine.h" + +class SystemStateInfo +{ +public: + static bool bParamStateFlag; + static int rgb; + static QSettings setIni; +}; + + +#endif // SYSTEMSTATEINFO + + + diff --git a/src/lpBase/baseClass.h b/src/lpBase/baseClass.h new file mode 100644 index 0000000..6e0a372 --- /dev/null +++ b/src/lpBase/baseClass.h @@ -0,0 +1,35 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:baseClass.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/08 + History:8:4:2015 13:13 + *******************************************************************************/ +#ifndef __BASE_CLASS_H_20150408 +#define __BASE_CLASS_H_20150408 + +//#include "baseDefine.h" +//#include "ZkImage.h" +#include +namespace QZK { + class QAtomicIntAdder{ + public: + QAtomicIntAdder(QAtomicInt& ai) + : m_ai(ai) { + ++m_ai; + } + ~QAtomicIntAdder() { + --m_ai; + } + private: + QAtomicInt& m_ai; + }; +}; + +namespace CZK { + +} + +#endif \ No newline at end of file diff --git a/src/lpBase/baseConstant.h b/src/lpBase/baseConstant.h new file mode 100644 index 0000000..684394d --- /dev/null +++ b/src/lpBase/baseConstant.h @@ -0,0 +1,20 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:baseConstant.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/08 + History:8:4:2015 12:55 + *******************************************************************************/ +#ifndef __BASE_CONSTNAT_H_20150608 +#define __BASE_CONSTNAT_H_20150608 + +namespace TP_STR{ + const char cszJsonSuffix[] = ".json"; + const char cszDatSuffix[] = ".dat"; +// const char cszCfgFolder[] = "config\\"; +// const char cszMainCfg[] = "main"; +} + +#endif \ No newline at end of file diff --git a/src/lpBase/baseDefine.h b/src/lpBase/baseDefine.h new file mode 100644 index 0000000..550fbfe --- /dev/null +++ b/src/lpBase/baseDefine.h @@ -0,0 +1,72 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:baseDefine.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/07 + History:7:4:2015 9:13 + *******************************************************************************/ +#ifndef __BASE_DEFINE_H_20150407 +#define __BASE_DEFINE_H_20150407 + +#include "sysInclude.h" + +#ifndef BYTE +typedef unsigned char BYTE; +#endif +#ifndef WORD +typedef unsigned short WORD; +#endif +#ifndef DWORD +typedef unsigned long DWORD; +#endif +//#ifndef size_t +//typedef unsigned int size_t; +//#endif + +#define MAX(a, b) (a) > (b) ? (a) : (b) +#define MIN(a, b) (a) > (b) ? (b) : (a) +#define ABS(a) (a) < 0 ? (-(a)) : (a) +#define SELF_ABS(a) if( a < 0 ) a = -a + + +#define ZStringA QByteArray + +//#define USE_PIXMAP_AS_SHOW +#ifdef USE_PIXMAP_AS_SHOW +#define ZShowImage QPixmap +#else +#define ZShowImage QImage +#endif + + +//#define QT_NO_DEBUG_OUTPUT //open this remark to enable qDebug +#ifndef tpDebugOut +#define tpDebugOut(...) qDebug(__VA_ARGS__) +#endif + +enum emTpColorFormat{ + TP_COLOR_NONE = 0, + TP_COLOR_Y800 = 1,//"GRAY8" + TP_COLOR_RGB24 = 2,//"RGB24" + TP_COLOR_RGB32 = 3,//"RGB32" + TP_COLOR_Y16 = 4,//"GRAY16" + TP_COLOR_RGB48 = 5, + TP_COLOR_BGR24 = 11,//"BGR24" + TP_COLOR_BGR32 = 12,//"BGR32" + // UYVY = 5, +}; + +//#define _USE_SPIDER_LOG + +enum emTpWarningCode { + WARNING_NO, + WARNING_COMM_CLOSED, +}; + +#ifndef PI +#define PI 3.1415927 +#endif + +#endif \ No newline at end of file diff --git a/src/lpBase/baseFunction.h b/src/lpBase/baseFunction.h new file mode 100644 index 0000000..55c9317 --- /dev/null +++ b/src/lpBase/baseFunction.h @@ -0,0 +1,31 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:baseFunction.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/08 + History:8:4:2015 13:04 + *******************************************************************************/ +#ifndef __BASE_FUNCTION_H_20150408 +#define __BASE_FUNCTION_H_20150408 + +#include "sysInclude.h" + +namespace SYS_F { + int GetCpus() { +#if defined(Q_OS_WIN) || defined(WIN32) || defined(WIN64) + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return sysInfo.dwNumberOfProcessors; +#else + return 0; +#endif + } +} + +namespace TP_F { + +} + +#endif \ No newline at end of file diff --git a/src/lpBase/baseInclude.h b/src/lpBase/baseInclude.h new file mode 100644 index 0000000..eeeeca5 --- /dev/null +++ b/src/lpBase/baseInclude.h @@ -0,0 +1,20 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:baseInclude.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/07 + History:7:4:2015 9:16 + *******************************************************************************/ +#ifndef __BASE_INCLUDE_H_20150407 +#define __BASE_INCLUDE_H_20150407 + +#include "sysInclude.h" +#include "baseDefine.h" + +#include "baseConstant.h" +#include "baseStruct.h" +#include "baseClass.h" + +#endif \ No newline at end of file diff --git a/src/lpBase/baseStruct.h b/src/lpBase/baseStruct.h new file mode 100644 index 0000000..8c5adce --- /dev/null +++ b/src/lpBase/baseStruct.h @@ -0,0 +1,474 @@ +/****************************************************************************** +Copyright(C):2015~2018 hzleaper +FileName:baseStruct.h +Author:zhikun wu +Email:zk.wu@hzleaper.com +Tools:vs2010 pc on company +Created:2015/04/09 +History:9:4:2015 10:33 +*******************************************************************************/ +#ifndef __BASE_STRUCT_H +#define __BASE_STRUCT_H + +#include "baseDefine.h" +#include "baseConstant.h" +#include "iAlgorithm.h" + +#define BASE_MAX_FILE_PATH 260 +#define BASE_MAX_FOLDER_NAME_SIZE 64 +#define BASE_MAX_FILE_NAME_SIZE 64 + +enum emTpFolderNameList { + TP_FOLDER_NAME_CONFIG = 0, + TP_FOLDER_NAME_PICTURE = 1, + TP_FOLDER_NAME_UI = 2, + TP_FOLDER_NAME_MAX_NUM = 32, +}; +typedef struct tagTP_GLOBAL_DATA{ + char tgdMainPath[BASE_MAX_FILE_PATH]; + char tgdUserPath[BASE_MAX_FILE_PATH]; + char tgdDllPath[BASE_MAX_FILE_PATH]; + char tgdFolderNames[TP_FOLDER_NAME_MAX_NUM][BASE_MAX_FOLDER_NAME_SIZE]; + // char tgdFileNames[TP_FILE_NAME_MAX_NUM][BASE_MAX_FILE_NAME_SIZE]; + void* tgdpDllPaths;//pointer to a stringlist +}TP_GLOBAL_DATA; + +inline ZStringA TPConfigFileWholePath(const TP_GLOBAL_DATA* pGlobal, const char* szFile) { + ZStringA szFileName(pGlobal->tgdMainPath); + szFileName.append(pGlobal->tgdFolderNames[TP_FOLDER_NAME_CONFIG]).append(szFile).append(TP_STR::cszJsonSuffix); + return szFileName; +} + +///////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////// + +enum emTpImageShowType { + TP_IMAGE_SHOW_NO = 0, + TP_IMAGE_SHOW_EVERY = 1, + TP_IMAGE_SHOW_NEW = 2, +}; + +typedef struct tagTP_CORE_SETTING{ + int threadsCount; + int imageShowType; +}TP_CORE_SETTING; + +enum emTpDeviceType { + DEV_CAM_NONE = 0, + DEV_CAM_VIRTUAL = 100, + DEV_CAM_VIRTUAL_FOLDER = 100, + DEV_CAM_BITFLOW = 110, + DEV_CAM_BITFLOW_BI = 110, + DEV_CAM_GIGE = 120, + DEV_CAM_USB = 130, + DEV_CAM_HIK = 140, + DEV_CAM_VIMBA = 150, + DEV_CAM_BAUMER = 160, + DEV_CAM_ADLINK = 170, + DEV_CAM_DAHUA = 180, + DEV_CAM_BASLER = 190, + DEV_CAM_PTGRAY = 200, + DEV_CAM_EURESYS = 220, + DEV_CAM_DALSA = 230, +}; + +static const QMap gCameraNamesMap = { + { DEV_CAM_NONE, "Unknown(0)" }, + { DEV_CAM_VIRTUAL, "Virtual Camera(100)" }, + { DEV_CAM_VIRTUAL_FOLDER, "Virtual Camera(100)" }, + { DEV_CAM_BITFLOW, "BitFlow(110)" }, + { DEV_CAM_BITFLOW_BI, "BitFlow(110)" }, + { DEV_CAM_GIGE, "Gige(120)" }, + { DEV_CAM_USB, "USB Camera(130)" }, + { DEV_CAM_HIK, "HIK(140)" }, + { DEV_CAM_VIMBA, "Vimba(150)" }, + { DEV_CAM_BAUMER, "Baumer(160)" }, + { DEV_CAM_ADLINK, "AdLink(170)" }, + { DEV_CAM_DAHUA, "DaHua(180)" }, + { DEV_CAM_BASLER, "Basler(190)" }, + { DEV_CAM_PTGRAY, "PtGray(200)" }, + { DEV_CAM_EURESYS, "Euresys(220)" }, + { DEV_CAM_DALSA, "Dalsa(230)" } +}; + +#define TP_STRING_MAX_SIZE 256 + +enum emTpCameraStatus{ + TP_CAMERA_STATUS_UNINIT = 0, // 未初始化(刚从配置文件加载) + TP_CAMERA_STATUS_INITED = 1, // 已初始化(即加载DLL成功) + TP_CAMERA_STATUS_OPENED = 2, // 已打开 + TP_CAMERA_STATUS_STARTED = 3, // 运行中 + TP_CAMERA_STATUS_STOPPED = 4, // 暂停 + TP_CAMERA_STATUS_CLOSED = 5, // 已关闭 +}; + +static const QMap gCameraStatusMap = { + { TP_CAMERA_STATUS_UNINIT, "Uninitialized" }, + { TP_CAMERA_STATUS_INITED, "Initialized" }, + { TP_CAMERA_STATUS_OPENED, "Opened" }, + { TP_CAMERA_STATUS_STARTED, "Running" }, + { TP_CAMERA_STATUS_STOPPED, "Paused" }, + { TP_CAMERA_STATUS_CLOSED, "Closed" } +}; + +typedef struct tagTP_CAMERA_OPTION { + tagTP_CAMERA_OPTION() + :id(0), deviceType(0), save(0), loop(0), width(0), + height(0), zoom(0), format(TP_COLOR_NONE), algorithm(0), exposure(0), + boardType(0), boardNum(0), gain(0), bIgnoreImage(false), + bNeedRgbSwapped(false), bAutoOpen(true), bAutoPush(true), + status(TP_CAMERA_STATUS_UNINIT) + { } + int id; + int deviceType; + BYTE save; + BYTE loop;// + BYTE reserve[2]; + QString showName; + QString uniqueName; + int width; + int height; + int zoom; + emTpColorFormat format; + QString folder; + QString saveImgSuffix; + int algorithm; + QString relyOnCamera; + long exposure; + int boardType; + int boardNum; + long gain; + QString dllsuffix; + QString sBoardName; + QString sBoardConfigFile; + + // used inner + bool bIgnoreImage; + QStringList* pSerials; + bool bNeedRgbSwapped; + QString macAddress; + bool bAutoOpen; + QString cameraFilePath; + bool bAutoPush; // 默认一旦得到图片,就直接push进pool。 + + // camera status - used for app inside + emTpCameraStatus status; + + bool operator==(const tagTP_CAMERA_OPTION& rhs) + { + return (id == rhs.id) && (deviceType == rhs.deviceType) && + (save == rhs.save) && (loop == rhs.loop) && + (showName == rhs.showName) && + (uniqueName == rhs.uniqueName) && + (zoom == rhs.zoom) && (format == rhs.format) && + (folder == rhs.folder) && + (saveImgSuffix == rhs.saveImgSuffix) && + (algorithm == rhs.algorithm) && (relyOnCamera == rhs.relyOnCamera) && + (exposure == rhs.exposure) && (boardType == rhs.boardType) && + (boardNum == rhs.boardNum) && + (gain == rhs.gain) && + (dllsuffix == rhs.dllsuffix) && + (sBoardName == rhs.sBoardName) && + (sBoardConfigFile == rhs.sBoardConfigFile) && + (bAutoPush == rhs.bAutoPush) && + (bNeedRgbSwapped == rhs.bNeedRgbSwapped); + } +}TP_CAMERA_OPTION; + +enum emTpCommType { + TP_COMM_TYPE_NO = 0, + TP_COMM_TYPE_SERIAL = 1, + TP_COMM_TYPE_USB = 2, + TP_COMM_TYPE_TCP_CLIENT = 3, // 主机作为客户端 + TP_COMM_TYPE_TCP_SERVER = 4, // 主机作为服务端 + TP_COMM_TYPE_TCP_CLIENT_SESSION = 5, // 主机作为服务端,接受的客户端会话 +}; + +static const QMap gCommNamesMap = { + { TP_COMM_TYPE_NO, "Unknown" }, + { TP_COMM_TYPE_SERIAL, "Serial Port" }, + { TP_COMM_TYPE_USB, "USB Port" }, + { TP_COMM_TYPE_TCP_CLIENT, "TCP Client" }, + { TP_COMM_TYPE_TCP_SERVER, "TCP Server" }, + { TP_COMM_TYPE_TCP_CLIENT_SESSION, "TCP Client Session" } +}; + +enum emTpCommStatus{ + TP_COMM_STATUS_DISABLE = 0, // 禁用状态 + TP_COMM_STATUS_OPENNING = 1, // 打开中或者连接建立中 + TP_COMM_STATUS_CONNECTED = 2, // 已打开或者连接已建立 + TP_COMM_STATUS_CLOSED = 3, // 已关闭 + TP_COMM_STATUS_CLOSING = 4, // 正在关闭 +}; + +typedef struct tagTP_COMM_SETTING_NODE { + tagTP_COMM_SETTING_NODE() + :type(TP_COMM_TYPE_NO), enable(0), + bound(115200), triggerInterval(0), + usb_pid(0), usb_vid(0), heartBeatInterval(5000), + strHostAddress("127.0.0.1"), hostPort(9040), + strListenIp("*"), listenPort(9040), nMaxClients(0), + nClientPort(0), status(TP_COMM_STATUS_DISABLE) + {} + + emTpCommType type; + int enable; + QString strName; + + //used for serial port + int bound; + int triggerInterval; + int meterDecodeInterval; + int usb_pid; + int usb_vid; + int heartBeatInterval; + + // used for tcp client + QString strHostAddress; + int hostPort; + + // used for tcp server + QString strListenIp; + int listenPort; + int nMaxClients; + + // used for tcp server client session + int nClientPort; + + // node status - used for app inside + emTpCommStatus status; + + bool operator==(const tagTP_COMM_SETTING_NODE& rhs) + { + return (type == rhs.type) && (enable == rhs.enable) && + (strName == rhs.strName) && (bound == rhs.bound) && + (triggerInterval == rhs.triggerInterval) && + (meterDecodeInterval == rhs.meterDecodeInterval) && + (usb_pid == rhs.usb_pid) && (usb_vid == rhs.usb_vid) && + (heartBeatInterval == rhs.heartBeatInterval) && + (strHostAddress == rhs.strHostAddress) && + (hostPort == rhs.hostPort) && (strListenIp == rhs.strListenIp) && + (listenPort == rhs.listenPort) && (nMaxClients == rhs.nMaxClients) && + (nClientPort == rhs.nClientPort); + } + +}TP_COMM_SETTING_NODE; + +enum emTpDeviceTriggerMode { + DEV_TRIGGER_MODE_STOP = 0, + DEV_TRIGGER_MODE_OUT = 1, + DEV_TRIGGER_MODE_AUTO = 2, + DEV_TRIGGER_MODE_FIXED_AUTO = 3, + DEV_TRIGGER_MODE_SOFT = 4, + // DEV_TRIGGER_MODE_MANUAL = 3, +}; + +enum emTpTriggerDirection { + TRIGGER_DIRECT_FOREWARD = 0, + TRIGGER_DIRECT_BACKWARD = 1, + TRIGGER_DIRECT_REPEAT = 2, +}; + +typedef struct tagTP_CAMERAPOOL_OPTION { + emTpDeviceTriggerMode triggerMode; + emTpTriggerDirection triggerDirection; + int frameRate; + int fixedFrames; +}TP_CAMERAPOOL_OPTION; + +typedef struct tagTP_CAMERA_WIN { + tagTP_CAMERA_WIN() + :images(1), buffers(2), + bitsPerPixel(32), index(0), + bufferW(0), bufferH(0), mirror(0), + bCacheOriginImage(false), bNeedRgbSwapped(false) + {} + QString key;//widget name + QString device;//camera key + quint8 images; + quint8 buffers; + quint8 bitsPerPixel; + quint8 index; + int bufferW; + int bufferH; + int mirror;//0:no, 1:horizontal, 2:vertical, 3: both + bool bCacheOriginImage; + bool bNeedRgbSwapped; + + bool operator==(const tagTP_CAMERA_WIN& rhs) + { + return (key == rhs.key) && (device == rhs.device) && + (images == rhs.images) && (buffers == rhs.buffers) && + (bitsPerPixel == rhs.bitsPerPixel) && + (index == rhs.index) && + (bufferW == rhs.bufferW) && (bufferH == rhs.bufferH) && + (mirror == rhs.mirror) && + (bCacheOriginImage == rhs.bCacheOriginImage) && + (bNeedRgbSwapped == rhs.bNeedRgbSwapped); + } +}TP_CAMERA_WIN; + +enum emTpUiDataType { + TP_UI_DATA_FROM_CORECTRL = 0, + TP_UI_DATA_FROM_ALGORITHM = 0x10000, + TP_UI_DATA_FROM_CAMERA = 0x20000, + TP_UI_DATA_FROM_COMMUNICATE = 0x30000, +}; + +Q_DECLARE_METATYPE(emTpUiDataType) + +typedef struct tagTP_IO_OUT_CONTROL { + BYTE index; + BYTE state;//0 or 1 + WORD mSecToSwitch;//0 for no Switch; +}TP_IO_OUT_CONTROL; + +static const QMap gColorNameMap = { + { TP_COLOR_NONE, "None" }, + { TP_COLOR_Y800, "GRAY8" }, + { TP_COLOR_RGB24, "RGB24" }, + { TP_COLOR_RGB32, "RGB32" }, + { TP_COLOR_Y16, "GRAY16" }, + { TP_COLOR_RGB48, "RGB48" }, + { TP_COLOR_BGR24, "BGR24" }, + { TP_COLOR_BGR32, "BGR32" } +}; + +#define TP_LOG_TAIL " - " << __FUNCTION__ +#define TP_DEBUG(msg) qDebug() << msg << TP_LOG_TAIL; +#define TP_WARN(msg) qWarning() << msg << TP_LOG_TAIL; +#define TP_ERROR(msg) qCritical() << msg << TP_LOG_TAIL; + +//global functions +namespace TP_FUNCS +{ +#define F_INLINE inline + F_INLINE QString ImageFileName(DWORD nFrameNumber, const TP_CAMERA_OPTION* pCamOpt, const char* szSuffix) + { + QStringList invaldcha = (QStringList() << "\\" << "/" << "*" << "?" << "<" << ">" << "|" << ":"); + QDateTime dt = QDateTime::currentDateTime(); + QString strCamName = pCamOpt->uniqueName; + foreach(const QString &str, invaldcha) { + strCamName.remove(str); + } + QString strName = pCamOpt->folder + strCamName + + "_" + dt.toString("yyyy_MM_dd_hh_mm_ss_zzz") + "_" + + QString::number(nFrameNumber) + szSuffix/*".BMP"*/; + + return strName; + } + + F_INLINE QString ImageFileName(DWORD nFrameNumber, const TP_CAMERA_OPTION* pCamOpt) + { + QStringList invaldcha = (QStringList() << "\\" << "/" << "*" << "?" << "<" << ">" << "|" << ":"); + QDateTime dt = QDateTime::currentDateTime(); + QString strCamName = pCamOpt->uniqueName; + //foreach(const QString &str, invaldcha) { + // strCamName.remove(str); + //} + strCamName.remove(":"); + QString strName = pCamOpt->folder + strCamName + + "_" + dt.toString("yyyy_MM_dd_hh_mm_ss_zzz") + "_" + + QString::number(nFrameNumber) + pCamOpt->saveImgSuffix; + + return strName; + } + + F_INLINE emTpColorFormat FromQImageFormat(const QImage &image) + { + if (QImage::Format_RGB32 == image.format()) + { + return TP_COLOR_RGB32; + } + else if (QImage::Format_RGB888 == image.format()) + { + return TP_COLOR_RGB24; + } + else if (QImage::Format_Indexed8 == image.format()) + { + return TP_COLOR_Y800; + } + else if (QImage::Format_RGB16 == image.format()) + { + return TP_COLOR_Y16; + } + else if (QImage::Format_ARGB32 == image.format()) + { + return TP_COLOR_RGB48; + } + else + { + return TP_COLOR_NONE; + } + } + + F_INLINE QImage::Format ToQImageFormat(emTpColorFormat colorFormat) + { + if (TP_COLOR_RGB32 == colorFormat) + { + return QImage::Format_RGB32; + } + else if (TP_COLOR_RGB24 == colorFormat) + { + return QImage::Format_RGB888; + } + else if (TP_COLOR_Y800 == colorFormat) + { + return QImage::Format_Indexed8; + } + else if (TP_COLOR_Y16 == colorFormat) + { + return QImage::Format_RGB16; + } + else if (TP_COLOR_RGB48 == colorFormat) + { + return QImage::Format_ARGB32; + } + else + { + return QImage::Format_Invalid; + } + } + + F_INLINE int BitsPerPixels(emTpColorFormat colorFormat) + { + if (TP_COLOR_RGB32 == colorFormat) + { + return 32; + } + else if (TP_COLOR_RGB24 == colorFormat) + { + return 24; + } + else if (TP_COLOR_Y800 == colorFormat) + { + return 8; + } + else if (TP_COLOR_Y16 == colorFormat) + { + return 16; + } + else + { + return 0; + } + } + + F_INLINE bool isValidImgSuffix(const QString& str) + { + static QRegExp rx("(\\w*|\\w*\\*)\\.(?:bmp|jpeg|jpg|png)", Qt::CaseInsensitive); + return rx.exactMatch(str); + } + + // IPV4 only + F_INLINE bool isValidIPAddress(const QString& str) + { + // [0,255].[0,255].[0,255].[0,255] + static QRegExp rx("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?).){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)"); + return rx.exactMatch(str); + } +} + +#endif \ No newline at end of file diff --git a/src/lpBase/iAlgorithm.h b/src/lpBase/iAlgorithm.h new file mode 100644 index 0000000..5e1c739 --- /dev/null +++ b/src/lpBase/iAlgorithm.h @@ -0,0 +1,185 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:tpAlgorithm.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/05/15 + History:15:5:2015 17:51 + *******************************************************************************/ +#ifndef __TP_ALGORITHM_H +#define __TP_ALGORITHM_H + +#include "baseDefine.h" +#include +#include +#include +#include +#include +//#include "baseStruct.h" + +#define ALG_SHARED_GLOBAL_KEY_APP_STATUS "app_status" +#define ALG_SHARED_GLOBAL_KEY_APP_CHECKED "app_checked_status" + +class IAlgorithmShared +{ +public: + enum emAppStatus { + APP_STATUS_DEFAULT = 0, + APP_STATUS_SAMPLING = 1, + }; + IAlgorithmShared() {} + virtual ~IAlgorithmShared() {} + virtual UINT64 ILastUpdateTime() = 0; + virtual QStringList IGetStringList(const QString& key) = 0; + virtual int IGetInt(const QString& key, int nDef = 0) = 0; +}; + + +///////////////////////////////////////////////////////////////////////////////////////////// + +#define ALG_OPTION_GLOBAL_KEY_SAMPLING_OBJECT "sampling_object" +class IAlgorithmOption +{ +public: + IAlgorithmOption() {} + virtual ~IAlgorithmOption() {} + virtual int GetIntValue(const QString& skey, int nDefault) = 0; + virtual void SetIntValue(const QString& skey, int value) = 0; + virtual QString GetStringValue(const QString& skey, const QString& default = "") = 0; + virtual void SetStringValue(const QString& skey, const QString& value) = 0; + virtual bool Save(bool bWait = true) = 0; + virtual QVariantMap VariantMap() = 0; + virtual bool DbSetValue(const QString& key, int type, const QByteArray& data) = 0; + virtual bool DbGetValue(const QString& key, int type, QByteArray& data) = 0; + virtual bool DbDelValue(const QString& key) = 0; + virtual QStringList DbGetKeysByType(int type) = 0; + virtual QVariant GetValue(const QString& skey, const QVariant& def) = 0; + virtual void SetValue(const QString& skey, const QVariant& value) = 0; +}; + +typedef struct tagTP_ALGORITHM_OPTION { + int algorithm; + class IAlgorithmOption* pImagProcCb; +} TP_ALGORITHM_OPTION; +//////////////////////////////////////////////////////////////////////////////////////////// + +enum emTpCameraProperty{ + TP_CAM_PROPERTY_NONE = 0, + TP_CAM_PROPERTY_EXPOSURE = 1, + TP_CAM_PROPERTY_BALANCE_RATIO_RED = 2, + TP_CAM_PROPERTY_BALANCE_RATIO_GREEN = 3, + TP_CAM_PROPERTY_BALANCE_RATIO_BLUE = 4, + TP_CAM_PROPERTY_FOLDER_NAME = 50, + TP_CAM_BITFLOW_BASE = 110, + TP_CAM_BITFLOE_SAVE_CFG = TP_CAM_BITFLOW_BASE, + TP_CAM_BITFLOW_ROW_RATE = TP_CAM_BITFLOW_BASE + 1, + TP_CAM_BITFLOW_COMMAND = TP_CAM_BITFLOW_BASE + 9, + TP_CAM_PROPERTY_FINISH = 0xFFFF +}; +#define TP_MAX_STRING_SIZE 260 +typedef struct tagTP_CAMERA_PROPERTY { + emTpCameraProperty property; + long value; + char szProperty[TP_MAX_STRING_SIZE]; + int nErrorCode;//return by camera +} TP_CAMERA_PROPERTY; + + +class IImageObject +{ +public: + IImageObject() { } + virtual ~IImageObject() { } + + virtual emTpColorFormat IColorFormat() = 0; + //@nOutBytesPerLine: 0 means there is no valid value + virtual BYTE* IImageData(int& nOutW, int& nOutH, int& nOutBitsPerPixel, int& nOutBytesPerLine) = 0; + virtual int IGetId() = 0; + virtual int IGetAlgorithm() = 0; + virtual int IGetFrameNum() = 0; + virtual int IGetTriggerCount() = 0; + virtual void ISetCameroProperty(TP_CAMERA_PROPERTY& property) = 0; + virtual void ISendDataToUI(void* pData) = 0; + ////////////////////////////////////////////////////////// + typedef void(*Comm_Callback_Func)(int cmd, BYTE* pData, int nDataLen, void* pContext); + typedef struct tagDATA_TO_COMM_HEAD { + char* commNames; + int cmd; + Comm_Callback_Func pfCallback; + void* pContext; + tagDATA_TO_COMM_HEAD() { + commNames = NULL; + cmd = 0; + pfCallback = NULL; + pContext = NULL; + } + }DATA_TO_COMM_HEAD; + virtual void IDataToComm(DATA_TO_COMM_HEAD& head, char* pData, int nLen) = 0; + enum emTpDefectType { + TP_DEF_CLEAN, + TP_DEF_RECT_F,//double for float, pData is Left, Top, Width, Height;nDataLen = sizeof(double) * 4; + TP_DEF_LINE_F, + TP_DEF_CIRCLE_F, + TP_DEF_ELLIPSE_F, + TP_DEF_POLYGON_F, + TP_DEF_POLYLINE_I, + }; + virtual void IDrawDefect(const char* szWinName, emTpDefectType defectType, BYTE* pData, int nDataLen) = 0; + virtual void IDrawDefectOrg(emTpDefectType defectType, BYTE* pData, int nDataLen) = 0; + virtual void ISafeDataToUI(void* pData, int nDataLen) = 0; + virtual void ISafeDataToUI(WORD type, void* pData, int nDataLen) = 0; + virtual INT64 IMeterCode() = 0; + virtual const char* IGetImageUtf8String() = 0; + virtual void IDrawImage(QImage& image = QImage(), const char* szWinName = NULL) = 0; + virtual QImage IImage() = 0; + virtual const ZStringA& IDllSuffix() = 0; + virtual void ISetResult(const QString& key, QVariant& val) = 0; + virtual void IAccumulateResult(const QString& key, QVariant& val) = 0; + virtual void IMoveToFolder(const QString& szFolder) = 0; + virtual IAlgorithmShared* IGetShared() = 0; + virtual void IVariantMapToUI(const QVariantMap& vMap) = 0; + virtual const ZStringA& ICameraSerial() = 0; + virtual const QVariant& IVarFromUI() = 0; + virtual quint64 ITimeStamp() = 0; +}; +//pColor is ARGB, eg 0xFF010101 +inline void simple_draw_image_8(IImageObject* pImgObj, uint *pColor, int nNum) { + //pImgObj->IDrawImage(QImage(pBuffer, w, h, QImage::Format_ARGB32).copy()); + QImage image = pImgObj->IImage(); + if (QImage::Format_Indexed8 == image.format() && NULL != pColor) { + QVector corFmt; + for (int i = 0; i < nNum; ++i) { + corFmt.append(pColor[i]); + } + image.setColorTable(corFmt); + } + pImgObj->IDrawImage(image); +} + +class IAlgorithm +{ +public: + IAlgorithm() {} + virtual ~IAlgorithm() {} + + virtual int IImageAnalysis(class IImageObject* pImgObj, TP_ALGORITHM_OPTION* pOpt, class IDetectorEngine* pDE) = 0; +protected: +}; + +//#define _LOAD_ALGORITHM_DLL_STATIC + +#ifdef ALGORITHM_EXPORTS +#define ALGORITHM_API extern "C" __declspec(dllexport) +#else +#ifndef _LOAD_ALGORITHM_DLL_STATIC +#define ALGORITHM_API extern "C" +#else +#define ALGORITHM_API extern "C" __declspec(dllimport) +#endif +#endif + +ALGORITHM_API IAlgorithm* Algorithm_Create(class IDetectorEngine*); +ALGORITHM_API void Algorithm_Delete(IAlgorithm* pAlg); + +#endif \ No newline at end of file diff --git a/src/lpBase/iCoreCtrl.h b/src/lpBase/iCoreCtrl.h new file mode 100644 index 0000000..e3bd826 --- /dev/null +++ b/src/lpBase/iCoreCtrl.h @@ -0,0 +1,244 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:iCoreCtrl.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/23 + History:23:3:2015 15:35 + *******************************************************************************/ +#ifndef __I_CORE_CTRL_H +#define __I_CORE_CTRL_H + +//#include "icamera.h" +#include "baseStruct.h" +#include +#include +//the parameter for the CoreCtrl module +typedef struct tagCORE_CTRL_IN_PARAM +{ + class IGuiCallback* pGuiCb; + TP_GLOBAL_DATA* pGlobalData; + TP_CORE_SETTING* pCoreSetting; +}CORE_CTRL_IN_PARAM; + +/**/ +class IAlgorithmResult +{ +public: + IAlgorithmResult(){} + virtual ~IAlgorithmResult(){} + virtual QVariantMap ToVariantMap() = 0; +}; + +//interface for gui +class ICoreCtrl +{ +public: + ICoreCtrl() {} + virtual ~ICoreCtrl() {} + virtual QList ICameraKeys() = 0; + virtual QList ICameraKeysOrderByIds() = 0; + virtual bool ICameraOption(const QString& strSerial, TP_CAMERA_OPTION& camOpt) = 0; + virtual bool ICameraOptionByKey(const QString& strSerial, TP_CAMERA_OPTION& camOpt) = 0; + virtual QList IEnumAvailableCameras(emTpDeviceType camType) = 0; + virtual void ISetTriggerMode(emTpDeviceTriggerMode triggerMode, long nFrameRate) = 0; + virtual emTpDeviceTriggerMode IGetTriggerMode() = 0; + virtual void IManualTrigger(emTpTriggerDirection direct) = 0; + virtual void ISnapImage(const QStringList& camKeys) = 0; + virtual void ISendSoftTrigger(const QStringList& camKeys) = 0; + virtual void IDrawImage(const QString& sKey, QPainter& painter, QRect& rt) = 0; + virtual QImage IShowImage(const QString& sWinId, int& nFrameNum, QString& szExShow) = 0; + virtual QMap IGetCamShowNames() = 0; + virtual IAlgorithmOption* IGetAlgorithmOption(int nAlgorithm) = 0; + virtual bool ISetCameraOption(const QString& strSerial, const TP_CAMERA_OPTION& camOpt) = 0; + virtual bool IReopenCameraes() = 0; + virtual void ISetVirtualImages(const QString& camera, const QStringList& szImages) = 0; + virtual QImage IOriginImage(const QString& sShowId) = 0; + virtual void ISetCameraProperty(const QString& camera, TP_CAMERA_PROPERTY& property) = 0; + virtual void ISetAlgorithmShared(const QString& key, const QVariant& value) = 0; + virtual void IWriteIo(const TP_IO_OUT_CONTROL& state) = 0; + virtual void ISetResult(const QString& key, QVariant& val) = 0; + + + //operate camera + virtual bool ICreateCamera(const QString& strSerial, int nType) = 0; + virtual bool IOpenCamera(const QString& strSerial) = 0; + virtual bool ICloseCamera(const QString& strSerial) = 0; + virtual bool IStartCamera(const QString& strSerial) = 0; + virtual bool IStopCamera(const QString& strSerial) = 0; + virtual bool IDeleteCamera(const QString& strSerial) = 0; + virtual bool IAddCamera(const QString& strName, const TP_CAMERA_OPTION& camOpt, bool bNew, bool bAutoRun = false) = 0; + + //operate camera window + virtual QList ICamWinKeys() = 0; + virtual bool ICamWinOptionByKey(const QString& strKey, TP_CAMERA_WIN& camwinOpt) = 0; + virtual bool IAddCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew) = 0; + virtual bool IDelCamWin(const QString& strKey) = 0; + + //更新json配置文件 + virtual bool updateCommJson(const QString& sJsonPath) = 0; + virtual bool updateCamJson(const QString& sJsonPath) = 0; + + virtual bool ICamsStartPush() = 0; + virtual bool ICamsPausePush() = 0; + +public://private: //use private when the old main is discarded; + virtual int IInitCore(class IDetectorEngine* pDE = NULL) = 0; + virtual void IFreeCore() = 0; + + virtual void IStartImageProcess() = 0; + virtual void IEndImageProcess() = 0; + + friend class QTpMain; +}; + +//callback interface from gui +class IGuiCallback +{ +public: + IGuiCallback() {} + virtual ~IGuiCallback() {} + virtual void IOnReady() {} + virtual void IUpdateShow(const QString& skey) = 0; + virtual void INewCameraImage(const QVariantMap& vMap) = 0; + virtual void IAddWindows(const QString& sWinName, const QString& sShowId) {}; + virtual void ISendDataToUI(emTpUiDataType dataType, const QString& camKey, void* pData) {}//弃用,用IVariantMapToUI()替代 + virtual void ICameraTrigger(bool bStartOrStop) = 0;//弃用,用ICommInterval()替代 + virtual void IDrawDefectToScene(const QString& szWinName, IImageObject::emTpDefectType defectType, QByteArray& data) = 0; + virtual void ISafeDataToUI(emTpUiDataType dataType, const QString& camKey, const QByteArray& data) = 0; + virtual void IWarning(int nWarningCode, void* data, int nDataLen) = 0; + virtual void ICommInterval(const char* szCom, int nCmd, BYTE* pData, int nDataLen) = 0; + virtual void IAlgorithmResult(const QVariantMap& varMap) = 0; + virtual void IVariantMapToUI(emTpUiDataType dataType, const QString& camKey, const QVariantMap& vMap) = 0; + virtual QVariant IGetVariantById(int id) = 0; + virtual void IIoStatesChanged(int nOldState, int nNewState) = 0; + virtual void ICommAchieved(const char* szCom, int nCmd, BYTE* pData, int nDataLen) = 0; + virtual WORD IGetWorkState() = 0;//心跳包中发送的用来控制是否触发的值,返回0不触发,返回1触发 +}; + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define TP_SIGNALS \ + Q_SIGNALS:\ + void sgAddWindows(const QString sWinName, const QString sShowId); \ + void sgUpdateShow(const QString skey); \ + void sgNewCameraImage(const QVariantMap& vMap); \ + void sgSendDataToUI(emTpUiDataType dataType, const QString camKey, void* pData); \ + void sgCameraTrigger(bool bStartOrStop); \ + void sgDrawDefectToScene(const QString szWinName, IImageObject::emTpDefectType defectType, QByteArray data); \ + void sgSafeDataToUI(emTpUiDataType dataType, const QString camKey, const QByteArray data); \ + void sgWarning(int nWarningCode, const QByteArray data); \ + void sgCommIntervale(const QString szCom, int nCmd, const QByteArray data); \ + void sgAlgorithmResult(const QVariantMap varMap); \ + void sgVariantMapToUI(emTpUiDataType dataType, const QString camKey, const QVariantMap vMap); \ + void sgIoStatesChanged(int nOldState, int nNewState); \ + void sgCommAchieved(const QString szCom, int nCmd, const QByteArray data);\ + +#define TP_SLOTS \ + public Q_SLOTS: \ + virtual void UpdateShow(const QString skey); \ + virtual void NewCameraImage(const QVariantMap& vMap); \ + virtual void AddWindows(const QString sWinName, const QString sShowId); \ + virtual void SendDataToUI(emTpUiDataType dataType, const QString camKey, void* pData); \ + virtual void CameraTrigger(bool bStartOrStop); \ + virtual void DrawDefectToScene(const QString szWinName, IImageObject::emTpDefectType defectType, QByteArray data); \ + virtual void SafeDataToUI(emTpUiDataType dataType, const QString camKey, const QByteArray data); \ + virtual void Warning(int nWarningCode, const QByteArray data); \ + virtual void CommIntervale(const QString szCom, int nCmd, const QByteArray data); \ + virtual void AlgorithmResult(const QVariantMap varMap); \ + virtual void VariantMapToUI(emTpUiDataType dataType, const QString camKey, const QVariantMap vMap); \ + virtual void IoStatesChanged(int nOldState, int nNewState); \ + virtual void CommAchieved(const QString szCom, int nCmd, const QByteArray data);\ + + +#define TP_CONSTRUCTOR \ + connectSignals(); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define TP_CALLBACKS_H \ + virtual void IAddWindows(const QString& sWinName, const QString& sShowId); \ + virtual void IUpdateShow(const QString& skey); \ + virtual void INewCameraImage(const QVariantMap& vMap); \ + virtual void ISendDataToUI(emTpUiDataType dataType, const QString& camKey, void* pData); \ + virtual void ICameraTrigger(bool bStartOrStop); \ + virtual void IDrawDefectToScene(const QString& szWinName, IImageObject::emTpDefectType defectType, QByteArray& data); \ + virtual void ISafeDataToUI(emTpUiDataType dataType, const QString& camKey, const QByteArray& data); \ + virtual void IWarning(int nWarningCode, void* data, int nDataLen); \ + virtual void ICommInterval(const char* szCom, int nCmd, BYTE* pData, int nDataLen); \ + virtual void IAlgorithmResult(const QVariantMap& varMap); \ + virtual void IVariantMapToUI(emTpUiDataType dataType, const QString& camKey, const QVariantMap& vMap); \ + virtual QVariant IGetVariantById(int id); \ + virtual void IIoStatesChanged(int nOldState, int nNewState); \ + virtual void ICommAchieved(const char* szCom, int nCmd, BYTE* pData, int nDataLen); \ + virtual WORD IGetWorkState(); \ + void connectSignals(); +/// +#define TP_CALLBACKS_CPP(cName) \ + void cName##::IUpdateShow(const QString& skey) { \ + emit sgUpdateShow(skey); \ + } \ + void cName##::INewCameraImage(const QVariantMap& vMap) { \ + emit sgNewCameraImage(vMap); \ + } \ + void cName##::IAddWindows(const QString& sWinName, const QString& sShowId) { \ + emit sgAddWindows(sWinName, sShowId); \ + } \ + void cName##::ISendDataToUI(emTpUiDataType dataType, const QString& camKey, void* pData) { \ + emit sgSendDataToUI(dataType, camKey, pData); \ + } \ + void cName##::ICameraTrigger(bool bStartOrStop) { \ + emit sgCameraTrigger(bStartOrStop); \ + } \ + void cName##::IDrawDefectToScene(const QString& szWinName, IImageObject::emTpDefectType defectType, QByteArray& data) { \ + emit sgDrawDefectToScene(szWinName, defectType, data); \ + } \ + void cName##::ISafeDataToUI(emTpUiDataType dataType, const QString& camKey, const QByteArray& data) { \ + emit sgSafeDataToUI(dataType, camKey, data); \ + } \ + void cName##::IWarning(int nWarningCode, void* data, int nDataLen) { \ + emit sgWarning(nWarningCode, QByteArray((char*)data, nDataLen)); \ + }\ + void cName##::ICommInterval(const char* szCom, int nCmd, BYTE* pData, int nDataLen) { \ + emit sgCommIntervale(QString(szCom), nCmd, QByteArray((char*)pData, nDataLen)); \ + } \ + void cName##::IAlgorithmResult(const QVariantMap& varMap) { \ + emit sgAlgorithmResult(varMap); \ + } \ + void cName##::IVariantMapToUI(emTpUiDataType dataType, const QString& camKey, const QVariantMap& vMap) { \ + emit sgVariantMapToUI(dataType, camKey, vMap); \ + } \ + void cName##::IIoStatesChanged(int nOldState, int nNewState) { \ + emit sgIoStatesChanged(nOldState, nNewState); \ + } \ + void cName##::ICommAchieved(const char* szCom, int nCmd, BYTE* pData, int nDataLen) { \ + emit sgCommAchieved(QString(szCom), nCmd, QByteArray((char*)pData, nDataLen)); \ + } \ + QVariant cName##::IGetVariantById(int id) { return QVariant(); } \ + WORD cName##::IGetWorkState() { return 0; } \ + void cName##::connectSignals() { \ + QMetaObject::Connection error = connect(this, SIGNAL(sgAddWindows(const QString, const QString)) \ + , this, SLOT(AddWindows(const QString, const QString))); \ + error = connect(this, SIGNAL(sgUpdateShow(const QString)), this, SLOT(UpdateShow(const QString))); \ + error = connect(this, SIGNAL(sgNewCameraImage(const QVariantMap)), this, SLOT(NewCameraImage(const QVariantMap))); \ + qRegisterMetaType("emTpUiDataType"); \ + error = connect(this, SIGNAL(sgSendDataToUI(emTpUiDataType, const QString, void*)) \ + , this, SLOT(SendDataToUI(emTpUiDataType, const QString, void*))); \ + error = connect(this, SIGNAL(sgCameraTrigger(bool)), this, SLOT(CameraTrigger(bool))); \ + qRegisterMetaType("IImageObject::emTpDefectType"); \ + error = connect(this, SIGNAL(sgDrawDefectToScene(const QString, IImageObject::emTpDefectType, QByteArray)) \ + , this, SLOT(DrawDefectToScene(const QString, IImageObject::emTpDefectType, QByteArray))); \ + error = connect(this, SIGNAL(sgSafeDataToUI(emTpUiDataType, const QString, const QByteArray)) \ + , this, SLOT(SafeDataToUI(emTpUiDataType, const QString, const QByteArray))); \ + error = connect(this, SIGNAL(sgWarning(int, const QByteArray)), this, SLOT(Warning(int, const QByteArray))); \ + error = connect(this, SIGNAL(sgCommIntervale(const QString, int, const QByteArray)) \ + , this, SLOT(CommIntervale(const QString, int, const QByteArray))); \ + error = connect(this, SIGNAL(sgAlgorithmResult(const QVariantMap)),this, SLOT(AlgorithmResult(const QVariantMap))); \ + error = connect(this, SIGNAL(sgVariantMapToUI(emTpUiDataType, const QString, const QVariantMap)) \ + , this, SLOT(VariantMapToUI(emTpUiDataType, const QString, const QVariantMap)));\ + error = connect(this, SIGNAL(sgIoStatesChanged(int, int)), this, SLOT(IoStatesChanged(int, int))); \ + error = connect(this, SIGNAL(sgCommAchieved(const QString, int, const QByteArray)) \ + , this, SLOT(CommAchieved(const QString, int, const QByteArray))); \ + } \ + +#endif \ No newline at end of file diff --git a/src/lpBase/iImgProc.h b/src/lpBase/iImgProc.h new file mode 100644 index 0000000..aca89a7 --- /dev/null +++ b/src/lpBase/iImgProc.h @@ -0,0 +1,46 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:iImgProc.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 17:58 + *******************************************************************************/ +#ifndef __I_IMG_PROC_H_20150326 +#define __I_IMG_PROC_H_20150326 + +#include "baseDefine.h" +#include "baseStruct.h" + +typedef struct tagIMGPROC_IN_PARAM{ + TP_GLOBAL_DATA* pGlobalData; +}IMGPROC_IN_PARAM; + +class IImgProc +{ +public: + IImgProc() {} + virtual ~IImgProc() {} + virtual int IImageProcess(class IImageObject* pImgObj, class IDetectorEngine* pDE = NULL) = 0; + virtual class IAlgorithmOption* IGetAlgorithmOption(int nAlgorithm) = 0; +}; + +// class IImageObject +// { +// public: +// IImageObject() { } +// virtual ~IImageObject() { } +// +// virtual emTpColorFormat IColorFormat() = 0; +// //@nOutBytesPerLine: 0 means there is no valid value +// virtual BYTE* IImageData(int& nOutW, int& nOutH, int& nOutBitsPerPixel, int& nOutBytesPerLine) = 0; +// virtual int IGetId() = 0; +// virtual int IGetAlgorithm() = 0; +// virtual int IGetFrameNum() = 0; +// virtual int IGetTriggerCount() = 0; +// ////////////////////////////////////////////////////////// +// virtual void IAddOverlay() { } +// }; + +#endif \ No newline at end of file diff --git a/src/lpBase/iIoCtrl.h b/src/lpBase/iIoCtrl.h new file mode 100644 index 0000000..36a49e4 --- /dev/null +++ b/src/lpBase/iIoCtrl.h @@ -0,0 +1,63 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:$FILE_BASE$.$FILE_EXT$ + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:$DATE$ + History:$DAY$:$MONTH$:$YEAR$ $HOUR$:$MINUTE$ + *******************************************************************************/ + +#include "zdefines.h" +#include + +enum tpemIoType { + TP_IO_ADLINK = 1, +}; + +class IIoInterface +{ +public: + IIoInterface() {} + virtual ~IIoInterface() {} + virtual int IIint() = 0; + virtual int IFree() = 0; + virtual int IReadI(qint32& state) = 0; + virtual int IReadO(qint32& state) = 0; + virtual int IWriteO(qint32& state) = 0; +}; + +#define TP_IOCTRL_DLL "tpIoCtrl" +class QLoadIoCtrl : public QLibrary +{ +public: + typedef IIoInterface*(*func_io_create)(int); + typedef void(*func_io_delete)(IIoInterface*); + QLoadIoCtrl(const QString& path) +#ifdef _DEBUG + : QLibrary(path + TP_IOCTRL_DLL + "d") +#else + : QLibrary(path + TP_IOCTRL_DLL) +#endif + { + _ioCreate = (func_io_create)resolve("tp_io_create"); + _ioDelete = (func_io_delete)resolve("tp_io_delete"); + } + ~QLoadIoCtrl(){} + + IIoInterface* Create(int type) { + if (NULL == _ioCreate) { + return NULL; + } + return _ioCreate(type); + } + + void Delete(IIoInterface* pio) { + if (NULL != _ioDelete) { + _ioDelete(pio); + } + } +private: + func_io_create _ioCreate; + func_io_delete _ioDelete; +}; \ No newline at end of file diff --git a/src/lpBase/icamera.h b/src/lpBase/icamera.h new file mode 100644 index 0000000..d137774 --- /dev/null +++ b/src/lpBase/icamera.h @@ -0,0 +1,105 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:TpICamera.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/20 + History:20:3:2015 17:12 + *******************************************************************************/ +#ifndef __TP_I_CAMERA_H +#define __TP_I_CAMERA_H + +//#include +#include "baseInclude.h" +#include "ZkCameraImage.h" + +typedef struct tagCAMERA_IN_PARAM { + class ICameraCallback* pCallback; + TP_GLOBAL_DATA* pGlobalData; +}CAMERA_IN_PARAM; + + +class ICameraPool +{ +public: + ICameraPool() {} + virtual ~ICameraPool() {} +public: + // operation for camera.json + virtual QList ICameraKeys() = 0; + virtual QList ICameraKeysOrderByIds() = 0; + virtual bool ICameraOption(const QString& strSerial, TP_CAMERA_OPTION& camOpt) = 0; + virtual bool ICameraOptionByKey(const QString& strSerial, TP_CAMERA_OPTION& camOpt) = 0; + virtual bool ISetCameraOption(const QString& strSerial, const TP_CAMERA_OPTION& camOpt) = 0; + + // try to get available (connected to the computer) by camera type(manufacturer) + virtual QList IEnumAvailableCameras(emTpDeviceType camType) = 0; + + virtual int ICreateCameraes() = 0; + virtual void IDeleteDameraes() = 0; + virtual bool ICreateCamera(const QString&, int nType) = 0; + virtual bool IOpenCamera(const QString& strSerial, bool bReopen = true) = 0; + virtual bool ICloseCamera(const QString& strSerial) = 0; + virtual bool IStartCamera(const QString& strSerial) = 0; + virtual bool IStopCamera(const QString& strSerial) = 0; + virtual bool IDeleteCamera(const QString& strSerial) = 0; + virtual bool IAddCamera(const QString& strName, const TP_CAMERA_OPTION& camOpt, bool bNew, bool bAutoRun = false) = 0; + + virtual int IOpenDevices(bool bReopenAll = true) = 0; + virtual int IOpenDevicesEx(bool bReopenAll = true) = 0; //如果camera.json里面配置了auto_open 值为false,则调用该函数不会打开对应相机 + virtual void ICloseDevices() = 0; + + virtual void IStartDevices() = 0; + virtual void IStopDevices(const QString& sDef = NULL) = 0; + virtual void ISetTriggerMode(emTpDeviceTriggerMode mode, emTpTriggerDirection nDirection = TRIGGER_DIRECT_FOREWARD, long nFrameRate = 0) = 0; + virtual emTpDeviceTriggerMode IGetTriggerMode() = 0; + virtual void IManualTrigger( emTpTriggerDirection nDirection = TRIGGER_DIRECT_FOREWARD) = 0; + virtual void ISnapImage(const QStringList& camKeys) = 0; + virtual void ISendSoftTrigger(const QStringList& camKeys) = 0; + virtual CZkCameraImage* IPopCameraImage() = 0; + virtual void IFreeCameraImage(CZkCameraImage *pCamImg) = 0; + //@camera: NULL means setting every camera + virtual void ISetCameraProperty(const QString& camera, emTpCameraProperty property, long nValue) = 0; + virtual void ISetCameraProperty(const QString& camera, TP_CAMERA_PROPERTY& property) = 0; + virtual QList ICameraWins() = 0; + virtual QMap IGetCamShowNames() = 0; + virtual void ISetCameraEncode(INT64 code, INT64 rate) = 0; + virtual void ISetVirtualImages(const QString& camera, const QStringList& szImages) = 0; + + //operate camera window + virtual QList ICamWinKeys() = 0; + virtual bool ICamWinOptionByKey(const QString& strKey, TP_CAMERA_WIN& camwinOpt) = 0; + virtual bool IAddCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew) = 0; + virtual bool IDelCamWin(const QString& strKey) = 0; + + virtual bool ICamsStartPush() = 0; + virtual bool ICamsPausePush() = 0; +}; + +// class ICamera +// { +// public: +// ICamera() {} +// virtual ~ICamera() {} +// public: +// int m_nCameraCode; +// }; + +class ICameraCallback +{ +public: + ICameraCallback() {} + virtual ~ICameraCallback() {} + virtual QVariant IGetVariantById(int nId) = 0; + virtual void INewCameraImage(const QVariantMap& vmap) = 0; +}; + +// class CTpICamera +// { +// public: +// CTpICamera(void); +// virtual ~CTpICamera(void); +// }; + +#endif \ No newline at end of file diff --git a/src/lpBase/lpalgorithm.h b/src/lpBase/lpalgorithm.h new file mode 100644 index 0000000..bd549ec --- /dev/null +++ b/src/lpBase/lpalgorithm.h @@ -0,0 +1,50 @@ +#ifndef LPALGORITHM_H +#define LPALGORITHM_H + +#include "lpbengine.h" + +class IAlgo +{ +public: + IAlgo(){} + virtual ~IAlgo(){} + virtual bool RegisterFunc(IAlgorithmLib* pAlgoLib) { return true; }; + virtual bool Exec(IDetectorTask *lpTask, IDetectorAlgorithm* lpAlgorithm) = 0; + virtual bool Init(IDetectorTask *lpTask, IDetectorAlgorithm* lpAlgorithm) = 0; + virtual void SaveUserParamValue(IDetectorTask *lpTask, IDetectorAlgorithm* lpAlgorithm, QString& strParamName) {}; + virtual void LoadUserParamValue(IDetectorTask *lpTask, IDetectorAlgorithm* lpAlgorithm, QString& strParamName, QVariant& value) {}; + virtual bool HandleEvent(IDetectorTask *lpTask, IDetectorAlgorithm* lpAlgorithm, QString& strParaName, int event) { return true; }; +}; + + +#define REGISTER_FUNCTION_IMPLEMENT(NAME, FUNC) \ + if (NULL != pAlgoLib) { \ + if (!pAlgoLib->RegisterFunc(#NAME, FUNC)) \ + return false; \ + } + +#define DEFINE_FUNCTION_PARAM_IMPLEMENT(FUNCNAME, PARAMNAME, TYPE, INITVAL, DIR) \ + if (NULL != pAlgoLib) { \ + LP_ALGORITHM_PARAM_DESC desc; \ + desc.strName = PARAMNAME; \ + desc.type = TYPE; \ + desc.InitVal = INITVAL; \ + desc.dir = DIR; \ + if (!pAlgoLib->DefFuncParam(#FUNCNAME, desc)) \ + return false; \ + } + +#ifdef ALGO_LIB +# define ALGO_EXPORT extern "C" __declspec(dllexport) +#else +# define ALGO_EXPORT extern "C" __declspec(dllimport) +#endif + +typedef void(*FnLpAlgoNewInstance)(IAlgo** lppAlgo); + +ALGO_EXPORT void LpAlgoNewInstance(IAlgo** lppAlgo); + +ALGO_EXPORT bool LpAlgoDeleteInstance(); + + +#endif // LPALGORITHM_H diff --git a/src/lpBase/lpbMetaTypeDefine.h b/src/lpBase/lpbMetaTypeDefine.h new file mode 100644 index 0000000..04b4c9a --- /dev/null +++ b/src/lpBase/lpbMetaTypeDefine.h @@ -0,0 +1,389 @@ +#ifndef __LPENGINE_METATYPE_DEFINE_H +#define __LPENGINE_METATYPE_DEFINE_H + +#ifndef LPENGINE_SMART_CAMERA +#include "lpbengine.h" +#else +#include "lpbdefine.h" +#endif + +namespace LpEngine +{ + void task_out_param_save(QDataStream & ds, const void * p) + { + LP_DETECTOR_ALGO_OUT_DATA* d = (LP_DETECTOR_ALGO_OUT_DATA*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + LP_DETECTOR_TASK_OUT_PARAM param = d->at(i); + ds << param.name; + ds << param.type; + ds << param.value; + } + } + } + + void task_out_param_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + LP_DETECTOR_ALGO_OUT_DATA* d = (LP_DETECTOR_ALGO_OUT_DATA*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + LP_DETECTOR_TASK_OUT_PARAM param; + ds >> param.name; + int type = 0; + ds >> type; + param.type = (AlgoParamType)type; + ds >> param.value; + + d->append(param); + } + } + } + + void double_list_save(QDataStream & ds, const void * p) + { + Double_List* d = (Double_List*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + ds << d->at(i); + } + } + } + + void double_list_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + Double_List* d = (Double_List*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + double dVal; + ds >> dVal; + + d->append(dVal); + } + } + } + + void linef_list_save(QDataStream & ds, const void * p) + { + LineF_List* d = (LineF_List*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + ds << d->at(i); + } + } + } + + void linef_list_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + LineF_List* d = (LineF_List*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + QLineF dVal; + ds >> dVal; + + d->append(dVal); + } + } + } + + void line_list_save(QDataStream & ds, const void * p) + { + Line_List* d = (Line_List*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + ds << d->at(i); + } + } + } + + void line_list_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + Line_List* d = (Line_List*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + QLine dVal; + ds >> dVal; + + d->append(dVal); + } + } + } + + void cv_mat_save(QDataStream & ds, const void * p) + { + cv::Mat* d = (cv::Mat*)p; + if (d) + { + int rows = d->rows; + int cols = d->cols; + int type = d->type(); + int step = d->step; + + ds << (int)rows; + ds << (int)cols; + ds << (int)type; + ds << (int)step; + ds.writeRawData((const char*)d->data, step*rows); + } + } + + void cv_mat_load(QDataStream & ds, void * p) + { + cv::Mat* d = (cv::Mat*)p; + if (d) + { + int rows, cols, type; + int step; + ds >> rows; + ds >> cols; + ds >> type; + ds >> step; + if (rows > 0 && step > 0) + { + uchar* pdata = new uchar[rows * step]; + ds.readRawData((char*)pdata, rows * step); + *d = cv::Mat(rows, cols, type, (void*)pdata, step); + } + } + } + + void point_list_save(QDataStream & ds, const void * p) + { + Point_List* d = (Point_List*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + ds << d->at(i); + } + } + } + + void point_list_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + Point_List* d = (Point_List*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + QPoint dVal; + ds >> dVal; + + d->append(dVal); + } + } + } + + void pointf_list_save(QDataStream & ds, const void * p) + { + PointF_List* d = (PointF_List*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + ds << d->at(i); + } + } + } + + void pointf_list_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + PointF_List* d = (PointF_List*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + QPointF dVal; + ds >> dVal; + + d->append(dVal); + } + } + } + + void ploygon_list_save(QDataStream & ds, const void * p) + { + Ploygon_List* d = (Ploygon_List*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + ds << d->at(i); + } + } + } + + void ploygon_list_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + Ploygon_List* d = (Ploygon_List*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + QPolygon dVal; + ds >> dVal; + + d->append(dVal); + } + } + } + + void ploygonf_list_save(QDataStream & ds, const void * p) + { + PloygonF_List* d = (PloygonF_List*)p; + if (d) + { + ds << d->size(); + for (int i = 0; i < d->size(); i++) + { + ds << d->at(i); + } + } + } + + void ploygonf_list_load(QDataStream & ds, void * p) + { + int nCount = 0; + ds >> nCount; + + PloygonF_List* d = (PloygonF_List*)p; + if (d) + { + for (int i = 0; i < nCount; i++) + { + QPolygonF dVal; + ds >> dVal; + + d->append(dVal); + } + } + } + + void mat_polygonf_save(QDataStream & ds, const void * p) + { + MatEx* d = (MatEx*)p; + if (d) + { + QVariant var; + var.setValue(d->first); + ds << var; + ds << d->second; + } + } + + void mat_polygonf_load(QDataStream & ds, void * p) + { + MatEx* d = (MatEx*)p; + if (d) + { + QVariant var; + ds >> var; + d->first = var.value(); + ds >> d->second; + } + } + + void algo_exec_result_save(QDataStream & ds, const void * p) + { + AlgoExecResult* d = (AlgoExecResult*)p; + if (d) + { + ds << d->code; + ds << d->errMsg; + } + } + + void algo_exec_result_load(QDataStream & ds, void * p) + { + + AlgoExecResult* d = (AlgoExecResult*)p; + if (d) + { + ds >> d->code; + ds >> d->errMsg; + + } + } + + void initUserTypeDefine() + { + qRegisterMetaType("task_out_param"); + QMetaType::registerStreamOperators("task_out_param", task_out_param_save, task_out_param_load); + + qRegisterMetaType("double_list"); + QMetaType::registerStreamOperators("double_list", double_list_save, double_list_load); + + qRegisterMetaType("linef_list"); + QMetaType::registerStreamOperators("linef_list", linef_list_save, linef_list_load); + + qRegisterMetaType("line_list"); + QMetaType::registerStreamOperators("line_list", line_list_save, line_list_load); + + qRegisterMetaType("cv_mat"); + QMetaType::registerStreamOperators("cv_mat", cv_mat_save, cv_mat_load); + + qRegisterMetaType("ploygon_list"); + QMetaType::registerStreamOperators("ploygon_list", ploygon_list_save, ploygon_list_load); + + qRegisterMetaType("ploygonf_list"); + QMetaType::registerStreamOperators("ploygonf_list", ploygonf_list_save, ploygonf_list_load); + + qRegisterMetaType("point_list"); + QMetaType::registerStreamOperators("point_list", point_list_save, point_list_load); + + qRegisterMetaType("pointf_list"); + QMetaType::registerStreamOperators("pointf_list", pointf_list_save, pointf_list_load); + + qRegisterMetaType("mat_polygonf"); + QMetaType::registerStreamOperators("mat_polygonf", mat_polygonf_save, mat_polygonf_load); + + qRegisterMetaType("algo_exec_result"); + QMetaType::registerStreamOperators("algo_exec_result", algo_exec_result_save, algo_exec_result_load); + } +} + + +#endif // __LPENGINE_METATYPE_DEFINE_H \ No newline at end of file diff --git a/src/lpBase/lpbdefine.h b/src/lpBase/lpbdefine.h new file mode 100644 index 0000000..8283de0 --- /dev/null +++ b/src/lpBase/lpbdefine.h @@ -0,0 +1,184 @@ +#ifndef LPBDEFINE_H +#define LPBDEFINE_H + +#include "cv.h" +#include "highgui.h" +#include +#include +#include "QJsonDocument" +#include "QJsonArray" +#include "QJsonObject" +#include +#include + +#ifndef BYTE +typedef unsigned char BYTE; +#endif +#ifndef WORD +typedef unsigned short WORD; +#endif +#ifndef DWORD +typedef unsigned long DWORD; +#endif +//#ifndef size_t +//typedef unsigned int size_t; +//#endif +#ifndef LPWORD +typedef WORD *LPWORD; +#endif +#ifndef LPDWORD +typedef DWORD *LPDWORD; +#endif + +#define BASE_MAX_FOLDER_NAME_SIZE 64 +#define BASE_MAX_FILE_NAME_SIZE 64 + + +#define LP_DETECTOR_BUSSINESS_CONFIG_DIR "./config/" +#define LP_DETECTOR_BUSSINESS_IMAGE_DIR "./images/" +#define LP_DETECTOR_BUSSINESS_IN_PARAM_FILE_DIR "./paramfiles/" +#define LP_DETECTOR_BUSSINESS_CONFIG_SOLUTIONMGR_FILE "./config/solutionmgr.json" +#define LP_DETECTOR_BUSSINESS_CONFIG_DEVICEMGR_FILE "./config/devicemgr.json" +#define LP_DETECTOR_BUSSINESS_DB "info.db" +#define LP_DETECTOR_BUSSINESS_CONFIG_FILE_NAME "systemInfo.ini" +#define LP_DETECTOR_BUSSINESS_CONFIG_SOLUTIONMGR_INFO_FILE "./solutions/info.json" +#define LP_DETECTOR_BUSSINESS_CONFIG_SOLUTION_DIR "./solutions/" +#define LP_DETECTOR_BUSSINESS_CONFIG_ALGO_DIR "./algorithmLib/" +#define LP_DETECTOR_BUSSINESS_CONFIG_PARAMSHARE_FILE "./config/paramshare.json" + + + + +#define LP_DETECTOR_INVALID_ID 0xffffffff + +#define LP_DETECTOR_ALGO_PARAM_INPUT_ROI "roi" +#define LP_DETECTOR_ALGO_PARAM_RELY_COORDINATE "rely_coordinate" +#define LP_DETECTOR_ALGO_PARAM_RELATIVE_ROI "relative_roi" + +const DWORD LP_DETECTOR_DEVICE_SERIALPORT_BASE = 0x0210FFFF; + +namespace EngineBase +{ + static cv::Mat QImage2cvMat(QImage image) + { + cv::Mat mat; + switch (image.format()) + { + case QImage::Format_ARGB32: + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); + break; + case QImage::Format_RGB888: + mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); + cv::cvtColor(mat, mat, CV_BGR2RGB); + break; + case QImage::Format_Indexed8: + mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); + break; + } + return mat; + } + + static QImage convMat2QImage(cv::Mat & mat) + { + //qDebug() << "ERROR: Mat could not be converted to QImage."; + // 8-bits unsigned, NO. OF CHANNELS = 1 + if (mat.type() == CV_8UC1) + { + QImage image(mat.cols, mat.rows, QImage::Format_Indexed8); + // Set the color table (used to translate colour indexes to qRgb values) + image.setColorCount(256); + for (int i = 0; i < 256; i++) + { + image.setColor(i, qRgb(i, i, i)); + } + // Copy input Mat + uchar *pSrc = mat.data; + for (int row = 0; row < mat.rows; row++) + { + uchar *pDest = image.scanLine(row); + memcpy(pDest, pSrc, mat.cols); + pSrc += mat.step; + } + + return image; + } + // 8-bits unsigned, NO. OF CHANNELS = 3 + else if (mat.type() == CV_8UC3) + { + // Copy input Mat + const uchar *pSrc = (const uchar*)mat.data; + // Create QImage with same dimensions as input Mat + QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888); + + return image.rgbSwapped(); + } + else if (mat.type() == CV_8UC4) + { + qDebug() << "CV_8UC4"; + // Copy input Mat + const uchar *pSrc = (const uchar*)mat.data; + // Create QImage with same dimensions as input Mat + QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32); + return image.copy(); + } + else + { + qDebug() << "ERROR: Mat could not be converted to QImage."; + return QImage(); + } + } + + static bool DeleteDir(const QString &dirName) + { + if (dirName.isEmpty()) + { + return false; + } + + QDir dir(dirName); + if (!dir.exists()) + { + return true; + } + + QString srcPath = QDir::toNativeSeparators(dirName); + if (!srcPath.endsWith(QDir::separator())) + srcPath += QDir::separator(); + + + QStringList fileNames = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden); + bool error = false; + for (QStringList::size_type i = 0; i != fileNames.size(); ++i) + { + QString filePath = srcPath + fileNames.at(i); + QFileInfo fileInfo(filePath); + if (fileInfo.isFile() || fileInfo.isSymLink()) + { + QFile::setPermissions(filePath, QFile::WriteOwner); + if (!QFile::remove(filePath)) + { + error = true; + } + } + else if (fileInfo.isDir()) + { + if (!DeleteDir(filePath)) + { + error = true; + } + } + } + + if (!dir.rmdir(QDir::toNativeSeparators(dir.path()))) + { + qWarning() << "remove dir" << dir.path() << " faild!"; + error = true; + } + + return !error; + } +} + +#endif // LPBDEFINE_H \ No newline at end of file diff --git a/src/lpBase/lpbengine.h b/src/lpBase/lpbengine.h new file mode 100644 index 0000000000000000000000000000000000000000..e64942dce075d02bd9bffde7b1d50a7eabec8680 GIT binary patch literal 41322 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 literal 0 HcmV?d00001 diff --git a/src/lpBase/lpdesigner.h b/src/lpBase/lpdesigner.h new file mode 100644 index 0000000..f445d97 --- /dev/null +++ b/src/lpBase/lpdesigner.h @@ -0,0 +1,41 @@ +#ifndef LPDESIGNER_H +#define LPDESIGNER_H + +#include "lpdsgdefine.h" +#include "SystemStateInfo.h" +#include +//#include "vld.h" +class IDetectorEngine; +class IDetectorTask; + +class IDetectorUISink +{ +public: + virtual void OnManualTrigger() = 0; + virtual void ResetItem(QPoint pos, QRect size) = 0; + virtual void OnSetParam(IDetectorTask* pTask) = 0; +}; + +class IDetectorUI +{ +public: + virtual bool ShowDeviceMgrDlg() = 0; + virtual bool ShowSolutionMgrDlg() = 0; + virtual bool ShowMainFrame() = 0; + virtual bool ShowReportView(QMap statInfo) = 0; +}; + + +#ifdef LPDESIGNER_LIB +# define LPDESIGNER_EXPORT extern "C" __declspec(dllexport) +#else +# define LPDESIGNER_EXPORT extern "C" __declspec(dllimport) +#endif + +typedef void(*FnLpDesignerNewInstance)(IDetectorUI **lppDetectorUI, IDetectorEngine *lpDE, IDetectorUISink* lpSink); + +LPDESIGNER_EXPORT void LpDesignerNewInstance(IDetectorUI **lppDetectorUI, IDetectorEngine *lpDE, IDetectorUISink* lpSink); + +LPDESIGNER_EXPORT bool LpDesignerDeleteInstance(); + +#endif // LPDESIGNER_H diff --git a/src/lpBase/lpdsgdefine.h b/src/lpBase/lpdsgdefine.h new file mode 100644 index 0000000..ddddf29 --- /dev/null +++ b/src/lpBase/lpdsgdefine.h @@ -0,0 +1,9 @@ +#ifndef LPDSGDEFINE_H +#define LPDSGDEFINE_H + + +#define LP_DETECTOR_DSG_TASK_ITEM_LOAD "Load" +#define LP_DETECTOR_UI_CONFIG_FILE_NAME "set.ini" +#define LP_DETECTOR_UI_REPORT_FOLDER "./report/" + +#endif // LPDSGDEFINE_H \ No newline at end of file diff --git a/src/lpBase/sysInclude.h b/src/lpBase/sysInclude.h new file mode 100644 index 0000000..da7ce4c --- /dev/null +++ b/src/lpBase/sysInclude.h @@ -0,0 +1,26 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:sysInclude.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/08 + History:8:4:2015 13:36 + *******************************************************************************/ +#ifndef __SYS_INCLUDE_H_20150408 +#define __SYS_INCLUDE_H_20150408 + +#ifdef QT_CORE_LIB +#include +#include +#include +#include +#include +#include +#endif + +#if defined(Q_OS_WIN) || defined(WIN32) || defined(WIN64) +#include +#endif + +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/AlgorithmResult.cpp b/src/lpCoreCtrl/AlgorithmResult.cpp new file mode 100644 index 0000000..580b455 --- /dev/null +++ b/src/lpCoreCtrl/AlgorithmResult.cpp @@ -0,0 +1,48 @@ +#include "AlgorithmResult.h" + +CAlgorithmResult::CAlgorithmResult() +{ + +} + +CAlgorithmResult::~CAlgorithmResult() +{ + +} + +QVariantMap CAlgorithmResult::ToVariantMap() +{ + QReadLocker locker(&m_lock); + return m_json.toVariantMap(); +} + +void CAlgorithmResult::SetResult(const QString& key, QVariant& val) +{ + QWriteLocker locker(&m_lock); + m_json.insert(key, QJsonValue::fromVariant(val)); +} +bool CAlgorithmResult::AccumulateResult(const QString& key, QVariant& val) +{ + QWriteLocker locker(&m_lock); + QJsonValue jval = m_json.value(key); + if (jval.isUndefined()) + { + m_json.insert(key, QJsonValue::fromVariant(val)); + } + else + { + if (!jval.isDouble()) + { + return false; + } + bool bOk; + double fv = val.toDouble(&bOk); + if (!bOk) + { + return false; + } + fv += jval.toDouble(); + m_json.insert(key, fv); + } + return true; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/AlgorithmResult.h b/src/lpCoreCtrl/AlgorithmResult.h new file mode 100644 index 0000000..ea4c86c --- /dev/null +++ b/src/lpCoreCtrl/AlgorithmResult.h @@ -0,0 +1,22 @@ +#ifndef CALGORITHMRESULT_H +#define CALGORITHMRESULT_H + +#include "iCoreCtrl.h" +#include +#include + +class CAlgorithmResult : public IAlgorithmResult +{ +public: + CAlgorithmResult(); + virtual ~CAlgorithmResult(); + virtual QVariantMap ToVariantMap(); + void SetResult(const QString& key, QVariant& val); + bool AccumulateResult(const QString& key, QVariant& val); +private: + QJsonObject m_json; + QReadWriteLock m_lock; + //friend class CImageObject; +}; + +#endif // CALGORITHMRESULT_H diff --git a/src/lpCoreCtrl/AlgorithmShared.cpp b/src/lpCoreCtrl/AlgorithmShared.cpp new file mode 100644 index 0000000..4c0b1dd --- /dev/null +++ b/src/lpCoreCtrl/AlgorithmShared.cpp @@ -0,0 +1,45 @@ +#include "AlgorithmShared.h" + +CAlgorithmShared::CAlgorithmShared() + : m_stamp(0) +{ + SetValue(ALG_SHARED_GLOBAL_KEY_APP_STATUS, APP_STATUS_DEFAULT); +} + +CAlgorithmShared::~CAlgorithmShared() +{ + +} + +void CAlgorithmShared::SetValue(const QString& key, const QVariant& value) +{ + m_values.insert(key, value); + updateStamp(); +} + +QStringList CAlgorithmShared::IGetStringList(const QString& key) +{ + if (!m_values.contains(key)) + { + return QStringList(); + } + return m_values[key].toStringList(); +} + +int CAlgorithmShared::IGetInt(const QString& key, int nDef/* = 0*/) +{ + if (!m_values.contains(key)) + { + return nDef; + } + bool bOk; + int nRet = m_values[key].toInt(&bOk); + if (bOk) + { + return nRet; + } + else + { + return nDef; + } +} \ No newline at end of file diff --git a/src/lpCoreCtrl/AlgorithmShared.h b/src/lpCoreCtrl/AlgorithmShared.h new file mode 100644 index 0000000..eac4458 --- /dev/null +++ b/src/lpCoreCtrl/AlgorithmShared.h @@ -0,0 +1,29 @@ +#ifndef ALGORITHMSHARED_H +#define ALGORITHMSHARED_H + +#include "iAlgorithm.h" +#include +#include + +class CAlgorithmShared : public IAlgorithmShared +{ +public: + CAlgorithmShared(); + ~CAlgorithmShared(); + virtual UINT64 ILastUpdateTime() { + return m_stamp; + } + virtual QStringList IGetStringList(const QString& key); + virtual int IGetInt(const QString& key, int nDef = 0); + + void SetValue(const QString& key, const QVariant& value); +private: + void updateStamp() { + m_stamp = QDateTime::currentDateTime().toTime_t(); + } +private: + QVariantMap m_values; + uint m_stamp; +}; + +#endif // ALGORITHMSHARED_H diff --git a/src/lpCoreCtrl/CoreCtrl.cpp b/src/lpCoreCtrl/CoreCtrl.cpp new file mode 100644 index 0000000..c5ff44e --- /dev/null +++ b/src/lpCoreCtrl/CoreCtrl.cpp @@ -0,0 +1,479 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:CoreCtrl.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/24 + History:24:3:2015 17:22 + *******************************************************************************/ +#include "CoreCtrl.h" +#include "globalCoreCtrl.h" +#include "baseFunction.h" +#include "QZkJsonParser.h" +#include +#include + +CCoreCtrl::CCoreCtrl(void) +{ + +} + +CCoreCtrl::~CCoreCtrl(void) +{ + IFreeCore(); +} + +int CCoreCtrl::IInitCore(class IDetectorEngine* pDE /*= NULL*/) +{ + int nRet = 0; + nRet = createShowWindows();//根据camera.json中的"windows"创建显示窗口内存, + //create threads + int nCount = gCoreSetting.threadsCount; + if (nCount <= 0) + { + nCount = SYS_F::GetCpus();//获取系统cpu线程数量 + } + /*创建图片处理线程*/ + for (int i = 0; i < nCount; ++i) + { + QTpThreadImage *pThd = new QTpThreadImage(this, pDE); + if( NULL == pThd ) + { + break; + } + +// pThd->StartThread(); +// pThd->setPriority(QThread::TimeCriticalPriority); + m_imgThdPool.append(pThd); + ++nRet; + } + + // + IStartImageProcess(); + //create cameraes... + glpCameralPool->ICreateCameraes();//更加camera.json 创建相机对象 + glpCameralPool->IOpenDevicesEx();//打开相机 + glpCameralPool->ISetTriggerMode(DEV_TRIGGER_MODE_OUT);//设置默认的触发模式 + glpCameralPool->IStartDevices();//设置相机开始工作 + return nRet; +} + +void CCoreCtrl::IFreeCore() +{ + glpCameralPool->IStopDevices();//设置相机停止工作 + glpCameralPool->ICloseDevices();//关闭相机 + glpCameralPool->IDeleteDameraes();//删除相机 + //删除图片处理线程 + while( !m_imgThdPool.isEmpty() ) + { + QTpThreadImage* pThd = m_imgThdPool.takeLast(); + pThd->EndThread(); + delete pThd; + } + //m_showImages.clear(releaseShowImage); + m_showWindows.clear(releaseShowWindow);//删除用用图像显示窗口内存 + + // 这里等待所有线程完成工作,虽然用clear也可以, + // 但是程序在退出的时候会跳出来错误。不够友好 + QThreadPool::globalInstance()->waitForDone(); + //QThreadPool::globalInstance()->clear(); +} + +void CCoreCtrl::IStartImageProcess() +{ + for (int i = 0; i < m_imgThdPool.size(); i++) + { + m_imgThdPool.at(i)->StartThread(); + m_imgThdPool.at(i)->setPriority(QThread::TimeCriticalPriority); + } +} + +void CCoreCtrl::IEndImageProcess() +{ + for (int i = 0; i < m_imgThdPool.size(); i++) + { + m_imgThdPool.at(i)->EndThread(); + } +} + +//void CCoreCtrl::releaseShowImage(const QString& k, QZkShowImage*& v, void* pData) +//{ +// delete v; +//} + +bool CCoreCtrl::ShowCameraImage(const ZStringA& serial, UINT nFrameNum, QImage& image, INT64 nStamp, int nIndex/* = 0*/, const QString& szExtend /*= NULL*/) +{ + QVariantMap map; + map.clear(); + map.insert("camera", serial); + map.insert("frame", nFrameNum); + QVariant vimg; + vimg.setValue(image); + map.insert("image", vimg); + glpGuiCallback->INewCameraImage(map); + + QString sWinId = serial + "_" + QString::number(nIndex); + CShowWindow* pWin = m_showWindows.value(sWinId, NULL); + if (NULL != pWin && pWin->AddCameraImage(image, nFrameNum, nStamp, szExtend)/*pWin->SetCameraImage(pCamImage)*/) + { + glpGuiCallback->IUpdateShow(pWin->CameraWin().key); + return true; + } + else + { + return false; + } +} + +void CCoreCtrl::DrawDefectOrg(IImageObject::emTpDefectType defectType, BYTE* pData, int nDataLen, CZkCameraImage* pCamImage) +{ + QString sWinId = QString::fromUtf8(pCamImage->Serial()) + "_" + QString::number(0); + CShowWindow* pWin = m_showWindows.value(sWinId, NULL); + if (NULL != pWin && pWin->DrawDefect(defectType, pData, nDataLen, pCamImage)) + { + glpGuiCallback->IUpdateShow(pWin->CameraWin().key); + } +} + +void CCoreCtrl::DrawImageOrg(QImage& srcImg, CZkCameraImage* pCamImage) +{ + if (!ShowCameraImage(pCamImage->Serial(), pCamImage->FrameNumber(), srcImg, pCamImage->Stamp(), 1, QString::fromUtf8(pCamImage->FileName()))) + { + QString sWinId = QString::fromUtf8(pCamImage->Serial()) + "_" + QString::number(0); + CShowWindow* pWin = m_showWindows.value(sWinId, NULL); + if (NULL != pWin && pWin->DrawImage(srcImg, pCamImage->FrameNumber(), QString::fromUtf8(pCamImage->FileName()))) + { + glpGuiCallback->IUpdateShow(pWin->CameraWin().key); + } + } +} + +void CCoreCtrl::ISetResult(const QString& key, QVariant& val) +{ + m_algResult.SetResult(key, val); + //callback to ui + glpGuiCallback->IAlgorithmResult(m_algResult.ToVariantMap()); +} + +void CCoreCtrl::AccumulateResult(const QString& key, QVariant& val) +{ + if (m_algResult.AccumulateResult(key, val)) + { + glpGuiCallback->IAlgorithmResult(m_algResult.ToVariantMap()); + } +} + +/* +void CCoreCtrl::setShowWindow(CShowWindow*& pWin, void* pData) +{ + ; +} +*/ + +//void CCoreCtrl::setShowImage(const QString& k, QZkShowImage*& v, void* pData) +//{ +// v->SetShowImage((CZkCameraImage*)pData); +//} + +QList CCoreCtrl::ICameraKeys() +{ + return glpCameralPool->ICameraKeys(); +} + +QList CCoreCtrl::ICameraKeysOrderByIds() +{ + return glpCameralPool->ICameraKeysOrderByIds(); +} + +bool CCoreCtrl::ICameraOption(const QString& strSerial, TP_CAMERA_OPTION& camOpt) +{ + return glpCameralPool->ICameraOption(strSerial, camOpt); +} + +bool CCoreCtrl::ICameraOptionByKey(const QString& strSerial, TP_CAMERA_OPTION& camOpt) +{ + return glpCameralPool->ICameraOptionByKey(strSerial, camOpt); +} + +QList CCoreCtrl::IEnumAvailableCameras(emTpDeviceType camType) +{ + return glpCameralPool->IEnumAvailableCameras(camType); +} + +void CCoreCtrl::ISetTriggerMode(emTpDeviceTriggerMode triggerMode, long nFrameRate) +{ + glpCameralPool->ISetTriggerMode(triggerMode, TRIGGER_DIRECT_FOREWARD, nFrameRate); +} + +void CCoreCtrl::IManualTrigger(emTpTriggerDirection direct) +{ + glpCameralPool->IManualTrigger(direct); +} + +void CCoreCtrl::ISnapImage(const QStringList& camKeys) +{ + glpCameralPool->ISnapImage(camKeys); +} + +void CCoreCtrl::ISendSoftTrigger(const QStringList& camKeys) +{ + glpCameralPool->ISendSoftTrigger(camKeys); +} + +void CCoreCtrl::IDrawImage(const QString& sKey, QPainter& painter, QRect& rt) +{ + /* + tagDrawShowImageParam param; + param.painter = &painter; + param.rt = &rt; + m_showImages.valueCall(sKey, drawShowImage, ¶m);*/ +} + +//void CCoreCtrl::drawShowImage(const QString& key, QZkShowImage*& v, void* pData) +//{ +// tagDrawShowImageParam* param = (tagDrawShowImageParam*)pData; +// v->DrawToPainter(param->painter, param->rt); +//// param->painter->drawPixmap() +//} + +int CCoreCtrl::createShowWindows() +{ + int nCount = 0; +// QList camWins = glpCameralPool->ICameraWins(); +// for (int i = 0; i < camWins.size(); ++i) +// { +// CShowWindow* pShow = new CShowWindow(camWins[i]); +// if (NULL == pShow) +// { +// continue; +// } +// if (m_showWindows.contains(pShow->WindowId())) +// { +// delete pShow; +// continue; +// } +// m_showWindows.insert(pShow->WindowId(), pShow); +// gGuiCallback_FuncCall(IAddWindows, camWins[i]->key, pShow->WindowId()); +// ++nCount; +// } + return nCount; +} + +void CCoreCtrl::releaseShowWindow(CShowWindow*& pWin, void* pData) +{ + delete pWin; + pWin = NULL; +} + +bool CCoreCtrl::IAddCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew) +{ + if (glpCameralPool->IAddCamWin(strKey, camwinOpt, bNew)) + { + if (bNew){ + QList camWins = glpCameralPool->ICameraWins(); + for (int i = 0; i < camWins.size(); ++i) + { + if (camWins[i]->key.compare(strKey, Qt::CaseInsensitive) == 0){ + CShowWindow* pShow = new CShowWindow(camWins[i]); + if (!pShow) return false; + + if (m_showWindows.contains(pShow->WindowId())) + { + delete pShow; + return false; + } + m_showWindows.insert(pShow->WindowId(), pShow); + gGuiCallback_FuncCall(IAddWindows, camWins[i]->key, pShow->WindowId()); + return true; + } + } + } + return true; + } + + return false; +} + +bool CCoreCtrl::IDelCamWin(const QString& strKey) +{ + TP_CAMERA_WIN optCamWin; + if (ICamWinOptionByKey(strKey, optCamWin)){ + QString strWinId = QString(optCamWin.device) + "_" + QString::number(optCamWin.index); + CShowWindow* pShow = m_showWindows.value(strWinId); + if (pShow){ + m_showWindows.remove(strWinId); + delete pShow; + pShow = NULL; + } + } + + return glpCameralPool->IDelCamWin(strKey); +} + +QImage CCoreCtrl::IShowImage(const QString& sShowId, int& nFrameNum, QString& szExShow) +{ + CShowWindow* pWin = m_showWindows.value(sShowId, NULL); + if (NULL == pWin) + { + nFrameNum = -1; + return QImage(); + } + else + { + nFrameNum = pWin->FrameNum(); + szExShow = pWin->ExtendString(); + return pWin->ShowImage(); + } +} + +QMap CCoreCtrl::IGetCamShowNames() +{ + return glpCameralPool->IGetCamShowNames(); +} + +IAlgorithmOption* CCoreCtrl::IGetAlgorithmOption(int nAlgorithm) +{ + return glpImgProc->IGetAlgorithmOption(nAlgorithm); +} + +bool CCoreCtrl::ISetCameraOption(const QString& strSerial, const TP_CAMERA_OPTION& camOpt) +{ + return glpCameralPool->ISetCameraOption(strSerial, camOpt); +} + +bool CCoreCtrl::IReopenCameraes() +{ + return glpCameralPool->IOpenDevices(); +} + +void CCoreCtrl::ISetVirtualImages(const QString& camera, const QStringList& szImages) +{ + glpCameralPool->ISetVirtualImages(camera, szImages); +} + +QImage CCoreCtrl::IOriginImage(const QString& sShowId) +{ + CShowWindow* pWin = m_showWindows.value(sShowId, NULL); + if (NULL == pWin) + { + return QImage(); + } + else + { + return pWin->OriginImage(); + } +} +void CCoreCtrl::ISetCameraProperty(const QString& camera, TP_CAMERA_PROPERTY& property) +{ + return glpCameralPool->ISetCameraProperty(camera, property); +} + +void CCoreCtrl::ISetAlgorithmShared(const QString& key, const QVariant& value) +{ + m_algShared.SetValue(key, value); +} + +void CCoreCtrl::IWriteIo(const TP_IO_OUT_CONTROL& state) +{ + +} + +bool CCoreCtrl::updateCommJson(const QString& sJsonPath) +{ + return false; + +} + +bool CCoreCtrl::updateCamJson(const QString& sJsonPath) +{ + int nError; + QJsonObject originObjJson = QZkJsonParser::ReadJsonObject(sJsonPath, &nError); + + QJsonObject cameraRoot; + QJsonObject cameraDevs; + + QList cameralist = ICameraKeys(); + foreach(const QString& strCameraName, cameralist) + { + TP_CAMERA_OPTION camopt; + if (ICameraOptionByKey(strCameraName, camopt)) + { + QJsonObject camObj; + camObj["id"] = camopt.id; + camObj["type"] = camopt.deviceType; + camObj["showname"] = camopt.showName; + + camObj["format"] = "None"; + QMap::const_iterator iter = gColorNameMap.find(camopt.format); + if (iter != gColorNameMap.constEnd()) + { + camObj["format"] = iter.value(); + } + + camObj["folder"] = camopt.folder; + camObj["zoom"] = camopt.zoom; + camObj["save"] = camopt.save; + camObj["save_img_suffix"] = camopt.saveImgSuffix; + camObj["algorithm"] = camopt.algorithm; + camObj["rely_on_camera"] = camopt.relyOnCamera; + camObj["board_type"] = camopt.boardType; + camObj["board_num"] = camopt.boardNum; + camObj["loop"] = camopt.loop; + camObj["algorithm_dll"] = camopt.dllsuffix.toInt(); + camObj["exposure"] = camopt.exposure; + camObj["gain"] = camopt.gain; + camObj["savefile_rgb_swapped"] = camopt.bNeedRgbSwapped; + camObj["board_name"] = camopt.sBoardName; + camObj["board_config_file"] = camopt.sBoardConfigFile; + + cameraDevs[camopt.uniqueName] = camObj; + } + } + + cameraRoot["devices"] = cameraDevs; + cameraRoot["pool"] = originObjJson["pool"].toObject(); + + + QJsonObject cameraWins; + QList camwinlist = ICamWinKeys(); + foreach(const QString& strCamWinName, camwinlist) + { + TP_CAMERA_WIN camwinOpt; + if (ICamWinOptionByKey(strCamWinName, camwinOpt)) + { + QJsonObject camwinObj; + camwinObj["bitsperpixel"] = camwinOpt.bitsPerPixel; + camwinObj["buffer_h"] = camwinOpt.bufferH; + camwinObj["buffer_w"] = camwinOpt.bufferW; + camwinObj["buffers"] = camwinOpt.buffers; + camwinObj["cache_origin"] = camwinOpt.bCacheOriginImage; + camwinObj["device"] = camwinOpt.device; + camwinObj["images"] = camwinOpt.images; + camwinObj["index"] = camwinOpt.index; + camwinObj["mirror"] = camwinOpt.mirror; + camwinObj["rgb_swapped"] = camwinOpt.bNeedRgbSwapped; + + + cameraWins[camwinOpt.key] = camwinObj; + } + } + cameraRoot["windows"] = cameraWins; + + return saveJsonToFile(cameraRoot, sJsonPath); +} + +bool CCoreCtrl::saveJsonToFile(const QJsonObject& json, const QString& fileFullName) +{ + QFile saveFile(fileFullName); + if (!saveFile.open(QIODevice::WriteOnly)) { + qWarning() << "Couldn't open save file - " << fileFullName + << " - " << __FUNCTION__; + return false; + } + + QJsonDocument saveDoc(json); + saveFile.write(saveDoc.toJson()); + + return true; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/CoreCtrl.h b/src/lpCoreCtrl/CoreCtrl.h new file mode 100644 index 0000000..3361ba9 --- /dev/null +++ b/src/lpCoreCtrl/CoreCtrl.h @@ -0,0 +1,100 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:CoreCtrl.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/24 + History:24:3:2015 17:21 + *******************************************************************************/ +#ifndef __CORE_CTRL_H +#define __CORE_CTRL_H + +#include "iCoreCtrl.h" +#include "qtpthreadimage.h" +#include "QZkMutexMap.h" +//#include "QZkShowImage.h" +#include "ShowWindow.h" +#include "AlgorithmResult.h" +#include "AlgorithmShared.h" + +class CCoreCtrl : public ICoreCtrl +{ +public: + CCoreCtrl(void); + virtual ~CCoreCtrl(void); + //初始化CoreCtrl + virtual int IInitCore(class IDetectorEngine* pDE = NULL); + //释放CoreCtrl + virtual void IFreeCore(); + + //开始执行算法处理 + virtual void IStartImageProcess(); + virtual void IEndImageProcess(); + + bool ShowCameraImage(const ZStringA& serial, UINT nFrameNum, QImage& image, INT64 nStamp, int nIndex = 0, const QString& szExtend = NULL); + void DrawDefectOrg(IImageObject::emTpDefectType defectType, BYTE* pData, int nDataLen, CZkCameraImage* pCamImage); + void DrawImageOrg(QImage& srcImg, CZkCameraImage* pCamImage); + void AccumulateResult(const QString& key, QVariant& val); + virtual void ISetResult(const QString& key, QVariant& val); + + +private: + virtual QList ICameraKeys(); + virtual QList ICameraKeysOrderByIds(); + virtual bool ICameraOption(const QString& strSerial, TP_CAMERA_OPTION& camOpt); + virtual bool ICameraOptionByKey(const QString& strSerial, TP_CAMERA_OPTION& camOpt); + virtual QList IEnumAvailableCameras(emTpDeviceType camType); // try to get available (connected to the computer) by camera type(manufacturer) + virtual void ISetTriggerMode(emTpDeviceTriggerMode triggerMode, long nFrameRate); + virtual emTpDeviceTriggerMode IGetTriggerMode() { return glpCameralPool->IGetTriggerMode(); } + virtual void IManualTrigger(emTpTriggerDirection direct); + virtual void ISnapImage(const QStringList& camKeys); + virtual void ISendSoftTrigger(const QStringList& camKeys); + virtual void IDrawImage(const QString& sKey, QPainter& painter, QRect& rt); + virtual QImage IShowImage(const QString& sShowId, int& nFrameNum, QString& szExShow); + virtual QMap IGetCamShowNames(); + virtual IAlgorithmOption* IGetAlgorithmOption(int nAlgorithm); + virtual bool ISetCameraOption(const QString& strSerial, const TP_CAMERA_OPTION& camOpt); + virtual bool IReopenCameraes(); + virtual void ISetVirtualImages(const QString& camera, const QStringList& szImages); + virtual QImage IOriginImage(const QString& sShowId); + virtual void ISetCameraProperty(const QString& camera, TP_CAMERA_PROPERTY& property); + virtual void ISetAlgorithmShared(const QString& key, const QVariant& value); + virtual void IWriteIo(const TP_IO_OUT_CONTROL& state); + + //camera operate + virtual bool ICreateCamera(const QString& strSerial, int nType) { return glpCameralPool->ICreateCamera(strSerial, nType); } + virtual bool IOpenCamera(const QString& strSerial) { return glpCameralPool->IOpenCamera(strSerial); } + virtual bool ICloseCamera(const QString& strSerial) { return glpCameralPool->ICloseCamera(strSerial); } + virtual bool IStartCamera(const QString& strSerial) { return glpCameralPool->IStartCamera(strSerial); } + virtual bool IStopCamera(const QString& strSerial) { return glpCameralPool->IStopCamera(strSerial); } + virtual bool IDeleteCamera(const QString& strSerial) { return glpCameralPool->IDeleteCamera(strSerial); } + virtual bool IAddCamera(const QString& strName, const TP_CAMERA_OPTION& camOpt, bool bNew, bool bAutoRun = false) { return glpCameralPool->IAddCamera(strName, camOpt, bNew, bAutoRun); } + + //operate camera window + virtual QList ICamWinKeys() { return glpCameralPool->ICamWinKeys(); } + virtual bool ICamWinOptionByKey(const QString& strKey, TP_CAMERA_WIN& camwinOpt) { return glpCameralPool->ICamWinOptionByKey(strKey, camwinOpt); } + virtual bool IAddCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew); + virtual bool IDelCamWin(const QString& strKey); + + //更新json配置文件 + virtual bool updateCommJson(const QString& sJsonPath); + virtual bool updateCamJson(const QString& sJsonPath); + bool saveJsonToFile(const QJsonObject& json, const QString& fileFullName); + + virtual bool ICamsStartPush() { return glpCameralPool->ICamsStartPush(); } + virtual bool ICamsPausePush() { return glpCameralPool->ICamsPausePush(); } +private: + int createShowWindows(); + static void releaseShowWindow(CShowWindow*& pWin, void* pData); +private: + QList m_imgThdPool; + //QZkMutexMap m_showImages; + QZkMutexMap m_showWindows; + CAlgorithmResult m_algResult; + CAlgorithmShared m_algShared; + + int m_nCoreCount; + friend class CImageObject; +}; +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/GlobalDataBase.cpp b/src/lpCoreCtrl/GlobalDataBase.cpp new file mode 100644 index 0000000..8189be5 --- /dev/null +++ b/src/lpCoreCtrl/GlobalDataBase.cpp @@ -0,0 +1,53 @@ +#include "GlobalDataBase.h" + +CGlobalDataBase::CGlobalDataBase(char *szExePath) +{ + int i = 0; + if( NULL != szExePath ) + { + i = std::strlen(szExePath); + if( i >= BASE_MAX_FILE_PATH ) + { + i = BASE_MAX_FILE_PATH - 1; + } + } + --i; + while( i > 0 ) + { + if( szExePath[i] == '\\' ) + { + break; + } + --i; + } + if ( i >= 0 ) + { + std::strncpy(tgdMainPath, szExePath, i+1); + tgdMainPath[i+1] = '\0'; + } + else + { + std::strcpy(tgdMainPath, ".\\"); + } + std::strcpy(tgdUserPath, tgdMainPath); + std::strcpy(tgdFolderNames[TP_FOLDER_NAME_CONFIG], "config\\"); + std::strcpy(tgdFolderNames[TP_FOLDER_NAME_PICTURE], "pic\\"); + + std::memset(&m_coreSetting, 0, sizeof(m_coreSetting)); +} + +CGlobalDataBase::~CGlobalDataBase() +{ + +} + +bool CGlobalDataBase::CoreSettingFromJson(QJsonObject& jsonObj) +{ + if( jsonObj.isEmpty() ) + { + return false; + } + m_coreSetting.threadsCount = jsonObj.value("image_threads").toInt(0); + m_coreSetting.imageShowType = jsonObj.value("show_type").toInt(TP_IMAGE_SHOW_NEW); + return true; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/GlobalDataBase.h b/src/lpCoreCtrl/GlobalDataBase.h new file mode 100644 index 0000000..f44c5a0 --- /dev/null +++ b/src/lpCoreCtrl/GlobalDataBase.h @@ -0,0 +1,20 @@ +#ifndef GLOBALDATABASE_H +#define GLOBALDATABASE_H + +#include "baseInclude.h" +#include + +class CGlobalDataBase : public TP_GLOBAL_DATA +{ +public: + CGlobalDataBase(char *szExePath); + ~CGlobalDataBase(); + + bool CoreSettingFromJson(QJsonObject& jsonObj); +public: + tagTP_CORE_SETTING m_coreSetting; +private: + +}; + +#endif // GLOBALDATABASE_H diff --git a/src/lpCoreCtrl/ImageObject.cpp b/src/lpCoreCtrl/ImageObject.cpp new file mode 100644 index 0000000..2e88d45 --- /dev/null +++ b/src/lpCoreCtrl/ImageObject.cpp @@ -0,0 +1,249 @@ +#include "ImageObject.h" +#include "CoreCtrl.h" +#include "zfunctions.h" + +CImageObject::CImageObject(class CCoreCtrl* pCoreCtrl) + : m_pCoreCtrl(pCoreCtrl) +{ + m_pCamImg = NULL; +} + +CImageObject::~CImageObject() +{ + +} + +bool CImageObject::SetCameraImage(CZkCameraImage* pCamImage) +{ + m_pCamImg = pCamImage; + return true; +} + +emTpColorFormat CImageObject::IColorFormat() +{ + if( NULL == m_pCamImg ) + { + return TP_COLOR_NONE; + } + else + { + return m_pCamImg->ColorFormat(); + } +} + +BYTE* CImageObject::IImageData(int& nOutW, int& nOutH, int& nOutBitsPerPixel, int& nBytesPerLine) +{ + if( NULL == m_pCamImg ) + { + return NULL; + } + else + { + return m_pCamImg->ImageData(nOutW, nOutH, nOutBitsPerPixel, nBytesPerLine); + } +} + +int CImageObject::IGetId() +{ + if( NULL == m_pCamImg ) + { + return 0; + } + else + { + return m_pCamImg->GetId(); + } +} + +int CImageObject::IGetAlgorithm() +{ + if( NULL == m_pCamImg ) + { + return 0; + } + else + { + return m_pCamImg->Algorithm(); + } +} + +int CImageObject::IGetFrameNum() +{ + if( NULL == m_pCamImg ) + { + return 0; + } + else + { + return m_pCamImg->FrameNumber(); + } +} + +int CImageObject::IGetTriggerCount() +{ + if( NULL == m_pCamImg ) + { + return 0; + } + else + { + return m_pCamImg->TriggerCount(); + } +} + +void CImageObject::ISetCameroProperty(TP_CAMERA_PROPERTY& property) +{ + if( NULL != m_pCamImg ) + { + glpCameralPool->ISetCameraProperty(m_pCamImg->Serial(), property); + } +} + +void CImageObject::ISendDataToUI(void* pData) +{ + if (NULL != m_pCamImg) + { + gGuiCallback_FuncCall(ISendDataToUI, TP_UI_DATA_FROM_ALGORITHM, m_pCamImg->Serial(), pData); + } +} + +void CImageObject::ISafeDataToUI(void* pData, int nDataLen) +{ + QByteArray data((char*)pData, nDataLen); + if (NULL != m_pCamImg) + { + gGuiCallback_FuncCall(ISafeDataToUI, TP_UI_DATA_FROM_ALGORITHM, m_pCamImg->Serial(), data); + } + else + { + gGuiCallback_FuncCall(ISafeDataToUI, TP_UI_DATA_FROM_ALGORITHM, NULL, data); + } +} + +void CImageObject::ISafeDataToUI(WORD type, void* pData, int nDataLen) +{ + QByteArray data((char*)pData, nDataLen); + emTpUiDataType emType = (emTpUiDataType)(TP_UI_DATA_FROM_ALGORITHM | type); + if (NULL != m_pCamImg) + { + gGuiCallback_FuncCall(ISafeDataToUI, emType, m_pCamImg->Serial(), data); + } + else + { + gGuiCallback_FuncCall(ISafeDataToUI, emType, NULL, data); + } +} + +void CImageObject::IDrawDefect(const char* szWinName, emTpDefectType defectType, BYTE* pData, int nDataLen) +{ + QByteArray data((char*)pData, nDataLen); + gGuiCallback_FuncCall(IDrawDefectToScene, szWinName, defectType, data); +} +void CImageObject::IDrawDefectOrg(emTpDefectType defectType, BYTE* pData, int nDataLen) +{ + if (NULL != m_pCoreCtrl && NULL != m_pCamImg) + { + m_pCoreCtrl->DrawDefectOrg(defectType, pData, nDataLen, m_pCamImg); + } +} + +INT64 CImageObject::IMeterCode() +{ + if (NULL == m_pCamImg) + { + return 0; + } + return m_pCamImg->MeterCode(); +} +const char* CImageObject::IGetImageUtf8String() +{ + if (NULL == m_pCamImg) + { + return 0; + } + return m_pCamImg->FileName().data(); +} + +void CImageObject::IDrawImage(QImage& image/* = QImage()*/, const char* szWinName/* = NULL*/) +{ + if (NULL != m_pCoreCtrl && NULL != m_pCamImg) + { + if (image.isNull()) + { + image = m_pCamImg->ToQImage(); + } + m_pCoreCtrl->DrawImageOrg(image, m_pCamImg); + } +} + +QImage CImageObject::IImage() +{ + if (NULL == m_pCamImg) + { + return QImage(); + } + else + { + return m_pCamImg->ToQImage(); + } +} + +const ZStringA& CImageObject::IDllSuffix() +{ + return m_pCamImg->DllSuffix(); +} + +void CImageObject::ISetResult(const QString& key, QVariant& val) +{ + if (NULL != m_pCoreCtrl) + { + m_pCoreCtrl->ISetResult(key, val); + } +} + +void CImageObject::IAccumulateResult(const QString& key, QVariant& val) +{ + if (NULL != m_pCoreCtrl) + { + m_pCoreCtrl->AccumulateResult(key, val); + } +} + +void CImageObject::IMoveToFolder(const QString& szFolder) +{ + if (QFile::exists(QString::fromUtf8(IGetImageUtf8String()))) + { + ZKF::qtCopyFile(IGetImageUtf8String(), szFolder.toUtf8().data()); + } + else + { + if (NULL == m_pCamImg) + { + return; + } + } +} + +IAlgorithmShared* CImageObject::IGetShared() +{ + if (NULL == m_pCoreCtrl) + { + return NULL; + } + return &m_pCoreCtrl->m_algShared; +} + +void CImageObject::IVariantMapToUI(const QVariantMap& vMap) +{ + glpGuiCallback->IVariantMapToUI(TP_UI_DATA_FROM_ALGORITHM, m_pCamImg->Serial(), vMap); +} + +const ZStringA& CImageObject::ICameraSerial() +{ + return m_pCamImg->Serial(); +} + +const QVariant& CImageObject::IVarFromUI() +{ + return m_pCamImg->VarFromUI(); +} \ No newline at end of file diff --git a/src/lpCoreCtrl/ImageObject.h b/src/lpCoreCtrl/ImageObject.h new file mode 100644 index 0000000..cfe35bb --- /dev/null +++ b/src/lpCoreCtrl/ImageObject.h @@ -0,0 +1,51 @@ +#ifndef IMAGEOBJECT_H +#define IMAGEOBJECT_H + +#include "globalCoreCtrl.h" +#include +#include "iAlgorithm.h" + +class CImageObject : public IImageObject +{ +public: + CImageObject(class CCoreCtrl* pCoreCtrl); + ~CImageObject(); + bool SetCameraImage(CZkCameraImage* pCamImage); +private: +// virtual const TP_IMAGE_NODE& ImageNode() { return m_imgNode; } + virtual emTpColorFormat IColorFormat(); + //@nBytesPerLine: 0 means there is no valid value + virtual BYTE* IImageData(int& nOutW, int& nOutH, int& nOutBitsPerPixel, int& nBytesPerLine); + virtual int IGetId(); + virtual int IGetAlgorithm(); + virtual int IGetFrameNum(); + virtual int IGetTriggerCount(); + virtual void ISetCameroProperty(TP_CAMERA_PROPERTY& property); + virtual void ISendDataToUI(void* pData); + virtual void IDrawDefect(const char* szWinName, emTpDefectType defectType, BYTE* pData, int nDataLen); + virtual void IDrawDefectOrg(emTpDefectType defectType, BYTE* pData, int nDataLen); + virtual void ISafeDataToUI(void* pData, int nDataLen); + virtual void ISafeDataToUI(WORD type, void* pData, int nDataLen); + virtual INT64 IMeterCode(); + virtual const char* IGetImageUtf8String(); + virtual void IDrawImage(QImage& image = QImage(), const char* szWinName = NULL); + virtual QImage IImage(); + virtual const ZStringA& IDllSuffix(); + virtual void ISetResult(const QString& key, QVariant& val); + virtual void IAccumulateResult(const QString& key, QVariant& val); + virtual void IMoveToFolder(const QString& szFolder); + virtual IAlgorithmShared* IGetShared(); + virtual void IVariantMapToUI(const QVariantMap& vMap); + virtual const ZStringA& ICameraSerial(); + virtual const QVariant& IVarFromUI(); + virtual quint64 ITimeStamp() { + return m_pCamImg->Stamp(); + } + virtual void IDataToComm(DATA_TO_COMM_HEAD& head, char* pData, int nLen) {}; +private: +// TP_IMAGE_NODE m_imgNode; + CZkCameraImage* m_pCamImg; + class CCoreCtrl* m_pCoreCtrl; +}; + +#endif // IMAGEOBJECT_H diff --git a/src/lpCoreCtrl/LoadModule.cpp b/src/lpCoreCtrl/LoadModule.cpp new file mode 100644 index 0000000..ccd5220 --- /dev/null +++ b/src/lpCoreCtrl/LoadModule.cpp @@ -0,0 +1,55 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:LoadModule.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/23 + History:23:3:2015 17:48 + *******************************************************************************/ +#include "LoadModule.h" + +CLoadModule::CLoadModule(const char *szLibName, const char *szFuncInit, const char *szFuncFree, const char* szPath /* = NULL */) +// :m_lib(szLibName) +{ + QString dllFile; + if( NULL != szPath ) + { + dllFile = QString(szPath) + szLibName; + } + else + { + dllFile = szLibName; + } +#ifdef _DEBUG + dllFile.append('d'); +#endif + m_lib.setFileName(dllFile); + m_fpInit = (Func_Lib_Init)m_lib.resolve(szFuncInit); + m_fpFree = (Func_Lib_Free)m_lib.resolve(szFuncFree); +} + + +CLoadModule::~CLoadModule(void) +{ + ModuleFree(); +} + +void* CLoadModule::ModuleInit(void* inParam /* = NULL */) +{ + if( NULL == m_fpInit ) + { + return NULL; + } + return m_fpInit(inParam); +} + +void CLoadModule::ModuleFree() +{ + if( NULL != m_fpFree ) + { + m_fpFree(); + m_fpFree = NULL; + m_lib.unload(); + } +} \ No newline at end of file diff --git a/src/lpCoreCtrl/LoadModule.h b/src/lpCoreCtrl/LoadModule.h new file mode 100644 index 0000000..c247e9d --- /dev/null +++ b/src/lpCoreCtrl/LoadModule.h @@ -0,0 +1,32 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:LoadModule.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/23 + History:23:3:2015 17:48 + *******************************************************************************/ +#ifndef __LOAD_MODULE_H +#define __LOAD_MODULE_H + +#include + +typedef void* (*Func_Lib_Init)(void *); +typedef void (*Func_Lib_Free)(); + +class CLoadModule +{ +public: + CLoadModule(const char *szLibName, const char *szFuncInit, const char *szFuncFree, const char* szPath = NULL); + ~CLoadModule(void); + + void* ModuleInit(void* inParam = NULL); + void ModuleFree(); +private: + QLibrary m_lib; + Func_Lib_Init m_fpInit; + Func_Lib_Free m_fpFree; +}; + +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/ModulesManager.cpp b/src/lpCoreCtrl/ModulesManager.cpp new file mode 100644 index 0000000..6e172a2 --- /dev/null +++ b/src/lpCoreCtrl/ModulesManager.cpp @@ -0,0 +1,109 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ModulesManager.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/23 + History:23:3:2015 16:20 + *******************************************************************************/ +#include "ModulesManager.h" +#include "LoadModule.h" +#include "globalCoreCtrl.h" + +#define EXTERN_LIB_FUNC(lib_name) \ + extern "C" void* lib_name##_Init(void* inParam);\ + extern "C" void lib_name##_Free(); + +#ifndef MDL_CAMERA_USE_DLL + EXTERN_LIB_FUNC(Lib_Camera) +#endif + +#ifndef MDL_IMGPROC_USE_DLL + EXTERN_LIB_FUNC(Lib_ImgProc) +#endif + +#ifndef MDL_COMMUNICATE_USE_DLL + EXTERN_LIB_FUNC(Lib_Communicate) +#endif + +inline void* CModulesManager::getModule(const char* szLib, const char* szInit, const char* szFree, void* pInitParam /* = NULL */) +{ + delModule(szLib); + CLoadModule* pLib = new CLoadModule(szLib, szInit, szFree, gpGlobalData->tgdDllPath); + return pLib->ModuleInit(pInitParam); +} + +inline void CModulesManager::delModule(const char* szLib) +{ + CLoadModule* pLib = m_mapLibs.value(szLib, NULL); + if( NULL != pLib ) + { + pLib->ModuleFree(); + delete pLib; + m_mapLibs.remove(szLib); + } +} + +CModulesManager::CModulesManager(void) +{ +} + + +CModulesManager::~CModulesManager(void) +{ +} + +int CModulesManager::LoadModules() +{ + int nRet = 0; + //load "tpCamera" modules + void *pReturn = NULL; + //Load camera library + CAMERA_IN_PARAM camInParam; + camInParam.pCallback = gpCoreCtrlGlobal; + camInParam.pGlobalData = gpGlobalData; +#ifdef MDL_CAMERA_USE_DLL + pReturn = getModule("tpCamera", "Lib_Camera_Init", "Lib_Camera_Free", &camInParam); +#else + pReturn = Lib_Camera_Init(&camInParam); +#endif + if( NULL == pReturn ) + { + return (-nRet); + } + glpCameralPool = (ICameraPool*)pReturn; + ++nRet; + //Load imgproc library + IMGPROC_IN_PARAM imgprocInParam; + imgprocInParam.pGlobalData = gpGlobalData; +#ifdef MDL_IMGPROC_USE_DLL + pReturn = getModule("tpImgProc", "Lib_ImgProc_Init", "Lib_ImpProc_Free", &imgprocInParam); +#else + pReturn = Lib_ImgProc_Init(&imgprocInParam); +#endif + if( NULL == pReturn ) + { + return (-nRet); + } + glpImgProc = (IImgProc*)pReturn; + ++nRet; + //load communicate library + ++nRet; + // + return nRet; +} + +void CModulesManager::FreeModules() +{ +#ifdef MDL_IMGPROC_USE_DLL + delModule("tpImgProc"); +#else + Lib_ImgProc_Free(); +#endif +#ifdef MDL_CAMERA_USE_DLL + delModule("tpCamera"); +#else + Lib_Camera_Free(); +#endif +} \ No newline at end of file diff --git a/src/lpCoreCtrl/ModulesManager.h b/src/lpCoreCtrl/ModulesManager.h new file mode 100644 index 0000000..26e1251 --- /dev/null +++ b/src/lpCoreCtrl/ModulesManager.h @@ -0,0 +1,38 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ModulesManager.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/23 + History:23:3:2015 16:20 + *******************************************************************************/ +#ifndef __MODULES_MANAGER_H +#define __MODULES_MANAGER_H + +//modues controls +//#define MDL_CAMERA_USE_DLL //open it if using camera's module as a dll +//#define MDL_IMGPROC_USE_DLL +//#define MDL_COMMUNICATE_USE_DLL + +///////////////////////////////////////// +#include "icamera.h" +#include "iCoreCtrl.h" +#include + +class CModulesManager +{ +public: + CModulesManager(void); + ~CModulesManager(void); + int LoadModules(); + void FreeModules(); +private: + inline void* getModule(const char* szLib, const char* szInit, const char* szFree, void* pInitParam = NULL); + inline void delModule(const char* szLib); +private: + QMap m_mapLibs; + +}; + +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/QZkShowImage.cpp b/src/lpCoreCtrl/QZkShowImage.cpp new file mode 100644 index 0000000..82f80e1 --- /dev/null +++ b/src/lpCoreCtrl/QZkShowImage.cpp @@ -0,0 +1,40 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:QZkShowImage.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/17 + History:17:4:2015 15:10 + *******************************************************************************/ +#include "QZkShowImage.h" +#include "ZkCameraImage.h" + +QZkShowImage::QZkShowImage() +{ + +} + +QZkShowImage::~QZkShowImage() +{ + +} + +void QZkShowImage::SetShowImage(class CZkCameraImage* pImg) +{ + m_showImage = pImg->ToShowImage(); + //test +// int depts = m_showImage.depth(); + // + m_nFrameNum = pImg->FrameNumber(); +} + +void QZkShowImage::DrawToPainter(QPainter *painter, QRect* rt) +{ +#ifdef USE_PIXMAP_AS_SHOW + painter->drawPixmap(*rt, m_showImage); +#else ZShowImage == QImage + painter->drawImage(*rt, m_showImage); +#endif + painter->drawText(25, 25, QString::number(m_nFrameNum)); +} \ No newline at end of file diff --git a/src/lpCoreCtrl/QZkShowImage.h b/src/lpCoreCtrl/QZkShowImage.h new file mode 100644 index 0000000..bcc6c8e --- /dev/null +++ b/src/lpCoreCtrl/QZkShowImage.h @@ -0,0 +1,31 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:QZkShowImage.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/17 + History:17:4:2015 15:10 + *******************************************************************************/ +#ifndef QZKSHOWIMAGE_H +#define QZKSHOWIMAGE_H + +#include +#include +#include "baseDefine.h" + +class QZkShowImage +{ +public: + QZkShowImage(); + ~QZkShowImage(); + + void SetShowImage(class CZkCameraImage* pImg); + void DrawToPainter(QPainter *painter, QRect* rt); + +private: + ZShowImage m_showImage; + long m_nFrameNum; +}; + +#endif // QZKSHOWIMAGE_H diff --git a/src/lpCoreCtrl/ShowWindow.cpp b/src/lpCoreCtrl/ShowWindow.cpp new file mode 100644 index 0000000..a7ffa78 --- /dev/null +++ b/src/lpCoreCtrl/ShowWindow.cpp @@ -0,0 +1,332 @@ +#include "ShowWindow.h" +#include "qtpthreadimage.h" +#include + +CShowWindow::CShowWindow(const TP_CAMERA_WIN* pCamWin) + : m_nLastStamp(0) +{ + m_winId = QString(pCamWin->device) + "_" + QString::number(pCamWin->index); + m_camWin = *pCamWin; + if (m_camWin.buffers > SHOW_MAX_BUFFERS) + { + m_camWin.buffers = SHOW_MAX_BUFFERS; + } + if (m_camWin.images <= 0) + { + m_camWin.images = 1; + } + if (m_camWin.bitsPerPixel > 32) + { + m_camWin.bitsPerPixel = 32; + } + m_bytesPerPexel = (m_camWin.bitsPerPixel + 7) / 8; + m_oneBufferSize = m_bytesPerPexel * m_camWin.bufferW * m_camWin.bufferH; + for (int i = 0; i < m_camWin.buffers; ++i) + { + m_ppBuffers[i] = new BYTE[m_oneBufferSize]; + if (NULL == m_ppBuffers[i]) + { + m_camWin.buffers = i; + break; + } + // memset(m_ppBuffers[i], 0xFF, m_oneBufferSize); + QImage(m_ppBuffers[i], m_camWin.bufferW, m_camWin.bufferH, bufferFormat()).fill(QColor(0, 0, 0)); + } + m_nBuffIdxWrite = 0; + m_nBuffIdxShow = 0; +// m_nBuffIdxLashShow = 0; +// m_nWriteImages = 0; +// m_nLastFrameNum = 0; + //...2015.08.31 + m_lastFrameNum = 0; + m_firstFrameNum = 0; + m_nowFrames = 0; + m_lastFrameFirst = 0; +// m_nLastTriggers = 0; +} + +CShowWindow::~CShowWindow() +{ + for (int i = 0; i < m_camWin.buffers; ++i) + { + if (NULL != m_ppBuffers[i]) + { + delete m_ppBuffers[i]; + } + } +} + +bool CShowWindow::AddCameraImage(QImage srcImg, UINT nFrameNum, UINT nStamp/* = 0*/, const QString& szExtend/* = NULL*/) +{ + bool bUpdate = false; + //UINT nFrameNum = pCamImage->FrameNumber(); + if (nFrameNum < m_firstFrameNum || 0 == nFrameNum) + { + return bUpdate; + } + if (1 == nFrameNum) + { + bUpdate = switchBuffers(); + } + else if (nFrameNum > m_lastFrameNum + 1)//wait for the first Frames + { + int nW = 0; + while (nFrameNum > m_lastFrameNum + 1 && nW < SHOW_MAX_WAIT_TIMES)//保证显示的时候图片的顺序 + { + QThread::msleep(25); + ++nW; + } + } + QMutexLocker locker(&m_mutexWrite); + //cache image + if (m_camWin.bCacheOriginImage && m_nLastStamp < nStamp) + { + m_nLastStamp = nStamp; + if (srcImg.text(QIMAGE_FUNC_KEY) == QIMAGE_FUNC_VALUE) + { + m_imgCache = srcImg.copy(); + } + else + { + m_imgCache = srcImg.copy(); + } + } + //判断图像是否有新的一组到来 + if (nFrameNum >= m_firstFrameNum + m_camWin.images) + { + bUpdate |= switchBuffers(); + } + // + // + if (0 == m_nowFrames)//set the first Frame + { + if (1 == nFrameNum) + { + m_firstFrameNum = nFrameNum; + m_lastFrameNum = nFrameNum; + } + else + { + m_firstFrameNum = (nFrameNum - 1) / m_camWin.images * m_camWin.images + 1;// + } + bUpdate = true; + } + // + if (nFrameNum > m_lastFrameNum) + { + m_lastFrameNum = nFrameNum; + } + ++m_nowFrames; + int nIdx = nFrameNum - m_firstFrameNum; + BYTE* pBuffer = m_ppBuffers[m_nBuffIdxWrite]; + if (NULL == pBuffer) + { + return bUpdate; + } + int nImageH = m_camWin.bufferH / m_camWin.images; + pBuffer += nIdx * nImageH * m_camWin.bufferW * m_bytesPerPexel; + QImage imgDest(pBuffer, m_camWin.bufferW, nImageH, bufferFormat()); + QPainter painter(&imgDest); + //QImage srcImg = pCamImage->ToQImage(); + //scale to width + if (m_camWin.bufferW < srcImg.width()) + { + srcImg = srcImg.scaledToWidth(m_camWin.bufferW); + } + //mirror the picture + if (m_camWin.mirror) + { + srcImg = srcImg.mirrored(m_camWin.mirror & 1, m_camWin.mirror & 2); + } + //mirror + if (m_camWin.bNeedRgbSwapped) + { + srcImg = srcImg.rgbSwapped(); + } + painter.drawImage(QPoint(0, 0), srcImg); +// painter.setPen(QColor(0, 150, 10)); +// painter.drawText(5, 15, QString::number(nFrameNum)); + //keep for drawing defects + if (m_bufferFrameNum.contains(pBuffer)) + { + m_frameNumBuffer.remove(m_bufferFrameNum.value(pBuffer)); + } + m_bufferFrameNum.insert(pBuffer, nFrameNum); + m_frameNumBuffer.insert(nFrameNum, pBuffer); + + // + if (m_nowFrames == m_camWin.images) + { + bUpdate |= switchBuffers(); + } + m_szExtend = szExtend; + return bUpdate; +} + +QImage CShowWindow::ShowImage() +{ + return QImage(m_ppBuffers[m_nBuffIdxShow], m_camWin.bufferW, m_camWin.bufferH, bufferFormat()); +} + +QImage::Format CShowWindow::bufferFormat() +{ + if (1 == m_bytesPerPexel) + { + return QImage::Format_Indexed8; + } + else if (2 == m_bytesPerPexel) + { + return QImage::Format_RGB16; + } + else if (3 == m_bytesPerPexel) + { + return QImage::Format_RGB888; + } + else + { + return QImage::Format_ARGB32_Premultiplied; + } +} + +bool CShowWindow::DrawDefect(IImageObject::emTpDefectType defectType, BYTE* pData, int nDataLen, CZkCameraImage* pCamImage) +{ + if (NULL == pCamImage || NULL == pData) + { + return false; + } + UINT nFrameNum = pCamImage->FrameNumber(); + // + QMutexLocker locker(&m_mutexWrite); + BYTE* pBuffer = m_frameNumBuffer.value(nFrameNum, NULL); + if (NULL == pBuffer) + { + return false; + } + int nImageH = m_camWin.bufferH / m_camWin.images; + QImage image(pBuffer, m_camWin.bufferW, nImageH, bufferFormat()); + if (image.isNull()) + { + return false; + } + //scale + double xScale = image.width() * 1.0 / pCamImage->Width(); + double yScale = image.height() * 1.0 / pCamImage->Height(); + //test + //uchar* bits = image.bits(); + //test + QRectF rtf; + switch (defectType) + { + case IImageObject::TP_DEF_RECT_F: + case IImageObject::TP_DEF_ELLIPSE_F: + if (nDataLen < 32) + { + break; + } + else + { + qreal* pfData = (qreal*)pData; + rtf.setRect(pfData[0] * xScale, pfData[1] * yScale, pfData[2] * xScale, pfData[3] * yScale); + } + break; + case IImageObject::TP_DEF_CIRCLE_F: + if (nDataLen < 24) + { + break; + } + else + { + qreal* pfData = (qreal*)pData; + rtf.setRect(pfData[0] * xScale, pfData[1] * yScale, pfData[2] * xScale, pfData[2] * yScale); + } + break; + case IImageObject::TP_DEF_POLYLINE_I: + break; + default: + return false; + } + // + QPainter painter(&image); + QPen pen(QColor(200, 10, 10)); + pen.setWidth(2); + painter.setPen(pen); + switch (defectType) + { + case IImageObject::TP_DEF_RECT_F: + painter.drawRect(rtf); + break; + case IImageObject::TP_DEF_CIRCLE_F: + case IImageObject::TP_DEF_ELLIPSE_F: + painter.drawEllipse(rtf); + break; + case IImageObject::TP_DEF_POLYLINE_I: + if (nDataLen < 16) + { + return false; + } + else + { + QPolygon polygon(nDataLen); + polygon.setPoints(nDataLen / 8, (int*)pData); + painter.drawPolyline(polygon); + } + break; + default: + return false; + } + if (pBuffer >= m_ppBuffers[m_nBuffIdxShow] && pBuffer < m_ppBuffers[m_nBuffIdxShow] + m_oneBufferSize) + { + return true; + } + else + { + return false; + } +} + +bool CShowWindow::DrawImage(QImage& srcImg, UINT nFrameNum, const QString& szExtend/* = NULL*/) +{ +// QMutexLocker locker(&m_mutexWrite); + BYTE* pBuffer = m_frameNumBuffer.value(nFrameNum, NULL); + if (NULL == pBuffer) + { + return false; + } + int nImageH = m_camWin.bufferH / m_camWin.images; + QImage image(pBuffer, m_camWin.bufferW, nImageH, bufferFormat()); + if (image.isNull()) + { + return false; + } + //scale + if (m_camWin.bufferW < srcImg.width()) + { + srcImg = srcImg.scaledToWidth(m_camWin.bufferW); + } + //mirror the picture + if (m_camWin.mirror) + { + srcImg = srcImg.mirrored(m_camWin.mirror & 1, m_camWin.mirror & 2); + } + QPainter painter(&image); + painter.drawImage(QPoint(0, 0), srcImg); + m_szExtend = szExtend; + return true; +} + +bool CShowWindow::switchBuffers() +{ + if (m_nowFrames > 0) + { + m_nBuffIdxShow = m_nBuffIdxWrite; + m_nBuffIdxWrite = (m_nBuffIdxWrite + 1) % m_camWin.buffers; + m_lastFrameFirst = m_firstFrameNum; + m_firstFrameNum = 0; + m_nowFrames = 0; + return true; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/src/lpCoreCtrl/ShowWindow.h b/src/lpCoreCtrl/ShowWindow.h new file mode 100644 index 0000000..2283fa6 --- /dev/null +++ b/src/lpCoreCtrl/ShowWindow.h @@ -0,0 +1,63 @@ +#ifndef SHOWWINDOW_H +#define SHOWWINDOW_H + +#include "globalCoreCtrl.h" +#include "ZkCameraImage.h" +#include +#include "QZkMutexMap.h" +//#include "iAlgorithm.h" + +#define SHOW_MAX_BUFFERS 2 +#define SHOW_MAX_WAIT_TIMES gCoreSetting.threadsCount//4 + +class CShowWindow +{ +public: + CShowWindow(const TP_CAMERA_WIN* pCamWin); + ~CShowWindow(); + const QString& WindowId() { + return m_winId; + } + const TP_CAMERA_WIN& CameraWin() { + return m_camWin; + } +// bool SetCameraImage(CZkCameraImage* pCamImage); + bool AddCameraImage(QImage srcImg, UINT nFrameNum, UINT nStamp = 0, const QString& szExtend = NULL); + QImage ShowImage(); + int FrameNum() { + return m_lastFrameNum; + } + bool DrawDefect(IImageObject::emTpDefectType defectType, BYTE* pData, int nDataLen, CZkCameraImage* pCamImage); + bool DrawImage(QImage& srcImg, UINT nFrameNum, const QString& szExtend = NULL); + QImage& OriginImage() { + return m_imgCache; + } + const QString ExtendString() { + return m_szExtend; + } +private: + QImage::Format bufferFormat(); + bool switchBuffers(); +private: + TP_CAMERA_WIN m_camWin; + QString m_winId; + int m_bytesPerPexel; + BYTE* m_ppBuffers[SHOW_MAX_BUFFERS]; + int m_oneBufferSize; + int m_nBuffIdxWrite; + int m_nBuffIdxShow; + QMutex m_mutexWrite; + // + QMap m_frameNumBuffer;//frame + QMap m_bufferFrameNum; + /*QAtomicInteger*/UINT m_lastFrameNum; + /*QAtomicInteger*/UINT m_firstFrameNum; + UINT m_lastFrameFirst; + QAtomicInteger m_nowFrames; + // + QImage m_imgCache; + INT64 m_nLastStamp; + QString m_szExtend; +}; + +#endif // SHOWWINDOW_H diff --git a/src/lpCoreCtrl/XmlConfigParser.cpp b/src/lpCoreCtrl/XmlConfigParser.cpp new file mode 100644 index 0000000..945675a --- /dev/null +++ b/src/lpCoreCtrl/XmlConfigParser.cpp @@ -0,0 +1,31 @@ +#include "XmlConfigParser.h" +#include +#include + + +CXmlConfigParser::CXmlConfigParser(void) +{ +} + + +CXmlConfigParser::~CXmlConfigParser(void) +{ +} + +QList CXmlConfigParser::Read2DList(const QString& sFileName, const QString& nameTag) +{ + QList retList; + QFile file(QDir::currentPath() + sFileName); + QXmlStreamReader xmlReader(&file); + xmlReader.readNext(); + bool bNameTag = false; + while( !xmlReader.atEnd() ) + { + if( xmlReader.isStartElement() && xmlReader.name() == nameTag )//find a tag + { + ;//.... + } + xmlReader.readNext(); + } + return retList; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/XmlConfigParser.h b/src/lpCoreCtrl/XmlConfigParser.h new file mode 100644 index 0000000..8e21ba4 --- /dev/null +++ b/src/lpCoreCtrl/XmlConfigParser.h @@ -0,0 +1,25 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:XmlConfigParser.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/25 + History:25:3:2015 13:23 + *******************************************************************************/ +#ifndef __XML_CONFIG_PARSER_H_20150325 +#define __XML_CONFIG_PARSER_H_20150325 + +#include +#include + +class CXmlConfigParser +{ +public: + CXmlConfigParser(void); + ~CXmlConfigParser(void); + //Qvariant is a QList, so the returned value is a 2-dimension list + QList Read2DList(const QString& sFileName, const QString& nameTag); +}; + +#endif diff --git a/src/lpCoreCtrl/ZkCameraImage.cpp b/src/lpCoreCtrl/ZkCameraImage.cpp new file mode 100644 index 0000000..bef8e0d --- /dev/null +++ b/src/lpCoreCtrl/ZkCameraImage.cpp @@ -0,0 +1,338 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ZkCameraImage.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/16 + History:16:4:2015 15:40 + *******************************************************************************/ +#include "ZkCameraImage.h" + +CZkCameraImage::CZkCameraImage() +{ + m_nId = 0; + m_nFrameNum = 0; + m_colorFormat = TP_COLOR_NONE; + m_nTriggerCount = 0; + m_pImage = NULL; + m_nCopiedLines = 0; + m_nMeterCode = 0; + m_nTimeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); +} + +CZkCameraImage::~CZkCameraImage() +{ + if (NULL != m_pImage) + { + delete m_pImage; + } +} + +int CZkCameraImage::CopyCameraObject(ICameraObject* pCamObj) +{ + // SetColorFormat(pCamObj->ColorFormat()); + if (NULL == pCamObj || NULL == pCamObj->m_pCamOpt) + { + return -1; + } + // TP_CAMERA_OPTION* pOpt = pCamObj->CameraOption(); + //if (NULL == pOpt) + //{ + // return -1; + //} + if (NULL == m_pImage) + { + int nW = pCamObj->m_nImageWidth; + int nH = pCamObj->m_nImageHeight; + if (pCamObj->m_pCamOpt->height > 0) + { + nH = pCamObj->m_pCamOpt->height; + } + m_pImage = new QImage(nW, nH, ToQImageFormat(pCamObj->m_nColorFormat)); + if (NULL == m_pImage) + { + return -1; + } + m_nCopiedLines = 0; + copyOption(pCamObj); + //SetFrameNum(pCamObj->FrameNumber()); + //SetTriggerCount(pCamObj->StartCount()); + //SetSerial(pOpt->uniqueName.toUtf8().data()); + //SetId(pOpt->id); + //SetAlgorithm(pOpt->algorithm); + //SetDllSuffix(pOpt->dllsuffix.toUtf8()); + } + int nLines = m_pImage->height() - m_nCopiedLines; + if (nLines > pCamObj->m_nImageHeight) + { + nLines = pCamObj->m_nImageHeight; + } + int nSizes = nLines * pCamObj->m_nImageWidth*((pCamObj->m_nBitsPerPixel + 7) / 8); + memcpy(m_pImage->scanLine(m_nCopiedLines), pCamObj->m_pCameraData, nSizes); + m_nCopiedLines += nLines; + //SetFileName(pCamObj->Utf8FileName()); + if (m_nCopiedLines == m_pImage->height()) + { + return 1; + } + else + { + return 0; + } +} + +int CZkCameraImage::CopyCameraObject2(ICameraObject* pCamObj) +{ + if (NULL == pCamObj || NULL == pCamObj->m_pCamOpt) + { + return 0; + } + if (NULL == pCamObj->m_funcFreeBuffer) + { + m_pImage = new QImage(); + *m_pImage = QImage(pCamObj->m_pCameraData, pCamObj->m_nImageWidth, pCamObj->m_nImageHeight, ToQImageFormat(pCamObj->m_nColorFormat)).copy(); + //m_pImage = new QImage(pCamObj->m_nImageWidth, pCamObj->m_nImageHeight, ToQImageFormat(pCamObj->m_nColorFormat)); + } + else + { + m_pImage = new QImage(pCamObj->m_pCameraData, pCamObj->m_nImageWidth, pCamObj->m_nImageHeight, ToQImageFormat(pCamObj->m_nColorFormat) + , pCamObj->m_funcFreeBuffer, pCamObj->m_pCameraData); + m_pImage->setText(QIMAGE_FUNC_KEY, QIMAGE_FUNC_VALUE); + } + if (NULL == m_pImage) + { + return 0; + } + copyOption(pCamObj); + return 1; +} + +/* +bool CZkCameraImage::CopyImageData(const BYTE* pData, int nW, int nH, int nBits, emTpColorFormat nColorFormat) +{ + SetColorFormat(nColorFormat); + if( ZIMAGE == m_imageType ) + { + return m_image.zk->Copy(pData, nW, nH, nBits); + } + else + { + *(m_image.q) = QImage(pData, nW, nH, ToQImageFormat()).copy(); + if( m_image.q->isNull() ) + { + return false; + } + else + { + return true; + } + } +} + +bool CZkCameraImage::CopyQImage(const QImage& image) +{ + if( ZIMAGE == m_imageType ) + { + delete m_image.zk; + m_image.q = new QImage(); + m_imageType = QIMAGE; + } + *(m_image.q) = image;//no copy + m_colorFormat = FromQImageFormat(image); + return true; +} +*/ + +ZShowImage CZkCameraImage::ToShowImage(int nShowW /* = 0 */, int nShowH /* = 0 */) +{ + if (NULL == m_pImage) + { + return QImage(); + } +#ifdef USE_PIXMAP_AS_SHOW + if (ZIMAGE == m_imageType) + { + QImage image = QImage(m_zimage.bits(), m_zimage.width(), m_zimage.height(), ToQImageFormat()); + return QPixmap::fromImage(image).copy(); + } + else + { + return QPixmap::fromImage(*(m_image.q)).copy(); + } +#else + /* + QImage image; + if( ZIMAGE == m_imageType ) + { + image = QImage(m_image.zk->bits(), m_image.zk->width(), m_image.zk->height(), ToQImageFormat()); + } + else + { + image = *(m_image.q); + }*/ + if (QImage::Format_Indexed8 == m_pImage->format()) + { + // + QVector colorFmt; + for (int i = 0; i < 256; ++i) + { + QColor clr1(i, i, i); + colorFmt.append(clr1.rgb()); + } + m_pImage->setColorTable(colorFmt); + return m_pImage->convertToFormat(QImage::Format_RGB32); + } + else + { + return m_pImage->copy(); + } +#endif +} + +QImage CZkCameraImage::ToQImage() +{ + if (NULL == m_pImage) + { + return QImage(); + } + //... + QImage::Format fmt = m_pImage->format(); + if (QImage::Format_Indexed8 == m_pImage->format() && m_pImage->colorTable().isEmpty()) + { + QVector colorFmt; + for (int i = 0; i < 256; ++i) + { +QColor clr1(i, i, i); +colorFmt.append(clr1.rgb()); + } + m_pImage->setColorTable(colorFmt); + } + return *m_pImage; +} + +QImage::Format CZkCameraImage::ToQImageFormat(emTpColorFormat colorFormat) +{ + if (TP_COLOR_RGB32 == colorFormat || + TP_COLOR_BGR32 == colorFormat) + { + return QImage::Format_RGB32; + } + else if (TP_COLOR_RGB24 == colorFormat || + TP_COLOR_BGR24 == colorFormat) + { + return QImage::Format_RGB888; + } + else if (TP_COLOR_Y800 == colorFormat) + { + return QImage::Format_Indexed8; + } + else if (TP_COLOR_Y16 == colorFormat) + { + return QImage::Format_RGB555;// QImage::Format_RGB16; + } + else if (TP_COLOR_RGB48 == colorFormat) + { + return QImage::Format_ARGB32; + } + else + { + return QImage::Format_Invalid; + } +} + +emTpColorFormat CZkCameraImage::FromQImageFormat(const QImage &image) +{ + if (QImage::Format_RGB32 == image.format()) + { + return TP_COLOR_RGB32; + } + else if (QImage::Format_RGB888 == image.format()) + { + return TP_COLOR_RGB24; + } + else if (QImage::Format_Indexed8 == image.format()) + { + return TP_COLOR_Y800; + } + else if (QImage::Format_RGB16 == image.format()) + { + return TP_COLOR_Y16; + } + else + { + return TP_COLOR_NONE; + } +} + +emTpColorFormat CZkCameraImage::FromString(const QString& strColor) +{ + if ("RGB32" == strColor) + { + return TP_COLOR_RGB32; + } + else if ("RGB24" == strColor) + { + return TP_COLOR_RGB24; + } + else if ("GRAY8" == strColor) + { + return TP_COLOR_Y800; + } + else if ("GRAY16" == strColor) + { + return TP_COLOR_Y16; + } + else if ("BGR24" == strColor) + { + return TP_COLOR_BGR24; + } + else if ("BGR32" == strColor) + { + return TP_COLOR_BGR32; + } + else + { + return TP_COLOR_NONE; + } +} + + +BYTE* CZkCameraImage::ImageData(int& nOutW, int& nOutH, int& nOutBitsPerPixel, int& nOutBytesPerLine) +{ + if (!m_pImage || m_pImage->isNull()) { + return NULL; + } + + nOutW = m_pImage->width(); + nOutH = m_pImage->height(); + + if (nOutW <= 0) { + qWarning() << "Invalid image data. - " << __FUNCTION__; + return NULL; + } + + nOutBitsPerPixel = m_pImage->bytesPerLine() * 8 / nOutW; + nOutBytesPerLine = m_pImage->bytesPerLine(); + + return m_pImage->bits(); +} + +bool CZkCameraImage::SaveToFile() +{ + if (NULL != m_pImage) + { + if (QImage::Format_Indexed8 == m_pImage->format()) + { + QVector colorFmt; + for (int i = 0; i < 256; ++i) + { + QColor clr1(i, i, i); + colorFmt.append(clr1.rgb()); + } + m_pImage->setColorTable(colorFmt); + } + + } + return m_pImage->save(m_fileName, ".BMP"); +} \ No newline at end of file diff --git a/src/lpCoreCtrl/ZkCameraImage.h b/src/lpCoreCtrl/ZkCameraImage.h new file mode 100644 index 0000000..e14511c --- /dev/null +++ b/src/lpCoreCtrl/ZkCameraImage.h @@ -0,0 +1,148 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ZkCameraImage.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/16 + History:16:4:2015 15:39 + *******************************************************************************/ +#ifndef ZKCAMERAIMAGE_H +#define ZKCAMERAIMAGE_H + +#include "baseDefine.h" +#include "baseStruct.h" +#include "ZkImage.h" +#include "iCameraObject.h" + +#define QIMAGE_FUNC_KEY "bool_func" +#define QIMAGE_FUNC_VALUE "func_true" + +class CZkCameraImage +{ +public: + CZkCameraImage(); + ~CZkCameraImage(); + int CopyCameraObject(ICameraObject* pCamObj); + int CopyCameraObject2(ICameraObject* pCamObj);//no combine and maybe use QImage +// bool CopyImageData(const BYTE* pData, int nW, int nH, int nBits, emTpColorFormat nColorFormat); +// bool CopyQImage(const QImage& image); + //void SetSerial(const char* szSerial) { + // m_strSerial = szSerial; + //} + const ZStringA& Serial() { + return m_strSerial; + } + uint GetId() { + return m_nId; + } + //void SetId(long nId) { + // m_nId = nId; + //} + //void SetFrameNum(long nFrameNum) { + // m_nFrameNum = nFrameNum; + //} + ULONG FrameNumber() { + return m_nFrameNum; + } + //void SetColorFormat(emTpColorFormat nColorFormat) { + // m_colorFormat = nColorFormat; + //} + emTpColorFormat ColorFormat() { + return m_colorFormat; + } + //void SetTriggerCount(long nTriggers) { + // m_nTriggerCount = nTriggers; + //} + long TriggerCount() { + return m_nTriggerCount; + } + ZShowImage ToShowImage(int nShowW = 0, int nShowH = 0); + QImage ToQImage(); + QImage::Format ToQImageFormat(emTpColorFormat colorFormat); + static emTpColorFormat FromQImageFormat(const QImage &image); + static emTpColorFormat FromString(const QString& strColor); + bool SaveToFile(const QString& strFile); + bool SaveToFile(); + BYTE* ImageData(int& nOutW, int& nOutH, int& nOutBitsPerPixel, int& nOutBytesPerLine); + void SetAlgorithm(long nAlgorithm) { + m_nAlgorithm = nAlgorithm; + } + long Algorithm() { + return m_nAlgorithm; + } + //void SetFileName(const ZStringA& strName) { + // m_fileName = strName; + //} + const ZStringA& FileName() { + return m_fileName; + } + void SetMeterCode(INT64 code) { + m_nMeterCode = code; + } + INT64 MeterCode() { + return m_nMeterCode; + } + int Width() { + return m_pImage->width(); + } + int Height() { + return m_pImage->height(); + } + INT64 Stamp() { + return m_nTimeStamp; + } + //void SetDllSuffix(const ZStringA& strName) { + // m_dllSuffix = strName; + //} + const ZStringA& DllSuffix() { + return m_dllSuffix; + } + void SetVarFromUI(const QVariant& v){ + m_varFromUI = v; + } + const QVariant& VarFromUI() { + return m_varFromUI; + } + void Mirror(bool horizontally, bool vertically) + { + if (!m_pImage) + { + return; + } + if (m_pImage->isNull()) + { + return; + } + *m_pImage = m_pImage->mirrored( + horizontally, vertically); + } +private: + void copyOption(ICameraObject* pCamObj) { + m_nFrameNum = pCamObj->m_nFrameNum; + m_nTriggerCount = pCamObj->m_nStartCount; + m_strSerial = pCamObj->m_pCamOpt->uniqueName.toUtf8(); + m_nId = pCamObj->m_pCamOpt->id; + m_nAlgorithm = pCamObj->m_pCamOpt->algorithm; + m_dllSuffix = pCamObj->m_pCamOpt->dllsuffix.toUtf8(); + m_fileName = pCamObj->m_szFileName; + m_colorFormat = pCamObj->m_nColorFormat; + } +private: + QImage* m_pImage; + int m_nCopiedLines; + ////////////////////////////////////////////////// + ZStringA m_strSerial; + long m_nId; + long m_nAlgorithm; + emTpColorFormat m_colorFormat; + ZStringA m_fileName; + ULONG m_nFrameNum; + long m_nTriggerCount; + INT64 m_nMeterCode; + INT64 m_nTimeStamp; + ZStringA m_dllSuffix; + QVariant m_varFromUI; +}; + +#endif // ZKCAMERAIMAGE_H diff --git a/src/lpCoreCtrl/ZkImage.cpp b/src/lpCoreCtrl/ZkImage.cpp new file mode 100644 index 0000000..e8bf6d5 --- /dev/null +++ b/src/lpCoreCtrl/ZkImage.cpp @@ -0,0 +1,74 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ZkImage.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/16 + History:16:4:2015 11:06 + *******************************************************************************/ +#include "ZkImage.h" + +CZkImage::CZkImage() +{ + m_pColorBuffer = NULL; + m_nbuffLen = 0; + m_nWidth = 0; + m_nHeight = 0; +} + +CZkImage::CZkImage(int nW, int nH, int nBits) +{ + CZkImage(); + allocBuffer(nW, nH, nBits); +} + +CZkImage::~CZkImage() +{ + freeBuffer(); +} + +bool CZkImage::allocBuffer(int nW, int nH, int nBits) +{ + SELF_ABS(nW); + SELF_ABS(nH); + SELF_ABS(nBits); + int nLen = (nW * nH * nBits + 7) / 8; + if( 0 == nLen ) + { + return false; + } + if( NULL == m_pColorBuffer || nLen != m_nbuffLen ) + { + freeBuffer(); + m_pColorBuffer = new BYTE[nLen]; + if( NULL == m_pColorBuffer ) + { + return false; + } + m_nbuffLen = nLen; + } + m_nWidth = nW; + m_nHeight = nH; + m_nBitsPerPixel = nBits; + return true; +} + +void CZkImage::freeBuffer() +{ + if( NULL != m_pColorBuffer ) + { + delete m_pColorBuffer; + m_pColorBuffer = NULL; + } +} + +bool CZkImage::Copy(const BYTE* pData, int nW, int nH, int nBits) +{ + if( !allocBuffer(nW, nH, nBits) ) + { + return false; + } + std::memcpy(m_pColorBuffer, pData, m_nbuffLen); + return true; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/ZkImage.h b/src/lpCoreCtrl/ZkImage.h new file mode 100644 index 0000000..1ed84c9 --- /dev/null +++ b/src/lpCoreCtrl/ZkImage.h @@ -0,0 +1,42 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ZkImage.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/16 + History:16:4:2015 11:06 + *******************************************************************************/ +#ifndef ZKIMAGE_H +#define ZKIMAGE_H + +#include "baseDefine.h" + +class CZkImage +{ +public: + CZkImage(); + CZkImage(int nW, int nH, int nBits); + virtual ~CZkImage(); + + bool Copy(const BYTE* pData, int nW, int nH, int nBits); + int width() { return m_nWidth; } + int height() { return m_nHeight; } + int bitsPerPixle() { + return m_nBitsPerPixel; + } + BYTE* bits() { return m_pColorBuffer; } + int buffLen() { return m_nbuffLen; } +private: + bool allocBuffer(int nW, int nH, int nBits); + void freeBuffer(); + +private: + BYTE* m_pColorBuffer; + int m_nbuffLen; + int m_nWidth; + int m_nHeight; + int m_nBitsPerPixel; +}; + +#endif // ZKIMAGE_H diff --git a/src/lpCoreCtrl/globalCoreCtrl.cpp b/src/lpCoreCtrl/globalCoreCtrl.cpp new file mode 100644 index 0000000..8d9770a --- /dev/null +++ b/src/lpCoreCtrl/globalCoreCtrl.cpp @@ -0,0 +1,36 @@ +#include "globalCoreCtrl.h" +#include "QZkJsonParser.h" + +void CGlobalCoreCtrl::readCoreCtrlCfg() +{ + //read config from ".json" file + QString szFileName(m_pGlobalData->tgdMainPath); + szFileName.append(m_pGlobalData->tgdFolderNames[TP_FOLDER_NAME_CONFIG]).append("corectrl").append(TP_STR::cszJsonSuffix); + QJsonObject objJson = QZkJsonParser::ReadJsonObject(szFileName); + if (objJson.isEmpty()) + { + return; + } + //core_setting + QJsonObject objTmp = objJson["core_setting"].toObject(); + m_coreSetting.threadsCount = objTmp.value("image_threads").toInt(0); + m_coreSetting.imageShowType = objTmp.value("show_type").toInt(TP_IMAGE_SHOW_NO); +} + +QVariant CGlobalCoreCtrl::IGetVariantById(int nId) +{ + if (!m_pGuiCallback){ + qCritical("m_pGuiCallback is null in CGlobalCoreCtrl::IGetVariantById."); + return QVariant(); + } + return m_pGuiCallback->IGetVariantById(nId); +} + +void CGlobalCoreCtrl::INewCameraImage(const QVariantMap& vmap) +{ + if (!m_pGuiCallback){ + qCritical("m_pGuiCallback is null in CGlobalCoreCtrl::IGetVariantById."); + return; + } + m_pGuiCallback->INewCameraImage(vmap); +} \ No newline at end of file diff --git a/src/lpCoreCtrl/globalCoreCtrl.h b/src/lpCoreCtrl/globalCoreCtrl.h new file mode 100644 index 0000000..4d6c094 --- /dev/null +++ b/src/lpCoreCtrl/globalCoreCtrl.h @@ -0,0 +1,73 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:globalCoreCtrl.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/24 + History:24:3:2015 17:30 + *******************************************************************************/ +#ifndef __GLOBAL_CORE_CTRL_H +#define __GLOBAL_CORE_CTRL_H + +#include "baseInclude.h" +#include "icamera.h" +#include "iCoreCtrl.h" +#include "iImgProc.h" +#include "GlobalDataBase.h" + + +#ifdef _USE_SPIDER_LOG +#include "logSpider.h" +#define CORE_CTRL_INIT(a, b) SPIDER_LOG_INIT(a, b) +#define CORE_CTRL_LOG(value) SPIDER_LOG_STRING("corectrl", value) +#define CORE_CTRL_FREE() SPIDER_LOG_FREE() +#else +#define CORE_CTRL_INIT(a, b) +#define CORE_CTRL_LOG(value) tpDebugOut(value) +#define CORE_CTRL_FREE() +#endif + +class CGlobalCoreCtrl : public ICameraCallback +{ +public: + CGlobalCoreCtrl(const CORE_CTRL_IN_PARAM* pParam){ + m_pCameraPool = NULL; + m_pImageProc = NULL; + m_pGuiCallback = pParam->pGuiCb; + m_pGlobalData = pParam->pGlobalData; + // m_pCoreSetting = pParam->pCoreSetting; + readCoreCtrlCfg(); + } + ~CGlobalCoreCtrl() {} + void AddCameraPool(ICameraPool* pCameraPool) { m_pCameraPool = pCameraPool; } +private: + void readCoreCtrlCfg(); + virtual QVariant IGetVariantById(int nId); + virtual void INewCameraImage(const QVariantMap& vmap); +public: + TP_GLOBAL_DATA* m_pGlobalData; + TP_CORE_SETTING m_coreSetting; + ICameraPool* m_pCameraPool; + IGuiCallback* m_pGuiCallback; + IImgProc* m_pImageProc; +}; + +extern CGlobalCoreCtrl* gpData; + +#define gpCoreCtrlGlobal gpData + +#define gpGlobalData ((TP_GLOBAL_DATA*)(gpData->m_pGlobalData)) +#define gCoreSetting gpData->m_coreSetting +#define glpCameralPool gpData->m_pCameraPool +//#define glpMainPath NULL != gpData ? gpData->tgdMainPath : ".\\" +// #define gcallCameraPool(fc) if( NULL != glpCameralPool ) glpCameralPool->##fc##() +// #define gcallCameraPoolParam(fc, ...) if( NULL != glpCameralPool) glpCameralPool->##fc##(__VA_VRGS__) +#define glpGuiCallback gpData->m_pGuiCallback +#define gfcbGuiSetImage(...) if( NULL != glpGuiCallback ) glpGuiCallback->ISetCameraImage(__VA_ARGS__) +#define gGuiCallback_FuncCall(fun, ...) if( NULL != glpGuiCallback ) glpGuiCallback->##fun##(__VA_ARGS__) + +#define glpImgProc gpData->m_pImageProc + + +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/iCameraObject.cpp b/src/lpCoreCtrl/iCameraObject.cpp new file mode 100644 index 0000000..dc8c584 --- /dev/null +++ b/src/lpCoreCtrl/iCameraObject.cpp @@ -0,0 +1,11 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:iCameraObject.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/05/26 + History:26:5:2015 15:35 + *******************************************************************************/ +#include "iCameraObject.h" + diff --git a/src/lpCoreCtrl/iCameraObject.h b/src/lpCoreCtrl/iCameraObject.h new file mode 100644 index 0000000..860c619 --- /dev/null +++ b/src/lpCoreCtrl/iCameraObject.h @@ -0,0 +1,313 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:iCameraObject.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/05/25 + History:25:5:2015 15:52 + *******************************************************************************/ +#ifndef _I_CAMERA_OBJECT_H +#define _I_CAMERA_OBJECT_H + +#include +#include +#include +#include +#include "baseStruct.h" +#include "QZkMutexList.h" +#include +#include +//#define _USE_CAMERA_SERIAL_ENCRYPTO + +class CSaveImgTask : public QRunnable +{ +public: + CSaveImgTask(const QString& path, BYTE* pdata, int BitsPerPixel, long imgwd = 0, + long imght = 0, emTpColorFormat fmt = TP_COLOR_NONE, bool brgbswp = false) + : msPath(path) + , mpData(0) + , mnImageWidth(imgwd) + , mnImageHeight(imght) + , mColorFormat(fmt) + , mbNeedRgbSwapped(brgbswp) + { + qint64 nSizes = mnImageHeight*mnImageWidth*((BitsPerPixel + 7) / 8); + QFileInfo srcInfo(path); + QDir dstDir = srcInfo.dir(); + if (nSizes > 0 && dstDir.exists()){ + try + { + mpData = new BYTE[nSizes]; + memcpy(mpData, pdata, nSizes); + } + catch (const std::bad_alloc& e) + { + qCritical() << "alloc error:" << e.what(); + } + catch (const std::exception& e) + { + qCritical() << "other exception:" << e.what(); + } + } + } + + ~CSaveImgTask(){ + if (mpData){ + delete mpData; + mpData = 0; + } + } + + void run() + { + if (!mpData) return; + + QImage image(mpData, mnImageWidth, mnImageHeight, + TP_FUNCS::ToQImageFormat(mColorFormat)); + + if (image.isNull()){ + qWarning() << "Could save image in CSaveImgTask : " << msPath; + return; + } + + if (QImage::Format_Indexed8 == image.format()) { + QVector colorFmt; + for (int i = 0; i < 256; ++i) { + QColor clr1(i, i, i); + colorFmt.append(clr1.rgb()); + } + image.setColorTable(colorFmt); + } + + if (mbNeedRgbSwapped) + { + image = image.rgbSwapped(); + } + image.save(msPath); + } +private: + QString msPath; + BYTE* mpData; + long mnImageWidth; + long mnImageHeight; + emTpColorFormat mColorFormat; + bool mbNeedRgbSwapped; +}; + +typedef void(*func_free_image_buffer)(void*); + +class ICameraObject +{ +public: + ICameraObject(TP_CAMERA_OPTION* pCamOpt, class IPoolCallback* pCb) + : m_pCamOpt(pCamOpt) + , m_pCb(pCb) + , m_funcFreeBuffer(NULL) + , m_bEnableCallBack(true) { + m_bHasOpened = false; + m_bHasStarted = false; + m_nImageWidth = 0; + m_nImageHeight = 0; + m_nBitsPerPixel = TP_FUNCS::BitsPerPixels((emTpColorFormat)pCamOpt->format);// 0; + m_nImageSize = m_nImageWidth * m_nImageHeight * ((m_nBitsPerPixel + 7) / 8); + m_nColorFormat = TP_COLOR_NONE; + m_nFixedFrames = 0; + m_nFrameNum = 0; + m_nStartCount = 0; + m_oldTriggerMode = DEV_TRIGGER_MODE_STOP; + m_newTriggerMode = DEV_TRIGGER_MODE_OUT; + m_pRelyOnCam = NULL; + m_pCameraData = NULL; + m_bIsCallback = false; + } + virtual ~ICameraObject() { + } + + virtual int IOpenCamera() = 0; + virtual void ICloseCamera() = 0; + virtual int IStartCamera() = 0; + virtual void IStopCamera() = 0; + + virtual void IStartPush() = 0; + virtual void IPausePush() = 0; + + virtual QList IEnumAvailableCameras(){ + QList ret; + return ret; + } + + virtual int ISnapCamera(emTpTriggerDirection direct) { + if( !m_bHasStarted || NULL == m_pCamOpt || m_pCamOpt->bIgnoreImage) { + return 0; + } + if( DEV_TRIGGER_MODE_FIXED_AUTO == m_oldTriggerMode && m_nFrameNum >= m_nFixedFrames ) { + return 0; + } + return 1; + } + virtual int ISendSoftTrigger() { + return ISnapCamera(TRIGGER_DIRECT_FOREWARD); + } + virtual int IAutoCapture() { return 0; } + virtual void ISetProperty(TP_CAMERA_PROPERTY* pCamProperty) {} + virtual const char* Utf8FileName() { + return m_szFileName.data();//TP_FUNCS::ImageFileName(m_nFrameNum, m_pCamOpt).toUtf8().data(); + } + virtual void ISetVirtualImages(const QStringList& szImages) {} + ///////////////////////////////////////////////////////// + void SetTriggerMode(emTpDeviceTriggerMode mode) { + m_newTriggerMode = mode; + } + void SetFixedFrames(int nFrames) { + m_nFixedFrames = nFrames; + } + void SetRelyOnCamera(ICameraObject* pCam) { + m_pRelyOnCam = pCam; + } + void SetReliedByCamera(ICameraObject* pCam) { + m_listReliedBy.appendOnce(pCam); + } + void setCallback(bool b) { + m_bIsCallback = b; + } + void enableCallback(bool b) { + m_bEnableCallBack = b; + } + ICameraObject* RelyOnCamera() { + return m_pRelyOnCam; + } + //int DeviceType() { + // return m_pCamOpt->deviceType; + //} + bool IsCallback() { + return m_bIsCallback; + } + bool IsCallbackEnabled() { + return m_bEnableCallBack; + } + bool SaveToFile() { + if (DEV_CAM_VIRTUAL != m_pCamOpt->deviceType) { + m_szFileName = TP_FUNCS::ImageFileName(m_nFrameNum, m_pCamOpt).toUtf8(); + } + + if (1 != m_pCamOpt->save || DEV_CAM_VIRTUAL == m_pCamOpt->deviceType) { + return false; + } + + //int curTaskCnt = QThreadPool::globalInstance()->activeThreadCount(); + //if (curTaskCnt > 5){ + // return false; + //} + + //QString imgPath = TP_FUNCS::ImageFileName(m_nFrameNum, m_pCamOpt); + //CSaveImgTask *imgSavtsk = new CSaveImgTask(imgPath, m_pCameraData, m_nBitsPerPixel, m_nImageWidth, + // m_nImageHeight, m_nColorFormat); + + //QThreadPool::globalInstance()->start(imgSavtsk); + //return true; + + + QImage image(m_pCameraData, m_nImageWidth, m_nImageHeight, TP_FUNCS::ToQImageFormat(m_nColorFormat)); + if (QImage::Format_Indexed8 == image.format()) { + QVector colorFmt; + for (int i = 0; i < 256; ++i) { + QColor clr1(i, i, i); + colorFmt.append(clr1.rgb()); + } + image.setColorTable(colorFmt); + } + + if (m_pCamOpt->bNeedRgbSwapped) + { + image = image.rgbSwapped(); + } + //qDebug() << TP_FUNCS::ImageFileName(m_nFrameNum, m_pCamOpt).toUtf8(); + return image.save(TP_FUNCS::ImageFileName(m_nFrameNum, m_pCamOpt).toUtf8()); + } + bool NeedStopping() { + if (DEV_TRIGGER_MODE_FIXED_AUTO == m_oldTriggerMode && m_nFrameNum >= m_nFixedFrames) { + return true; + } + else { + return false; + } + } + + virtual int GetCameraStatus(int nType) + { + return 0; + } + +protected: + QString getSerial(const QString& serialNumber) { + if (NULL == m_pCamOpt || NULL == m_pCamOpt->pSerials) { + return NULL; + } + for (QStringList::iterator it = m_pCamOpt->pSerials->begin(); it != m_pCamOpt->pSerials->end(); ++it) { + if (serialNumber == *it) { + return *it; + } + } + return NULL; + } +public: + TP_CAMERA_OPTION* m_pCamOpt; +protected: + bool m_bHasOpened; + bool m_bHasStarted; + bool m_bEnableCallBack; + long m_nImageWidth; + long m_nImageHeight; + long m_nImageSize;//bytes of the images + int m_nBitsPerPixel; + emTpColorFormat m_nColorFormat; + int m_nFixedFrames; + UINT m_nFrameNum; + long m_nStartCount; + emTpDeviceTriggerMode m_oldTriggerMode; + emTpDeviceTriggerMode m_newTriggerMode; + //emCameraDataType m_dataType; + BYTE* m_pCameraData; + ZStringA m_szFileName; + class IPoolCallback* m_pCb; + func_free_image_buffer m_funcFreeBuffer; +private: + ICameraObject* m_pRelyOnCam; + QZkMutexList m_listReliedBy; + bool m_bIsCallback; + friend class CZkCameraImage; + friend class CCameraPool; + friend class CLibCameraes; +}; + +class IPoolCallback +{ +public: + IPoolCallback() {} + virtual ~IPoolCallback() {} + virtual int IPushCameraData(ICameraObject* pCamObj) = 0; + virtual int IPushCameraData2(ICameraObject* pCamObj) = 0; + virtual char* IGetMainPath() = 0; +}; + +#define _USE_CAMERA_DLL +#define _USE_BITFLOW +#define _USE_VIRTUAL +#define _USE_GIGE + +/*****************prefix list*************************** + # DEV_DLL_BITFLOW_BI Bitflow_ + # DEV_DLL_VIRTUAL_FOLDER Virtual_ + # DEV_DLL_GIGE Gige_ + */ + +#ifdef _USE_CAMERA_DLL +#define API_DLL_EXPORT extern "C" __declspec(dllexport) +#define API_NAME_PREFIX(pfix, fname) fname +#else +#defien API_DLL_EXPORT extern "C" +#define API_NAME_PREFIX(pfix, fname) pfix##fname +#endif + +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/qtpthreadbase.cpp b/src/lpCoreCtrl/qtpthreadbase.cpp new file mode 100644 index 0000000..73539c1 --- /dev/null +++ b/src/lpCoreCtrl/qtpthreadbase.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:qtpthreadbase.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 17:06 + *******************************************************************************/ +#include "qtpthreadbase.h" + +QTpThreadBase::QTpThreadBase(QObject *parent) + : QThread(parent) +{ + m_bIsRunning = false; + m_bRunSignal = false; +} + +QTpThreadBase::~QTpThreadBase() +{ + +} + + +void QTpThreadBase::StartThread() +{ + if( !m_bIsRunning ) + { + m_bRunSignal = true; + m_bIsRunning = true; + start(); + } +} + +void QTpThreadBase::EndThread() +{ + m_bRunSignal = false; + while(m_bIsRunning ) + { + QThread::msleep(25); + } +} + +void QTpThreadBase::run() +{ + m_bIsRunning = true; + while( m_bRunSignal ) + { + if( !loop() ) + { + break; + } + //... wait signal or condition + } + m_bIsRunning = false; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/qtpthreadbase.h b/src/lpCoreCtrl/qtpthreadbase.h new file mode 100644 index 0000000..2bbc6f3 --- /dev/null +++ b/src/lpCoreCtrl/qtpthreadbase.h @@ -0,0 +1,33 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:qtpthreadbase.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 17:06 + *******************************************************************************/ +#ifndef QTPTHREADBASE_H +#define QTPTHREADBASE_H + +#include "baseInclude.h" +#include + +class QTpThreadBase : public QThread +{ +public: + QTpThreadBase(QObject *parent); + ~QTpThreadBase(); + + void StartThread(); + void EndThread(); +protected: + virtual bool loop() { msleep(25); return false; } +private: + virtual void run(); +private: + bool m_bRunSignal; + bool m_bIsRunning; +}; + +#endif // QTPTHREADBASE_H diff --git a/src/lpCoreCtrl/qtpthreadimage.cpp b/src/lpCoreCtrl/qtpthreadimage.cpp new file mode 100644 index 0000000..2c78523 --- /dev/null +++ b/src/lpCoreCtrl/qtpthreadimage.cpp @@ -0,0 +1,77 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:qtpthreadimage.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 17:10 + *******************************************************************************/ +#include "qtpthreadimage.h" +#include "globalCoreCtrl.h" +#include "CoreCtrl.h" +#include + +QTpThreadImage::QTpThreadImage(class CCoreCtrl* pCtrl, class IDetectorEngine* pDE /*= NULL*/,QObject *parent /* = NULL */) + : QTpThreadBase(parent) + , m_pCoreCtrl(pCtrl) + , m_objImage(pCtrl) + , m_pDE(pDE) +{ +// LOG_SPIDER::InitLog("", "log.txt"); +} + +QTpThreadImage::~QTpThreadImage() +{ + LOG_SPIDER::FreeLog(); +} + +bool QTpThreadImage::loop() +{ + QTime timespan; + + timespan.start(); + CZkCameraImage* pCamImage = glpCameralPool->IPopCameraImage(); + int span = timespan.elapsed(); + if (span > 100) + { + qWarning() << "Pop image from poll cost " << span << " MSecs" + << " - " __FUNCTION__; + } + + if( NULL != pCamImage ) + { + { + // send image to UI + timespan.restart(); + m_pCoreCtrl->ShowCameraImage(pCamImage->Serial() + , pCamImage->FrameNumber() + , pCamImage->ToQImage() + , pCamImage->Stamp() + , 0 + , QString::fromUtf8(pCamImage->FileName()));//(pCamImage); + + qDebug() << "Show camera image to UI cost " << timespan.elapsed() << " MSecs" + << " - " __FUNCTION__; + } + + { + // Algorithm process image + timespan.restart(); + m_objImage.SetCameraImage(pCamImage); + glpImgProc->IImageProcess(&m_objImage, m_pDE); + qDebug() << "Process Image(ID:" << pCamImage->FrameNumber() + << ") cost " << timespan.elapsed() << " MSecs" + << " - " __FUNCTION__; + } + //release CameraImage + m_objImage.SetCameraImage(NULL); + glpCameralPool->IFreeCameraImage(pCamImage); + } + else + { + QTpThreadBase::loop(); + } + + return true; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/qtpthreadimage.h b/src/lpCoreCtrl/qtpthreadimage.h new file mode 100644 index 0000000..2334f35 --- /dev/null +++ b/src/lpCoreCtrl/qtpthreadimage.h @@ -0,0 +1,30 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:qtpthreadimage.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 17:10 + *******************************************************************************/ +#ifndef QTPTHREADIMAGE_H +#define QTPTHREADIMAGE_H + +#include "qtpthreadbase.h" +#include "ImageObject.h" +#include "LogBySpider.h" + +class QTpThreadImage : public QTpThreadBase +{ +public: + QTpThreadImage(class CCoreCtrl* pCtrl, class IDetectorEngine* pDE = NULL ,QObject *parent = NULL); + ~QTpThreadImage(); +private: + virtual bool loop(); +private: + class CCoreCtrl* m_pCoreCtrl; + class IDetectorEngine* m_pDE; + CImageObject m_objImage; +}; + +#endif // QTPTHREADIMAGE_H diff --git a/src/lpCoreCtrl/tadpoleGuiHeader.h b/src/lpCoreCtrl/tadpoleGuiHeader.h new file mode 100644 index 0000000..af784f8 --- /dev/null +++ b/src/lpCoreCtrl/tadpoleGuiHeader.h @@ -0,0 +1,57 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:$FILE_BASE$.$FILE_EXT$ + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:$DATE$ + History:$DAY$:$MONTH$:$YEAR$ $HOUR$:$MINUTE$ + *******************************************************************************/ +#ifndef __TADPOLE_GUI_HEADER_H +#define __TADPOLE_GUI_HEADER_H + +#define TP_OBJ_NAME_ACTION_START "tp_action_start"//开始自动触发的Action名称 +#define TP_OBJ_NAME_ACTION_STOP "tp_action_stop"//停止自动触发的Action名称 +#define TP_OBJ_NAME_ACTION_SHILFT "tp_action_shift"//开始/停止自动触发的Action名称 +#define TP_OBJ_NAME_ACTION_NEXT "tp_action_next"//手动获取一张图片的Action名称 +#define TP_OBJ_NAME_ACTION_LAST "tp_action_last" + +#define TP_OBJ_CUR_TRIGMODE_LBL "tp_cam_cur_trigger_mode_lbl" + +#define TP_OBJ_NAME_PUSHBUTTON_NEXT "tp_button_next" +#define TP_OBJ_NAME_PUSHBUTTON_LAST "tp_button_last" +#define TP_OBJ_NAME_PUSHBUTTON_SHIFT "tp_button_shift" +#define TP_OBJ_NAME_PUSHBUTTON_START "tp_button_start" +#define TP_OBJ_NAME_PUSHBUTTON_STOP "tp_button_stop" +#define TP_OBJ_NAME_PUSHBUTTON_GHOST_APP "tp_button_ghost_app" +#define TP_OBJ_NAME_PUSHBUTTON_SAVE_ALGORITHM "tp_button_save_algorithm" +#define TP_OBJ_NAME_PUSHBUTTON_SAVE_CAMERAES "tp_button_save_cameraes" +#define TP_OBJ_NAME_PUSHBUTTON_FIX_AUTO "tp_button_fix_auto" +#define TP_OBJ_NAME_PUSHBUTTON_INNER_TRIGGER_SHILFT "tp_button_inner_trigger_shilft" + +#define TP_OBJ_NAME_COMBOBOX_SELECT_CAMERAES "tp_combox_select_cameraes" +#define TP_OBJ_NAME_COMBOBOX_SELECT_ALGORITHMS "tp_combox_select_algorithms" +#define TP_OBJ_NAME_EDIT_CAM_OPT_FOLDER "tp_edit_cam_opt_folder" +#define TP_OBJ_NAME_EDIT_CAM_OPT_ALGORITHM "tp_edit_cam_opt_algorithm" +#define TP_OBJ_NAME_EDIT_CAM_OPT_BOARD_TYPE "tp_edit_cam_opt_board_type" +#define TP_OBJ_NAME_EDIT_CAM_OPT_BOARD_NUM "tp_edit_cam_opt_board_num" +#define TP_OBJ_NAME_CHECKBOX_CAM_OPT_SAVE "tp_checkbox_cam_opt_save" +#define TP_OBJ_NAME_EDIT_FIX_FRAMES "tp_edit_fix_frames" +#define TP_OBJ_NAME_EDIT_MSECPERFRAME "tp_edit_msecperframe" + +#define TP_OBJ_NAME_WIDGET_ALGORITHM "tp_widget_algorithm" +#define TP_OBJ_NAME_WIDGET_ALGORITHM_RESULT "tp_widget_algorithm_result" + +//#define TP_OBJ_WIDGET_DRAW_CURVE_ "tp_obj_widget_draw_curve_" +#define TP_PROP_BOOL_NEED_REPEAT_MANUL "tp_prop_bool_need_repeat_manul" +#define TP_OBJ_NAME_CHECKBOX_SHILT_IMAGES "tp_checkbox_shilt_images" + +#define TP_OBJ_NAME_CHECKBOX_TRIGGER_SOFT "tp_checkbox_trigger_soft" +#define TP_OBJ_NAME_CHECKBOX_TRIGGER_AUTO "tp_checkbox_trigger_auto" +#define TP_OBJ_NAME_EDIT_TRIGGER_INTERVAL "tp_edit_trigger_interval" +#define TP_OBJ_NAME_PUSHBUTTON_SEND_TRIGGER "tp_button_send_trigger" + +#define TP_OBJ_NAME_CHECKBOX_APP_STATUS "tp_app_checked_status" + + +#endif //__TADPOLE_GUI_HEADER_H \ No newline at end of file diff --git a/src/lpCoreCtrl/tpCamera/CameraPool.cpp b/src/lpCoreCtrl/tpCamera/CameraPool.cpp new file mode 100644 index 0000000..02ed05c --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/CameraPool.cpp @@ -0,0 +1,883 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:CameraPool.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/25 + History:25:3:2015 11:28 + *******************************************************************************/ +#include "CameraPool.h" +#include + +CCameraPool::CCameraPool(void) + : m_thdTrigger(this) + , m_libs(gpGlobalData->tgdDllPath) +// :m_mutexImgQueue(QMutex::Recursive) +{ + m_nTriggerMode = DEV_TRIGGER_MODE_STOP; + m_nFrameRate = 0; + m_nCameraEncode = 0; +} + +CCameraPool::~CCameraPool(void) +{ + releaseImagesList(); +} + +int CCameraPool::InitPool() +{ + int nRet = 0;//IOpenDevices(true); + m_thdTrigger.StartTrigger(); + return nRet; +} + +void CCameraPool::FreePool() +{ + m_thdTrigger.EndTrigger(); + ICloseDevices(); + IDeleteDameraes(); +} + +int CCameraPool::ICreateCameraes() +{ + QList keys = gpGlobalCamera->CameraKeys();//cameraes.keys(); + for (int i = 0; i < keys.size(); ++i) + { + if (m_cameraes.contains(keys[i])) + { + continue; + } + ICameraObject* pObject = m_libs.Create(gpGlobalCamera->CameraOption(keys[i]), this); + if (NULL == pObject) + { + continue; + } + pObject->m_pCamOpt->status = TP_CAMERA_STATUS_INITED; + m_cameraes.insert(keys[i], pObject); + } + return m_cameraes.size(); +} + +bool CCameraPool::ICreateCamera(const QString& strSerial, int nType) +{ + if (m_cameraes.contains(strSerial)) + { + return false; + } + + TP_CAMERA_OPTION camOpt; + camOpt.deviceType = nType; + camOpt.uniqueName = strSerial; + + ICameraObject* pObject = m_libs.Create(&camOpt, this); + if (NULL == pObject) + { + return false; + } + + pObject->m_pCamOpt->status = TP_CAMERA_STATUS_INITED; + m_cameraes.insert(strSerial, pObject); + return true; +} + +bool CCameraPool::IOpenCamera(const QString& strSerial, bool bReopen) +{ + ICameraObject* pCamera = m_cameraes.value(strSerial); + if (!pCamera) + return false; + + //if (pCamera->m_pCamOpt->status != TP_CAMERA_STATUS_INITED && + // pCamera->m_pCamOpt->status != TP_CAMERA_STATUS_CLOSED){ + // return false; + //} + + if (bReopen) + { + pCamera->ICloseCamera(); + pCamera->m_pCamOpt->status = TP_CAMERA_STATUS_CLOSED; + } + + int nret = pCamera->IOpenCamera(); + if (nret == 1){ + pCamera->m_pCamOpt->status = TP_CAMERA_STATUS_OPENED; + return true; + } + return false; +} + +bool CCameraPool::ICloseCamera(const QString& strSerial) +{ + ICameraObject* pCamera = m_cameraes.value(strSerial); + if (!pCamera) + return false; + + pCamera->ICloseCamera(); + pCamera->m_pCamOpt->status = TP_CAMERA_STATUS_CLOSED; + return true; +} + +bool CCameraPool::IDeleteCamera(const QString& strSerial) +{ + ICameraObject* pCamera = m_cameraes.value(strSerial); + if (pCamera){ + pCamera->IStopCamera(); + pCamera->ICloseCamera(); + + pCamera->m_pCamOpt->status = TP_CAMERA_STATUS_UNINIT; + m_libs.Delete(pCamera); + } + m_cameraes.remove(strSerial); + gpGlobalCamera->delCameraOption(strSerial); + + return true; +} + +bool CCameraPool::IAddCamera(const QString& strName, const TP_CAMERA_OPTION& camOpt, bool bNew, bool bAutoRun/* = false */) +{ + bool bret = false; + if (!bNew){ + bret = ICloseCamera(strName); + if (!bret) return bret; + } + + gpGlobalCamera->updateCameraOption(strName, camOpt); + + if (bNew){ + ICameraObject* pCamObj = m_libs.Create(gpGlobalCamera->CameraOption(strName), this); + if (pCamObj) + { + pCamObj->m_pCamOpt->status = TP_CAMERA_STATUS_INITED; + m_cameraes.insert(strName, pCamObj); + } + else{ + return false; + } + } + + bret = IOpenCamera(strName, true); + if (!bret) return bret; + + if (bAutoRun){ + bret = IStartCamera(strName); + } + return bret; +} + +bool CCameraPool::IStartCamera(const QString& strSerial) +{ + ICameraObject* pCamera = m_cameraes.value(strSerial); + if (!pCamera) + return false; + + //if (pCamera->m_pCamOpt->status != TP_CAMERA_STATUS_OPENED && + // pCamera->m_pCamOpt->status != TP_CAMERA_STATUS_STOPPED ) + //{ + // return false; + //} + + int nret = pCamera->IStartCamera(); + if (nret == 1){ + pCamera->m_pCamOpt->status = TP_CAMERA_STATUS_STARTED; + return true; + } + return false; +} + +bool CCameraPool::IStopCamera(const QString& strSerial) +{ + ICameraObject* pCamera = m_cameraes.value(strSerial); + if (!pCamera) + return false; + + pCamera->IStopCamera(); + pCamera->m_pCamOpt->status = TP_CAMERA_STATUS_STOPPED; + return true; +} + +void CCameraPool::IDeleteDameraes() +{ + IStopDevices(); + ICloseDevices(); + m_cameraes.clear(cameraErased, &m_libs); +} + +void CCameraPool::cameraOpen(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + + if (pCam->IOpenCamera() == 1) + { + pCam->m_pCamOpt->status = TP_CAMERA_STATUS_OPENED; + } +} + +void CCameraPool::cameraOpenEx(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + if (pCam->m_pCamOpt->bAutoOpen) + { + if (pCam->IOpenCamera() == 1) + { + pCam->m_pCamOpt->status = TP_CAMERA_STATUS_OPENED; + } + } +} + +int CCameraPool::IOpenDevices(bool bReopenAll) +{ + if( bReopenAll ) + { + ICloseDevices(); + } + m_cameraes.iterateCall(cameraOpen, NULL); + m_cameraes.iterateCall(cameraCalRelyOn, &m_cameraes); + //calRelyOn(); + //ISetTriggerMode(DEV_TRIGGER_MODE_AUTO, TRIGGER_DIRECT_FOREWARD, gGlobalPoolOption.frameRate); + return 1; +} + +int CCameraPool::IOpenDevicesEx(bool bReopenAll) +{ + if (bReopenAll) + { + ICloseDevices(); + } + m_cameraes.iterateCall(cameraOpenEx, NULL); + m_cameraes.iterateCall(cameraCalRelyOnEx, &m_cameraes); + //calRelyOn(); + //ISetTriggerMode(DEV_TRIGGER_MODE_AUTO, TRIGGER_DIRECT_FOREWARD, gGlobalPoolOption.frameRate); + return 1; +} + +void CCameraPool::cameraClose(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + + pCam->ICloseCamera(); + pCam->m_pCamOpt->status = TP_CAMERA_STATUS_CLOSED; +} +void CCameraPool::ICloseDevices() +{ + m_cameraes.iterateCall(cameraClose, NULL); +// m_devVessel.Erase(NULL, deviceErased, false); + //m_cameraes.clear(cameraErased, &m_libs); +} + + +void CCameraPool::IStartDevices() +{ + + m_cameraes.iterateCall(cameraStartDevices, NULL); +} + +void CCameraPool::IStopDevices(const QString& sDef/* = NULL*/) +{ + if (sDef.isEmpty()) + { + m_cameraes.iterateCall(cameraStopDevices, NULL); + } + else + { + m_cameraes.valueCall(sDef, cameraStopDevices, NULL); + } +} + +bool CCameraPool::ICamsStartPush() +{ + m_cameraes.iterateCall(camerasStartPush, NULL); + return true; +} + +void CCameraPool::camerasStartPush(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + + //if (pCam->m_pCamOpt->deviceType != 110) return; + + pCam->IStartPush(); +} + +bool CCameraPool::ICamsPausePush() +{ + m_cameraes.iterateCall(camerasPausePush, NULL); + return true; +} + +void CCameraPool::camerasPausePush(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + + //if (pCam->m_pCamOpt->deviceType != 110) return; + + pCam->IPausePush(); +} + +void CCameraPool::fixedFramesStopCamera(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + if (pCam->NeedStopping()) + { + pCam->IStopCamera(); + pCam->m_pCamOpt->status = TP_CAMERA_STATUS_STOPPED; + } +} + + + +void CCameraPool::DeviceLost(const QString& strName) +{ + ICameraObject* pObj = m_cameraes.take(strName); + if (NULL != pObj) + { + m_libs.Delete(pObj); + } +// m_devVessel.Erase(strName, deviceErased, false); +// m_virVessel.Erase(strName, deviceErased, false); +} + +QList CCameraPool::ICameraKeys() +{ + return gpGlobalCamera->CameraKeys(); +} + +QList CCameraPool::ICameraKeysOrderByIds() +{ + return gpGlobalCamera->CameraKeysOrderByIds(); +} + +bool CCameraPool::ICameraOption(const QString& strSerial, TP_CAMERA_OPTION& camOpt) +{ + return gpGlobalCamera->CameraOption(strSerial, camOpt); +} + +bool CCameraPool::ICameraOptionByKey(const QString& strSerial, TP_CAMERA_OPTION& camOpt) +{ + return gpGlobalCamera->CameraOptionByKey(strSerial, camOpt); +} + +bool CCameraPool::ISetCameraOption(const QString& strSerial, const TP_CAMERA_OPTION& camOpt) +{ + bool bRet = gpGlobalCamera->SetCameraOption(strSerial, camOpt); + if (bRet) + { + ICameraObject* pCam = m_cameraes.value(strSerial, NULL); + if (NULL != pCam) + { + pCam->ISetProperty(NULL); + } + } + return bRet; +} + +QList CCameraPool::IEnumAvailableCameras(emTpDeviceType camType) +{ + QList ret; + + // if the camera dll has been loaded + QZkMutexMap::const_iterator iter = m_cameraes.constBegin(); + while (iter != m_cameraes.constEnd()) + { + ICameraObject* pCam = iter.value(); + if (pCam && pCam->m_pCamOpt->deviceType == camType){ + ret = pCam->IEnumAvailableCameras(); + return ret; + } + iter++; + } + + // the camera dll has not been loaded + TP_CAMERA_OPTION camOpt; + camOpt.deviceType = camType; + + ret = m_libs.enumAvailableCameras(&camOpt, this); + return ret; +} + +void CCameraPool::ISetTriggerMode(emTpDeviceTriggerMode mode, emTpTriggerDirection nDirection /* = TRIGGER_DIRECT_FOREWARD */, long nFrameRate /* = 0 */) +{ + gGlobalPoolOption.triggerMode = mode; + gGlobalPoolOption.triggerDirection = nDirection; + gGlobalPoolOption.frameRate = nFrameRate; + gGlobalPoolOption.fixedFrames = nFrameRate; + m_nTriggerMode = mode; + m_nFrameRate = nFrameRate; + //set every device's trigger + m_thdTrigger.LockWorked(); + m_cameraes.iterateCall(cameraSetTriggerMode, &gGlobalPoolOption); + IStartDevices(); + m_thdTrigger.UnlockWorked(); + //begin or end trigger's thread + if( DEV_TRIGGER_MODE_AUTO != mode && + DEV_TRIGGER_MODE_FIXED_AUTO != mode ) + { + //m_thdTrigger.EndThread(); + m_thdTrigger.SetAutoTrigger(false); + } + else + { + m_thdTrigger.SetFrame(nFrameRate); + //m_thdTrigger.StartTrigger(); + m_thdTrigger.SetAutoTrigger(true); + } +} + + +void CCameraPool::IManualTrigger(emTpTriggerDirection nDirection /* = TRIGGER_DIRECT_FOREWARD */) +{ + if( DEV_TRIGGER_MODE_OUT != m_nTriggerMode ) + { + ISetTriggerMode(DEV_TRIGGER_MODE_OUT); + } + tagCOMMIT_TRIGGER_PARAM prm; + prm.direct = nDirection; + prm.count = 0; + prm.bFromMenual = true; + m_cameraes.iterateCall(cameraSnaps, &prm); +// SnapImages(prm); +} + +void gfunc_snap_camera_image(const QString& key, class ICameraObject*& pCam, void* pData) +{ + if (!pCam) + return; + if (NULL != pCam->RelyOnCamera()) + { + return; + } + QStringList* pKeys = (QStringList*)pData; + if (!pKeys->contains(key)) + { + return; + } + pCam->ISnapCamera(TRIGGER_DIRECT_FOREWARD); +} + +void CCameraPool::ISnapImage(const QStringList& camKeys) +{ + m_cameraes.iterateCall2(gfunc_snap_camera_image, (void*)(&camKeys)); +} + +void gfunc_send_soft_trigger(const QString& key, class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + + if (NULL != pCam->RelyOnCamera()) + { + return; + } + if (NULL != pData) + { + QStringList* pKeys = (QStringList*)pData; + if (!pKeys->contains(key)) + { + return; + } + } + pCam->ISendSoftTrigger(); +} +void CCameraPool::ISendSoftTrigger(const QStringList& camKeys) +{ + if (camKeys.isEmpty()) + { + m_cameraes.iterateCall2(gfunc_send_soft_trigger, NULL); + } + else + { + m_cameraes.iterateCall2(gfunc_send_soft_trigger, (void*)(&camKeys)); + } +} + +void CCameraPool::IntervalCaptureImages(tagCOMMIT_TRIGGER_PARAM& prm) +{ + m_cameraes.iterateCall(cameraIntervalCaptures, &prm); + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +//**difficult to understand** +//iterate the virtual camera's vessel and the device's vessel as the parameter +void CCameraPool::calRelyOn() +{ + m_cameraes.iterateCall(cameraCalRelyOn, &m_cameraes); +} + + +void CCameraPool::ISetCameraProperty(const QString& camera, emTpCameraProperty property, long nValue) +{ +} + +void CCameraPool::ISetCameraProperty(const QString& camera, TP_CAMERA_PROPERTY& property) +{ + if( camera.isNull() ) + { + m_cameraes.iterateCall(cameraSetProperty, &property); + } + else + { + m_cameraes.valueCall(camera, cameraSetProperty, &property); + } +} + + +void CCameraPool::cameraSetProperty(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + pCam->ISetProperty((TP_CAMERA_PROPERTY*)pData); +} + +//ICameraObject* CCameraPool::CreateCamera(TP_CAMERA_OPTION* pCamOpt) +//{ +// if( NULL == pCamOpt ) +// { +// return NULL; +// } +// ICameraObject* pObj = NULL; +// if( DEV_CAMERA == pCamOpt->deviceType ) +// { +// //pObj = new CGigeCamera(pCamOpt, this); +// } +// else if( DEV_VIRTUAL == pCamOpt->deviceType ) +// { +// //pObj = new CVirtualCamera(pCamOpt, this); +// } +// else if( DEV_BITFLOW == pCamOpt->deviceType ) +// { +// //pObj = new CBitflowCameraBi(pCamOpt, this); +// } +// return pObj; +//} + +QList CCameraPool::ICameraWins() +{ + return gpGlobalCamera->CameraWins(); +} + +CZkCameraImage* CCameraPool::IPopCameraImage() +{ + return m_imagesList.takeFirst(NULL); + /*CZkCameraImage *pCamImg = m_imagesList.takeFirst(NULL); + if (NULL != pCamImg) + { + + } + return pCamImg;*/ +} + +////////////////////////////////////////////////////////////////////////////////////////////// +int CCameraPool::createDeviceByOptiones() +{ + QList keys = gpGlobalCamera->CameraKeys();//cameraes.keys(); + for( int i = 0; i < keys.size(); ++i ) + { + if( m_cameraes.contains(keys[i]) ) + { + continue; + } + ICameraObject* pObject = m_libs.Create(gpGlobalCamera->CameraOption(keys[i]), this); + if( NULL == pObject ) + { + continue; + } + if( 0 == pObject->IOpenCamera() ) + { + m_libs.Delete(pObject); + continue; + } + pObject->m_pCamOpt->status = TP_CAMERA_STATUS_OPENED; + m_cameraes.insert(keys[i], pObject); + } + return m_cameraes.size(); +} + +void CCameraPool::cameraErased(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + CLibCameraes* pLibs = (CLibCameraes*)pData; + pCam->IStopCamera(); + pCam->ICloseCamera(); + + pCam->m_pCamOpt->status = TP_CAMERA_STATUS_UNINIT; + pLibs->Delete(pCam); +} + +void CCameraPool::cameraStartDevices(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + + //if (pCam->m_pCamOpt->status != TP_CAMERA_STATUS_OPENED && + // pCam->m_pCamOpt->status != TP_CAMERA_STATUS_STOPPED) + //{ + // return; + //} + + if (pCam->IStartCamera() == 1){ + pCam->m_pCamOpt->status = TP_CAMERA_STATUS_STARTED; + } +} + +void CCameraPool::cameraStopDevices(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + pCam->IStopCamera(); + pCam->m_pCamOpt->status = TP_CAMERA_STATUS_STOPPED; +} + +void CCameraPool::cameraSetTriggerMode(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + TP_CAMERAPOOL_OPTION* pOpt = (TP_CAMERAPOOL_OPTION*)pData; + pCam->SetFixedFrames(pOpt->fixedFrames); + pCam->SetTriggerMode(pOpt->triggerMode); +} + +void CCameraPool::cameraSnaps(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + tagCOMMIT_TRIGGER_PARAM* pParam = (tagCOMMIT_TRIGGER_PARAM*)pData; + if( NULL != pCam->RelyOnCamera() ) + { + return; + } + pCam->ISnapCamera(pParam->direct); +} + +void CCameraPool::cameraIntervalCaptures(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + //tagCOMMIT_TRIGGER_PARAM* pParam = (tagCOMMIT_TRIGGER_PARAM*)pData; + if (NULL != pCam->RelyOnCamera()) + { + return; + } + //pCam->ISnapCamera(pParam->direct); + pCam->IAutoCapture(); +} + +void CCameraPool::cameraCalRelyOn(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + QZkMutexMap* pCameraes = (QZkMutexMap*)pData; + TP_CAMERA_OPTION* pOpt = pCam->m_pCamOpt; + if( DEV_CAM_VIRTUAL_FOLDER == pOpt->deviceType && NULL == pCam->RelyOnCamera() && pCameraes->contains(pOpt->relyOnCamera) ) + { + ICameraObject* pCamRelyOn = pCameraes->value(pOpt->relyOnCamera, NULL); + if( NULL != pCamRelyOn ) + { + pCam->SetRelyOnCamera(pCamRelyOn); + pCamRelyOn->SetReliedByCamera(pCam); + } + } +} + +void CCameraPool::cameraCalRelyOnEx(class ICameraObject*& pCam, void* pData) +{ + if (!pCam) return; + QZkMutexMap* pCameraes = (QZkMutexMap*)pData; + TP_CAMERA_OPTION* pOpt = pCam->m_pCamOpt; + if (DEV_CAM_VIRTUAL_FOLDER == pOpt->deviceType && pOpt->bAutoOpen && NULL == pCam->RelyOnCamera() && pCameraes->contains(pOpt->relyOnCamera)) + { + ICameraObject* pCamRelyOn = pCameraes->value(pOpt->relyOnCamera, NULL); + if (NULL != pCamRelyOn) + { + pCam->SetRelyOnCamera(pCamRelyOn); + pCamRelyOn->SetReliedByCamera(pCam); + } + } +} + +int CCameraPool::IPushCameraData(ICameraObject* pCamObj) +{ + if (m_imagesList.size() >= 32) + { + qWarning("Cache OverFlow, Camera Image Count:%d", m_imagesList.size()); + if (pCamObj->m_nFrameNum > 0) + { + pCamObj->m_nFrameNum--; + } + return 0; + } + + if (NULL == pCamObj || NULL == pCamObj->m_pCamOpt ) + { + return 0; + } + if (pCamObj->m_pCamOpt->bIgnoreImage) + { + if (pCamObj->m_nFrameNum > 0) + { + pCamObj->m_nFrameNum--; + } + return 0; + } + // + pCamObj->SaveToFile(); + + CZkCameraImage *pImage = m_imagesMerging.value(pCamObj->m_pCamOpt->uniqueName, NULL); + if (NULL == pImage) + { + pImage = new CZkCameraImage(); + } + int nRet = 0; + if (NULL != pImage) + { + // + if (!gpGlobalCamCallback){ + delete pImage; + return 0; + } + try{ + pImage->SetVarFromUI(gpGlobalCamCallback->IGetVariantById(pCamObj->m_pCamOpt->id)); + + //QVariantMap map; + //map.clear(); + //map.insert("width", pCamObj->m_nImageWidth); + //map.insert("height", pCamObj->m_nImageHeight); + //map.insert("size", pCamObj->m_nImageSize); + //map.insert("frame", pCamObj->m_nFrameNum); + //qint64 nSizes = pCamObj->m_nImageHeight*pCamObj->m_nImageWidth*((pCamObj->m_nBitsPerPixel + 7) / 8); + ////QByteArray baimgdat = QByteArray::fromRawData((const char*)(pCamObj->m_pCameraData), nSizes); + //QByteArray baimgdat = QByteArray((const char*)(pCamObj->m_pCameraData)); + //map.insert("imgdata", baimgdat); + + //std::string imgb64 = base64_encode(pCamObj->m_pCameraData, nSizes); + //int b64len = imgb64.length(); + //QString qsimgb64(imgb64.c_str()); + //map.insert("imgb64", qsimgb64); + //gpGlobalCamCallback->INewCameraImage(map); + } + catch (...){ + qCritical() << "Critical error." << __FUNCTION__; + return 0; + } + // + int nRet = pImage->CopyCameraObject(pCamObj); + if (-1 == nRet) + { + delete pImage; + pImage = NULL; + } + else if (1 == nRet) + { + pImage->SetMeterCode(m_nCameraEncode); + m_imagesList.append(pImage); + pImage = NULL; + } + m_imagesMerging.insert(pCamObj->m_pCamOpt->uniqueName, pImage); + nRet = 1; + } + + //checks to stop trigger in fixed_auto + if (pCamObj->NeedStopping()) + { + //IStopDevices(pCamObj->CameraOption()->uniqueName);//不是好方法,如果正在SetTriggerStart,这里可能会立刻Stop这次Start + //pCamObj->IStopCamera();//不是好方法,如果正在SetTriggerStart,这里可能会立刻Stop这次Start + m_cameraes.valueCall(pCamObj->m_pCamOpt->uniqueName, fixedFramesStopCamera, NULL); + } + return nRet; +} + +//don't combine image +int CCameraPool::IPushCameraData2(ICameraObject* pCamObj) +{ + if (m_imagesList.size() >= 32) + { + qWarning("Cache OverFlow, Camera Image Count:%d", m_imagesList.size()); + } + + quint64 msec = QDateTime::currentMSecsSinceEpoch(); + if (NULL == pCamObj || NULL == pCamObj->m_pCamOpt) + { + return 0; + } + if (pCamObj->m_pCamOpt->bIgnoreImage) + { + if (pCamObj->m_nFrameNum > 0) + { + pCamObj->m_nFrameNum--; + } + return 0; + } + // + pCamObj->SaveToFile(); + // + CZkCameraImage* pImage = new CZkCameraImage(); + if (NULL == pImage) + { + return 0; + } + if (0 == pImage->CopyCameraObject2(pCamObj)) + { + delete pImage; + return 0; + } + + pImage->SetVarFromUI(gpGlobalCamCallback->IGetVariantById(pCamObj->m_pCamOpt->id)); + m_imagesList.append(pImage); + // + if (pCamObj->NeedStopping()) + { + m_cameraes.valueCall(pCamObj->m_pCamOpt->uniqueName, fixedFramesStopCamera, NULL); + } + return 1; +} + +char* CCameraPool::IGetMainPath() +{ + return gpGlobalCamera->m_pGlobalData->tgdMainPath; +} +void CCameraPool::IFreeCameraImage(CZkCameraImage *pCamImg) +{ + delete pCamImg; +} + +QMap CCameraPool::IGetCamShowNames() +{ + return gpGlobalCamera->GetCamShowNames(); +} + +void CCameraPool::ISetCameraEncode(INT64 code, INT64 rate) +{ + m_nCameraEncode = code; + m_nEncoderRate = rate; +} + +void CCameraPool::ISetVirtualImages(const QString& camera, const QStringList& szImages) +{ + if (camera.isEmpty()) + { + m_cameraes.iterateCall(cameraSetVirtuleImages, (void*)&szImages); + } + else + { + m_cameraes.valueCall(camera, cameraSetVirtuleImages, (void*)&szImages); + } +} +void CCameraPool::cameraSetVirtuleImages(class ICameraObject*& pCam, void* pData) +{ + QStringList* pszImages = (QStringList*)pData; + if (pCam->m_pCamOpt->deviceType / 10 == DEV_CAM_VIRTUAL / 10) + { + pCam->ISetVirtualImages(*pszImages); + } +} + +QList CCameraPool::ICamWinKeys() +{ + return gpGlobalCamera->camWinKeys(); +} + +bool CCameraPool::ICamWinOptionByKey(const QString& strKey, TP_CAMERA_WIN& camwinOpt) +{ + + return gpGlobalCamera->camWinByKey(strKey, camwinOpt); +} + +bool CCameraPool::IAddCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew) +{ + return gpGlobalCamera->addCamWin(strKey, camwinOpt, bNew); +} + +bool CCameraPool::IDelCamWin(const QString& strKey) +{ + return gpGlobalCamera->delCamWin(strKey); +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpCamera/CameraPool.h b/src/lpCoreCtrl/tpCamera/CameraPool.h new file mode 100644 index 0000000..4d5af2c --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/CameraPool.h @@ -0,0 +1,150 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:CameraPool.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/25 + History:25:3:2015 11:26 + *******************************************************************************/ +#ifndef __CAMERA_POOL_H_20150325 +#define __CAMERA_POOL_H_20150325 + +#include "icamera.h" +#include +#include +//#include +#include +#include "triggerthread.h" +#include "QZkMutexList.h" +#include "QZkMutexMap.h" +#include "iCameraObject.h" +#include "LibCameraes.h" + + +class CCameraPool : public ICameraPool, public IPoolCallback +{ +public: + CCameraPool(void); + virtual ~CCameraPool(void); + + int InitPool(); + void FreePool(); + int ImagesSize() { + return m_imagesList.size(); + } + void DeviceLost(const QString& strName); + //called by "CTriggerThread" + struct tagCOMMIT_TRIGGER_PARAM{ + emTpTriggerDirection direct; + DWORD count; + bool bFromMenual; + }; + void IntervalCaptureImages(tagCOMMIT_TRIGGER_PARAM& prm); +private: + virtual QList ICameraKeys();//获取相机的keys,在camera.json中配置 + virtual QList ICameraKeysOrderByIds();//获取相机的keys,但根据id排序 + virtual bool ICameraOption(const QString& strSerial, TP_CAMERA_OPTION& camOpt);//根据相机key获取相机的配置属性 + virtual bool ICameraOptionByKey(const QString& strSerial, TP_CAMERA_OPTION& camOpt);//根据相机key获取相机的配置属性 + virtual bool ISetCameraOption(const QString& strSerial, const TP_CAMERA_OPTION& camOpt); + // + virtual int ICreateCameraes(); + virtual void IDeleteDameraes(); + + virtual QList IEnumAvailableCameras(emTpDeviceType camType); // try to get available (connected to the computer) by camera type(manufacturer) + + virtual bool ICreateCamera(const QString& strSerial, int nType); + virtual bool IOpenCamera(const QString& strSerial, bool bReopen); + virtual bool ICloseCamera(const QString& strSerial); + virtual bool IStartCamera(const QString& strSerial); + virtual bool IStopCamera(const QString& strSerial); + virtual bool IDeleteCamera(const QString& strSerial); + virtual bool IAddCamera(const QString& strName, const TP_CAMERA_OPTION& camOpt, bool bNew, bool bAutoRun = false); + + // + virtual int IOpenDevices(bool bReopenAll); + virtual int IOpenDevicesEx(bool bReopenAll); + virtual void ICloseDevices(); + virtual void IStartDevices(); + virtual void IStopDevices(const QString& sDef = NULL); + virtual void ISetTriggerMode(emTpDeviceTriggerMode mode, emTpTriggerDirection nDirection = TRIGGER_DIRECT_FOREWARD, long nFrameRate = 0); + virtual emTpDeviceTriggerMode IGetTriggerMode(){ + return m_nTriggerMode; + } + virtual void IManualTrigger( emTpTriggerDirection nDirection = TRIGGER_DIRECT_FOREWARD); + virtual void ISnapImage(const QStringList& camKeys); + virtual void ISendSoftTrigger(const QStringList& camKeys); + virtual QList ICameraWins(); + virtual CZkCameraImage* IPopCameraImage(); + virtual int ICameraImages() { + return m_imagesList.size(); + } + virtual void IFreeCameraImage(CZkCameraImage *pCamImg); + /////////////////////////////////////////////////////////////////////////////////////// + virtual void ISetCameraProperty(const QString& camera, emTpCameraProperty property, long nValue); + virtual void ISetCameraProperty(const QString& camera, TP_CAMERA_PROPERTY& property); + virtual QMap IGetCamShowNames(); + virtual void ISetCameraEncode(INT64 code, INT64 rate); + virtual void ISetVirtualImages(const QString& camera, const QStringList& szImages); + + //operate camera window + virtual QList ICamWinKeys(); + virtual bool ICamWinOptionByKey(const QString& strKey, TP_CAMERA_WIN& camwinOpt); + virtual bool IAddCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew); + virtual bool IDelCamWin(const QString& strKey); + + virtual bool ICamsStartPush(); + virtual bool ICamsPausePush(); + + /////////////////////////////////////////////////////////////////////////////////////////// +// ICameraObject* CreateCamera(TP_CAMERA_OPTION* pCamOpt); + void calRelyOn(); + /////////////////////////////////////////////////////////////////////////////////////////////// + void releaseImagesList() { + //m_imagesList.iterateCall(iterateCallImageErase, NULL); + m_imagesList.clear(iterateCallImageErase, NULL); + m_imagesMerging.clear(iterateCallImageErase, NULL); + //m_imagesCache.clear(iterateCallImageErase, NULL); + } + static void iterateCallImageErase(class CZkCameraImage*& pImage, void* pData) { + delete pImage; + } + //using class ICameraObject + int createDeviceByOptiones(); + static void cameraOpen(class ICameraObject*& pCam, void* pData); + static void cameraOpenEx(class ICameraObject*& pCam, void* pData); + static void cameraClose(class ICameraObject*& pCam, void* pData); + static void cameraErased(class ICameraObject*& pCam, void* pData); + static void cameraStartDevices(class ICameraObject*& pCam, void* pData); + static void cameraStopDevices(class ICameraObject*& pCam, void* pData); + static void cameraSetTriggerMode(class ICameraObject*& pCam, void* pData); + static void cameraSnaps(class ICameraObject*& pCam, void* pData); + static void cameraIntervalCaptures(class ICameraObject*& pCam, void* pData); + static void cameraCalRelyOn(class ICameraObject*& pCam, void* pData); + static void cameraCalRelyOnEx(class ICameraObject*& pCam, void* pData); + static void cameraSetProperty(class ICameraObject*& pCam, void* pData); + static void fixedFramesStopCamera(class ICameraObject*& pCam, void* pData); + static void cameraSetVirtuleImages(class ICameraObject*& pCam, void* pData); + //IPoolCallback interface + virtual int IPushCameraData(ICameraObject* pCamObj); + virtual int IPushCameraData2(ICameraObject* pCamObj); + virtual char* IGetMainPath(); + + static void camerasStartPush(class ICameraObject*& pCam, void* pData); + static void camerasPausePush(class ICameraObject*& pCam, void* pData); +private: + //**the key is unique name, not serial number** + QZkMutexMap m_imagesMerging; + QZkMutexList m_imagesList; + //QZkMutexMap m_imagesCache; + CTriggerThread m_thdTrigger; + emTpDeviceTriggerMode m_nTriggerMode; + long m_nFrameRate; + ////////////////////////////////////////////////////////////// + QZkMutexMap m_cameraes; + CLibCameraes m_libs; + INT64 m_nCameraEncode; + INT64 m_nEncoderRate; +}; + +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/tpCamera/LibCameraes.cpp b/src/lpCoreCtrl/tpCamera/LibCameraes.cpp new file mode 100644 index 0000000..7b1fa6d --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/LibCameraes.cpp @@ -0,0 +1,203 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:LibCameraes.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/05/26 + History:26:5:2015 16:34 + *******************************************************************************/ +#include "LibCameraes.h" +#include +#include + +#define PREFIX_API(pfx) \ + extern "C" ICameraObject* pfx##Camera_Create(TP_CAMERA_OPTION* pCamOpt, IPoolCallback* pCb);\ + extern "C" void pfx##Camera_Delete(ICameraObject*); + +#ifndef _USE_CAMERA_DLL + +#ifdef _USE_BITFLOW +PREFIX_API(Bitflow_) +#endif + +#ifdef _USE_VIRTUAL +PREFIX(Virtual_) +#endif + +#ifdef _USE_GIGE +PREFIX(Gige_) +#endif + +#endif + +CLibCameraes::CLibCameraes(char* szPath) + : m_szPath(szPath) +{ + +} + +CLibCameraes::~CLibCameraes() +{ + m_camApiMap.clear(clearCall); +} + +ICameraObject* CLibCameraes::Create(TP_CAMERA_OPTION* pCamOpt, IPoolCallback* pCb) +{ + if( NULL == pCamOpt || NULL == pCb ) + { + return NULL; + } + if( !LoadCameraApi((emTpDeviceType)pCamOpt->deviceType) ) + { + return NULL; + } + CAMERA_API* pApi = m_camApiMap.value(pCamOpt->deviceType, NULL); + if( NULL == pApi || NULL == pApi->fpCreate ) + { + return NULL; + } + ICameraObject* pCam = pApi->fpCreate(pCamOpt, pCb); + if (NULL != pCam) + { + pApi->cameraes++; + } + return pCam; +} + +bool CLibCameraes::Delete(ICameraObject* pCam) +{ + if( NULL == pCam ) + { + return false; + } + int nKey = pCam->m_pCamOpt->deviceType; + CAMERA_API* pApi = m_camApiMap.value(nKey, NULL); + if( NULL == pApi || NULL == pApi->fpDelete ) + { + return false; + } + pApi->fpDelete(pCam); + pApi->cameraes--; + /* + if (0 == pApi->cameraes)//release the dll + { + clearCall(pApi, NULL); + m_camApiMap.remove(nKey); + } + */ + return true; +} + +bool CLibCameraes::LoadCameraApi(emTpDeviceType devType) +{ + if( m_camApiMap.contains(devType) ) + { + return true; + } + CAMERA_API* pApi = new CAMERA_API; + if( NULL == pApi ) + { + return false; + } +#ifdef _USE_CAMERA_DLL + QString fileName(m_szPath); +// if( !fileName.endsWith('\\') && !fileName.endsWith('/') ) +// { +// fileName.append('\\'); +// } + int nType = devType/10 * 10; + QString strAlias; + strAlias = "tpCam_" + QString::number(nType); + + fileName.append(strAlias); +#ifdef _DEBUG + fileName.append('d'); +#endif + // + pApi->pLib = new QLibrary(fileName); + if( NULL == pApi->pLib ) + { + delete pApi; + return false; + } + + pApi->fpCreate = (Func_Create)pApi->pLib->resolve("Camera_Create"); + pApi->fpDelete = (Func_Delete)pApi->pLib->resolve("Camera_Delete"); + if( NULL == pApi->fpCreate || NULL == pApi->fpDelete ) + { + QString strErr = QString("Failed to Load camera's dll: %1; ERROR: %2") + .arg(fileName.toLocal8Bit().data()) + .arg(pApi->pLib->errorString().toLocal8Bit().data()); + qCritical() << strErr << " - " << __FUNCTION__; + + delete pApi->pLib; + delete pApi; + return false; + } +#else + switch(devType) + { + case DEV_CAM_BITFLOW: + pApi->fpCreate = API_NAME_PREFIX(Bitflow_, Camera_Create); + pApi->fpDelete = API_NAME_PREFIX(Bitflow_, Camera_Delete); + break; + case DEV_CAM_VIRTUAL: + pApi->fpCreate = API_NAME_PREFIX(Virtual_, Camera_Create); + pApi->fpDelete = API_NAME_PREFIX(Virtual_, Camera_Delete); + break; + case DEV_CAM_GIGE: + pApi->fpCreate = API_NAME_PREFIX(Gige_, Camera_Create); + pApi->fpDelete = API_NAME_PREFIX(Gige_, Camera_Delete); + break; + case DEV_CAM_USB: + pApi->fpCreate = API_NAME_PREFIX(Usb_, Camera_Create); + pApi->fpDelete = API_NAME_PREFIX(Usb_, Camera_Delete); + break; + default: + delete pApi; + return false; + } +#endif + pApi->cameraes = 0; + m_camApiMap.insert(devType, pApi); + return true; +} + +void CLibCameraes::clearCall(CAMERA_API*& pCamApi, void* pData) +{ +#ifdef _USE_CAMERA_DLL + delete pCamApi->pLib; +#endif + delete pCamApi; +} + +QList CLibCameraes::enumAvailableCameras(TP_CAMERA_OPTION* pCamOpt, IPoolCallback* pCb) +{ + QList ret; + if (!LoadCameraApi((emTpDeviceType)pCamOpt->deviceType)) + { + return ret; + } + CAMERA_API* pApi = m_camApiMap.value(pCamOpt->deviceType, NULL); + if (NULL == pApi || NULL == pApi->fpCreate) + { + return ret; + } + ICameraObject* pCam = pApi->fpCreate(pCamOpt, pCb); + if (NULL != pCam) + { + ret = pCam->IEnumAvailableCameras(); + int nKey = pCam->m_pCamOpt->deviceType; + pApi->fpDelete(pCam); + + if (0 == pApi->cameraes)//release the dll + { + clearCall(pApi, NULL); + + m_camApiMap.remove(nKey); + } + } + + return ret; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpCamera/LibCameraes.h b/src/lpCoreCtrl/tpCamera/LibCameraes.h new file mode 100644 index 0000000..5e7ba68 --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/LibCameraes.h @@ -0,0 +1,54 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:LibCameraes.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/05/26 + History:26:5:2015 16:34 + *******************************************************************************/ +#ifndef LIBCAMERAES_H +#define LIBCAMERAES_H + +#include "iCameraObject.h" +#include "QZkMutexMap.h" +#include +#include + +class CLibCameraes +{ +public: + CLibCameraes(char* szPath); + ~CLibCameraes(); + ICameraObject* Create(TP_CAMERA_OPTION* pCamOpt, IPoolCallback* pCb); + bool Delete(ICameraObject* pCam); + + QList enumAvailableCameras(TP_CAMERA_OPTION* pCamOpt, IPoolCallback* pCb); +private: + typedef ICameraObject*(*Func_Create)(TP_CAMERA_OPTION* pCamOpt, IPoolCallback* pCb); + typedef void(*Func_Delete)(ICameraObject* pCam); + typedef struct tagCAMERA_API { + Func_Create fpCreate; + Func_Delete fpDelete; +#ifdef _USE_CAMERA_DLL + QLibrary* pLib; +#endif + int cameraes; + } CAMERA_API; + bool LoadCameraApi(emTpDeviceType devType); + static void clearCall(CAMERA_API*& pCamApi, void* pData); +private: + QZkMutexMap m_camApiMap; + char* m_szPath; +}; + +inline bool is_dll_library(const QString& name) +{ +#ifdef _DEBUG + return QFile::exists(name + "d.dll"); +#else + return QFile::exists(name + ".dll"); +#endif +} + +#endif // LIBCAMERAES_H diff --git a/src/lpCoreCtrl/tpCamera/globalCamera.cpp b/src/lpCoreCtrl/tpCamera/globalCamera.cpp new file mode 100644 index 0000000..c3867e8 --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/globalCamera.cpp @@ -0,0 +1,460 @@ +/****************************************************************************** +Copyright(C):2015~2018 hzleaper +FileName:globalCamera.cpp +Author:zhikun wu +Email:zk.wu@hzleaper.com +Tools:vs2010 pc on company +Created:2015/04/09 +History:9:4:2015 13:23 +*******************************************************************************/ +#include +#include "globalCamera.h" +#include "ZkCameraImage.h" +#include +#include + +#ifdef _USE_CAMERA_SERIAL_ENCRYPTO +#include "QZkSerialNumbers.h" +#endif + +const char CGlobalCamera::cs_szCfgName[] = "camera"; + +CGlobalCamera::CGlobalCamera(const CAMERA_IN_PARAM* pInParam) + :m_mtxCam(QMutex::Recursive) +{ + m_pCallback = pInParam->pCallback; + m_pGlobalData = pInParam->pGlobalData; + readConfig(); + readSerials(); +} +CGlobalCamera::~CGlobalCamera() +{ + releaseConfig(); +} +bool CGlobalCamera::CameraOption(const QString& serials, TP_CAMERA_OPTION& camOpt) +{ + // camOpt.serials = serials; + return m_cameraesOption.iterateCall(copyCameraOptionBySerial, &camOpt); +} +bool CGlobalCamera::copyCameraOptionBySerial(const QString& key, TP_CAMERA_OPTION*& pCamOpt, void* pData) +{ + TP_CAMERA_OPTION* pDes = (TP_CAMERA_OPTION*)pData; + if (pCamOpt->uniqueName == pDes->uniqueName) + { + *pDes = *pCamOpt; + return true; + } + return false; +} + +bool CGlobalCamera::CameraOptionByKey(const QString& sKey, TP_CAMERA_OPTION& camOpt) +{ + return m_cameraesOption.valueCall(sKey, copyCameraOptionByKey, &camOpt); +} + +void CGlobalCamera::copyCameraOptionByKey(const QString& key, TP_CAMERA_OPTION*& pCamOpt, void* pData) +{ + TP_CAMERA_OPTION* pDes = (TP_CAMERA_OPTION*)pData; + *pDes = *pCamOpt; +} + +bool CGlobalCamera::SetCameraOption(const QString& sKey, const TP_CAMERA_OPTION& camOpt) +{ + TP_CAMERA_OPTION* pOpt = m_cameraesOption.value(sKey, NULL); + if (NULL == pOpt) + { + return false; + } + pOpt->save = camOpt.save; + pOpt->algorithm = camOpt.algorithm; + pOpt->exposure = camOpt.exposure; + pOpt->folder = camOpt.folder; + checkFolder(pOpt->folder); + + pOpt->gain = camOpt.gain; + pOpt->bIgnoreImage = camOpt.bIgnoreImage; + // m_jsonCamera.value("devices").toObject().value(pOpt->uniqueName) + return true; +} + +bool CGlobalCamera::SaveCameraOption(const TP_CAMERA_OPTION& camOpt) +{ + QJsonObject jsonDevices = m_jsonCamera.value("devices").toObject(); + if (jsonDevices.isEmpty()) + { + return false; + } + //TODO:... here + QJsonObject jsonCam = jsonDevices.value(camOpt.uniqueName).toObject(); + if (jsonCam.isEmpty()) + { + return false; + } + jsonCam.insert("folder", camOpt.folder); + + jsonDevices.insert(camOpt.uniqueName, jsonCam); + m_jsonCamera.insert("devices", jsonDevices); + return true; +} + +void CGlobalCamera::readConfig() +{ + int nError; + //read config from ".json" file + QString szFileName(m_pGlobalData->tgdMainPath); + szFileName.append(m_pGlobalData->tgdFolderNames[TP_FOLDER_NAME_CONFIG]).append(cs_szCfgName).append(TP_STR::cszJsonSuffix); + // szFileName.append(TP_STR::cszCfgFolder) + QMutexLocker locker(&m_mtxCam); + QJsonObject objJson = m_jsonCamera = QZkJsonParser::ReadJsonObject(szFileName, &nError); + tpDebugOut("camera.json paths: %s", szFileName.toLocal8Bit().data()); + tpDebugOut("read camera.json result: %d", nError); + if (objJson.isEmpty()) + { + tpDebugOut("fail to read camera.json"); + return; + } + QJsonObject objTmp = objJson["pool"].toObject(); + m_optPool.triggerMode = (emTpDeviceTriggerMode)objTmp.value("triggermode").toInt(0); + m_optPool.triggerDirection = (emTpTriggerDirection)objTmp.value("triggerdirection").toInt(0); + m_optPool.frameRate = objTmp.value("framerate").toInt(0); + m_optPool.fixedFrames = objTmp.value("fixedframes").toInt(0); + // + QJsonValue value = objJson["devices"]; + + QJsonObject userJson = getAppJsonUser(); + QJsonArray objEnableCameras = userJson["enable_cameras"].toArray(); + QStringList enableCamList; + for (int i = 0; i < objEnableCameras.size(); ++i) + { + enableCamList.append(objEnableCameras[i].toString()); + } + if (!value.isUndefined() && value.isObject()) + { + QJsonObject objDevices = value.toObject(); + for (QJsonObject::Iterator it = objDevices.begin(); it != objDevices.end(); ++it) + { + tpDebugOut("Camera key:%s", it.key().toLocal8Bit().data()); + if (!it.value().isObject() || + m_cameraesOption.contains(it.key())) + { + continue; + } + QJsonObject objCam = it.value().toObject(); + TP_CAMERA_OPTION* pCamOpt = new TP_CAMERA_OPTION; + if (NULL == pCamOpt) + { + continue; + } + //inner + pCamOpt->bIgnoreImage = false; + pCamOpt->pSerials = &m_serialsList; + // + pCamOpt->uniqueName = it.key(); + pCamOpt->id = objCam.value("id").toInt(0); + pCamOpt->deviceType = objCam.value("type").toInt(0); + pCamOpt->showName = objCam.value("showname").toString(""); + pCamOpt->width = objCam.value("width").toInt(0); + pCamOpt->height = objCam.value("height").toInt(0); + QJsonValue tempV = objCam.value("format"); + if (tempV.isString()) + { + pCamOpt->format = CZkCameraImage::FromString(tempV.toString()); + } + else + { + pCamOpt->format = TP_COLOR_NONE; + } + pCamOpt->zoom = objCam.value("zoom").toInt(0); + + pCamOpt->folder = objCam.value("folder").toString(".\\"); + ////check if folder valid + //QDir camDir(pCamOpt->folder); + //if (!camDir.exists()) + //{ + // // maybe a relative path, try add app path + // QString strCamFolder = QString(m_pGlobalData->tgdMainPath) + "/" + pCamOpt->folder; + // camDir.setPath(strCamFolder); + // if (camDir.exists()) + // { + // pCamOpt->folder = camDir.absolutePath() + "/"; + // } + //} + + pCamOpt->save = objCam.value("save").toInt(0); + + pCamOpt->saveImgSuffix = objCam.value("save_img_suffix").toString(".BMP"); + if (!TP_FUNCS::isValidImgSuffix(pCamOpt->saveImgSuffix)) + { + pCamOpt->saveImgSuffix = ".BMP"; + } + + pCamOpt->algorithm = objCam.value("algorithm").toInt(pCamOpt->id); + pCamOpt->relyOnCamera = objCam.value("rely_on_camera").toString(); + pCamOpt->exposure = objCam.value("exposure").toInt(0); + pCamOpt->boardType = objCam.value("board_type").toInt(64); + pCamOpt->boardNum = objCam.value("board_num").toInt(0); + pCamOpt->sBoardName = objCam.value("board_name").toString(); + pCamOpt->sBoardConfigFile = objCam.value("board_config_file").toString(); + pCamOpt->gain = objCam.value("gain").toInt(0); + pCamOpt->loop = objCam.value("loop").toInt(0); + pCamOpt->bNeedRgbSwapped = objCam.value("savefile_rgb_swapped").toInt(0); + pCamOpt->macAddress = objCam.value("mac_address").toString(); + pCamOpt->bAutoPush = objCam.value("auto_push").toInt(1); + pCamOpt->bAutoOpen = objCam.value("auto_open").toBool(true); + if (!enableCamList.contains(it.key())){ + pCamOpt->bAutoOpen = false; + } + + pCamOpt->cameraFilePath = objCam.value("camera_file").toString(); + if (objCam.value("algorithm_dll").toDouble()) + { + pCamOpt->dllsuffix = QString::number(objCam.value("algorithm_dll").toInt(pCamOpt->algorithm)); + } + else + { + pCamOpt->dllsuffix = objCam.value("algorithm_dll").toString(QString::number(pCamOpt->algorithm)); + } + //m_cfgCameraes[pCamOpt->uniqueName] = pCamOpt; + checkFolder(pCamOpt->folder); + pCamOpt->status = TP_CAMERA_STATUS_UNINIT; + m_cameraesOption.insert(pCamOpt->uniqueName, pCamOpt); + } + } + objTmp = objJson["windows"].toObject(); + if (!objTmp.isEmpty()) + { + for (QJsonObject::Iterator it = objTmp.begin(); it != objTmp.end(); ++it) + { + QJsonObject objWin = it.value().toObject(); + if (objWin.isEmpty()) + { + continue; + } + TP_CAMERA_WIN* pWin = new TP_CAMERA_WIN; + if (NULL == pWin) + { + continue; + } + pWin->key = it.key(); + pWin->device = objWin.value("device").toString(); + pWin->images = objWin.value("images").toInt(1); + pWin->buffers = objWin.value("buffers").toInt(2); + pWin->bitsPerPixel = objWin.value("bitsperpixel").toInt(32); + pWin->index = objWin.value("index").toInt(0); + pWin->bufferW = objWin.value("buffer_w").toInt(0); + pWin->bufferH = objWin.value("buffer_h").toInt(0); + pWin->mirror = objWin.value("mirror").toInt(0); + pWin->bCacheOriginImage = objWin.value("cache_origin").toBool(); + pWin->bNeedRgbSwapped = objWin.value("rgb_swapped").toBool(); + m_cameraWins.append(pWin); + } + } +} + +void CGlobalCamera::checkFolder(QString& sFolder) +{ + if (sFolder.startsWith(".\\")) + { + sFolder.replace(".\\", m_pGlobalData->tgdMainPath); + } + if (!sFolder.endsWith('\\')) + { + sFolder.append('\\'); + } +} + +void CGlobalCamera::releaseConfig(const QString& key /* = NULL */) +{ + m_cameraesOption.clear(releaseCameraOption); + m_cameraWins.clear(releaseCameraWin, NULL); +} + +void CGlobalCamera::releaseCameraOption(const QString& key, TP_CAMERA_OPTION*& pCamOpt, void* pData) +{ + delete pCamOpt; +} + +void CGlobalCamera::releaseCameraWin(tagTP_CAMERA_WIN*& pWin, void* pData) +{ + delete pWin; +} + +QList CGlobalCamera::CameraKeysOrderByIds() +{ + QList options = m_cameraesOption.values(); + qSort(options.begin(), options.end(), optionCompare); + QList strList; + for (QList::Iterator it = options.begin(); it != options.end(); ++it) + { + strList.append((*it)->uniqueName); + } + return strList; +} + +bool CGlobalCamera::optionCompare(TP_CAMERA_OPTION* op1, TP_CAMERA_OPTION* op2) +{ + return (op1->id < op2->id); +} + +QMap CGlobalCamera::GetCamShowNames() +{ + QMap knMap; + for (QZkMutexMap::iterator it = m_cameraesOption.begin(); it != m_cameraesOption.end(); ++it) + { + knMap.insert(it.key(), it.value()->showName); + } + //for (QZkMutexList::Iterator it = m_cameraWins.begin(); it != m_cameraWins.end(); ++it) + //{ + // if (0 == (*it)->index && m_cameraesOption.contains((*it)->device)) + // { + // knMap.insert((*it)->device, (*it)->key); + // } + //} + return knMap; +} + +void CGlobalCamera::readSerials() +{ +#ifdef _USE_CAMERA_SERIAL_ENCRYPTO + QString szFileName(m_pGlobalData->tgdMainPath); + szFileName.append("serials.dat"); + QZkSerialNumbers serials; + QList bytes = serials.AddFileName(szFileName); + for (QList::iterator it = bytes.begin(); it != bytes.end(); ++it) + { + m_serialsList.append(QString::fromUtf8(*it)); + } +#endif +} + +void CGlobalCamera::delCameraOption(const QString& serials) +{ + QMutexLocker locker(&m_mtxCam); + + TP_CAMERA_OPTION* pcamOpt = m_cameraesOption.value(serials); + if (pcamOpt){ + m_cameraesOption.remove(serials); + delete pcamOpt; + pcamOpt = NULL; + } +} + +void CGlobalCamera::updateCameraOption(const QString& serials, const TP_CAMERA_OPTION& camOpt) +{ + QMutexLocker locker(&m_mtxCam); + + // update if exist, otherwise new one. + if (m_cameraesOption.contains(serials)) + { + TP_CAMERA_OPTION* pCamOpt = m_cameraesOption.value(serials); + emTpCameraStatus oldStatus = pCamOpt->status; + *pCamOpt = camOpt; + pCamOpt->status = oldStatus; + pCamOpt->pSerials = &m_serialsList; + return; + } + + TP_CAMERA_OPTION* pCamOpt = new TP_CAMERA_OPTION; + if (NULL == pCamOpt) return; + + *pCamOpt = camOpt; + pCamOpt->pSerials = &m_serialsList; + m_cameraesOption.insert(pCamOpt->uniqueName, pCamOpt); +} + +QList CGlobalCamera::camWinKeys() +{ + QMutexLocker locker(&m_mtxCam); + + QList ret; + QZkMutexList::const_iterator it = m_cameraWins.constBegin(); + for (; it != m_cameraWins.constEnd(); ++it) + { + ret.append((*it)->key); + } + return ret; +} + +bool CGlobalCamera::camWinByKey(const QString& strKey, TP_CAMERA_WIN& camwinOpt) +{ + QMutexLocker locker(&m_mtxCam); + QZkMutexList::const_iterator it = m_cameraWins.constBegin(); + for (; it != m_cameraWins.constEnd(); ++it) + { + TP_CAMERA_WIN* pOpt = *it; + if (pOpt->key.compare(strKey, Qt::CaseInsensitive) == 0){ + camwinOpt = *pOpt; + return true; + } + } + return false; +} + +bool CGlobalCamera::addCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew) +{ + QMutexLocker locker(&m_mtxCam); + + // update if exist, otherwise new one. + QZkMutexList::iterator it = m_cameraWins.begin(); + for (; it != m_cameraWins.end(); ++it) + { + TP_CAMERA_WIN* pOpt = *it; + if (pOpt->key.compare(strKey, Qt::CaseInsensitive) == 0){ + *pOpt = camwinOpt; + return true; + } + } + + TP_CAMERA_WIN* pOpt = new TP_CAMERA_WIN; + if (NULL == pOpt) return false; + + *pOpt = camwinOpt; + m_cameraWins.append(pOpt); + + return true; +} + +bool CGlobalCamera::delCamWin(const QString& strKey) +{ + QMutexLocker locker(&m_mtxCam); + QZkMutexList::iterator it = m_cameraWins.begin(); + for (; it != m_cameraWins.end(); ++it) + { + TP_CAMERA_WIN* pOpt = *it; + if (pOpt->key.compare(strKey, Qt::CaseInsensitive) == 0){ + m_cameraWins.removeOne(pOpt); + delete pOpt; + pOpt = NULL; + return true; + } + } + return true; +} + +QJsonObject CGlobalCamera::getAppJsonUser() +{ + QJsonObject userJson; + + QString strUiPath(m_pGlobalData->tgdMainPath); + strUiPath.append(m_pGlobalData->tgdFolderNames[TP_FOLDER_NAME_UI]); + + QString strAppJsonPath(strUiPath); + strAppJsonPath.append(QCoreApplication::applicationName()); +#ifdef _DEBUG + if (strAppJsonPath.endsWith('d')) + { + strAppJsonPath.chop(1); + } +#endif + strAppJsonPath.append(".json"); + if (!QFile::exists(strAppJsonPath)) + { + strAppJsonPath = strUiPath + "app.json"; + } + + int nerr; + QJsonObject objJson = QZkJsonParser::ReadJsonObject(strAppJsonPath, &nerr); + + userJson = objJson["user"].toObject(); + return userJson; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpCamera/globalCamera.h b/src/lpCoreCtrl/tpCamera/globalCamera.h new file mode 100644 index 0000000..6effc86 --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/globalCamera.h @@ -0,0 +1,97 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:globalCamera.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/07 + History:7:4:2015 8:53 + *******************************************************************************/ +#ifndef __GLOBAL_CAMERA_H_20150407 +#define __GLOBAL_CAMERA_H_20150407 + +#include "icamera.h" +#include "iCameraObject.h" +#include "QZkJsonParser.h" +#include +#include +#include +#include "QZkMutexMap.h" +#include "QZkMutexList.h" + + + +class CGlobalCamera +{ +public: + CGlobalCamera(const CAMERA_IN_PARAM* pInParam); + ~CGlobalCamera(); + bool CameraOption(const QString& serials, TP_CAMERA_OPTION& camOpt); + bool CameraOptionByKey(const QString& sKey, TP_CAMERA_OPTION& camOpt); + bool SetCameraOption(const QString& sKey, const TP_CAMERA_OPTION& camOpt); + TP_CAMERA_OPTION* CameraOption(const QString& serials) { + return m_cameraesOption.value(serials, NULL); + } + QList CameraKeys() { + return m_cameraesOption.keys(); + } + QList CameraKeysOrderByIds(); + QList CameraWins() { + return m_cameraWins; + } + QMap GetCamShowNames(); + bool SaveCameraOption(const TP_CAMERA_OPTION& camOpt); + void delCameraOption(const QString& serials); + void updateCameraOption(const QString& serials, const TP_CAMERA_OPTION& camOpt); + + QList camWinKeys(); + bool camWinByKey(const QString& strKey, TP_CAMERA_WIN& camwinOpt); + bool addCamWin(const QString& strKey, const TP_CAMERA_WIN& camwinOpt, bool bNew); + bool delCamWin(const QString& strKey); + +public: + TP_GLOBAL_DATA* m_pGlobalData; + ICameraCallback* m_pCallback; +// QHash m_cfgCameraes; + TP_CAMERAPOOL_OPTION m_optPool; +private: + void readConfig(); + void releaseConfig(const QString& key = NULL); + void checkFolder(QString& sFolder); + void readSerials(); + QJsonObject getAppJsonUser(); + + static void releaseCameraOption(const QString& key, TP_CAMERA_OPTION*& pCamOpt, void* pData); + static bool copyCameraOptionBySerial(const QString& key, TP_CAMERA_OPTION*& pCamOpt, void* pData); + static void copyCameraOptionByKey(const QString& key, TP_CAMERA_OPTION*& pCamOpt, void* pData); + static bool optionCompare(TP_CAMERA_OPTION* op1, TP_CAMERA_OPTION* op2); + static void releaseCameraWin(tagTP_CAMERA_WIN*& pWin, void* pData); +private: + QMutex m_mtxCam; + const static char cs_szCfgName[]; + QZkMutexMap m_cameraesOption; + QZkMutexList m_cameraWins; + QJsonObject m_jsonCamera; + QStringList m_serialsList; +}; +//const char CGlobalCamera::m_szCfgFile[] = "camera"; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +//***inline functions*** + + + +extern class CGlobalCamera* gpGlobalCameraZZZ; + +#define gpGlobalCamera gpGlobalCameraZZZ + +//#define gpMainPath gpGlobalCamera->m_pszMainPath +#define gpGlobalData gpGlobalCamera->m_pGlobalData +#define gpGlobalCamCallback gpGlobalCamera->m_pCallback +#define callCamCallback(fc, defaultReturn, ...) (NULL != gpGlobalCamCallback) ? gpGlobalCamCallback->##fc##(__VA_ARGS__) : defaultReturn +// #define callGlobalCamera(fc, dValue, ...) (NULL != gpGlobalCamera ) ? gpGlobalCamera->##fc##(__VA_ARGS__) : dValue +// #define callGlobalCameraNoP(fc, dValue) (NULL != gpGlobalCamera ) ? gpGlobalCamera->##fc##() : dValue +//#define gpCameraesHash() (NULL != gpGlobalCamera) ? gpGlobalCamera->m_cfgCameraes : QHash() +#define gGlobalPoolOption gpGlobalCamera->m_optPool + +#endif \ No newline at end of file diff --git a/src/lpCoreCtrl/tpCamera/tpCamera.cpp b/src/lpCoreCtrl/tpCamera/tpCamera.cpp new file mode 100644 index 0000000..23d6822 --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/tpCamera.cpp @@ -0,0 +1,60 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:tpCamera.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/23 + History:23:3:2015 16:31 + *******************************************************************************/ +#include "CameraPool.h" +#include "globalCamera.h" + +#ifdef TPCAMERA_EXPORTS +#define TPCAMERA_API extern "C" __declspec(dllexport) +#else +#define TPCAMERA_API extern "C" +#endif + +CGlobalCamera* gpGlobalCameraZZZ = NULL; +CCameraPool *gpCameraPool = NULL; + + +//don't change the names of these global functions +TPCAMERA_API void* Lib_Camera_Init(void* inParam) +{ + CAMERA_IN_PARAM cinParam = {0}; + if( NULL != inParam ) + { + std::memcpy(&cinParam, inParam, sizeof(CAMERA_IN_PARAM)); + } + if( NULL == gpGlobalCamera ) + { + gpGlobalCamera = new CGlobalCamera(&cinParam); + } + // + if( NULL == gpCameraPool ) + { + gpCameraPool = new CCameraPool(); + } + if( NULL != gpCameraPool ) + { + gpCameraPool->InitPool(); + } + return gpCameraPool; +} + +TPCAMERA_API void Lib_Camera_Free() +{ + if( NULL != gpCameraPool ) + { + gpCameraPool->FreePool(); + delete gpCameraPool; + gpCameraPool = NULL; + } + if( NULL != gpGlobalCamera ) + { + delete gpGlobalCamera; + gpGlobalCamera = NULL; + } +} diff --git a/src/lpCoreCtrl/tpCamera/triggerthread.cpp b/src/lpCoreCtrl/tpCamera/triggerthread.cpp new file mode 100644 index 0000000..ba61f0f --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/triggerthread.cpp @@ -0,0 +1,61 @@ +#include "triggerthread.h" +#include "CameraPool.h" +#include "globalCamera.h" + +CTriggerThread::CTriggerThread(class CCameraPool* pPool, QObject *parent /* = NULL */) + : QTpThreadBase(parent) + , m_pPool(pPool) +{ + m_msecOfInterval = 0; + m_nTriggerCount = 0; + m_bAutoTrigger = false; +} + +CTriggerThread::~CTriggerThread() +{ + EndTrigger(); +} + +void CTriggerThread::SetFrame(long nFrameRate) +{ + if( nFrameRate <= 0 ) + { + m_msecOfInterval = 0; + } + else + { + m_msecOfInterval = 1000 / nFrameRate; + } +} +void CTriggerThread::StartTrigger() +{ + EndThread(); + m_LastTriggerTime = QTime::currentTime(); + StartThread(); +} + +void CTriggerThread::EndTrigger() +{ + EndThread(); +} + +bool CTriggerThread::loop() +{ + if( m_bAutoTrigger && m_pPool->ImagesSize() < 32) + { + QMutexLocker locker(&m_mutexWorked); + QTime nowTime = QTime::currentTime(); + int nIntervals = m_LastTriggerTime.msecsTo(nowTime); + if (nIntervals >= m_msecOfInterval) + { + CCameraPool::tagCOMMIT_TRIGGER_PARAM prm; + prm.direct = gGlobalPoolOption.triggerDirection; + prm.count = ++m_nTriggerCount; + prm.bFromMenual = false; + m_pPool->IntervalCaptureImages(prm); + m_LastTriggerTime = nowTime; + } + } + msleep(5); + return true; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpCamera/triggerthread.h b/src/lpCoreCtrl/tpCamera/triggerthread.h new file mode 100644 index 0000000..a6b4768 --- /dev/null +++ b/src/lpCoreCtrl/tpCamera/triggerthread.h @@ -0,0 +1,37 @@ +#ifndef TRIGGERTHREAD_H +#define TRIGGERTHREAD_H + +#include "qtpthreadbase.h" +#include +#include +#include "globalCamera.h" + +class CTriggerThread : public QTpThreadBase +{ +public: + CTriggerThread(class CCameraPool* pPool, QObject *parent = NULL); + ~CTriggerThread(); + void SetFrame(long nFrameRate); + void StartTrigger(); + void EndTrigger(); + void LockWorked() { + m_mutexWorked.lock(); + } + void UnlockWorked() { + m_mutexWorked.unlock(); + } + void SetAutoTrigger(bool bAuto) { + m_bAutoTrigger = bAuto; + } +private: + virtual bool loop(); +private: + class CCameraPool* m_pPool; + DWORD m_msecOfInterval; + QTime m_LastTriggerTime; + DWORD m_nTriggerCount; + QMutex m_mutexWorked; + bool m_bAutoTrigger; +}; + +#endif // TRIGGERTHREAD_H diff --git a/src/lpCoreCtrl/tpCoreCtrl.cpp b/src/lpCoreCtrl/tpCoreCtrl.cpp new file mode 100644 index 0000000..d415292 --- /dev/null +++ b/src/lpCoreCtrl/tpCoreCtrl.cpp @@ -0,0 +1,75 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:tpCoreCtrl.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/24 + History:24:3:2015 12:46 + *******************************************************************************/ +#include "CoreCtrl.h" + +#ifdef TPCORECTRL_EXPORTS +#define TPCORECTRL_API extern "C" __declspec(dllexport) +#else +#define TPCORECTRL_API extern "C" +#endif + +#include "ModulesManager.h" +#include "globalCoreCtrl.h" +#include + +CModulesManager gMdlesMgr; +CGlobalCoreCtrl* gpData = NULL; + +class CCoreCtrl* gpCoreCtrl = NULL; + +TPCORECTRL_API void* Lib_CoreCtrl_Init(void* inParam) +{ + CORE_CTRL_LOG("Lib_CoreCtrl_Init start!"); + //create and initialize the global data(class CGlobalCoreCtrl's object) + CORE_CTRL_IN_PARAM* pCciParam = (CORE_CTRL_IN_PARAM*)inParam; + gpData = new CGlobalCoreCtrl(pCciParam); + if( NULL == gpData ) + { + return NULL; + } + // + CORE_CTRL_INIT(gpGlobalData->tgdMainPath, gpGlobalData->tgdMainPath); + // + if( gMdlesMgr.LoadModules() <= 0 ) + { + return NULL; + } + ////////////////////////////////////////////////////////////////// + if( NULL == gpCoreCtrl ) + { + gpCoreCtrl = new CCoreCtrl(); + } + /* + if( NULL != gpCoreCtrl ) + { + gpCoreCtrl->IInitCore(); + } + */ + CORE_CTRL_LOG("Lib_CoreCtrl_Init finished!"); + return gpCoreCtrl; +} + +TPCORECTRL_API void Lib_CoreCtrl_Free() +{ + if( NULL != gpCoreCtrl ) + { + gpCoreCtrl->IFreeCore(); + delete gpCoreCtrl; + gpCoreCtrl = NULL; + } + ///////////////////////////////////////////////// + gMdlesMgr.FreeModules(); + CORE_CTRL_FREE(); + if( NULL != gpData ) + { + delete gpData; + gpData = NULL; + } +} diff --git a/src/lpCoreCtrl/tpImgProc/AlgorithmOption.cpp b/src/lpCoreCtrl/tpImgProc/AlgorithmOption.cpp new file mode 100644 index 0000000..5329c92 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/AlgorithmOption.cpp @@ -0,0 +1,178 @@ +#include "AlgorithmOption.h" +#include "baseConstant.h" + +const char CAlgorithmOption::cs_szSuffix[] = ".dat"; + +CAlgorithmOption::CAlgorithmOption(const QString& strFile, int nAlgorithm, QSqliteAlgorithm* pDbAlg) + : m_pDbAlg(pDbAlg) +{ + m_fileName = strFile; + m_nAlgorithm = nAlgorithm; + QString szFileName = strFile + "_" + QString::number(nAlgorithm); + szFileName.append(TP_STR::cszJsonSuffix).append(cs_szSuffix); + int error = -1; + m_objJson = QZkJsonParser::ReadJsonObject(szFileName, &error); + if (0 != error) + { + szFileName = strFile + "_" + QString::number(nAlgorithm); + szFileName.append(TP_STR::cszJsonSuffix); + m_objJson = QZkJsonParser::ReadJsonObject(szFileName, &error); + } + m_bNeedSave = false; + m_nAlgorithmsUsings = 0; + m_pDbAlg->CreateTable(nAlgorithm); +} + +CAlgorithmOption::~CAlgorithmOption() +{ + +} + +int CAlgorithmOption::GetIntValue(const QString& skey, int nDefault) +{ + QJsonValue v = m_objJson.value(skey); + if (v.isUndefined() || !v.isDouble()) + { + Insert(skey, QJsonValue(nDefault)); + return nDefault; + } + else + { + return v.toInt(nDefault); + } +} + +void CAlgorithmOption::SetIntValue(const QString& skey, int value) +{ + Insert(skey, QJsonValue(value)); +} + +QString CAlgorithmOption::GetStringValue(const QString& skey, const QString& default/* = ""*/) +{ + QJsonValue v = m_objJson.value(skey); + if (v.isUndefined() || !v.isString()) + { + Insert(skey, QJsonValue(default)); + return default; + } + else + { + return v.toString(); + } +} + +void CAlgorithmOption::SetStringValue(const QString& skey, const QString& value) +{ + Insert(skey, QJsonValue(value)); +} + +QJsonObject CAlgorithmOption::GetObjectValue(const QString& skey) +{ + QJsonValue v = m_objJson.value(skey); + if (v.isUndefined() || !v.isString()) + { + return QJsonObject(); + } + else + { + return v.toObject(); + } +} + +bool CAlgorithmOption::Save(bool bWait/* = true*/) +{ + if (!m_bNeedSave) + { + return true; + } + if (!bWait) + { + if (!m_wLock.tryLockForWrite()) + { + return false; + } + } + else + { + m_wLock.lockForWrite(); + } + //if (!m_bNeedSave || !m_wLock.tryLockForWrite()) + //{ + // return; + //} + QString szFileName = m_fileName + "_" + QString::number(m_nAlgorithm); + szFileName.append(TP_STR::cszJsonSuffix).append(cs_szSuffix); + bool b = QZkJsonParser::WriteJsonObject(szFileName, m_objJson); + m_bNeedSave = !b; + m_wLock.unlock(); + return b; +} + +QVariantMap CAlgorithmOption::VariantMap() +{ + return m_objJson.toVariantMap(); +} + +bool CAlgorithmOption::DbSetValue(const QString& key, int type, const QBuffer& data) +{ + if (NULL == m_pDbAlg) + { + return false; + } + return m_pDbAlg->ReplaceRecord(m_nAlgorithm, key, type, data.buffer()); +} + +bool CAlgorithmOption::DbSetValue(const QString& key, int type, const QByteArray& data) +{ + if (NULL == m_pDbAlg) + { + return false; + } + return m_pDbAlg->ReplaceRecord(m_nAlgorithm, key, type, data); +} + +bool CAlgorithmOption::DbDelValue(const QString& key) +{ + if (NULL == m_pDbAlg) + { + return false; + } + return m_pDbAlg->DeleteRecord(m_nAlgorithm, key); +} + +bool CAlgorithmOption::DbGetValue(const QString& key, int type, QByteArray& data) +{ + if (NULL == m_pDbAlg) + { + return false; + } + return m_pDbAlg->ReadRecord(m_nAlgorithm, key, type, data); + //m_pDbAlg->ReplaceRecord(m_nAlgorithm, key, type, data); +} + +QStringList CAlgorithmOption::DbGetKeysByType(int type) +{ + if (NULL == m_pDbAlg) + { + return QStringList(); + } + return m_pDbAlg->KeysByType(m_nAlgorithm, type); +} + +QVariant CAlgorithmOption::GetValue(const QString& skey, const QVariant& def) +{ + QJsonValue v = m_objJson.value(skey); + if (v.isUndefined()) + { + Insert(skey, QJsonValue::fromVariant(def)); + return def; + } + else + { + return v.toVariant(); + } +} +void CAlgorithmOption::SetValue(const QString& skey, const QVariant& value) +{ + Insert(skey, QJsonValue::fromVariant(value)); +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpImgProc/AlgorithmOption.h b/src/lpCoreCtrl/tpImgProc/AlgorithmOption.h new file mode 100644 index 0000000..b3e1003 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/AlgorithmOption.h @@ -0,0 +1,47 @@ +#ifndef CALGORITHMOPTION_H +#define CALGORITHMOPTION_H + +#include "iAlgorithm.h" +#include +#include "QZkJsonParser.h" +#include +#include "QSqliteAlgorithm.h" + +class CAlgorithmOption : public IAlgorithmOption +{ +public: + CAlgorithmOption(const QString& strFile, int nAlgorithm, QSqliteAlgorithm* pDbAlg); + ~CAlgorithmOption(); + virtual int GetIntValue(const QString& skey, int nDefault); + virtual void SetIntValue(const QString& skey, int value); + virtual QString GetStringValue(const QString& skey, const QString& default = ""); + virtual void SetStringValue(const QString& skey, const QString& value); + virtual QJsonObject GetObjectValue(const QString& skey); + virtual bool Save(bool bWait = true); + virtual QVariantMap VariantMap(); + virtual bool DbSetValue(const QString& key, int type, const QBuffer& data); + virtual bool DbSetValue(const QString& key, int type, const QByteArray& data); + virtual bool DbDelValue(const QString& key); + virtual bool DbGetValue(const QString& key, int type, QByteArray& data); + virtual QStringList DbGetKeysByType(int type); + virtual QVariant GetValue(const QString& skey, const QVariant& def); + virtual void SetValue(const QString& skey, const QVariant& value); +private: + void Insert(const QString& skey, const QJsonValue& v) { + QWriteLocker locker(&m_wLock); + m_objJson.insert(skey, v); + m_bNeedSave = true; + tpDebugOut("Insert value in algorithm_%d.json;key=%s, value=%s", m_nAlgorithm, skey.toLocal8Bit().data(), v.toString("NULL").toLocal8Bit().data()); + } +private: + QJsonObject m_objJson; + int m_nAlgorithm; + QString m_fileName; + int m_nAlgorithmsUsings; + bool m_bNeedSave; + QReadWriteLock m_wLock; + const static char cs_szSuffix[]; + QSqliteAlgorithm* m_pDbAlg; +}; + +#endif // CALGORITHMOPTION_H diff --git a/src/lpCoreCtrl/tpImgProc/IImageAlgorithm.cpp b/src/lpCoreCtrl/tpImgProc/IImageAlgorithm.cpp new file mode 100644 index 0000000..987e906 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/IImageAlgorithm.cpp @@ -0,0 +1,28 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:IImageAlgorithm.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/22 + History:22:4:2015 13:34 + *******************************************************************************/ +#include "IImageAlgorithm.h" +#include "ImgProc.h" + +IImageAlgorithm::IImageAlgorithm(class CImgProc* pImgProc) + : m_pImgProc(pImgProc) +{ + +} + +IImageAlgorithm::~IImageAlgorithm() +{ + +} + +int IImageAlgorithm::ImageAnalysis(class IImageObject* pImgObj, TP_ALGORITHM_OPTION* pOpt) +{ + int nRet = IaImageAnalysis(pImgObj, pOpt); + return nRet; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpImgProc/IImageAlgorithm.h b/src/lpCoreCtrl/tpImgProc/IImageAlgorithm.h new file mode 100644 index 0000000..6c13bcc --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/IImageAlgorithm.h @@ -0,0 +1,30 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:IImageAlgorithm.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/22 + History:22:4:2015 13:34 + *******************************************************************************/ +#ifndef IIMAGEALGORITHM_H +#define IIMAGEALGORITHM_H + +#include "iImgProc.h" + +class IImageAlgorithm +{ +public: + IImageAlgorithm(class CImgProc* pImgProc); + virtual ~IImageAlgorithm(); + + virtual int ImageAnalysis(class IImageObject* pImgObj, TP_ALGORITHM_OPTION* pOpt); +protected: +private: + virtual int IaImageAnalysis(class IImageObject* pImgObj, const TP_ALGORITHM_OPTION* pAlgOpt) = 0; +private: + class CImgProc* m_pImgProc; +}; + + +#endif // IIMAGEALGORITHM_H diff --git a/src/lpCoreCtrl/tpImgProc/ImageAlgorithm.cpp b/src/lpCoreCtrl/tpImgProc/ImageAlgorithm.cpp new file mode 100644 index 0000000..35f36b1 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/ImageAlgorithm.cpp @@ -0,0 +1,23 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ImageAlgorithm.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/21 + History:21:4:2015 9:58 + *******************************************************************************/ +#include "ImageAlgorithm.h" + +// CImageAlgorithm::CImageAlgorithm(class CImgProc* pImgProc) +// : IImageAlgorithm(pImgProc) +// , m_pThis(this) +// { +// m_pSelfData = new tagIMAGE_DATA;//NULL; +// std::memset(m_pSelfData, 0, sizeof(tagIMAGE_DATA)); +// } +// +// CImageAlgorithm::~CImageAlgorithm() +// { +// delete m_pSelfData; +// } diff --git a/src/lpCoreCtrl/tpImgProc/ImageAlgorithm.h b/src/lpCoreCtrl/tpImgProc/ImageAlgorithm.h new file mode 100644 index 0000000..787aa39 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/ImageAlgorithm.h @@ -0,0 +1,34 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ImageAlgorithm.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/04/21 + History:21:4:2015 9:58 + *******************************************************************************/ +#ifndef IMAGEALGORITHM_H +#define IMAGEALGORITHM_H + +#include "baseDefine.h" +#include "baseStruct.h" +#include "iImgProc.h" + +#include "IImageAlgorithm.h" + +#define THIS (class IImageAlgorithm*) + + +class CImageAlgorithm : public IImageAlgorithm +{ +public: + CImageAlgorithm(class CImgProc* pImgProc); + virtual ~CImageAlgorithm(); + +private: + virtual int IaImageAnalysis(class IImageObject* pImgObj, const TP_ALGORITHM_OPTION* pAlgOpt); + void* m_pSelfData; + class IImageAlgorithm* m_pThis; +}; + +#endif // IMAGEALGORITHM_H diff --git a/src/lpCoreCtrl/tpImgProc/ImgProc.cpp b/src/lpCoreCtrl/tpImgProc/ImgProc.cpp new file mode 100644 index 0000000..d81ce34 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/ImgProc.cpp @@ -0,0 +1,217 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ImgProc.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 18:03 + *******************************************************************************/ +#include "ImgProc.h" +#include "baseConstant.h" +#include +#include +#include "QZkJsonParser.h" +#include "zfunctions.h" + +/////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +const char CImgProc::cs_strCfgName[] = "algorithm"; +TP_GLOBAL_DATA* CImgProc::s_pGlobalData = NULL; + +CImgProc::CImgProc(IMGPROC_IN_PARAM* pParam) + : m_dbAlg(pParam->pGlobalData->tgdUserPath) +{ + s_pGlobalData = pParam->pGlobalData; + m_bIsInited = false; + m_nAlgorithmsUsings = 0; + m_pDefAlgorithmDll = NULL; +} + +CImgProc::~CImgProc(void) +{ + FreeProc(); +} + +int CImgProc::InitProc() +{ + m_pDefAlgorithmDll = createAlgorithmDll("0", NULL); + //if (NULL == m_pDefAlgorithmDll) + //{ + // return 0; + //} + m_bIsInited = true; + return 1; +} + +void CImgProc::FreeProc() +{ + m_bIsInited = false; + while( m_nAlgorithmsUsings > 0 ) + { + QThread::msleep(25); + } + // m_algorithms.clear(releaseAlgorithm); + m_threadAlgorithms.clear(releaseAlgorithmApis); + m_algorithmOptions.clear(releaseAlgorithmOption); + m_algorithmDlls.clear(releaseAlgorithmDlls); + if (NULL != m_pDefAlgorithmDll) + { + delete m_pDefAlgorithmDll; + m_pDefAlgorithmDll = NULL; + } +} + +void CImgProc::releaseAlgorithm(const Qt::HANDLE& k, IAlgorithm*& v, void* pData) +{ + //Algorithm_Delete(v); + //FREE_ALGORITHM(v); +} + +void CImgProc::releaseAlgorithmOption(const int& k, CAlgorithmOption*& v, void* pData) +{ + delete v; +} + +CAlgorithmOption* CImgProc::createAlgorithmOption(const int& k, void* pData) +{ + CImgProc* pThis = (CImgProc*)pData; + QString szFileName(s_pGlobalData->tgdMainPath); + szFileName.append(s_pGlobalData->tgdFolderNames[TP_FOLDER_NAME_CONFIG]).append(cs_strCfgName); + CAlgorithmOption* pv = new CAlgorithmOption(szFileName, k, &pThis->m_dbAlg); + return pv; +} + +int CImgProc::IImageProcess(class IImageObject* pImgObj, class IDetectorEngine* pDE /*= NULL*/) +{ + if (NULL == pImgObj || !m_bIsInited) + { + return 0; + } + QZK::QAtomicIntAdder adder(m_nAlgorithmsUsings);//Img退出,删除AlgorithmOption有用,需要等所有的算法都用完了才可以删除 + //获取算法相关的参数 + TP_ALGORITHM_OPTION algOption; + algOption.algorithm = pImgObj->IGetAlgorithm();//获取算法数值,camera.json中的设置 + CAlgorithmOption* pAlgOpt = m_algorithmOptions.value2(algOption.algorithm, createAlgorithmOption, this);//列表中不包含相关参数就创建一个参数相关的Option对象 + algOption.pImagProcCb = pAlgOpt; + ///////////////////////////////////////////////////////////////////////// + Qt::HANDLE h = QThread::currentThreadId();//或取当前线程的ID + QString dllSuffix = QString::fromUtf8(pImgObj->IDllSuffix()); + // + tagALGORITHM_API* pApiNode = m_threadAlgorithms[h].value(dllSuffix);//获取当前线程的算法对象, + if (NULL == pApiNode)//若获取算法对象不成功,则需要创建一个 + { + CLoadAlgorithm* pDll = NULL; + if (0 == algOption.algorithm || (pDll = m_algorithmDlls.value2(dllSuffix, createAlgorithmDll, NULL)) == NULL)//根据算法库的后缀加载相应的tpAlgorithm_*.dll + { + pDll = m_pDefAlgorithmDll;//如果带后缀的算法库加载失败,则用默认的tpAlgorithm.dll算法库 + } + if (NULL == pDll) + { + //qWarning() << "Failed to " + return 0; + } + pApiNode = new tagALGORITHM_API; + if (NULL == pApiNode) + { + return 0; + } + pApiNode->pApi = pDll->m_fAlgCreata(pDE);//获取算法库中的类对象 + if (NULL == pApiNode->pApi) + { + delete pApiNode; + return 0; + } + pApiNode->pDll = pDll; + m_threadAlgorithms[h].insert(dllSuffix, pApiNode);//存储当前线程用到的类对象 + } + + //qWarning() << "ImageAnalysis Start: " << QDateTime::currentMSecsSinceEpoch() + // << " Image ID : " << pImgObj->IGetFrameNum() + // << " Thread ID : " << QThread::currentThreadId() + // << endl; + + int nRet = pApiNode->pApi->IImageAnalysis(pImgObj, &algOption, pDE); + try + { + // nRet = pApiNode->pApi->IImageAnalysis(pImgObj, &algOption, pDE); + } + catch (...) + { + qWarning() << "ERROR: Algorithm Crash!!! " + << "Image URL:" + << QString(pImgObj->IGetImageUtf8String()) + << endl; + QVariantMap vMap; + vMap.insert("ui_error", "ERROR: Algorithm Crash!!! Image URL:" + QString(pImgObj->IGetImageUtf8String())); + pImgObj->IVariantMapToUI(vMap); + } + + //qWarning() << "ImageAnalysis Over: " << QDateTime::currentMSecsSinceEpoch() + // << " Image ID : " << pImgObj->IGetFrameNum() + // << " Thread ID : " << QThread::currentThreadId() + // << endl; + + pAlgOpt->Save(); + return nRet; +} + +class IAlgorithmOption* CImgProc::IGetAlgorithmOption(int nAlgorithm) +{ + return m_algorithmOptions.value2(nAlgorithm, createAlgorithmOption, this); +} + +CLoadAlgorithm* CImgProc::createAlgorithmDll(const QString& k, void* pData) +{ + CLoadAlgorithm *pAlgDll = new CLoadAlgorithm(); + if (NULL == pAlgDll) + { + return NULL; + } + //TP_ALGORITHM_OPTION* pAlgOpt = (TP_ALGORITHM_OPTION*)pData; + QString dllFileName; +#ifdef _DEBUG + dllFileName.append("tpAlgorithmd"); +#else + dllFileName.append("tpAlgorithm"); +#endif + if (k != "0") + { + dllFileName.append("_").append(k); + } + QString dllPath(s_pGlobalData->tgdDllPath); + if (!ZKF::qtDllExit(dllPath, dllFileName)) + { + dllPath = s_pGlobalData->tgdMainPath; + } + dllFileName = dllPath + dllFileName; + if (!pAlgDll->LoadAlg(dllFileName)) + { + delete pAlgDll; + return NULL; + } + return pAlgDll; +} + +void CImgProc::releaseAlgorithmApis(QMap& v, void* pData) +{ + for (QMap::Iterator it = v.begin(); it != v.end(); ++it) + { + tagALGORITHM_API* pNode = it.value(); + if (NULL != pNode) + { + if (NULL != pNode->pApi && NULL != pNode->pDll) + { + pNode->pDll->m_fAlgDelete(pNode->pApi); + } + delete pNode; + } + } + v.clear(); +} + +void CImgProc::releaseAlgorithmDlls(CLoadAlgorithm*& v, void* pData) +{ + delete v; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpImgProc/ImgProc.h b/src/lpCoreCtrl/tpImgProc/ImgProc.h new file mode 100644 index 0000000..5dfbce8 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/ImgProc.h @@ -0,0 +1,56 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:ImgProc.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 18:03 + *******************************************************************************/ +#include "iImgProc.h" +/*#include "ImageAlgorithm.h"*/ +#include "QZkMutexMap.h" +#include "baseClass.h" +#include +#include +#include "LoadAlgorithm.h" +#include "AlgorithmOption.h" +#include "LoadAlgorithm.h" + +class CImgProc : public IImgProc +{ +public: + CImgProc(IMGPROC_IN_PARAM* pParam); + virtual ~CImgProc(void); + + int InitProc(); + void FreeProc(); +private: + virtual int IImageProcess(class IImageObject* pImgObj, class IDetectorEngine* pDE = NULL); + virtual class IAlgorithmOption* IGetAlgorithmOption(int nAlgorithm); +private: + static CAlgorithmOption* createAlgorithmOption(const int& k, void* pData); + static void releaseAlgorithm(const Qt::HANDLE& k, class IAlgorithm*& v, void* pData); + static void releaseAlgorithmOption(const int& k, CAlgorithmOption*& v, void* pData); + //class IImageProc* m_pImgProc; +public: + static TP_GLOBAL_DATA* s_pGlobalData; + const static char cs_strCfgName[]; +private: + bool m_bIsInited; + QSqliteAlgorithm m_dbAlg; + //QZkMutexMap m_algorithms; + QAtomicInt m_nAlgorithmsUsings; + QZkMutexMap m_algorithmOptions; + //class CLoadAlgorithm* m_pLibAlgorithm; + QZkMutexMap m_algorithmDlls; + CLoadAlgorithm* m_pDefAlgorithmDll;//nAlgorithm = 0; tpAlgorithm.dll + struct tagALGORITHM_API{ + class IAlgorithm* pApi; + class CLoadAlgorithm* pDll; + }; + QZkMutexMap> m_threadAlgorithms; + static CLoadAlgorithm* createAlgorithmDll(const QString& k, void* pData); + static void releaseAlgorithmApis(QMap& v, void* pData); + static void releaseAlgorithmDlls(CLoadAlgorithm*& v, void* pData); +}; diff --git a/src/lpCoreCtrl/tpImgProc/LoadAlgorithm.cpp b/src/lpCoreCtrl/tpImgProc/LoadAlgorithm.cpp new file mode 100644 index 0000000..e2dcfc3 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/LoadAlgorithm.cpp @@ -0,0 +1,72 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:LoadAlgorithm.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/05/21 + History:21:5:2015 14:33 + *******************************************************************************/ +#include "LoadAlgorithm.h" +#include + +//CLoadAlgorithm::Func_Alg_Create CLoadAlgorithm::sf_AlgCreata = NULL; +//CLoadAlgorithm::Func_Alg_Delete CLoadAlgorithm::sf_AlgDelete = NULL; +//class QLibrary* CLoadAlgorithm::s_pLib = NULL; + +CLoadAlgorithm::CLoadAlgorithm() +{ + m_pLib = NULL; + m_fAlgCreata = NULL; + m_fAlgDelete = NULL; +} + +CLoadAlgorithm::~CLoadAlgorithm() +{ + FreeAlg(); +} + +bool CLoadAlgorithm::LoadAlg(const QString &szDllName) +{ +#ifdef _USE_ALGORITHM_STATIC + m_fAlgCreata = Algorithm_Create; + m_fAlgDelete = Algorithm_Delete; +#else + if (NULL == m_pLib) + { + m_pLib = new QLibrary(szDllName); + if (NULL == m_pLib) + { + return false; + } + } + if (!m_pLib->load()) + { + tpDebugOut("Failed to Load dll: %s", szDllName.toLocal8Bit().data()); + return false; + } + m_fAlgCreata = (Func_Alg_Create)m_pLib->resolve("Algorithm_Create"); + m_fAlgDelete = (Func_Alg_Delete)m_pLib->resolve("Algorithm_Delete"); +#endif + if (NULL == m_fAlgCreata || NULL == m_fAlgDelete) + { + tpDebugOut("Failed to get functions from %s", szDllName.toLocal8Bit().data()); + return false; + } + return true; +} + +void CLoadAlgorithm::FreeAlg() +{ + m_fAlgCreata = NULL; + m_fAlgDelete = NULL; +#ifdef _USE_ALGORITHM_STATIC + +#else + if (NULL != m_pLib) + { + delete m_pLib; + m_pLib = NULL; + } +#endif +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpImgProc/LoadAlgorithm.h b/src/lpCoreCtrl/tpImgProc/LoadAlgorithm.h new file mode 100644 index 0000000..9546ea3 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/LoadAlgorithm.h @@ -0,0 +1,35 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:LoadAlgorithm.h + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/05/21 + History:21:5:2015 14:33 + *******************************************************************************/ +#ifndef LOADALGORITHM_H +#define LOADALGORITHM_H + +#include "iAlgorithm.h" + +class CLoadAlgorithm +{ +public: + CLoadAlgorithm(); + ~CLoadAlgorithm(); +public: + bool LoadAlg(const QString &szDllName); + void FreeAlg(); +public: + typedef IAlgorithm* (*Func_Alg_Create)(class IDetectorEngine* pDE); + typedef void(*Func_Alg_Delete)(IAlgorithm*); + Func_Alg_Create m_fAlgCreata; + Func_Alg_Delete m_fAlgDelete; +private: + class QLibrary* m_pLib; +}; + +//#define ALLOC_ALGORITHM() (NULL == CLoadAlgorithm::sf_AlgCreata) ? NULL : CLoadAlgorithm::sf_AlgCreata() +//#define FREE_ALGORITHM(obj) if ( NULL != CLoadAlgorithm::sf_AlgDelete) CLoadAlgorithm::sf_AlgDelete(obj) + +#endif // LOADALGORITHM_H diff --git a/src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.cpp b/src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.cpp new file mode 100644 index 0000000..bfd73d3 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.cpp @@ -0,0 +1,95 @@ +#include "QSqliteAlgorithm.h" + +#define _TP_SQLITE_ALGORITHM_DB "algorithm.db" + +#define _TP_SQLITE_CRETAE_ALGORITHM \ +"CREATE TABLE IF NOT EXISTS e_algorithm_%1(alg_key VARCHAR(255) \ +, alg_type INT(32) \ +, alg_data BLOB \ +, alg_stamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP \ +, PRIMARY KEY(alg_key, alg_type))" + +#define _TP_SQLITE_REPLACE_ALGORITHM \ + "REPLACE INTO e_algorithm_%1(alg_key, alg_type, alg_data) VALUES('%2', '%3', :data_buffer)" +#define _TP_SQLITE_SELECT_RECORD_DATA \ + "SELECT alg_key, alg_type, alg_data FROM e_algorithm_%1 WHERE alg_key='%2' AND alg_type='%3'" +#define _TP_SQLITE_SELECT_KEYS_BY_TYPE \ + "SELECT alg_key, alg_type FROM e_algorithm_%1 WHERE alg_type = '%2'" +#define _TP_SQLITE_DELETE_RECORD \ + "DELETE FROM e_algorithm_%1 WHERE alg_key='%2'" + +QSqliteAlgorithm::QSqliteAlgorithm(const QString& dbPath) + : QZkDbSqlite(dbPath+_TP_SQLITE_ALGORITHM_DB) +{ + +} + +QSqliteAlgorithm::~QSqliteAlgorithm() +{ + +} + +bool QSqliteAlgorithm::CreateTable(int nAlgorithm) +{ + if (!isOpen() && !open()) + { + return false; + } + QString strSql = QString(_TP_SQLITE_CRETAE_ALGORITHM).arg(QString::number(nAlgorithm)); + QSqlQuery sql = exec(strSql); + if (QSqlError::NoError != lastError().type()) + { + return false; + } + return true; +} + +bool QSqliteAlgorithm::ReplaceRecord(int nAlgorithm, const QString& key, int type, const QByteArray& data) +{ + QString strCmd(_TP_SQLITE_REPLACE_ALGORITHM); + strCmd = strCmd.arg(QString::number(nAlgorithm), key, QString::number(type)); + QSqlQuery sql(*this); + sql.prepare(strCmd); + sql.bindValue(":data_buffer", data, QSql::In); + return sql.exec(); +} + +bool QSqliteAlgorithm::DeleteRecord(int nAlgorithm, const QString& key) +{ + QString strCmd = QString(_TP_SQLITE_DELETE_RECORD).arg(QString::number(nAlgorithm), key); + QSqlQuery sql = exec(strCmd); + if (QSqlError::NoError != sql.lastError().type()) + { + return false; + } + return true; +} + +bool QSqliteAlgorithm::ReadRecord(int nAlgorithm, const QString& key, int type, QByteArray& data) +{ + QString strCmd = QString(_TP_SQLITE_SELECT_RECORD_DATA).arg(QString::number(nAlgorithm), key, QString::number(type)); + QSqlQuery sql = exec(strCmd); + if (QSqlError::NoError != lastError().type()) + { + return false; + } + if (!sql.next()) + { + return false; + } + QSqlRecord record = sql.record(); + data = record.value("alg_data").toByteArray(); + return true; +} + +QStringList QSqliteAlgorithm::KeysByType(int nAlgorithm, int type) +{ + QStringList strList; + QString strCmd = QString(_TP_SQLITE_SELECT_KEYS_BY_TYPE).arg(QString::number(nAlgorithm), QString::number(type)); + QSqlQuery sql = exec(strCmd); + while (sql.next()) + { + strList.append(sql.record().value("alg_key").toString()); + } + return strList; +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.h b/src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.h new file mode 100644 index 0000000..7a2cb17 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/QSqliteAlgorithm.h @@ -0,0 +1,21 @@ +#ifndef QSQLITEALGORITHM_H +#define QSQLITEALGORITHM_H + +#include "QZkDbSqlite.h" + +class QSqliteAlgorithm : public QZkDbSqlite +{ +public: + QSqliteAlgorithm(const QString& dbPath); + ~QSqliteAlgorithm(); + + bool CreateTable(int nAlgorithm); + bool ReplaceRecord(int nAlgorithm, const QString& key, int type, const QByteArray& data); + bool DeleteRecord(int nAlgorithm, const QString& key); + bool ReadRecord(int nAlgorithm, const QString& key, int type, QByteArray& data); + QStringList KeysByType(int nAlgorithm, int type); +private: + +}; + +#endif // QSQLITEALGORITHM_H diff --git a/src/lpCoreCtrl/tpImgProc/tpImgProc.cpp b/src/lpCoreCtrl/tpImgProc/tpImgProc.cpp new file mode 100644 index 0000000..ae29644 --- /dev/null +++ b/src/lpCoreCtrl/tpImgProc/tpImgProc.cpp @@ -0,0 +1,54 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:tpImgProc.cpp + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:2015/03/26 + History:26:3:2015 17:56 + *******************************************************************************/ +#include "ImgProc.h" +#include "LoadAlgorithm.h" + +#ifdef TPIMGPROC_EXPORTS +#define TPIMGPROC_API extern "C" __declspec(dllexport) +#else +#define TPIMGPROC_API extern "C" +#endif + +CImgProc* gpImgProc = NULL; + +TPIMGPROC_API void* Lib_ImgProc_Init(void* inParam) +{ + IMGPROC_IN_PARAM* pParam = (IMGPROC_IN_PARAM*)inParam; + /* + char szDllName[260]; + strcpy(szDllName, pParam->pGlobalData->tgdDllPath); +#ifdef _DEBUG + strcat(szDllName, "tpAlgorithmd"); +#else + strcat(szDllName, "tpAlgorithm"); +#endif + CLoadAlgorithm::LoadAlg(szDllName); + */ + if( NULL == gpImgProc ) + { + gpImgProc = new CImgProc((IMGPROC_IN_PARAM*)pParam); + if( NULL != gpImgProc ) + { + gpImgProc->InitProc(); + } + } + return gpImgProc; +} + +TPIMGPROC_API void Lib_ImgProc_Free() +{ + if( NULL != gpImgProc ) + { + gpImgProc->FreeProc(); + delete gpImgProc; + gpImgProc = NULL; + } + //CLoadAlgorithm::FreeAlg(); +} \ No newline at end of file diff --git a/src/lpCoreCtrl/tpJsonConfig.h b/src/lpCoreCtrl/tpJsonConfig.h new file mode 100644 index 0000000..c66a429 --- /dev/null +++ b/src/lpCoreCtrl/tpJsonConfig.h @@ -0,0 +1,29 @@ +/****************************************************************************** + Copyright(C):2015~2018 hzleaper + FileName:$FILE_BASE$.$FILE_EXT$ + Author:zhikun wu + Email:zk.wu@hzleaper.com + Tools:vs2010 pc on company + Created:$DATE$ + History:$DAY$:$MONTH$:$YEAR$ $HOUR$:$MINUTE$ + *******************************************************************************/ +#ifndef __TP_JSON_CONFIG_H +#define __TP_JSON_CONFIG_H + +namespace CAMERA_JSON //camera.json +{ + namespace DEVICES //"devices" + { + + } + namespace POOL //"pool" + { + + } + namespace WINDOWS //"windows" + { + + } +} + +#endif//__TP_JSON_CONFIG_H diff --git a/src/lpMain/AutoTrigger.cpp b/src/lpMain/AutoTrigger.cpp new file mode 100644 index 0000000..8a7390e --- /dev/null +++ b/src/lpMain/AutoTrigger.cpp @@ -0,0 +1,29 @@ +#include "AutoTrigger.h" +#include "qtimer.h" + + +AutoTrigger::AutoTrigger() +{ + m_pTimer = new QTimer(this); + connect(m_pTimer, SIGNAL(timeout()), this, SIGNAL(sgTrig())); +} + + +AutoTrigger::~AutoTrigger() +{ + stop(); + delete m_pTimer; +} + +bool AutoTrigger::start(int nInterval) +{ + m_pTimer->stop(); + m_pTimer->start(nInterval); + return true; +} + +bool AutoTrigger::stop() +{ + m_pTimer->stop(); + return true; +} diff --git a/src/lpMain/AutoTrigger.h b/src/lpMain/AutoTrigger.h new file mode 100644 index 0000000..55a92df --- /dev/null +++ b/src/lpMain/AutoTrigger.h @@ -0,0 +1,21 @@ +#ifndef _AUTOTRIGGER_H_ +#define _AUTOTRIGGER_H_ + +#include + +class AutoTrigger : public QObject +{ + Q_OBJECT +public: + AutoTrigger(); + ~AutoTrigger(); + + bool start(int nInterval = 1000); + bool stop(); +signals : + void sgTrig(); +private: + class QTimer *m_pTimer{ nullptr }; +}; + +#endif \ No newline at end of file diff --git a/src/lpMain/CoreCtrl/CDllCoreCtrl.cpp b/src/lpMain/CoreCtrl/CDllCoreCtrl.cpp new file mode 100644 index 0000000..b529d3f --- /dev/null +++ b/src/lpMain/CoreCtrl/CDllCoreCtrl.cpp @@ -0,0 +1,79 @@ +#include "CDllCoreCtrl.h" + +#define _USE_LIB_CORECTRL_DLL +#ifdef _USE_LIB_CORECTRL_DLL +#include "DllLoader.h" +#else +extern "C" void* Lib_CoreCtrl_Init(void* inParam); +extern "C" void Lib_CoreCtrl_Free(); +#endif +#include "QZkJsonParser.h" + +CDllCoreCtrl::CDllCoreCtrl(const ZStringList& szDllPaths, IGuiCallback* pGuiCb,class IDetectorEngine* pDE /*= NULL*/) +{ + m_szDllPaths = szDllPaths; + memset(&m_tpGlobal, 0, sizeof(m_tpGlobal)); + m_tpGlobal.tgdpDllPaths = &m_szDllPaths; + if (m_szDllPaths.size() > 0) + { + strncpy(m_tpGlobal.tgdMainPath, m_szDllPaths.last().toUtf8().data(), BASE_MAX_FILE_PATH - 1); + strncpy(m_tpGlobal.tgdUserPath, m_szDllPaths.first().toUtf8().data(), BASE_MAX_FILE_PATH - 1); + strcpy(m_tpGlobal.tgdDllPath, m_tpGlobal.tgdMainPath); + } + QJsonObject jsonUser; + int nerr; + QJsonObject rootBoj = QZkJsonParser::ReadJsonObject(".\\ui\\app.json", &nerr); + QString folderStr = jsonUser.value("config").toString("config\\"); + strcpy(m_tpGlobal.tgdFolderNames[TP_FOLDER_NAME_CONFIG], folderStr.toUtf8().data()); + folderStr = jsonUser.value("pic").toString("pic\\"); + strcpy(m_tpGlobal.tgdFolderNames[TP_FOLDER_NAME_PICTURE], folderStr.toUtf8().data()); + strcpy(m_tpGlobal.tgdFolderNames[TP_FOLDER_NAME_UI], "ui\\"); + CORE_CTRL_IN_PARAM inParam; + inParam.pGlobalData = &m_tpGlobal; + inParam.pCoreSetting = NULL; + inParam.pGuiCb = pGuiCb; +#ifdef _USE_LIB_CORECTRL_DLL + m_pLibCoreCtrl = new CDllLoaderM("lpCoreCtrl", "Lib_CoreCtrl_Init", "Lib_CoreCtrl_Free", m_szDllPaths); + if (NULL != m_pLibCoreCtrl) + { + m_pCoreCtrl = (ICoreCtrl*)m_pLibCoreCtrl->ModuleInit(&inParam); + if (NULL != m_pCoreCtrl) + { + m_pCoreCtrl->IInitCore(pDE); + } + else + { + tpDebugOut("failed to get instance from tpCoreCtrl.dll"); + } + } + else + { + tpDebugOut("failed to load tpCoreCtrl.dll"); + } +#else + m_pLibCoreCtrl = NULL; + m_pCoreCtrl = (ICoreCtrl*)Lib_CoreCtrl_Init(&inParam); +#endif +} + +CDllCoreCtrl::~CDllCoreCtrl() +{ + if (NULL != m_pCoreCtrl) + { + m_pCoreCtrl->IFreeCore(); +#ifdef _USE_LIB_CORECTRL_DLL + if (NULL != m_pLibCoreCtrl) + { + m_pLibCoreCtrl->ModuleFree(); + } +#else + Lib_CoreCtrl_Free(); +#endif + m_pCoreCtrl = NULL; + } + if (NULL != m_pLibCoreCtrl) + { + delete m_pLibCoreCtrl; + m_pLibCoreCtrl = NULL; + } +} diff --git a/src/lpMain/CoreCtrl/CDllCoreCtrl.h b/src/lpMain/CoreCtrl/CDllCoreCtrl.h new file mode 100644 index 0000000..b6a0b8a --- /dev/null +++ b/src/lpMain/CoreCtrl/CDllCoreCtrl.h @@ -0,0 +1,21 @@ +#ifndef CDLLCORECTRL_H +#define CDLLCORECTRL_H + +#include "iCoreCtrl.h" +#include "zclasses.h" + +class CDllCoreCtrl +{ +public: + CDllCoreCtrl(const ZStringList& szDllPaths, IGuiCallback* pGuiCb, class IDetectorEngine* pDE /*= NULL*/); + ~CDllCoreCtrl(); +public: + TP_GLOBAL_DATA m_tpGlobal; + class CDllLoaderM* m_pLibCoreCtrl; + ICoreCtrl* m_pCoreCtrl; + ZStringList m_szDllPaths; + + +}; + +#endif // CDLLCORECTRL_H diff --git a/src/lpMain/CoreCtrl/CDllDetectorEngine.cpp b/src/lpMain/CoreCtrl/CDllDetectorEngine.cpp new file mode 100644 index 0000000..cf06805 --- /dev/null +++ b/src/lpMain/CoreCtrl/CDllDetectorEngine.cpp @@ -0,0 +1,79 @@ +#include "CDllDetectorEngine.h" +#include "iCoreCtrl.h" + +CDllDetectorEngine::CDllDetectorEngine() +{ + m_pDE = NULL; +#ifdef _DEBUG + m_lib.setFileName("lpbengined.dll"); +#else + m_lib.setFileName("lpbengine.dll"); +#endif + + if (!m_lib.load()) + { + qWarning("failed to load lpbengine"); + } + + FnLpNewEngineInstance pfnLpNewInstance = (FnLpNewEngineInstance)m_lib.resolve("LpNewEngineInstance"); + if (pfnLpNewInstance) + pfnLpNewInstance(&m_pDE, NULL); + + if (!m_pDE) + { + qWarning("failed to get instance from lpbengine"); + } + +} + +CDllDetectorEngine::~CDllDetectorEngine() +{ + Quit(); +} + +bool CDllDetectorEngine::Initialize(class ICoreCtrl* pCoreCtrl) +{ + if (m_pDE && pCoreCtrl) + { + void* p = NULL; + m_pDE->GetDataInterface(DEVICEMGR, &p); + IDetectorDeviceMgr* pDeviceMgr = (IDetectorDeviceMgr*)p; + if (pDeviceMgr) + { + pDeviceMgr->RegisterCoreCtrl(pCoreCtrl); + QList strCameraKeys = pCoreCtrl->ICameraKeys(); + for (int i = 0; i < strCameraKeys.size(); i++) + { + + IDetectorCameraDevice* pCamera = (IDetectorCameraDevice*)pDeviceMgr->AddDevice(CAMERA); + if (pCamera) + { + TP_CAMERA_OPTION option; + pCoreCtrl->ICameraOptionByKey(strCameraKeys.at(i), option); + pCamera->SetName(strCameraKeys.at(i)); + pCamera->SetID(option.id); + } + } + } + + m_pDE->GetDataInterface(SHAREAPARAM, &p); + IAlgorithmShare* pAlgoShare = (IAlgorithmShare*)p; + if (pAlgoShare) + { + pAlgoShare->RegisterCoreCtrl(pCoreCtrl); + } + + return true; + } + return false; +} + +void CDllDetectorEngine::Quit() +{ + FnLpDeleteEngineInstance pfnLpDeleteInstance = (FnLpDeleteEngineInstance)m_lib.resolve("LpDeleteEngineInstance"); + if (pfnLpDeleteInstance) + pfnLpDeleteInstance(); + + if (m_lib.isLoaded()) + m_lib.unload(); +} \ No newline at end of file diff --git a/src/lpMain/CoreCtrl/CDllDetectorEngine.h b/src/lpMain/CoreCtrl/CDllDetectorEngine.h new file mode 100644 index 0000000..bd2150c --- /dev/null +++ b/src/lpMain/CoreCtrl/CDllDetectorEngine.h @@ -0,0 +1,19 @@ +#ifndef CDLLDETECTORENGINE_H +#define CDLLDETECTORENGINE_H + +#include "lpbengine.h" + +class CDllDetectorEngine +{ +public: + CDllDetectorEngine(); + ~CDllDetectorEngine(); +public: + bool Initialize(class ICoreCtrl* pCoreCtrl); + void Quit(); + IDetectorEngine *m_pDE; +private: + QLibrary m_lib; +}; + +#endif // CDLLDETECTORENGINE_H diff --git a/src/lpMain/CoreCtrl/QDetectorDesignerMgr.cpp b/src/lpMain/CoreCtrl/QDetectorDesignerMgr.cpp new file mode 100644 index 0000000..b94fa2b --- /dev/null +++ b/src/lpMain/CoreCtrl/QDetectorDesignerMgr.cpp @@ -0,0 +1,64 @@ +#include "QDetectorDesignerMgr.h" + +QDetectorDesignerMgr::QDetectorDesignerMgr() +{ + m_pDesigner = NULL; + m_pDE = NULL; +} + +QDetectorDesignerMgr::~QDetectorDesignerMgr() +{ +// Quit(); +} + +bool QDetectorDesignerMgr::Initialize(IDetectorEngine* lpDE) +{ + if (!lpDE) + return false; + m_pDE = lpDE; + +#ifdef _DEBUG + m_lib.setFileName("lpdesignerd.dll"); +#else + m_lib.setFileName("lpdesigner.dll"); +#endif + if (!m_lib.load()) { + qDebug() << "lpdesigner lib load failed"; + return false; + } + qDebug() << "lib load ok"; + FnLpDesignerNewInstance pfnLpNewInstance = (FnLpDesignerNewInstance)m_lib.resolve("LpDesignerNewInstance"); + if (pfnLpNewInstance) + pfnLpNewInstance(&m_pDesigner, m_pDE, this); + + if (!m_pDesigner) + return false; + + return true; +} + +IDetectorUI* QDetectorDesignerMgr::GetDesignerInterface() const +{ + return m_pDesigner; +} + +void QDetectorDesignerMgr::Quit() +{ + if (m_lib.isLoaded()) + m_lib.unload(); + if (m_pDesigner) + { + delete m_pDesigner; + m_pDesigner = NULL; + } +} + +void QDetectorDesignerMgr::OnManualTrigger() +{ + emit sgCloseWindow(); +} + +void QDetectorDesignerMgr::OnSetParam(IDetectorTask* pTask) +{ + //return m_pApp->SetParam(pTask); +} diff --git a/src/lpMain/CoreCtrl/QDetectorDesignerMgr.h b/src/lpMain/CoreCtrl/QDetectorDesignerMgr.h new file mode 100644 index 0000000..c1931ed --- /dev/null +++ b/src/lpMain/CoreCtrl/QDetectorDesignerMgr.h @@ -0,0 +1,31 @@ +#ifndef QDETECTORDESIGNERMGR_H +#define QDETECTORDESIGNERMGR_H + +#include ".\lpbengine.h" +#include ".\lpdesigner.h" +#include "qobject.h" + +class QDetectorDesignerMgr : public QObject, public IDetectorUISink +{ + Q_OBJECT +public: + QDetectorDesignerMgr(); + virtual ~QDetectorDesignerMgr(); + bool Initialize(IDetectorEngine* lpDE); + void Quit(); + IDetectorUI * GetDesignerInterface() const; + virtual void OnManualTrigger(); + virtual void ResetItem(QPoint pos, QRect size){} + virtual void OnSetParam(IDetectorTask* pTask); + +signals: + void sgCloseWindow(); +private: + +private: + QLibrary m_lib; + IDetectorUI *m_pDesigner; + IDetectorEngine *m_pDE; +}; + +#endif // QDETECTORDESIGNERMGR_H \ No newline at end of file diff --git a/src/lpMain/IStation.h b/src/lpMain/IStation.h new file mode 100644 index 0000000..9c066d5 --- /dev/null +++ b/src/lpMain/IStation.h @@ -0,0 +1,56 @@ +#ifndef _ISTATION_H_ +#define _ISTATION_H_ + +#include +#include +#include "Serialport_global.h" +#define _WF_UNIQUE_SPLIT "_-_" + +enum EM_WIDGET_TYPE { + emListModel, emLableImageShow, emLabelStationName +}; +class WfModel; +class IStation : public QObject +{ + Q_OBJECT +public: + IStation(){}; + virtual ~IStation(){}; + virtual int stationId() = 0; + virtual QString stationKey() = 0; + virtual QString stationShowName() = 0; + virtual void setCamInfo(int nId, int alg, QString uniqueName, QString showName) = 0; + virtual void setComInfo(QString strName, int nCmd) = 0; + virtual QVariant getVariant(){ return QVariant(); } + virtual void revResult() = 0; + virtual bool trigImage(const QString folder = QString()) = 0; + virtual bool isWorkingOk() = 0; + + virtual QString currentRunningModel() const = 0; + virtual void setCurrentModel(QString strModel) = 0; + virtual void setCurrentModel(int index) = 0; + virtual QString currentSelectModel() = 0; + virtual int modelCount() { return 0; } + virtual QStringList modelList() { return QStringList(); } + virtual QString model(int index) { return QString(); } + virtual QString modelByPlcCmd(int nIndex) { return QString(); } + virtual WfModel *wfModel(QString) = 0; + virtual bool addModel(QString strModel) = 0; + virtual bool delModel(QString strModel) = 0; + virtual QString uniqueModel(const QString &model){ return QString(); } + + virtual bool sendResult(double ) = 0; + virtual bool setWidget(QString str, QWidget *pWgt) = 0; + + virtual bool startBatchTest(QString) = 0; + virtual bool IStandard(QString) = 0; + void setSerialPortPtr(ISerialPortTool *ptr){ m_pPort = ptr; } +signals: + void sgPrint2Window(QString &); + void sgUpdateLable(); + void sgShowImage(const QImage &img); +protected: + ISerialPortTool *m_pPort{ nullptr }; +}; + +#endif \ No newline at end of file diff --git a/src/lpMain/IWfCtrl.h b/src/lpMain/IWfCtrl.h new file mode 100644 index 0000000..a412430 --- /dev/null +++ b/src/lpMain/IWfCtrl.h @@ -0,0 +1,36 @@ +#ifndef _IWFCTRL_H_ +#define _IWFCTRL_H_ + +#include +#include +class IStation; +class WfModel; +class IWfCtrl :public QObject +{ + Q_OBJECT +public: + IWfCtrl(){}; + virtual~IWfCtrl(){}; + + virtual bool IOnlineMode() { return false; } + virtual void ISetOnlineModel(bool) {}; + virtual bool IBatchModel() = 0; + virtual bool IConnectStatus() = 0; + virtual QString IGetCommName() = 0; + virtual QString IGetCurrentRuningModel(int) = 0; + virtual void ISetUserInfo(QString& user, int& level)=0; + virtual bool IGetUserInfo(QString& user, int& level) = 0; + virtual void registerConnect() =0; + virtual QStringList IGetStationKeys() = 0; + virtual IStation* IGetStationById(int) = 0; + virtual IStation* IGetStationByKey(QString) = 0; + virtual WfModel *IGetModelInfo(int, QString) = 0; + virtual bool IUpdateModelInfo() = 0; + virtual QMap IGetModelInfos() = 0; + virtual bool IAddModel(int, QString) = 0; + virtual bool IDeleteModel(int, QString) = 0; + virtual bool ISelModel(int, QString) = 0; + virtual void ISetModifyModel(bool) = 0; + virtual bool IStandard(int nIndex ,QString strModel)=0; +}; +#endif diff --git a/src/lpMain/ModelTable.cpp b/src/lpMain/ModelTable.cpp new file mode 100644 index 0000000..226a3cf --- /dev/null +++ b/src/lpMain/ModelTable.cpp @@ -0,0 +1,122 @@ +#include "ModelTable.h" +#include "IStation.h" +#include "QDebug" +#include "qtableview.h" +#include "qabstractitemview.h" +#include "WfModel.h" +#pragma execution_character_set("utf-8") + +QVariant ModelModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const +{ + if (role == Qt::DisplayRole) { + int nCol = index.column(); + int nRow = index.row(); + //qDebug() << index.row(); + if (0 == nCol) { + if (nRow != 0) { + int a = 1; + } + return m_pStation->model(nRow); + } + else if (1== nCol) { + return m_pStation->wfModel(m_pStation->model(nRow))->nIndex; + } + else if (2==nCol) { + //return m_pStation->wfModel(m_pStation->model(nRow))->bCaliState == true ? "已标定" : "未标定"; + return m_pStation->IStandard(m_pStation->model(nRow)) == true ? QObject::tr("已标定") : QObject::tr("未标定"); + } + } + else if (role == Qt::BackgroundRole) { + QString str1 = m_pStation->currentRunningModel(); + QString str2 = m_pStation->model(index.row()); + if (str1 == str2) { + return QColor(255, 0, 0); + } + else { + return QColor(255, 255, 255); + } + +// int nCol = index.column(); +// int nRow = index.row(); +// if (2 == nCol) { +// return m_pStation->IStandard(m_pStation->model(nRow)) == true ? QColor(255, 255, 255) : QColor(255, 0, 255); +// } + } + return QVariant(); +} + +int ModelModel::columnCount(const QModelIndex &parent /*= QModelIndex()*/) const +{ + return 3; +} + +int ModelModel::rowCount(const QModelIndex &parent /*= QModelIndex()*/) const +{ + return m_pStation->modelCount(); +} + +QVariant ModelModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const +{ + if (Qt::DisplayRole == role ) { + if (Qt::Horizontal == orientation) { + if (section < m_lstHeader.size()) { + return m_lstHeader.at(section); + } + else { + return QVariant(); + } + } + else { + return section + 1; + } + + } + return QVariant(); +} + +ModelModel::ModelModel(IStation *pStation) + : m_pStation(pStation) +{ + m_lstHeader = QStringList() << QObject::tr("模型号") << QObject::tr("PLC索引值") << QObject::tr("标定情况"); +} + +void ModelModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +ModelView::ModelView(QTableView *pView, IStation *pStation) +{ + m_pView = pView; + m_pView->setSelectionBehavior(QAbstractItemView::SelectRows); + //QObject::connect(pView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onSelectItem(const QModelIndex&))); + m_pModel = new ModelModel(pStation); + m_pView->setModel(m_pModel); +} + +ModelView::~ModelView() +{ + delete m_pModel; + delete m_pView; + m_pView = NULL; + m_pModel = NULL; +} + +void ModelView::resetModel() +{ + m_pModel->resetModel(); +} + +void ModelView::scroll2Bottom() +{ + m_pView->scrollToBottom(); +} + +int ModelView::currentRow() +{ + if (m_pView) { + return m_pView->currentIndex().row(); + } + return -1; +} diff --git a/src/lpMain/ModelTable.h b/src/lpMain/ModelTable.h new file mode 100644 index 0000000..d88be5d --- /dev/null +++ b/src/lpMain/ModelTable.h @@ -0,0 +1,37 @@ +#pragma once +#include "qabstractitemmodel.h" +#include "qtableview.h" +#include "qmap.h" +#include "qstring.h" +#include "qobject.h" + +class IStation; +class ModelModel : public QAbstractTableModel +{ +public: + ModelModel(IStation *); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + void resetModel(); +private: + IStation *m_pStation; + QStringList m_lstHeader; +}; + +class ModelView : public QObject +{ + Q_OBJECT +public: + ModelView(QTableView *, IStation *); + ~ModelView(); + void resetModel(); + void scroll2Bottom(); + int currentRow(); + ModelModel *m_pModel; + QTableView * m_pView; +}; diff --git a/src/lpMain/QDiskCleanThread/QDiskCleanThread.cpp b/src/lpMain/QDiskCleanThread/QDiskCleanThread.cpp new file mode 100644 index 0000000..30dc4d6 --- /dev/null +++ b/src/lpMain/QDiskCleanThread/QDiskCleanThread.cpp @@ -0,0 +1,184 @@ +#include +#include +#include +#include "QDiskCleanThread.h" +#include "solarCellHelper.h" + + +bool compareFileCreatTime(const QFileInfo &fileInfo1, const QFileInfo &fileInfo2) +{ + if (fileInfo1.created() < fileInfo2.created()) + { + return true; + } + + return false; +} + + +QDiskCleanThread::QDiskCleanThread(QObject * parent) + : QThread(parent) + , m_bExit(false) + , m_strImgStorageFolder(QString()) + , isUse(false) + , m_nDay(7) + , m_DelFile(false) + , nMiniSize(10) +{ + bModel = CleanFile; + nSleepTime = 300; +} + +QDiskCleanThread::~QDiskCleanThread() +{ + ExitThread(); +} + +void QDiskCleanThread::run() +{ + while (!m_bExit) + { + qWarning() << "Start Scan Disk ..."; + QString strDisk = m_strImgStorageFolder.left(2); + int space = solarCell::SolarCellHelper::GetDiskFreeSpace(strDisk); + if (space < 1024 * 10) + { + isUse = true; + m_nDay = 3; + + } + + + qWarning() << "spacesize =" << space; + if (bModel == CleanFile)//清空图片数据的操作 + { + if (isUse) + { + QFileInfoList fileInfoList; + fileInfoList.clear(); + solarCell::SolarCellHelper::GetFileInfoList(fileInfoList, m_strImgStorageFolder); + QDateTime timedate = QDateTime::currentDateTime(); + //timedate.setTime(QTime(0, 0, 0));//以零点时间为准 开始比较时间 + int nDay = 0; + if (m_nDay <= 0) + nDay = 3;//限制天数 不能低于3天 + else + nDay = m_nDay; + + timedate = timedate.addDays(-nDay); + GetFileListByTime(fileInfoList, timedate); + if (fileInfoList.size() > 10 && + !m_strImgStorageFolder.isEmpty()) + { + for (int nIndex = 0; nIndex < fileInfoList.size(); nIndex++) + { + QFileInfo qFileInfo = fileInfoList.at(nIndex); + qFileInfo.dir().remove(qFileInfo.fileName()); + } + } + space = solarCell::SolarCellHelper::GetDiskFreeSpace(strDisk); + qWarning() << "Delete Imgs ..."; + } + + + if (space < 1024 * nMiniSize) + { + m_DelFile = true; + } + + if (m_DelFile) + { + QFileInfoList fileInfoList; + fileInfoList.clear(); + solarCell::SolarCellHelper::GetFileInfoList(fileInfoList, m_strImgStorageFolder); + qSort(fileInfoList.begin(), fileInfoList.end(), compareFileCreatTime); + //删除时间最老的300张图片 + int delNum = 600; + if (delNum > fileInfoList.size()) + { + delNum = fileInfoList.size(); + } + for (int i = 0; i < delNum; i++) + { + QString filePath = fileInfoList.at(i).absolutePath(); + QString fileName = fileInfoList.at(i).fileName(); + QString createTIme = fileInfoList.at(i).created().toString("yyyy-MM-dd hh:mm:ss zzz"); + if (fileName.right(4).toLower() == ".jpg" || + fileName.right(4).toLower() == ".bmp" || + fileName.right(4).toLower() == ".png") + { + QFile::remove(fileInfoList.at(i).absolutePath() + "/" + fileInfoList.at(i).fileName()); + } + } + m_DelFile = false; + qWarning() << "Delete Imgs Finish..."; + } + } + else if (bModel == CleanDir)//清空图片文件夹测操作 + { + if (isUse) + { + QFileInfoList fileInfoList; + fileInfoList.clear(); + solarCell::SolarCellHelper::GetDirInfoList_root(fileInfoList, m_strImgStorageFolder); + QDateTime timedate = QDateTime::currentDateTime(); + if (m_nDay <= 0) + m_nDay = 3;//限制天数 不能低于3天 + timedate = timedate.addDays(-m_nDay); + GetDirListByTime(fileInfoList, timedate); + if (fileInfoList.size() > 0 && + !m_strImgStorageFolder.isEmpty()) + { + for (int nIndex = 0; nIndex < fileInfoList.size(); nIndex++) + solarCell::SolarCellHelper::DelDiretory(fileInfoList.at(nIndex).absoluteFilePath()); + } + } + } + qWarning() << "End Scan Disk ..."; + //三十秒判断一次 + if (nSleepTime <= 2) + nSleepTime = 2; + int sleeptime = nSleepTime * 1000; + msleep(10); + } +} + +void QDiskCleanThread::ExitThread() +{ + m_bExit = true; +} + +void QDiskCleanThread::SetImgStorageFolder(QString strFolder) +{ + m_strImgStorageFolder = strFolder; +} + +void QDiskCleanThread::GetDirListByTime(QFileInfoList &qDirList, QDateTime &qDateTime) +{ + QFileInfoList tmpList; + tmpList.clear(); + for (int nIndex = 0; nIndex < qDirList.size(); nIndex++){ + QFileInfo nFileInfo = qDirList.at(nIndex); + if (nFileInfo.fileName() == "." || nFileInfo.fileName() == "..") + continue; + if (nFileInfo.created() +#include +enum CleanModel +{ + CleanFile = 0, + CleanDir +}; +class QDiskCleanThread : public QThread +{ + Q_OBJECT; + +public: + QDiskCleanThread(QObject * parent = 0); + virtual ~QDiskCleanThread(); + + void setModel(CleanModel nModel = CleanFile){ bModel = nModel; }; + void setSleepS(int nTime){ nSleepTime = nTime; }; + void setUseFlag(bool bFalg = false){//是否开启轮询监控 + isUse = bFalg; + }; + void setMiniSize(int minsize){ + nMiniSize = minsize; + if (nMiniSize <= 0) + nMiniSize = 0; + }; + void setDays(int nday = 7){ m_nDay = nday; };//超期天数 + void ExitThread(); + void SetImgStorageFolder(QString strFolder); + void GetDirListByTime(QFileInfoList &qDirList, QDateTime &qDateTime); + void GetFileListByTime(QFileInfoList &qFileList, QDateTime &qDateTime); + +protected: + void run(); + +signals: + void sgDiskSpace(int nSpace, bool nFlag); +private: + bool m_bExit{ false }; + QString m_strImgStorageFolder;//监控的文件夹 + bool isUse{ false }; + int m_nDay{ 30 }; + bool m_DelFile{ false };//重复删除标志位 用于判断硬盘数据是否是过大 如果过大 会删除两次 + int nMiniSize{ 10 };//监控硬盘最小的内存大小 G单位计算 小于该数量 删除数据 + CleanModel bModel{ CleanFile };//线程工作模式选择 + int nSleepTime{ 30 };//线程挂起时间 单位 秒 +}; +#endif//_QDISK_CLEAN_THREAD_H__ diff --git a/src/lpMain/QDiskCleanThread/WorkChecker.cpp b/src/lpMain/QDiskCleanThread/WorkChecker.cpp new file mode 100644 index 0000000..07b9e81 --- /dev/null +++ b/src/lpMain/QDiskCleanThread/WorkChecker.cpp @@ -0,0 +1,51 @@ +#include "WorkChecker.h" +#include +#include + + +WorkChecker::WorkChecker() +{ + m_bWorking = true; + m_lLastTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_nCheckInterval = 300; + m_pTimer = new QTimer(this); + connect(m_pTimer, SIGNAL(timeout()), this, SLOT(onChecker())); + start(); +} + + +WorkChecker::~WorkChecker() +{ +} + +void WorkChecker::registerWorking() +{ + m_lLastTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_bWorking = true; +} + +bool WorkChecker::isWorking() const +{ + return m_bWorking; +} + +void WorkChecker::onChecker() +{ + long long l = QDateTime::currentDateTime().toMSecsSinceEpoch(); + long long l1 = l - m_lLastTime; + //m_lLastTime = l; + if (l1 > 3000) { + m_bWorking = false; + } +} + +void WorkChecker::start() +{ + stop(); + m_pTimer->start(m_nCheckInterval); +} + +void WorkChecker::stop() +{ + m_pTimer->stop(); +} diff --git a/src/lpMain/QDiskCleanThread/WorkChecker.h b/src/lpMain/QDiskCleanThread/WorkChecker.h new file mode 100644 index 0000000..a5cc0d4 --- /dev/null +++ b/src/lpMain/QDiskCleanThread/WorkChecker.h @@ -0,0 +1,23 @@ +#ifndef _WORKCHECKER_H_ +#define _WORKCHECKER_H_ +#include +class WorkChecker : public QObject +{ + Q_OBJECT +public: + WorkChecker(); + ~WorkChecker(); + void registerWorking(); + bool isWorking() const ; + + void start(); + void stop(); +public slots: + void onChecker(); +private: + class QTimer *m_pTimer{ nullptr }; + int m_nCheckInterval{ 300 }; + bool m_bWorking{ true }; + long long m_lLastTime{ 0 }; +}; +#endif diff --git a/src/lpMain/QDiskCleanThread/solarCellHelper.h b/src/lpMain/QDiskCleanThread/solarCellHelper.h new file mode 100644 index 0000000..b434b36 --- /dev/null +++ b/src/lpMain/QDiskCleanThread/solarCellHelper.h @@ -0,0 +1,90 @@ +#ifndef _SOLAR_CELL_HELPER_H__ +#define _SOLAR_CELL_HELPER_H__ + +#include +#include +#include +#include +#include +#include + + +namespace solarCell +{ + class SolarCellHelper + { + public: + static inline qint64 GetDiskFreeSpace(QString strDriver) + { + LPCWSTR lpcwstrDriver = (LPCWSTR)strDriver.utf16(); + + ULARGE_INTEGER liFreeBytesAvailable, liTotalBytes, liTotalFreeBytes; + + if (!GetDiskFreeSpaceEx(lpcwstrDriver, &liFreeBytesAvailable, &liTotalBytes, &liTotalFreeBytes)) + { + qWarning() << "ERROR: Call to GetDiskFreeSpaceEx() failed."; + return 0; + } + + //返回MB + return (quint64)liTotalFreeBytes.QuadPart / 1024 / 1024; + } + static inline void GetFileInfoList(QFileInfoList& fileInfoList, QString strFolder) + { + QDir qDir(strFolder); + QFileInfoList filesListTmp = qDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Time); + fileInfoList += filesListTmp; + + QStringList dirList = qDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (int i = 0; i < dirList.size(); i++) + { + GetFileInfoList(fileInfoList, strFolder + "/" + dirList.at(i)); + } + } + + static inline void GetDirInfoList_child(QFileInfoList& dirInfoList, QString strFolder) + { + QDir qDir(strFolder); + QFileInfoList filesListTmp = qDir.entryInfoList(QDir::Dirs, QDir::Time); + dirInfoList += filesListTmp; + + QStringList dirList = qDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (int i = 0; i < dirList.size(); i++) + { + GetDirInfoList_child(dirInfoList, strFolder + "/" + dirList.at(i)); + } + } + + static inline void GetDirInfoList_root(QFileInfoList& dirInfoList, QString strFolder) + { + QDir qDir(strFolder); + QFileInfoList filesListTmp = qDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time); + dirInfoList += filesListTmp; + } + + static inline bool DelDiretory(const QString &dirPath) + { + if (dirPath.isEmpty()) + return false; + QDir qDir(dirPath); + if (!qDir.exists()) + return true; + qDir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + QFileInfoList qFileList = qDir.entryInfoList(); + foreach(QFileInfo qFileInfo, qFileList) + { + if (qFileInfo.isFile()) + qFileInfo.dir().remove(qFileInfo.fileName()); + else + DelDiretory(qFileInfo.absoluteFilePath()); + } + return qDir.rmpath(qDir.absolutePath()); + } + + + };//Class SolarCellHelper + +} +#endif//_SOLAR_CELL_HELPER_H__ + + diff --git a/src/lpMain/Resource/app.png b/src/lpMain/Resource/app.png new file mode 100644 index 0000000000000000000000000000000000000000..b2bc4268772e9f10c30bbcbcec9b08134c284c80 GIT binary patch literal 5841 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 bF}ME*fo9fnC}(rT00000NkvXXu0mjfbtgVc literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/app2.png b/src/lpMain/Resource/app2.png new file mode 100644 index 0000000000000000000000000000000000000000..d47d80dcc768a04226d92185ad586518707e3b65 GIT binary patch literal 48804 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 aiL3B8GXA6eZMPeVfT067)T~l}67^qU>8wxy literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/toolBar/cali.png b/src/lpMain/Resource/toolBar/cali.png new file mode 100644 index 0000000000000000000000000000000000000000..a327c91a7928b0d2040d6c094e364c71b245a3bb GIT binary patch literal 17226 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 literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/toolBar/education24.png b/src/lpMain/Resource/toolBar/education24.png new file mode 100644 index 0000000000000000000000000000000000000000..e40e5845370104db0ad641e5e6c82064988b61fd GIT binary patch literal 17539 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 literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/toolBar/help.png b/src/lpMain/Resource/toolBar/help.png new file mode 100644 index 0000000000000000000000000000000000000000..f781626ed3423996377ffeaa3d5442e9ed6fae29 GIT binary patch literal 13342 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 Bq9*_V literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/toolBar/messenger.png b/src/lpMain/Resource/toolBar/messenger.png new file mode 100644 index 0000000000000000000000000000000000000000..bd0dffb717ad24f40ace9d0c2969f35f4706f47e GIT binary patch literal 2987 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 literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/toolBar/save.png b/src/lpMain/Resource/toolBar/save.png new file mode 100644 index 0000000000000000000000000000000000000000..ce706e4ade13359e567855433cf839963f1e60fb GIT binary patch literal 18714 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 F0RWL{1gii5 literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/toolBar/setting.png b/src/lpMain/Resource/toolBar/setting.png new file mode 100644 index 0000000000000000000000000000000000000000..75ca02cec20eb87d5336d29ac9ae30e35a676885 GIT binary patch literal 8022 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 F{{i-Sb87$q literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/toolBar/test.png b/src/lpMain/Resource/toolBar/test.png new file mode 100644 index 0000000000000000000000000000000000000000..96758f81f2e939e696bf76d0e8d05107264fb4d9 GIT binary patch literal 16087 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 literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/wanfeng.png b/src/lpMain/Resource/wanfeng.png new file mode 100644 index 0000000000000000000000000000000000000000..c4532adceba1a2e7b69be0635a886b1e55c2ffa7 GIT binary patch literal 21164 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 Hu0mjfnP&+P literal 0 HcmV?d00001 diff --git a/src/lpMain/Resource/wanfeng2.png b/src/lpMain/Resource/wanfeng2.png new file mode 100644 index 0000000000000000000000000000000000000000..b6660de97a4525157451623addf34a58f541e077 GIT binary patch literal 7586 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 Ef}!iT)c^nh literal 0 HcmV?d00001 diff --git a/src/lpMain/Station.cpp b/src/lpMain/Station.cpp new file mode 100644 index 0000000..e059a69 --- /dev/null +++ b/src/lpMain/Station.cpp @@ -0,0 +1,601 @@ +#include "Station.h" +#include "qobject.h" +#include "iCoreCtrl.h" +#include "TrigDetector.h" +#include "QDebug" +#include "QSqliteWheelHubWf.h" +#include "ModelTable.h" +#include "IWfCtrl.h" +#include "WfModel.h" +#include +#include "lpSysLog.h" + +#pragma execution_character_set("utf-8") + +int Station::s_nTrigWaitTimes = 5; + +Station::Station(QSqliteWheelHubWf *p1, ICoreCtrl *p2, IWfCtrl * p3) + : m_pView(NULL), m_pLblImage(NULL), m_pLblStationName(NULL), + m_pDbWf(p1) +{ + m_pResultTextShow = NULL; + m_pCoreCtrl = p2; + m_pWfCtrl = p3; + m_pTrigDetector = new CTrigDetector; + connect(m_pTrigDetector, SIGNAL(sgErrorTrig(int)), this, SLOT(onErrTrig(int))); + connect(this, SIGNAL(sgPrint2Window(QString &)), this, SLOT(onAddResult2TextEdit(QString &))); + connect(this, SIGNAL(sgShowImage(const QImage&)), this, SLOT(showImage(const QImage&))); + connect(this, SIGNAL(sgUpdateLable()), this, SLOT(updateAll())); + connect(this, SIGNAL(sgaddTrigCount(int)), m_pTrigDetector, SLOT(addTrigCount(int))); + connect(this, SIGNAL(sgaddRevCount(int)), m_pTrigDetector, SLOT(addRevCount(int))); + m_pTrigDetector->start(); +} + + +Station::~Station() +{ + if (m_pTrigDetector) + { + m_pTrigDetector->stop(); + delete m_pTrigDetector; + } + if (m_pView) { + delete m_pView; + } +} + +void Station::setCamInfo(int nId, int alg, QString uniqueName, QString showName) +{ + this->nId = nId; + this->nAlgorithm = alg; + this->szCamKey = uniqueName; + this->szShow = showName; +} + +QString genCurrentTime(QString strFormat = "yyyy-MM-dd hh:mm::ss") { + QDateTime qTime = QDateTime::currentDateTime(); + QString strTime = qTime.toString(strFormat); + return strTime; +} + +bool Station::addModel(QString strModel) +{ + if (m_models.contains(strModel)) { + return false; + } + m_models.append(strModel); + if (m_pView) { + m_pView->resetModel(); + m_pView->scroll2Bottom(); + } + qDebug() << "add model :" << strModel; + return true; +} + + +bool Station::delModel(QString strModel) +{ + m_models.removeOne(strModel); + if (m_models.size() == 0) { + setCurrentModel(QString()); + } + if (m_pView) { + m_pView->resetModel(); + } + qDebug() << "del model" << strModel; + return true; +} + +int Station::modelCount() { + return m_models.size(); +} + +QString Station::getModelUnique(const QString &model) +{ + return QString::number(nId) + _WF_UNIQUE_SPLIT + szCamKey + _WF_UNIQUE_SPLIT + model; +} + +bool Station::updateCurrentModel() +{ + //update currrent model + if (!m_models.contains(m_strCurrentModel)) { + m_strCurrentModel = ""; + } + + if (m_strCurrentModel.isEmpty() && modelCount() != 0) { + m_strCurrentModel = m_models.first(); + } + return true; +} + + +unsigned short Station::swapBigEndian(unsigned short &data) +{ + data = ((data & 0x00FF) << 8) + ((data & 0xFF00) >> 8); + return data; +} + +void Station::setCurrentModel(QString strModel) +{ + if (strModel.isEmpty()) { + qDebug() << "setCurrentModel, model is empty"; + return; + } + else { + m_strCurrentModel = strModel; + } + if (m_pView && !m_strCurrentModel.isEmpty()) { + m_pView->resetModel(); + } + + updateAll(); +} + +void Station::setCurrentModel(int modelIndex) +{ + if (modelCount() != 0 && modelCount() > modelIndex && modelIndex >= 0) { + setCurrentModel(m_models[modelIndex]); + } +} + +bool Station::initCurrentModel() +{ + if (modelCount() <= 1) { + if (modelCount() == 1) { + setCurrentModel(m_models[0]); + } + else if (modelCount() == 0) { + setCurrentModel(""); + } + return true; + } + + return false; +} + +bool Station::updateCurrentModel2Label() +{ + if (m_pLblStationName) { + bool bCali = false; + WfModel *pModel = wfModel(m_strCurrentModel); + if (pModel) { + //bCali = pModel->bCaliState; + bCali = IStandard(m_strCurrentModel); + } + + QString strText = "

%2%3 __ %4 __%5< / span>< / p>< / body>< / html>"; + strText = strText.arg("color:#ff0000;").arg(QObject::tr("工位")) + .arg(nId).arg(m_strCurrentModel == "" ? "?" : m_strCurrentModel) + .arg(bCali == true ? QObject::tr("已标定") : QObject::tr("未标定")); + + m_pLblStationName->setText(strText); + } + + return true; +} + +bool Station::updateAll() +{ + updateCurrentModel(); + updateCurrentModel2Label(); + return true; +} + +bool Station::onAddResult2TextEdit(QString &str) +{ + if (!m_pResultTextShow) { + return false; + } + m_pResultTextShow->append(str); + return true; +} + +QVariant Station::getVariant() +{ + QVariantMap vMap; + vMap.insert("current_model", getModelUnique(m_strCurrentModel)); + return vMap; +} + +void Station::onErrTrig(int nErrCount) +{ + if (nErrCount > s_nTrigWaitTimes * 4) { + qWarning() << "stationid = " << nId << ", error count is bigger than xxx"; + } + else if (s_nTrigWaitTimes * 3 == nErrCount) { + qWarning() << "stationid =" << nId << ",nErrCount = " << nErrCount; + QString str = genTimerInfo() + "camera disconnect!"; + onAddResult2TextEdit(str); + str = genTimerInfo() + "camera count reset, but not trig again!"; + onAddResult2TextEdit(str); + m_pTrigDetector->correct(); + str = genTimerInfo() + "try reopen camera!"; + onAddResult2TextEdit(str); + m_pCoreCtrl->IStopCamera(szCamKey); + m_pCoreCtrl->ICloseCamera(szCamKey); + if (m_pWfCtrl->IBatchModel()) { + changeImageFolder(""); + } + bool bb = m_pCoreCtrl->IOpenCamera(szCamKey); + str = QString("reopen_%1").arg(bb); + onAddResult2TextEdit(str); + bool b = m_pCoreCtrl->IStartCamera(szCamKey); + if (b) { + str = genTimerInfo() + "try reopen successful!"; + m_pCoreCtrl->ISnapImage(QStringList()<IBatchModel()) { + trigImage(); + } + + qDebug() << "stationid = " << nId << ", " << str; + } + else if (s_nTrigWaitTimes == nErrCount || s_nTrigWaitTimes * 2 == nErrCount) { + qWarning() << "camera trig failed!"; + QString str = genTimerInfo() + "camera trig failed!"; + onAddResult2TextEdit(str); + str = genTimerInfo() + "try to trig again!"; + onAddResult2TextEdit(str); + + QStringList strList = m_pCoreCtrl->ICameraKeys(); + if (strList.contains(szCamKey)) + m_pCoreCtrl->ISnapImage(QStringList() << szCamKey); + qWarning() << str; + } + else { + qDebug() << "stationid = " << nId << ", wait for image result " << nErrCount; + } +} + +int Station::stationId() +{ + return nId; +} + +QString Station::model(int index) +{ + if (index >= 0 && index < m_models.size()) { + return m_models.at(index); + } + return QString(); +} + +QStringList Station::modelList() +{ + return m_models; +} + +void Station::revResult() +{ + emit(sgaddRevCount(1)); + //m_pTrigDetector->addRevCount(); + if (m_pWfCtrl->IBatchModel()) { + + } +} + +bool Station::trigImage(const QString folder/* = QString()*/) +{ + if (!folder.isEmpty()) { + m_pCoreCtrl->IStopCamera(szCamKey); + m_pCoreCtrl->ICloseCamera(szCamKey); + changeImageFolder(folder); + + TP_CAMERA_OPTION optCam; + m_pCoreCtrl->ICameraOptionByKey(szCamKey, optCam); + if (optCam.status != TP_CAMERA_STATUS_OPENED && + optCam.status != TP_CAMERA_STATUS_STARTED){ + if (m_pCoreCtrl->IOpenCamera(szCamKey)) + { + bool b = m_pCoreCtrl->IStartCamera(szCamKey); + } + } + } + else { + if (m_pWfCtrl->IBatchModel()) { + + } + } + TP_CAMERA_OPTION optCam; + m_pCoreCtrl->ICameraOptionByKey(szCamKey, optCam); + if (optCam.status != TP_CAMERA_STATUS_OPENED && + optCam.status != TP_CAMERA_STATUS_STARTED){ + if (m_pCoreCtrl->IOpenCamera(szCamKey)) + { + bool b = m_pCoreCtrl->IStartCamera(szCamKey); + } + } + + //filter + if (!m_pTrigDetector->filterTrig()) { + return false; + } + // check + if (!m_pTrigDetector->isWaitForTrig()) { + return false; + } + + QString str = genTimerInfo(); + //m_pTrigDetector->addTrigCount(); + emit(sgaddTrigCount(1)); + str += "trig succussful: " + QString::number(m_pTrigDetector->getTrigCount()); + emit sgPrint2Window(str); + qDebug() << str; + QStringList strList = m_pCoreCtrl->ICameraKeys(); + if (strList.contains(szCamKey)) + { + m_pCoreCtrl->ISnapImage(QStringList() << szCamKey); + SYSLOG_STATUS << QString("●工位%1:相机触发拍照,型号为[%2],型号索引ID为[%3],时间:%4") + .arg(stationId()) + .arg(m_strCurrentModel) + .arg(m_CurrentPLCIndex) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + return true; +} + +bool Station::setWidget(QString str, QWidget *pWgt) +{ +// if (str.startsWith(_WF_CAM_LISTWIDGET_PREFIX)) { + m_pView = new ModelView(qobject_cast(pWgt), this); +// } +// else if (str.startsWith(_WF_LABEL_IMAGE_SHOW_PREFIX)) { +// m_pLblImage = qobject_cast(pWgt); +// } +// else if (str.startsWith(_WF_LABEL_STATION_NAME_PREFIX)) { +// m_pLblStationName = qobject_cast(pWgt); +// updateCurrentModel2Label(); +// } +// else if (str.startsWith(_WF_TEXT_EDIT_RESULT_PREFIX)) { +// m_pResultTextShow = qobject_cast(pWgt); +// if (m_pResultTextShow) { +// m_pResultTextShow->clear(); +// QTextDocument *pDoc = m_pResultTextShow->document(); +// pDoc->setMaximumBlockCount(200); +// m_pResultTextShow->append(Station::genTimerInfo() + "start successful"); +// } +// } +// else { +// return false; +// } + + return true; +} + +bool Station::IStandard(QString strModel) +{ + if (!m_pWfCtrl) + return false; + return m_pWfCtrl->IStandard(stationId(), strModel); +} + +bool Station::sendResult(double angleValue) +{ + if (m_pPort) + { + const int c_nDataNum = 8; + const double c_dMaxAngle = 360.0; + if (angleValue < 0) { + angleValue += c_dMaxAngle; + } + + unsigned short data[c_nDataNum] = { 0 }; + data[2] = int(angleValue); + double tmp = angleValue - int(angleValue); + tmp *= 1000.0; + data[3] = int(tmp); + SComFrame sendFram; + sendFram.cmd = m_nCmd; + sendFram.data1 = data[0]; + sendFram.data2 = data[1]; + sendFram.data3 = data[2]; + sendFram.data4 = data[3]; + sendFram.data5 = data[4]; + sendFram.data6 = data[5]; + sendFram.data7 = data[6]; + sendFram.data8 = data[7]; + m_pPort->enquequeData(sendFram); + + if (angleValue >= c_dMaxAngle) { + IImageObject::DATA_TO_COMM_HEAD head2; + unsigned short data2[c_nDataNum] = { 0 }; + data2[0] = 6 + nId - 1; + data2[1] = 50; +// for (int i = 0; i < c_nDataNum; i++) { +// swapBigEndian(data2[i]); +// } + SComFrame sendFram2; + sendFram2.cmd = 0x48; + sendFram2.data1 = data2[0]; + sendFram2.data2 = data2[1]; + sendFram2.data3 = data2[2]; + sendFram2.data4 = data2[3]; + sendFram2.data5 = data2[4]; + sendFram2.data6 = data2[5]; + sendFram2.data7 = data2[6]; + sendFram2.data8 = data2[7]; + m_pPort->enquequeData(sendFram2); + } + + } + else + { + const int c_nDataNum = 8; + const double c_dMaxAngle = 360.0; + if (angleValue < 0) { + angleValue += c_dMaxAngle; + } + + unsigned short data[c_nDataNum] = { 0 }; + data[2] = int(angleValue); + double tmp = angleValue - int(angleValue); + tmp *= 1000.0; + data[3] = int(tmp); +// QString strPrint = "data1 = %1, data2 = %2"; +// strPrint = strPrint.arg(data[2]).arg(data[3]); + for (int i = 0; i < c_nDataNum; i++) { + swapBigEndian(data[i]); + } + IImageObject::DATA_TO_COMM_HEAD head; + head.cmd = m_nCmd; + QByteArray comArray = m_strComName.toLatin1(); + head.commNames = comArray.data(); + head.pfCallback = NULL; + head.pContext = this; + if (!m_pCoreCtrl) { + qWarning() << "send result, core ctrl is null"; + return false; + } + //m_pCoreCtrl->IDataToComm(head, (char*)data, c_nDataNum * 2); + + // send io ctrl + if (angleValue >= c_dMaxAngle) { + IImageObject::DATA_TO_COMM_HEAD head2; + unsigned short data2[c_nDataNum] = { 0 }; + head2.cmd = m_nCmd; + head2.commNames = comArray.data(); + head2.pContext = NULL; + head2.pfCallback = NULL; + head2.cmd = 0x48; + data2[0] = 6 + nId - 1; + data2[1] = 50; + for (int i = 0; i < c_nDataNum; i++) { + swapBigEndian(data2[i]); + } + //m_pCoreCtrl->IDataToComm(head2, (char*)data2, c_nDataNum * 2); + qDebug() << "send result, angle is bigger than 360"; + } + } + return true; +} + +void Station::setComInfo(QString strName, int nCmd) +{ + m_strComName = strName; + m_nCmd = nCmd; +} + +Q_SLOT void Station::showImage(const QImage &img) +{ +// if (m_pImgViewer) +// { +// QImage imgShow = img; +// m_pImgViewer->setImg(imgShow); +// return; +// } + + if (!m_pLblImage) { + qDebug() << "label image is null"; + return; + } + qDebug() << "show image"; + //QRect rt = m_pLblImage->rect(); + if (img.isNull()) + { + QString str = QObject::tr("检测到错误: 模型与图像尺寸不匹配,请重新标定模型!!!"); + m_pLblImage->setText(str); + } + else + { + QRect rt = m_pLblImage->rect(); + int h = img.height(); + int w = img.width(); + float d = w*1.0 / h; + if (d > 0) + { + int h2 = rt.width() / d; + rt.setHeight(h2); + } + QImage imgShow = img.scaled(QSize(rt.size())); + m_pLblImage->setPixmap(QPixmap::fromImage(imgShow)); + } + +} + +QString Station::currentSelectModel() +{ + if (m_pView) { + int row = m_pView->currentRow(); + if (modelCount() > row) { + return model(row); + } + } + return QString(); +} + +QString Station::genTimerInfo(QString strPre /*= ""*/, QString strFormat /*= ""*/, QString strEnd /*= ": "*/) +{ + QTime dTime = QTime::currentTime(); + QString strTime = dTime.toString() + ":" + QString::number(dTime.msec()) + strEnd; + return strTime; +} + +QString Station::stationKey() +{ + return szCamKey; +} + +QString Station::stationShowName() +{ + return szShow; +} + +WfModel * Station::wfModel(QString str) +{ + return m_pWfCtrl->IGetModelInfo(nId, str); + +} + +bool Station::isWorkingOk() +{ + return true; +} + +QString Station::modelByPlcCmd(int nIndex) +{ + //qWarning() << "modelByPlcCmd, nIndex is :" << nIndex; + for (int i = 0; i < m_models.size(); i++) { + WfModel *pModel = wfModel(m_models.at(i)); + if (pModel && pModel->nIndex == nIndex) { + QString strModel =m_models.at(i); + if (currentRunningModel() == strModel) + { + m_CurrentPLCIndex = nIndex; + return strModel; + } + m_CurrentPLCIndex = nIndex; + return strModel; + } + } + m_CurrentPLCIndex = 0; + qWarning() << "modelByPlcCmd, nIndex is invalid; index = " << nIndex; + return QString(); +} + +bool Station::startBatchTest(QString strBasePath) +{ + if (m_pWfCtrl->IBatchModel()) { + + return true; + } + + return false; +} + +bool Station::changeImageFolder(const QString str) +{ + TP_CAMERA_OPTION camOpt; + camOpt.uniqueName = szCamKey; + m_pCoreCtrl->ICameraOption(szCamKey, camOpt); + camOpt.folder = str; + m_pCoreCtrl->ISetCameraOption(szCamKey, camOpt); + if (m_pWfCtrl->IBatchModel()) { + } + + return true; +} diff --git a/src/lpMain/Station.h b/src/lpMain/Station.h new file mode 100644 index 0000000..211bb89 --- /dev/null +++ b/src/lpMain/Station.h @@ -0,0 +1,96 @@ +#pragma once +#include "qlist.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qlabel.h" +#include "qtextedit.h" +#include "IStation.h" +enum EM_ADD_DELETE_OPERATION { + emADD = 0, emDELETE = 1 +}; + + + +#define _WF_LIST_SPLIT "_-_" +#define _WF_CURRENT_OBJECT_PREFIX "current_object_" +#define _WF_READ_FROM_PLC_STATE "read_from_plc_state" +#define _WF_CALI_STATE_STATION_PREFIX "cali_state_station_" + +class ICoreCtrl; +class QSqliteWheelHubWf; +class IWfCtrl; +class Station : public IStation +{ + Q_OBJECT +public: + Station(QSqliteWheelHubWf *p1, ICoreCtrl *p2, IWfCtrl * p3); + virtual ~Station(); + virtual int stationId(); + virtual QString stationKey(); + virtual QString stationShowName(); + virtual void setCamInfo(int nId, int alg, QString uniqueName, QString showName); + virtual void setComInfo(QString strName, int nCmd); + virtual QVariant getVariant(); + virtual void revResult(); + virtual bool trigImage(const QString folder = QString()); + virtual bool isWorkingOk(); + + virtual QString currentRunningModel() const { return m_strCurrentModel; } + virtual QString currentSelectModel(); + virtual void setCurrentModel(QString strModel); + virtual void setCurrentModel(int index); + virtual int modelCount(); + virtual QStringList modelList(); + virtual QString model(int index); + virtual QString modelByPlcCmd(int nIndex); + virtual WfModel *wfModel(QString); + virtual bool addModel(QString strModel); + virtual bool delModel(QString strModel); + + virtual bool sendResult(double); + virtual bool setWidget(QString str, QWidget *pWgt); + virtual bool IStandard(QString strModel); + + Q_SLOT void showImage(const QImage &img); + Q_SLOT bool onAddResult2TextEdit(QString &str); + Q_SLOT void onErrTrig(int); + + virtual bool startBatchTest(QString strBasePath); + bool changeImageFolder(const QString); + + static unsigned short swapBigEndian(unsigned short &data); + static QString genTimerInfo(QString strPre = "", QString strFormat = "", QString strEnd = ": "); +private: + class CTrigDetector *m_pTrigDetector; + class ModelView *m_pView; + QLabel *m_pLblImage{ NULL }; + QLabel *m_pLblStationName; + QTextEdit *m_pResultTextShow; + int nId; + int nAlgorithm; + QString szShow; + QString szCamKey; + + QString m_strComName; + int m_nCmd; + + static int s_nTrigWaitTimes; + QStringList m_models; + QString m_strCurrentModel; + int m_CurrentPLCIndex{ 0 }; + + QSqliteWheelHubWf *m_pDbWf; + ICoreCtrl *m_pCoreCtrl; + IWfCtrl *m_pWfCtrl; + class LabelManTest4Ui *m_pBatchTest; +public: + QString getModelUnique(const QString &model); + bool updateCurrentModel2Label(); + bool initCurrentModel(); + Q_SLOT bool updateAll(); + bool updateCurrentModel(); +signals: + void sgaddTrigCount(int ); + void sgaddRevCount(int); +}; + diff --git a/src/lpMain/TrigDetector.cpp b/src/lpMain/TrigDetector.cpp new file mode 100644 index 0000000..688a164 --- /dev/null +++ b/src/lpMain/TrigDetector.cpp @@ -0,0 +1,184 @@ +#include "TrigDetector.h" +#include "QDebug" +#include "qtimer.h" +#include "qdatetime.h" + + +CTrigDetector::CTrigDetector(QObject *parent) :QObject(parent) ,m_nMaxErrorCount(20), nTickCount(10) +{ + lastTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + nCount = 0; + m_nTrigCount = m_nReceiveCount = 0; + m_nMaxTrigRecDiff = 1; + m_nCheckInterval = 4000; + m_nErrorTime = 2000; + m_nTimerID = -1; + m_pTimer = new QTimer(this); + m_setting = new QSettings(".\\user\\systemfile.ini", QSettings::IniFormat); + readSettingFile(); + connect(m_pTimer, SIGNAL(timeout()), this, SLOT(onChecker())); +} + + +CTrigDetector::~CTrigDetector() +{ + writeSettingFile(); + stop(); + delete m_pTimer; + if (m_setting){ + delete m_setting; + m_setting = NULL; + } +} + +void CTrigDetector::addTrigCount(int n /*= 1*/) +{ + if (m_nTrigCount - m_nReceiveCount >= m_nMaxTrigRecDiff) { + //error + qWarning() << "add trig count error, trig:" << m_nTrigCount << ", rev:" << m_nReceiveCount; + } + else { + m_nTrigCount++; + } +} + +bool CTrigDetector::start() +{ + stop(); + m_pTimer->start(m_nCheckInterval); + return true; +} + +// bool CTrigDetector::restart(int nInterval /*= -1*/) +// { +// if (nInterval > 0) { +// m_nCheckInterval = nInterval; +// } +// qDebug() << "reset trig count"; +// m_nReceiveCount = m_nTrigCount; +// start(); +// return true; +// } + +void CTrigDetector::onChecker() +{ + if (m_nTrigCount < m_nReceiveCount) { + qDebug() << "trig < rece"; + m_nReceiveCount = m_nTrigCount; + qDebug() << "m_nReceiveCount = m_nTrigCoun";// .... + } + else if (m_nReceiveCount < m_nTrigCount) { + m_nTmpErrorCount++; + emit(sgErrorTrig(m_nTmpErrorCount)); + } + else { + m_nTmpErrorCount = 0; + } +} + +bool CTrigDetector::isRunOk() +{ + if (m_nTrigCount == m_nReceiveCount || m_nReceiveCount + 1 == m_nTrigCount) { + return true; + } + return false; +} + +void CTrigDetector::addRevCount(int n /*= 1*/) +{ + m_nReceiveCount++; + m_nTmpErrorCount = 0; + + if (m_nReceiveCount > m_nTrigCount) { + //error + qWarning() << "addRevCount, rev bigger than trig"; + } + qDebug() << "rev result, count = " << m_nReceiveCount; +} + +bool CTrigDetector::stop() +{ + m_pTimer->stop(); + return true; +} + +bool CTrigDetector::isWaitForTrig() +{ + if (m_nReceiveCount == m_nTrigCount) { + qDebug() << "trig successful, trig count:" << m_nTrigCount; + return true; + } + qWarning() << "trig failed, trig count:" << m_nTrigCount << "; rev count:" << m_nReceiveCount; + return false; +} + +bool CTrigDetector::correct() +{ + qDebug() << "correct trig and rev"; + m_nReceiveCount = m_nTrigCount; + m_nTmpErrorCount = 0; + return true; +} + +bool CTrigDetector::filterTrig() +{ + /*static QMap s_mpLastTime; + long long dTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + if (s_mpLastTime.contains(this)) { + long long lastTime = s_mpLastTime.value(this); + s_mpLastTime.take(this); + s_mpLastTime.insert(this, dTime); + long int interval = dTime - lastTime; + + if (interval < 350) { + qWarning() << "trig less than interval"; + if (nCount++ > nTickCount) + { + qWarning() << "correct"; + nCount = 0; + correct(); + return true; + } + return false; + } + } + s_mpLastTime.insert(this, dTime); + nCount = 0; + return true;*/ + + long long dTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + long int interval = dTime - lastTime; + lastTime = dTime; + if (interval < 0) + return true; + if (interval < 350) { + qWarning() << "trig less than interval"; + if (nCount++ > nTickCount) + { + qWarning() << "correct"; + nCount = 0; + correct(); + return true; + } + return false; + } + nCount = 0; + return true; +} + +void CTrigDetector::readSettingFile() +{ + if (m_setting){ + QString m_str = "/TrigDetector/"; + nTickCount = m_setting->value(m_str + "nTickFilter", 10).toInt(); + } +} + +void CTrigDetector::writeSettingFile() +{ + if (m_setting){ + QString m_str = "/TrigDetector/"; + m_setting->setValue(m_str + "nTickFilter", nTickCount); + } +} + diff --git a/src/lpMain/TrigDetector.h b/src/lpMain/TrigDetector.h new file mode 100644 index 0000000..71c48e8 --- /dev/null +++ b/src/lpMain/TrigDetector.h @@ -0,0 +1,69 @@ +#ifndef _TRIGDETECTOR_H_ +#define _TRIGDETECTOR_H_ + +#include +#include + +class CTrigDetector : public QObject +{ + Q_OBJECT +public: + CTrigDetector(QObject *parent =0); + ~CTrigDetector(); + + + void setCheckInterval(int nInterval) { + m_nCheckInterval = nInterval; + } + void setErrorTime(int nTime) { + m_nErrorTime = nTime; + } + int getCheckInterval() const { + return m_nCheckInterval; + } + int getErrorTime() const { + return m_nErrorTime; + } + int getCheckErrorCount() const { + return m_nTmpErrorCount; + } + int getTrigCount() const { + return m_nTrigCount; + } + int getRevCount() const { + return m_nReceiveCount; + } + bool correct(); + bool start(); + bool stop(); + bool isRunOk(); + bool isWaitForTrig(); + bool filterTrig(); + void readSettingFile(); + void writeSettingFile(); +signals: + void sgErrorTrig(int); + void sgSnapImage(); +public slots: + void onChecker(); + void addTrigCount(int n = 1); + void addRevCount(int n = 1); +private: + int m_nCheckInterval{0}; + int m_nErrorTime{0}; + int m_nTrigCount{0}; + int m_nReceiveCount{0}; + const int m_nMaxErrorCount; + int m_nTmpErrorCount{0}; + int m_nMaxTrigRecDiff{0}; + int m_nTimerID{0}; + class QTimer *m_pTimer{ nullptr }; + + QSettings *m_setting{ nullptr }; + int nTickCount{0}; + + int nCount{0}; + long long lastTime{0}; +}; + +#endif \ No newline at end of file diff --git a/src/lpMain/WfColossus.cpp b/src/lpMain/WfColossus.cpp new file mode 100644 index 0000000..68f0cb5 --- /dev/null +++ b/src/lpMain/WfColossus.cpp @@ -0,0 +1,380 @@ +#include "WfColossus.h" +#include "lpbengine.h" +#include "WfModel.h" +#include "lpbengine.h" + +#define WF_MODEL_TEMPLATE_NAME "TEMPLATE" +#pragma execution_character_set("utf-8") +WfColossus::WfColossus(IDetectorEngine*p) :m_IsModify(false) +{ + m_pDetEngine = p; + + QSettings setTaskInfo("TaskInfo.ini", QSettings::IniFormat); + int size = setTaskInfo.value("StationNum").toInt(); + for (int nIndex = 1; nIndex <= size; nIndex++){ + QString strKey = QString("Station_%1").arg(nIndex); + QString strModel = setTaskInfo.value(strKey).toString(); + if (!strModel.isEmpty()){ + m_CurRunTaskName[nIndex] = strModel; + } + } +} + + +WfColossus::~WfColossus() +{ +} + +bool WfColossus::addModel(int nIndex, QString strModel) +{ + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + qWarning() << "add colossus, all solution is null"; + return false; + } + + bool bCreate = false; + QString strName = QString("%1##%2").arg(nIndex).arg(WF_MODEL_TEMPLATE_NAME); + IDetectorTask* pTask = pSolutions->GetTask(strName); + if (pTask) + { + IDetectorTask *pNew = pSolutions->AddAndCopyTask(pTask); + PLP_DETECTOR_TASK tTsk = pNew->GetTaskInfo(); + tTsk->strName = combineWithSymbol(QString::number(nIndex), strModel); + tTsk->bIsRun = false; + pNew->SetTaskInfo(tTsk); + bCreate = true; + } + + if (!bCreate) { + LP_DETECTOR_TASK tTsk; + tTsk.strName = combineWithSymbol(QString::number(nIndex), strModel); + IDetectorTask *pTaskNew = pSolutions->AddTask(&tTsk); + pTaskNew->LoadFile(""); + bCreate = true; + } + + m_IsModify = true; + return bCreate; +} + +bool WfColossus::delModel(int nIndex, QString strModel) +{ + IDetectorSolution* pSolutions = ptrRunSolution(); + QString strName = QString("%1##%2").arg(nIndex).arg(strModel); + if (pSolutions) + { + return pSolutions->DeleteTask(strName); + } + + return false; +} + +QMap WfColossus::allTask() +{ + QMap lstDst; + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + qWarning() << "allTask, all solution is null"; + return lstDst; + } + + QStringList strKeys = pSolutions->GetAllTaskNames(); + + for (int i = 0; i < strKeys.size(); i++) { + IDetectorTask* pTask = pSolutions->GetTask(strKeys[i]); + if (!pTask) + continue; + PLP_DETECTOR_TASK tInfo = pTask->GetTaskInfo(); + QStringList l = splitBySymbol(tInfo->strName); + if (l.size() != 2) { + continue; + } + if (l.last() == WF_MODEL_TEMPLATE_NAME) { + continue; + } + WfModel *pInfo = new WfModel; + QVariantMap vMap = tInfo->property.toMap(); + pInfo->bCaliState = vMap.value("model_cali").toBool(); + pInfo->nCount = vMap.value("model_count").toInt();; + pInfo->nIndex = vMap.value("model_plc").toInt();; + pInfo->strCreateTime = vMap.value("model_time").toString();; + lstDst.insert(tInfo->strName, pInfo); + } + return lstDst; +} + +QStringList WfColossus::allRunningTask() +{ + QStringList lstDst; + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + qDebug() << "allRunningTask pSolutions is null"; + return lstDst; + } + QMap m = pSolutions->GetRunningTask(); + for (QMap::iterator it = m.begin(); it != m.end(); ++it){ + if (*it) + { + lstDst.append((*it)->GetTaskInfo()->strName); + } + } + if (lstDst.size() <= 0) + { + for (QMap::iterator it = m_CurRunTaskName.begin(); it != m_CurRunTaskName.end(); ++it){ + QString nerName = QString("%1##%2").arg(it.key()).arg(*it); + lstDst.append(nerName); + } + } + return lstDst; +} + +bool WfColossus::selModel(int nIndex, QString strModel) +{ + QString strName = combineWithSymbol(QString::number(nIndex), strModel); + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + return false; + } + + m_CurRunTaskName[nIndex] = strModel; + + pSolutions->SetRunTask(nIndex, strName); + + + QSettings setTaskInfo("TaskInfo.ini", QSettings::IniFormat); + setTaskInfo.setValue("StationNum", m_CurRunTaskName.size()); + for (QMap::iterator it = m_CurRunTaskName.begin(); it != m_CurRunTaskName.end(); ++it){ + + setTaskInfo.setValue(QString("Station_%1").arg(it.key()), *it); + } + emit sgSetModel(nIndex, strModel); + +// IDetectorTask *pTasks[c_nMaxTask]; +// int n = pSolutions->EnumTask(pTasks, c_nMaxTask); +// //qDebug() << "all task count = " << n; +// bool bChanged = false; +// for (int i = 0; i < n; i++) { +// IDetectorTask* pTask = pTasks[i]; +// PLP_DETECTOR_TASK tInfo = pTask->GetTaskInfo(); +// QString str = tInfo->strName; +// QStringList l = splitBySymbol(str); +// if (l.size() != 2) { +// continue; +// } +// if (l.first().toInt() == nIndex) { +// if (tInfo->bIsRun == true && tInfo->strName != strName) { +// bChanged = true; +// } +// pTask->GetTaskInfo()->bIsRun = false; +// if (l.last() == strModel) { +// pTask->GetTaskInfo()->bIsRun = true; +// } +// } +// } +// if (bChanged == true) { +// int a = 0; +// int nID = ptrSolMgr()->GetRunSolution()->GetID(); +// if (nID == 2) +// int b = 0; +// qDebug() << " change models ID:" << nID; +// ptrSolMgr()->SetRunSolution(nID); +// } + //if () + /*m_IsModify = true;*/ + return true; +} + +bool WfColossus::modModel(int nIndex, QString strNewName,QString strOldName) +{ + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + qWarning() << "mod colossus, all solution is null"; + return false; + } +// IDetectorTask *pTasks[c_nMaxTask]; +// int n = pSolutions->EnumTask(pTasks, c_nMaxTask); + bool bModify = false; +// for (int i = 0; i < n; i++) { +// IDetectorTask *pTask = pTasks[i]; +// QString str = pTask->GetTaskInfo()->strName; +// QStringList l = str.split("##"); +// if (l.size() != 2) { +// continue; +// } +// if (l.first().toInt() == nIndex && l.last() == strOldName) { +// IDetectorTask *pNew = pSolutions->AddAndCopyTask(pTask); +// PLP_DETECTOR_TASK tTsk = pNew->GetTaskInfo(); +// tTsk->strName = combineWithSymbol(QString::number(nIndex), strNewName); +// tTsk->bIsRun = false; +// pNew->SetTaskInfo(tTsk); +// bModify = true; +// break; +// } +// } + +// if (!bModify) { +// LP_DETECTOR_TASK tTsk; +// tTsk.strName = combineWithSymbol(QString::number(nIndex), strNewName); +// IDetectorTask *pTaskNew = pSolutions->AddTask(&tTsk); +// bModify = true; +// } + delModel(nIndex, strOldName); + m_IsModify = true; + return bModify; +} + +bool WfColossus::bStandard(int nIndex ,QString strModel) +{ + IDetectorSolution* pSolutions = ptrRunSolution(); + if (pSolutions) + { + QString strName = QString("%1##%2").arg(nIndex).arg(strModel); + IDetectorTask* pTask = pSolutions->GetTask(strName); + if (pTask) + { + bool b = pTask->GetTaskInfo()->templateImg.data == NULL; + if (b == true) + return false; + else + return true; + } + } + return false; +} + +bool WfColossus::saveTask(QMap infos) +{ + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + qWarning() << "running solution is null "; + return false; + } + for (QMap::iterator it = infos.begin(); it != infos.end(); it++) + { + QString strName = it.key(); + IDetectorTask *pTask = pSolutions->GetTask(strName); + if (pTask) + { + PLP_DETECTOR_TASK tInfo = pTask->GetTaskInfo(); + QVariantMap vMap = tInfo->property.toMap(); + vMap.insert("model_plc", (*it)->nIndex); + vMap.insert("model_count", (*it)->nCount); + vMap.insert("model_time", (*it)->strCreateTime); + tInfo->property = vMap; + pTask->SetTaskInfo(tInfo); + } + } + ptrSolMgr()->Save(); + return true; +} + +QStringList WfColossus::getStationModels(int nIndex) +{ + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + return QStringList(); + } + QStringList strList; +// IDetectorTask *pTasks[c_nMaxTask]; +// int n = pSolutions->EnumTask(pTasks, c_nMaxTask); +// bool bModify = false; +// for (int i = 0; i < n; i++) { +// IDetectorTask *pTask = pTasks[i]; +// QString str = pTask->GetTaskInfo()->strName; +// QStringList l = str.split("##"); +// if (l.size() != 2) { +// continue; +// } +// if (l.first().toInt() == nIndex) { +// strList.append(l.last()); +// } +// } + return strList; +} + +QStringList WfColossus::splitBySymbol(QString str) +{ + return str.split("##"); +} + +QString WfColossus::combineWithSymbol(QString s1, QString s2) +{ + return s1 + "##" + s2; +} + +class IDetectorSolutionMgr * WfColossus::ptrSolMgr() const +{ + void * p; + m_pDetEngine->GetDataInterface(SOLUTIONMGR, &p); + return (IDetectorSolutionMgr*)p; +} + +class IDetectorSolution * WfColossus::ptrRunSolution() const +{ + IDetectorSolutionMgr*pMgr = ptrSolMgr(); + if (pMgr) { + return pMgr->GetRunSolution(); + } + qWarning() << "solution mgr is null"; + return NULL; +} + +// bool WfColossus::getOnlineModel() const +// { +// IDetectorSystemInfo * p; +// m_pDetEngine->GetDataInterface(SYSTEMINFO, (void**)&p); +// if (p) { +// return p->GetValue("OnlineMode").toBool(); +// } +// return false; +// } +// +// void WfColossus::setOnlineModel(bool b) +// { +// IDetectorSystemInfo * p; +// m_pDetEngine->GetDataInterface(SYSTEMINFO, (void**)&p); +// if (p) { +// p->SetValue("OnlineMode", b); +// } +// } + +bool WfColossus::getBatchModel() const +{ + IDetectorSystemInfo * p; + m_pDetEngine->GetDataInterface(SYSTEMINFO, (void**)&p); + if (p) { + return p->GetValue("wf_batch_test", false).toBool(); + } + return false; +} + +bool WfColossus::updateModelInfo(QMap &infos) +{ + IDetectorSolution* pSolutions = ptrRunSolution(); + if (!pSolutions) { + qWarning() << "updateModelInfo solution is null "; + return false; + } +// IDetectorTask *pTasks[c_nMaxTask]; +// int n = pSolutions->EnumTask(pTasks, c_nMaxTask); +// for (int i = 0; i < n; i++) { +// IDetectorTask *pTask = pTasks[i]; +// if (!pTask) { +// qWarning() << "updateModelInfo, task is null " << i; +// continue; +// } +// QString str = pTask->GetTaskInfo()->strName; +// WfModel *pModel = infos.value(str); +// if (!pModel) { +// continue; +// } +// PLP_DETECTOR_TASK tInfo = pTask->GetTaskInfo(); +// QVariantMap vMap = tInfo->property.toMap(); +// pModel->bCaliState = vMap.value("model_cali").toBool(); +// } + + return true; +} + + diff --git a/src/lpMain/WfColossus.h b/src/lpMain/WfColossus.h new file mode 100644 index 0000000..9ee07cc --- /dev/null +++ b/src/lpMain/WfColossus.h @@ -0,0 +1,46 @@ +#ifndef _WFCOLOSSUS_H_ +#define _WFCOLOSSUS_H_ +#include +#include +#include +class IDetectorEngine; +class WfColossus:public QObject +{ + Q_OBJECT +public: + WfColossus(IDetectorEngine* p); + ~WfColossus(); + bool addModel(int, QString); + bool delModel(int, QString); + bool selModel(int, QString); + bool modModel(int nIndex, QString strNewName, QString strOldName); + bool bStandard(int ,QString); + class IDetectorSolutionMgr *ptrSolMgr() const; + class IDetectorSolution *ptrRunSolution() const; + QMap allTask(); + + bool updateModelInfo(QMap &info); +// bool getOnlineModel() const; +// void setOnlineModel(bool b); + bool getBatchModel() const; + QStringList allRunningTask(); + bool saveTask(QMap); + + void setModifyState(bool bFlag){ m_IsModify = bFlag; }; + bool getModifyState() const{ return m_IsModify; }; + void ReSetModifyState() { m_IsModify = false; }; + + QStringList getStationModels(int nIndex); +signals: + void sgSetModel(int, QString); +private: + QStringList splitBySymbol(QString); + QString combineWithSymbol(QString, QString); + + bool m_IsModify{ false };//Ƿ޸ ʾǷҪ +private: + IDetectorEngine *m_pDetEngine{ nullptr }; + QMap m_CurRunTaskName; +}; + +#endif \ No newline at end of file diff --git a/src/lpMain/WfCtrl.cpp b/src/lpMain/WfCtrl.cpp new file mode 100644 index 0000000..dd55a8b --- /dev/null +++ b/src/lpMain/WfCtrl.cpp @@ -0,0 +1,395 @@ +#include "WfCtrl.h" +#include "Station.h" +#include "QSqliteWheelHubWf.h" +#include "iCoreCtrl.h" +#include "baseStruct.h" +#include "lpbengine.h" +#include "WfModel.h" +#include "WfColossus.h" +#include "WorkChecker.h" +#include "QZkJsonParser.h" + +#pragma execution_character_set("utf-8") +CWfCtrl::CWfCtrl(ICoreCtrl* p1, WfColossus* p3) +{ + m_nOnlineMode = false; + + m_pConnectChecker = new WorkChecker; + m_pCoreCtrl = p1; + m_pColossus = p3; + m_setting = new QSettings(QCoreApplication::applicationDirPath()+"\\user\\systemfile.ini", QSettings::IniFormat); + readSettingFile(); + QString path; + m_pWfDb = new QSqliteWheelHubWf(path + "//user//"); + initGolbal(); + initStation(); + initModel(); + initCurrentModel(); + QSettings systemIniFile("systemInfo.ini", QSettings::IniFormat); + m_nOnlineMode = systemIniFile.value("OnlineMode").toBool(); + { + QString strErrorPath = QCoreApplication::applicationDirPath()+"/errorImage/"; + QDiskCleanThread *pDCleanThread = new QDiskCleanThread; + pDCleanThread->setUseFlag(nCheckThreadEable); + pDCleanThread->setDays(nCheckImgFileDays); + pDCleanThread->SetImgStorageFolder(strErrorPath); + pDCleanThread->setMiniSize(nMinSpaceSize); + pDCleanThread->start(); + m_pDCThreadList.append(pDCleanThread); + + + QString strRltImgPath = QCoreApplication::applicationDirPath() + "/DBFiles/Images/"; + QDiskCleanThread *pCleanDir = new QDiskCleanThread; + pCleanDir->setModel(CleanDir); + pCleanDir->setSleepS(3600); + pCleanDir->setUseFlag(nCheckThreadEable); + pCleanDir->setDays(nCheckDirDays); + pCleanDir->SetImgStorageFolder(strRltImgPath); + pCleanDir->setMiniSize(nMinSpaceSize); + pCleanDir->start(); + m_pDCThreadList.append(pCleanDir); + } +} + +CWfCtrl::~CWfCtrl() +{ + writeSettingFile(); + if (m_pDCThreadList.size() > 0){ + for (int nIndex = 0; nIndex < m_pDCThreadList.size(); nIndex++) + { + QDiskCleanThread *pDCThread = m_pDCThreadList.at(nIndex); + if (pDCThread) + { + pDCThread->ExitThread(); + pDCThread->quit(); + pDCThread->wait(); + delete pDCThread; + pDCThread = NULL; + } + } + } + m_pDCThreadList.clear(); + //m_pColossus->setOnlineModel(m_nOnlineMode); + delete m_pConnectChecker; + delete m_pWfDb; + qDeleteAll(m_pStationMap); + m_pStationMap.clear(); + qDeleteAll(m_mpModels); + m_mpModels.clear(); + qDebug() << "~CWfCtrl"; + if (m_setting){ + delete m_setting; + m_setting = NULL; + } +} + +void CWfCtrl::ISetOnlineModel(bool b) +{ + m_nOnlineMode = b; + QSettings systemIniFile("systemInfo.ini", QSettings::IniFormat); + systemIniFile.setValue("OnlineMode", m_nOnlineMode); +} + +void CWfCtrl::initGolbal() +{ + //m_nOnlineMode = m_pColossus->getOnlineModel(); + + // get comm name +// QString strConfig = m_pMainBack->IJsonUser().value("config").toString(); +// QString strPath = m_pMainBack->IDllPaths().size() == 0 ? ".\\" : m_pMainBack->IDllPaths().first(); +// QJsonObject objJson = QZkJsonParser::ReadJsonObject(strPath + "\\" + strConfig + "\\communicate.json"); +// if (!objJson.empty()) { +// QJsonValue jValue = objJson["COM"]; +// if (jValue.isArray()) { +// QJsonArray jArray = jValue.toArray(); +// for (int i = 0; i < jArray.size(); ++i) { +// if (!jArray[i].isObject()) { +// continue; +// } +// QJsonObject objTmp = jArray[i].toObject(); +// m_strCommName = objTmp.value("name").toString(); +// qDebug() << "comm name is " << m_strCommName; +// break; +// } +// } +// } +// else { +// qWarning() << "can not find communicate.json"; +// } +} + +bool CWfCtrl::IOnlineMode() +{ + return m_nOnlineMode; +} + +IStation* CWfCtrl::IGetStationById(int id) +{ + for each (IStation* var in m_pStationMap) { + if (var->stationId() == id) { + return var; + } + } + return NULL; +} + +IStation* CWfCtrl::IGetStationByKey(QString key) +{ + return m_pStationMap.value(key); +} + +QStringList CWfCtrl::IGetStationKeys() +{ + return m_pStationMap.keys(); +} + +bool CWfCtrl::initModel() +{ + m_mpModels = m_pColossus->allTask(); + QStringList lstModels = m_mpModels.keys(); + for (int i = 0; i < lstModels.size(); i++) { + QStringList lst = lstModels.at(i).split("##"); + if (lst.size() != 2) { + continue; + } + int nId = lst.first().toInt(); + IStation* pStation = IGetStationById(nId); + if (pStation) { + pStation->addModel(lst.last()); + } + } + return true; +} + +bool CWfCtrl::initStation() +{ + QStringList camKeys = m_pCoreCtrl->ICameraKeys(); + QString strCom = "COM3"; + for (QStringList::iterator it = camKeys.begin(); it != camKeys.end(); ++it) { + TP_CAMERA_OPTION camOpt; + if (!m_pCoreCtrl->ICameraOptionByKey(*it, camOpt)) { + continue; + } + IStation *pStation = new Station(m_pWfDb, m_pCoreCtrl, this); + pStation->setCamInfo(camOpt.id, camOpt.algorithm, camOpt.uniqueName, camOpt.showName); + pStation->setComInfo(strCom, 0x46 + camOpt.id - 1); + m_pStationMap.insert(camOpt.uniqueName, pStation); + + if (camOpt.deviceType != 100)//虚拟相机模式下不开启定时删除图像的模式 + { + QDiskCleanThread *pDCleanThread = new QDiskCleanThread; + pDCleanThread->setUseFlag(nCheckThreadEable); + pDCleanThread->setDays(nCheckImgFileDays); + //QString strErrorPath = QCoreApplication::applicationDirPath() + "//errorImage//"; + //pDCleanThread->SetImgStorageFolder(strErrorPath); + pDCleanThread->SetImgStorageFolder(camOpt.folder); + pDCleanThread->setMiniSize(nMinSpaceSize); + pDCleanThread->start(); + m_pDCThreadList.append(pDCleanThread); + } + } + return true; +} + +bool CWfCtrl::initCurrentModel() +{ + QStringList lstModels = m_pColossus->allRunningTask(); + for each (IStation* var in m_pStationMap) { + bool bFlag = false; + for (int i = 0; i < lstModels.size(); i++) { + QStringList lst = lstModels.at(i).split("##"); + if (lst.size() != 2) { + continue; + } + if (lst.first().toInt() == var->stationId()) { + if (var->modelList().contains(lst.last())) { + var->setCurrentModel(lst.last()); + m_pColossus->selModel(var->stationId(), lst.last()); + bFlag = true; + } + break; + } + } + if (!bFlag) { + QString strModel = var->modelCount() == 0 ? QString() : var->model(0); + var->setCurrentModel(strModel); + m_pColossus->selModel(var->stationId(), strModel); + } + } + m_pColossus->ReSetModifyState(); + return true; +} + +bool CWfCtrl::IAddModel(int nStation, QString strModel) +{ + IStation *pStation = IGetStationById(nStation); + if (!pStation) { + return false; + } + bool b = pStation->addModel(strModel); + if (!b) { + return false; + } + if (m_pColossus) { + m_pColossus->addModel(nStation, strModel); + } + m_mpModels.insert(genModelName(nStation, strModel), new WfModel); + qDebug() << "CWfCtrl::IAddModel:" << strModel; + return true; +} + +bool CWfCtrl::IDeleteModel(int nStation, QString strModel) +{ + IStation *pStation = IGetStationById(nStation); + if (!pStation) { + return false; + } + QString strNow = pStation->currentRunningModel(); + if (strNow == strModel) { + return false; + } + pStation->delModel(strModel); + if (m_pColossus) { + m_pColossus->delModel(nStation, strModel); + } + m_mpModels.remove(genModelName(nStation, strModel)); + qDebug() << "CWfCtrl::IDeleteModel:" << strModel; + return true; +} + +bool CWfCtrl::ISelModel(int nStation, QString strModel) +{ + if (strModel.isEmpty()) { + qDebug() << "station=" << nStation << ",model=" << strModel; + return false; + } + IStation *pStation = IGetStationById(nStation); + if (pStation) { + pStation->setCurrentModel(strModel); + if (m_pColossus) { + //需要测试 + m_pColossus->selModel(nStation, strModel/*pStation->currentRunningModel()*/); + //m_pColossus->ReSetModifyState(); + } + } + + return true; +} + +void CWfCtrl::ISetModifyModel(bool bflag) +{ + if (m_pColossus){ + m_pColossus->setModifyState(bflag); + } +} + +QString CWfCtrl::IGetCurrentRuningModel(int nIndex) +{ + IStation *pStation = IGetStationById(nIndex); + if (!pStation) { + return QString(); + } + QString strNow = pStation->currentRunningModel(); + return strNow; +} + +WfModel * CWfCtrl::IGetModelInfo(int nIndex, QString strModel) +{ +// if (m_pColossus) { +// m_pColossus->selModel(nStation, pStation->currentRunningModel()); +// } + return m_mpModels.value(genModelName(nIndex, strModel)); +} + +QString CWfCtrl::genModelName(int nIndex, QString strModel) +{ + return QString::number(nIndex) + "##" + strModel; +} + +QMap CWfCtrl::IGetModelInfos() +{ + return m_mpModels; +} + +bool CWfCtrl::IStandard(int nIndex,QString strModel) +{ + return m_pColossus->bStandard(nIndex,strModel); +} + +bool CWfCtrl::IConnectStatus() +{ + if (m_pConnectChecker) { + return m_pConnectChecker->isWorking(); + } + return false; +} + +void CWfCtrl::registerConnect() +{ + if (m_pConnectChecker) { + m_pConnectChecker->registerWorking(); + } +} + +bool CWfCtrl::IBatchModel() +{ + return m_pColossus->getBatchModel(); +} + +bool CWfCtrl::IUpdateModelInfo() +{ + m_pColossus->updateModelInfo(m_mpModels); + for each (IStation* var in m_pStationMap) { + emit var->sgUpdateLable(); + } + return true; +} + +QString CWfCtrl::IGetCommName() +{ + return "null com"; +} + +bool CWfCtrl::IGetUserInfo(QString& user, int& level) +{ +// if (!m_pMainBack) { +// return false; +// } +// return m_pMainBack->IGetUserInfo(user, level); + + if (m_strUserName.isEmpty()) + user = QObject::tr("未登录"); + else + user = m_strUserName; + level = m_nLevel; + return true; +} + +void CWfCtrl::ISetUserInfo(QString& user, int& level) +{ + m_strUserName = user; + m_nLevel = level; +} + +void CWfCtrl::readSettingFile() +{ + if (m_setting){ + QString m_str = "/CheckThread/"; + nCheckImgFileDays = m_setting->value(m_str + "days", 7).toInt(); + nCheckDirDays = m_setting->value(m_str + "days_dir", 30).toInt(); + nCheckThreadEable = m_setting->value(m_str + "IsUse", true).toBool(); + nMinSpaceSize = m_setting->value(m_str + "spacesize", 10).toInt(); + } +} + +void CWfCtrl::writeSettingFile() +{ + if (m_setting){ + QString m_str = "/CheckThread/"; + m_setting->setValue(m_str + "days", nCheckImgFileDays); + m_setting->setValue(m_str + "days_dir", nCheckDirDays); + m_setting->setValue(m_str + "IsUse", nCheckThreadEable); + m_setting->setValue(m_str + "spacesize", nMinSpaceSize); + } +} + diff --git a/src/lpMain/WfCtrl.h b/src/lpMain/WfCtrl.h new file mode 100644 index 0000000..38c6aae --- /dev/null +++ b/src/lpMain/WfCtrl.h @@ -0,0 +1,69 @@ +#ifndef _WFCTRL_H_ +#define _WFCTRL_H_ + +#include +#include +#include +#include "IWfCtrl.h" +#include "QDiskCleanThread.h" + +class ICoreCtrl; +class IMainCallback; +class WfColossus; +class CWfCtrl : public IWfCtrl +{ + Q_OBJECT +public: + CWfCtrl(ICoreCtrl *, WfColossus*); + virtual~CWfCtrl(); + virtual bool IOnlineMode(); + virtual void ISetOnlineModel(bool); + virtual bool IBatchModel(); + virtual bool IConnectStatus(); + virtual QString IGetCommName(); + virtual void ISetUserInfo(QString& user, int& level); + virtual bool IGetUserInfo(QString& user, int& level); + virtual void registerConnect(); + virtual QStringList IGetStationKeys(); + virtual IStation* IGetStationById(int); + virtual IStation* IGetStationByKey(QString); + virtual WfModel *IGetModelInfo(int, QString); + virtual bool IUpdateModelInfo(); + virtual QMapIGetModelInfos(); + virtual bool IStandard(int,QString strModel); + + virtual bool IAddModel(int, QString); + virtual bool IDeleteModel(int, QString); + virtual bool ISelModel(int, QString); + virtual void ISetModifyModel(bool bflag); + virtual QString IGetCurrentRuningModel(int); +private: + void initGolbal(); + bool initStation(); + bool initModel(); + bool initCurrentModel(); + static QString genModelName(int nIndex, QString strModel); + void readSettingFile(); + void writeSettingFile(); +private: + bool m_nOnlineMode{ false }; + //QString m_strCommName; + class WorkChecker *m_pConnectChecker{ nullptr }; + QMap m_pStationMap; + QMap m_mpModels; + ICoreCtrl *m_pCoreCtrl{ nullptr }; + IMainCallback *m_pMainBack{ nullptr }; + WfColossus *m_pColossus{ nullptr }; + class QSqliteWheelHubWf *m_pWfDb{ nullptr }; + + QList m_pDCThreadList; + QSettings *m_setting{ nullptr }; + int nCheckImgFileDays;//ⱣͼƬʱ䣨bmpʽͼƬ 㷨Զģ2~5MС + int nCheckDirDays;//ⱣͼƬļгʱ䣨ͼ 100kСͼƬ + bool nCheckThreadEable; + int nMinSpaceSize; + + QString m_strUserName; + int m_nLevel{ 0 }; +}; +#endif diff --git a/src/lpMain/WfModel.h b/src/lpMain/WfModel.h new file mode 100644 index 0000000..c2d4b44 --- /dev/null +++ b/src/lpMain/WfModel.h @@ -0,0 +1,16 @@ +#ifndef _WFMODEL_H_ +#define _WFMODEL_H_ + +#include +class WfModel +{ +public: + WfModel() { nCount = 0; bCaliState = false; nIndex = 100; }; + + int nCount; + QString strCreateTime; + bool bCaliState; + int nIndex; +}; + +#endif diff --git a/src/lpMain/algela/QTipWidget.cpp b/src/lpMain/algela/QTipWidget.cpp new file mode 100644 index 0000000..e477dbc --- /dev/null +++ b/src/lpMain/algela/QTipWidget.cpp @@ -0,0 +1,118 @@ +#include "QTipWidget.h" + +int ShowMessage(QString text) +{ + QTipWidget::showTips(QString(text), TIPS_SHORT); + return 0; +} + +void ShowMessagems(QString text, int time) +{ + QTipWidget::showTips(QString(text), time); +} +QTipWidget::QTipWidget(QWidget *parent) : + QDockWidget(parent) +{ + initGui(); +} + +void QTipWidget::initGui() +{ + this->setFeatures(QDockWidget::NoDockWidgetFeatures); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::CustomizeWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); + this->setAllowedAreas(Qt::NoDockWidgetArea); + this->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + this->setWindowOpacity(0.75); + setCursor(Qt::WaitCursor); + + mainFrame = new QFrame(this); + this->setWidget(mainFrame); + mainFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Plain); + mainFrame->setLineWidth(2); + mainFrame->setMidLineWidth(1); + + mainLayout = new QHBoxLayout(); + mainLayout->setSpacing(12); + mainLayout->setMargin(12); + mainFrame->setLayout(mainLayout); + + //iconLbl = new QLabel(this); + //iconLbl->setText(""); + //mainLayout->addWidget(iconLbl); + tipsLbl = new QLabel(this); + tipsLbl->setWordWrap(false); + tipsLbl->setAlignment(Qt::AlignCenter); + mainLayout->addWidget(tipsLbl); + QFont font = tipsLbl->font(); + font.setPointSize(14); + font.setBold(true); + tipsLbl->setFont(font); + + iconMovie = NULL; + + this->setStyleSheet("QWidget{ color:#FFFFFF;} QFrame { background-color : #515C63;}");//color:lightgray; +} + +void QTipWidget::setIcon(const QString& iconUrl) +{ + if (!iconUrl.isEmpty()) + { + if (NULL != iconMovie) + { + delete iconMovie; + } + iconMovie = new QMovie(this); + iconMovie->setFileName(iconUrl); + iconLbl->setMovie(iconMovie); + iconMovie->start(); + } +} + +void QTipWidget::setText(const QString& txt) +{ + tipsLbl->setText(txt); +} + +void QTipWidget::showTips(const QString& txt, int seconds) +{ + QTipWidget* tips = new QTipWidget(); + tips->setText(txt); + + QTimer::singleShot(seconds * 500, tips, SLOT(close())); + QTimer::singleShot(seconds * 501, tips, SLOT(deleteLater()));//延迟自动删除内存 + + tips->show(); + + QDesktopWidget dw; + QRect rect = dw.availableGeometry(); + int posX = (rect.width() - tips->width()) / 2; + int posY = (rect.height() - tips->height()) / 2; + tips->move(posX, posY); + QApplication::setActiveWindow(tips); + tips->raise(); +} + +QTipWidget* QTipWidget::showWaitingTips(const QString& iconUrl, const QString& txt) +{ + QTipWidget* tips = new QTipWidget(); + tips->setIcon(iconUrl); + tips->setText(txt); + + tips->show(); + + QDesktopWidget dw; + QRect rect = dw.availableGeometry(); + int posX = (rect.width() - tips->width()) / 2; + int posY = (rect.height() - tips->height()) / 2; + tips->move(posX, posY); + QApplication::setActiveWindow(tips); + tips->raise(); + + return tips; +} + +void QTipWidget::enterEvent(QEvent* event) +{ + this->raise(); + this->setFocus(Qt::MouseFocusReason); +} diff --git a/src/lpMain/algela/QTipWidget.h b/src/lpMain/algela/QTipWidget.h new file mode 100644 index 0000000..4e41cd2 --- /dev/null +++ b/src/lpMain/algela/QTipWidget.h @@ -0,0 +1,42 @@ +#ifndef YTIPSWIDGET_H +#define YTIPSWIDGET_H + + +#include +#include + +#define TIPS_LONG (5) +#define TIPS_SHORT (3) + +/* + *ϢʾؼԶʱϢ + * +*/ +int ShowMessage(QString text); +void ShowMessagems(QString text, int time); +class QTipWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit QTipWidget(QWidget *parent = 0); + + void setIcon(const QString& iconUrl); + void setText(const QString& txt); + static void showTips(const QString& txt, int seconds);//ⲿҪú룺ʾϢʾʱ + static QTipWidget* showWaitingTips(const QString& iconUrl, const QString& txt); + +public slots: + +protected: + virtual void enterEvent(QEvent* event); + +private: + void initGui(); + QFrame* mainFrame; + QHBoxLayout* mainLayout; + QLabel* iconLbl; + QLabel* tipsLbl; + QMovie* iconMovie; +}; + +#endif // YTIPSWIDGET_H diff --git a/src/lpMain/algela/RoiImgViewer.cpp b/src/lpMain/algela/RoiImgViewer.cpp new file mode 100644 index 0000000..35d1508 --- /dev/null +++ b/src/lpMain/algela/RoiImgViewer.cpp @@ -0,0 +1,670 @@ +#include "RoiImgViewer.h" +#include +//#include "qtUtils.h" +#include +#include + + + //#define DEBUG_PRINT + + RoiImgViewer::RoiImgViewer(QWidget* parent /*= 0*/) + : lpImgViewer(parent) + , mMouseCaptureStatus(MouseCaptureNone) + , mDrawStatus(DrawNone) + , mMouseCaptureDistance(10) + , mpDrawingLineItem(NULL) + , mpDrawingRectItem(NULL) + , mpSelectedRoiNode(NULL) + { + connect(scene(), SIGNAL(selectionChanged()), this, SLOT(updateSelectedRoiNode())); + } + + QGraphicsPixmapItem* RoiImgViewer::setImg(QImage& img) + { + if (lpImgViewer::setImg(img)) + { + mpImgItem->setFlag(QGraphicsItem::ItemIsMovable, false); + return mpImgItem; + } + else + { + return NULL; + } + } + + QGraphicsItem* RoiImgViewer::firstSelectedItem() + { + auto selectedItemList = scene()->selectedItems(); + if (selectedItemList.size()) + { + return selectedItemList.front(); + } + else + { + return NULL; + } + } + + void RoiImgViewer::setDrawStatus(DrawStatus s) + { + + mDrawStatus = s; + // if(mpImgItem){ + // if(DrawNone == s){ + // mpImgItem->setFlag(QGraphicsItem::ItemIsMovable, true); + // printf("RoiImgViewer::setDrawStatus true\n"); + // } + // else{ + // mpImgItem->setFlag(QGraphicsItem::ItemIsMovable, false); + // printf("RoiImgViewer::setDrawStatus false\n"); + // } + // } + } + + void RoiImgViewer::clearRois(int type /*= -1*/) + { + QGraphicsScene* pScene = scene(); + if (type < 0) + { + foreach(auto itemList, mRoiItemMap) + { + foreach(auto pItem, itemList) + { + pScene->removeItem(pItem); + delete pItem; + } + } + mRoiItemMap.clear(); + } + else + { + auto iterItem = mRoiItemMap.find(type); + if (iterItem != mRoiItemMap.end()) + { + foreach(auto pItem, mRoiItemMap[type]) + { + pScene->removeItem(pItem); + delete pItem; + } + mRoiItemMap.erase(iterItem); + } + } + + if (type < 0 || type == QGraphicsRectItem::Type) + { + mRoiNodeList.clear(); + mpSelectedRoiNode = NULL; + } + + rejectDrawingItem(); + } + + void RoiImgViewer::clearExistRois() + { + QGraphicsScene* pScene = scene(); + + foreach(auto itemList, mRoiItemMap) + { + foreach(auto pItem, itemList) + { + pScene->removeItem(pItem); + delete pItem; + } + } + mRoiItemMap.clear(); + mRoiNodeList.clear(); + mpSelectedRoiNode = NULL; + } + + void RoiImgViewer::addRoiRect(QRectF rect, qreal scale, QString channelName, QString id, bool isTemplate /*= true*/, QColor nonTmplColor /*= Qt::yellow*/) + { + DrawStatus backupDrawStatus = mDrawStatus; + QString backupChannelName = getChannel(); + + setChannel(channelName); + setDrawStatus(DrawRect); + mDrawingRect = rect; + createDrawingItem(); + + qreal backupFixedSizeScale = fixedSizeScale(); + setFixedSizeScale(scale); + acceptDrawingItem(); + lastRectRoi()->id = id; + lastRectRoi()->isTemplate = isTemplate; + if (!isTemplate) + { + //setPenColor(lastRectRoi()->pItem, nonTmplColor); + QPen pen = lastRectRoi()->pItem->pen(); + pen.setColor(nonTmplColor); + lastRectRoi()->pItem->setPen(pen); + } + setFixedSizeScale(backupFixedSizeScale); + + mDrawStatus = backupDrawStatus; + setChannel(backupChannelName); + } + + void RoiImgViewer::addRoiRect(QRectF rect, qreal scale, QString id) + { + setDrawStatus(DrawRect); + // QPolygonF qrect = mapToScene(rect.toRect()); + // mDrawingRect = qrect.boundingRect(); + mDrawingRect = rect; + createDrawingItem(); + updateDrawingItem(); + //setFixedSizeScale(scale); + acceptDrawingItem(); + lastRectRoi()->id = id; + } + + void RoiImgViewer::addRoiLine(QLineF line, QString id) + { + setDrawStatus(DrawLine); + mDrawingLine = line; + createDrawingItem(); + updateDrawingItem(); + acceptDrawingItem(); + //lastRectRoi()->id = id; + setDrawStatus(DrawNone); + } + + LP_ROI_NODE* RoiImgViewer::lastRectRoi() + { + if (mRoiNodeList.size()) + { + return &mRoiNodeList.last(); + } + else + { + return NULL; + } + } + + void RoiImgViewer::mousePressEvent(QMouseEvent *event) + { + QGraphicsItem* pItem = firstSelectedItem(); + if (!pItem) + { + lpImgViewer::mousePressEvent(event); + pItem = firstSelectedItem(); + } + + QPoint mousePos = event->pos(); + QPointF scenePos = mapToScene(mousePos); + + if (event->button() == Qt::LeftButton) + { + + if (!pItem) + { + // nothing is selected, start drawing + switch (mDrawStatus) + { + case DrawLine: + mDrawingLine.setP1(scenePos); + mDrawingLine.setP2(scenePos); + break; + case DrawRect: + mDrawingRect.setTopLeft(scenePos); + mDrawingRect.setBottomRight(scenePos); + break; + default: + break; + } + createDrawingItem(); + return; + } + else + { + // something is selected + + // map pos to selected item's local coordinates + QPointF itemMousePos = pItem->mapFromScene(scenePos); +#ifdef DEBUG_PRINT + printf("viewer mouse press pos %d %d\n", mousePos.x(), mousePos.y()); + printf("scene mouse press pos %f %f\n", scenePos.x(), scenePos.y()); + printf("item mouse press pos %f %f\n", itemMousePos.x(), itemMousePos.y()); +#endif + QGraphicsRectItem* pRectItem = dynamic_cast(pItem); + if (pRectItem) + { + QRectF r = pRectItem->rect(); +#ifdef DEBUG_PRINT + printf("item rect %f %f %f %f\n", r.x(), r.y(), r.width(), r.height()); +#endif + mMouseCaptureStatus = decideMouseCaptureStatus(r, itemMousePos, mMouseCaptureDistance); + + if (mMouseCaptureStatus != MouseCaptureNone) + { + pRectItem->setFlag(QGraphicsItem::ItemIsMovable, false); + return; + } + } + QGraphicsLineItem* pLineItem = dynamic_cast(pItem); + if (pLineItem) + { + QLineF l = pLineItem->line(); + + mMouseCaptureStatus = decideMouseCaptureStatus(l, itemMousePos, mMouseCaptureDistance); + + if (mMouseCaptureStatus != MouseCaptureNone) + { + pLineItem->setFlag(QGraphicsItem::ItemIsMovable, false); + return; + } + } + } + } + + lpImgViewer::mousePressEvent(event); + } + + void RoiImgViewer::mouseMoveEvent(QMouseEvent *evt) + { + lpImgViewer::mouseMoveEvent(evt); + + QGraphicsItem* pItem = dynamic_cast( + firstSelectedItem()); + + QPoint mousePos = evt->pos(); + QPointF scenePos = mapToScene(mousePos); + + if (!pItem) + { + if (evt->buttons() == Qt::LeftButton) + { + switch (mDrawStatus) + { + case DrawLine: + mDrawingLine.setP2(scenePos); + break; + case DrawRect: + mDrawingRect.setBottomRight(scenePos); + break; + default: + break; + } + updateDrawingItem(); + } + } + else + { + QPointF itemMousePos = pItem->mapFromScene(scenePos); + + if (pItem->type() == QGraphicsRectItem().type()) + { + QGraphicsRectItem* pRectItem = qgraphicsitem_cast(pItem); + // map pos to selected item's local coordinates + + QRectF r = pRectItem->rect(); +#ifdef DEBUG_PRINT + printf("item rect %f %f %f %f\n", r.x(), r.y(), r.width(), r.height()); + printf("viewer mouse move pos %d %d\n", evt->pos().x(), evt->pos().y()); + printf("scene mouse move pos %f %f\n", scenePos.x(), scenePos.y()); + printf("item mouse move pos %f %f\n", itemMousePos.x(), itemMousePos.y()); + printf("mouse capture status %d\n", mMouseCaptureStatus); +#endif + if (evt->buttons() == Qt::LeftButton) + { + QRectF oriRect = r; + switch (mMouseCaptureStatus) + { + case MouseCaptureLeft: + r.setLeft(itemMousePos.x()); + r.setWidth(oriRect.right() - r.left()); + break; + case MouseCaptureRight: + r.setRight(itemMousePos.x()); + r.setWidth(r.right() - oriRect.left()); + break; + case MouseCaptureTop: + r.setTop(itemMousePos.y()); + r.setHeight(oriRect.bottom() - r.top()); + break; + case MouseCaptureBottom: + r.setBottom(itemMousePos.y()); + r.setHeight(r.bottom() - oriRect.top()); + break; + default: + case MouseCaptureNone: + break; + } + pRectItem->setRect(r); + } + + MouseCaptureStatus s = decideMouseCaptureStatus(r, itemMousePos, mMouseCaptureDistance); +#ifdef DEBUG_PRINT + printf("cur mouse status %d\n", s); +#endif + if (s == MouseCaptureLeft || s == MouseCaptureRight) + { + setCursor(Qt::SizeHorCursor); + } + else if (s == MouseCaptureTop || s == MouseCaptureBottom) + { + setCursor(Qt::SizeVerCursor); + } + else + { + setCursor(Qt::ArrowCursor); + } + } + else if (pItem->type() == QGraphicsLineItem().type()) + { + QGraphicsLineItem* pLineItem = qgraphicsitem_cast(pItem); + + } + } + + } + + void RoiImgViewer::mouseReleaseEvent(QMouseEvent *event) + { + if (event->button() == Qt::LeftButton) + { + mMouseCaptureStatus = MouseCaptureNone; + + QGraphicsItem* pItem = firstSelectedItem(); + if (!pItem) + { + // draw + switch (mDrawStatus) + { + case DrawLine: + if (mpDrawingLineItem) + { + emit roiAdded(mpDrawingLineItem); + } + break; + case DrawRect: + if (mpDrawingRectItem) + { + emit roiAdded(mpDrawingRectItem); + } + break; + default: + break; + } + acceptDrawingItem(); + } + else + { + // edit + pItem->setFlag(QGraphicsItem::ItemIsMovable, true); + + int itemType = pItem->type(); + if (itemType == QGraphicsRectItem().type()) + { + QGraphicsRectItem* pRectItem = qgraphicsitem_cast(pItem); + } + else if (itemType == QGraphicsLineItem().type()) + { + QGraphicsLineItem* pLineItem = qgraphicsitem_cast(pItem); + } + + emit roiChanged(pItem); + } + } + lpImgViewer::mouseReleaseEvent(event); + } + + void RoiImgViewer::wheelEvent(QWheelEvent *evt) + { + lpImgViewer::wheelEvent(evt); + + if (!mpSelectedRoiNode) + { + return; + } + + mpSelectedRoiNode->fScale = mFixedSizeScale; + } + + void RoiImgViewer::keyPressEvent(QKeyEvent *event) + { + if (mpSelectedRoiNode && Qt::Key_Delete == event->key()) { + QGraphicsScene* pScene = scene(); + + foreach(auto itemList, mRoiItemMap) + { + foreach(auto pItem, itemList) + { + if (pItem == mpSelectedRoiNode->pItem) { + pScene->removeItem(pItem); + + auto i1 = std::find_if(itemList.begin(), itemList.end(), [&](QGraphicsItem* pi) + { + return pi == pItem; + }); + itemList.erase(i1); + + auto i2 = std::find_if(mRoiNodeList.begin(), mRoiNodeList.end(), [&](LP_ROI_NODE& roiNode) + { + return roiNode.pItem == pItem; + }); + mRoiNodeList.erase(i2); + + delete pItem; + mpSelectedRoiNode = NULL; + return; + } + } + } + } + } + + QRectF extRect(const QRectF& r, float w) + { + return QRectF(r.x() - w, r.y() - w, r.width() + 2.0 * w, r.height() + 2.0*w); + } + + RoiImgViewer::MouseCaptureStatus RoiImgViewer::decideMouseCaptureStatus(const QRectF r, QPointF p, float capDis /*= 10*/) + { + MouseCaptureStatus ret = MouseCaptureNone; + + QRectF validRect = extRect(r, capDis); + if (!validRect.contains(p)) + { + return ret; + } + + if (abs(p.x() - r.left()) < capDis) + { + ret = MouseCaptureLeft; + } + else if (abs(p.x() - r.right()) < capDis) + { + ret = MouseCaptureRight; + } + else if (abs(p.y() - r.top()) < capDis) + { + ret = MouseCaptureTop; + } + else if (abs(p.y() - r.bottom()) < capDis) + { + ret = MouseCaptureBottom; + } + + return ret; + } + + RoiImgViewer::MouseCaptureStatus RoiImgViewer::decideMouseCaptureStatus(const QLineF l, QPointF p, float capDis /*= 10*/) + { + return MouseCaptureNone; + } + + void RoiImgViewer::updateSelectedRoiNode() + { + QGraphicsItem* pItem = firstSelectedItem(); + + LP_ROI_NODE *pRoiNode = findRoiNode(pItem); + if (pRoiNode != mpSelectedRoiNode) + { + mpSelectedRoiNode = pRoiNode; + emit roiSelectionChanged(mpSelectedRoiNode); + } + } + + void RoiImgViewer::updateDrawingItem() + { + switch (mDrawStatus) + { + case DrawLine: + if (mpDrawingLineItem) + { + QPen pen = mpDrawingLineItem->pen(); + pen.setColor(Qt::yellow); + pen.setWidth(5); + mpDrawingLineItem->setPen(pen); + mpDrawingLineItem->setLine(mDrawingLine); + } + else + { + qWarning() << tr("line item is not created"); + } + break; + case DrawRect: + if (mpDrawingRectItem) + { + QPen pen = mpDrawingRectItem->pen(); + pen.setColor(Qt::yellow); + pen.setWidth(5); + mpDrawingRectItem->setPen(pen); + mpDrawingRectItem->setRect(mDrawingRect); + } + else + { + qWarning() << tr("line item is not created"); + } + break; + default: + break; + } + } + + void RoiImgViewer::createDrawingItem() + { + switch (mDrawStatus) + { + case DrawLine: + if (!mpDrawingLineItem) + { + mpDrawingLineItem = scene()->addLine(mDrawingLine); + mpDrawingLineItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); + } + else + { + qWarning() << tr("line item is already created"); + } + break; + case DrawRect: + if (!mpDrawingRectItem) + { + mpDrawingRectItem = scene()->addRect(mDrawingRect); + mpDrawingRectItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); + } + else + { + qWarning() << tr("rect item is already created"); + } + break; + default: + break; + } + } + + void RoiImgViewer::acceptDrawingItem() + { + //clearExistRois(); + switch (mDrawStatus) + { + case DrawLine: + if (mpDrawingLineItem) + { + acceptRoiItem(mpDrawingLineItem); + mpDrawingLineItem = NULL; + } + break; + case DrawRect: + if (mpDrawingRectItem) + { + acceptRoiItem(mpDrawingRectItem); + mpDrawingRectItem = NULL; + } + break; + default: + break; + } + } + + void RoiImgViewer::rejectDrawingItem() + { + switch (mDrawStatus) + { + case DrawLine: + scene()->removeItem(mpDrawingLineItem); + mpDrawingLineItem = NULL; + break; + case DrawRect: + scene()->removeItem(mpDrawingRectItem); + mpDrawingRectItem = NULL; + break; + default: + break; + } + } + + void RoiImgViewer::resetAllDrawingItems() + { + mpDrawingLineItem = NULL; + mpDrawingRectItem = NULL; + } + + void RoiImgViewer::acceptRoiItem(QGraphicsItem* pItem) + { + int type = pItem->type(); + if (mRoiItemMap.contains(type)) + { + mRoiItemMap[type].append(pItem); + } + else + { + QList itemList; + itemList << pItem; + mRoiItemMap.insert(type, itemList); + } + + if (type == QGraphicsRectItem::Type) + { + LP_ROI_NODE roi_node; + roi_node.fScale = fixedSizeScale(); + roi_node.szChannel = getChannel(); + roi_node.pItem = qgraphicsitem_cast(pItem); + + //roi_node.id = uniqueTimeID(); + qint64 msecs = QDateTime::currentMSecsSinceEpoch(); + roi_node.id = QString::number(msecs, 16); + + roi_node.isTemplate = true; + mRoiNodeList.append(roi_node); + } + + } + + LP_ROI_NODE* RoiImgViewer::findRoiNode(QGraphicsItem* pItem) + { + auto i = std::find_if(mRoiNodeList.begin(), mRoiNodeList.end(), [&](LP_ROI_NODE& roiNode) + { + return roiNode.pItem == pItem; + }); + if (i == mRoiNodeList.end()) + { + return NULL; + } + else + { + return &(*i); + } + } + + diff --git a/src/lpMain/algela/RoiImgViewer.h b/src/lpMain/algela/RoiImgViewer.h new file mode 100644 index 0000000..c21ddca --- /dev/null +++ b/src/lpMain/algela/RoiImgViewer.h @@ -0,0 +1,109 @@ +#pragma once + +#include "lpImgViewer.h" +#include + +typedef struct tagLP_ROI_NODE { + QString id; + qreal fScale; + QString szChannel; + QGraphicsRectItem* pItem; + bool isTemplate; +}LP_ROI_NODE, *P_LP_ROI_NODE; +Q_DECLARE_METATYPE(LP_ROI_NODE*) + + + typedef QMap > RoiItemMap; + + class RoiImgViewer : public lpImgViewer + { + Q_OBJECT + + public: + enum MouseCaptureStatus + { + MouseCaptureLeft, + MouseCaptureRight, + MouseCaptureTop, + MouseCaptureBottom, + MouseCaptureNone + }; + + enum DrawStatus + { + DrawLine, + DrawRect, + DrawNone + }; + + public: + RoiImgViewer(QWidget* parent = 0); + ~RoiImgViewer() {}; + + virtual void wheelEvent(QWheelEvent *evt); + + QGraphicsPixmapItem* setImg(QImage& img); + + QGraphicsItem* firstSelectedItem(); + + void setDrawStatus(DrawStatus s); + + void clearRois(int type = -1); + void clearExistRois(); + + float getMouseCaptureDistance() const { return mMouseCaptureDistance; } + void setMouseCaptureDistance(float val) { mMouseCaptureDistance = val; } + + void addRoiRect(QRectF rect, qreal scale, QString channelName, QString id, bool isTemplate = true, QColor nonTmplColor = Qt::yellow); + void addRoiRect(QRectF rect, qreal scale, QString id); + + void addRoiLine(QLineF line, QString id); + + QList getRectROIs() const { return mRoiNodeList; } + LP_ROI_NODE* lastRectRoi(); + + protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *evt); + void mouseReleaseEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + + MouseCaptureStatus decideMouseCaptureStatus(const QRectF r, + QPointF p, float capDis = 10); + MouseCaptureStatus decideMouseCaptureStatus(const QLineF l, + QPointF p, float capDis = 10); + + protected slots: + void updateSelectedRoiNode(); + + protected: + float mMouseCaptureDistance; + + MouseCaptureStatus mMouseCaptureStatus; + + DrawStatus mDrawStatus; + QLineF mDrawingLine; + QRectF mDrawingRect; + QGraphicsLineItem* mpDrawingLineItem; + QGraphicsRectItem* mpDrawingRectItem; + void updateDrawingItem(); + void createDrawingItem(); + void acceptDrawingItem(); + void rejectDrawingItem(); + void resetAllDrawingItems(); + + void acceptRoiItem(QGraphicsItem* pItem); + LP_ROI_NODE* findRoiNode(QGraphicsItem* pItem); + RoiItemMap mRoiItemMap; + QList mRoiNodeList; + LP_ROI_NODE* mpSelectedRoiNode; + + signals: + void roiChanged(QGraphicsItem* pItem); + void roiAdded(QGraphicsItem* pItem); + void roiSelectionChanged(LP_ROI_NODE* pSelectedRoiNode); + + private: + + }; + diff --git a/src/lpMain/algela/lpImgViewer.cpp b/src/lpMain/algela/lpImgViewer.cpp new file mode 100644 index 0000000..0832500 --- /dev/null +++ b/src/lpMain/algela/lpImgViewer.cpp @@ -0,0 +1,235 @@ +#include "lpImgViewer.h" +#include +#include +#include +#include +#include + + + + lpImgViewer::lpImgViewer(QWidget* parent /*= 0*/) + : QGraphicsView(parent), mpImgItem(NULL), mpInfoLabel(NULL), mpImg(NULL), m_scale(1.0), mMinScaleThre(0.01), + mIsFixedSizeScale(false), mFixedSizeScale(1.0), mImgScaleFunc(NULL), mIsAutoResetTransform(false) + { + QGraphicsScene* lpScene = new QGraphicsScene; + setScene(lpScene); + setResizeAnchor(QGraphicsView::AnchorUnderMouse); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + } + + lpImgViewer::lpImgViewer(QGraphicsScene *scene, QWidget *parent /*= 0*/) + : QGraphicsView(scene, parent), mpImgItem(NULL), mpInfoLabel(NULL), mpImg(NULL), m_scale(1.0), + mIsFixedSizeScale(false), mFixedSizeScale(1.0), mImgScaleFunc(NULL), mIsAutoResetTransform(false) + { + setResizeAnchor(QGraphicsView::AnchorUnderMouse); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + } + + + void lpImgViewer::setInitScale(qreal val) + { + scaleImg2(val); + } + + + + + + QGraphicsPixmapItem* lpImgViewer::setImg(QImage& img) + { + if (mpImg) + delete mpImg; + mpImg = new QImage(img); + + if (!mpImgItem) + mpImgItem = scene()->addPixmap(QPixmap::fromImage(img, Qt::NoFormatConversion)); + else + mpImgItem->setPixmap(QPixmap::fromImage(img, Qt::NoFormatConversion)); + + mpImgItem->setOffset(-img.width() / 2.0, -img.height() / 2.0); + + mImgOriSize = img.size(); + mMinScaleThre = 1.0 / std::min(mImgOriSize.width(), mImgOriSize.height()); + //qreal scale = std::min((qreal)size().width() / img.width(), (qreal)size().height() / img.height()); + + if (isAutoResetTransform()) + { + mpImgItem->setPos(0, 0); + qreal scale = 1.0f; + scaleImg2(scale); + this->centerOn(0, 0); + } + mpImgItem->setFlags(QGraphicsItem::ItemIsMovable); + + return mpImgItem; + } + + void lpImgViewer::wheelEvent(QWheelEvent *evt) + { + if (mpImgItem) + { + qreal numDegrees = evt->angleDelta().y() / 120; + + if (!isFixedSizeScale()) + { + qreal newScale = transform().m11(); + if (numDegrees > 0) + newScale *= std::pow(1.1, numDegrees); + else if (numDegrees < 0) + newScale *= std::pow(0.9, -numDegrees); + + mpImgItem->setTransformOriginPoint(0, 0); + scaleImg2(newScale); + } + else + { + qreal newScale = fixedSizeScale(); + if (numDegrees > 0) + newScale *= std::pow(1.2, numDegrees); + else if (numDegrees < 0) + newScale *= std::pow(0.8, -numDegrees); + + if (newScale > 1.0) newScale = 1.0; + if (newScale < mMinScaleThre) newScale = mMinScaleThre; + + setFixedSizeScale(newScale); + + scaleImageWithFixedViewSize(); + } + } + } + + void lpImgViewer::mouseMoveEvent(QMouseEvent *evt) + { + if (mpImgItem) + { + QPoint curpos = (mpImgItem->mapFromScene(this->mapToScene(evt->pos())) + + QPointF(mImgOriSize.width() / 2.0, mImgOriSize.height() / 2.0) + QPointF(-0.5, -0.5)).toPoint(); + + if (mpInfoLabel) + selectPixel(curpos); + else + emit pixelSelected(curpos); + } + + if (mpImgItem && mpImgItem->isSelected()) + { + QPointF pos = mpImgItem->scenePos(); + qDebug() << pos; + emit imgMoved(pos); + } + + QGraphicsView::mouseMoveEvent(evt); + } + + void lpImgViewer::resizeEvent(QResizeEvent* evt) + { + QGraphicsView::resizeEvent(evt); + } + + void lpImgViewer::mouseReleaseEvent(QMouseEvent *evt) + { + QGraphicsView::mouseReleaseEvent(evt); + if (mpImgItem) + mpImgItem->setSelected(false); + } + + void lpImgViewer::closeEvent(QCloseEvent *evt) + { + deleteLater(); + } + + void lpImgViewer::moveImg(QPointF pos) + { + if (mpImgItem) + mpImgItem->setPos(pos); + } + + QString lpImgViewer::pixelInfoStr(QPoint pos, QRgb _rgb) + { + QColor rgb(_rgb); + QColor hsv = rgb.toHsv(); + QString infoStr("Position:[%1, %2] Color(RGB):[%3, %4, %5] (HSV):[%6, %7, %8]"); + infoStr = infoStr.arg(pos.x()).arg(pos.y()) + .arg(rgb.red()).arg(rgb.green()).arg(rgb.blue()) + .arg(hsv.hue() / 2).arg(hsv.saturation()).arg(hsv.value()); + return infoStr; + } + + void lpImgViewer::selectPixel(QPoint pos) + { + if (pos.x() < 0 || pos.y() < 0 || pos.x() > mImgOriSize.width() || pos.y() > mImgOriSize.height()) + { + mpInfoLabel->setText(QString("out of image...")); + return; + } + + if (!mpImgItem) + { + return; + } + + QImage img = mpImgItem->pixmap().toImage(); + QRgb pixel = img.pixel(pos); + + mpInfoLabel->setText(pixelInfoStr(pos, pixel)); + } + + void lpImgViewer::scaleImageWithFixedViewSize() + { + if (!mpImg) + { + return; + } + QImage scaledImage; + if (!imgScaleFunc()) + { + scaledImage = mpImg->scaled(mpImg->width()*mFixedSizeScale, mpImg->height()*mFixedSizeScale, + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + mpImgItem->setPixmap(QPixmap::fromImage(scaledImage.scaled(mpImg->width(), + mpImg->height()), Qt::NoFormatConversion)); + } + else + { + scaledImage = mImgScaleFunc(*mpImg, mFixedSizeScale, Qt::SmoothTransformation); + mpImgItem->setPixmap(QPixmap::fromImage(mImgScaleFunc(scaledImage, + 1.0 / mFixedSizeScale, Qt::FastTransformation), Qt::NoFormatConversion)); + } + + emit imgScaledWithFixedSize(fixedSizeScale()); + } + + void lpImgViewer::scaleImg(QTransform tf, QPointF center) + { + this->resetTransform(); + this->setTransform(tf); + this->centerOn(center); + } + + void lpImgViewer::scaleImg2(qreal scale) + { + m_scale = scale; + QTransform t; + t.scale(scale, scale); + this->setTransform(t); + + QPointF center = this->mapToScene(width() / 2, height() / 2); + + emit imgScaled(this->transform(), center); + } + + void lpImgViewer::clear() + { + m_scale = 1.0f; + m_channel = ""; + scene()->clear(); + mpImgItem = NULL; + + if (mpImg) + { + delete mpImg; + mpImg = NULL; + } + } + + diff --git a/src/lpMain/algela/lpImgViewer.h b/src/lpMain/algela/lpImgViewer.h new file mode 100644 index 0000000..ab63cec --- /dev/null +++ b/src/lpMain/algela/lpImgViewer.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include + +typedef QImage (*ImageScaleFun)(QImage, double, int); + +class lpImgViewer : public QGraphicsView +{ + Q_OBJECT +public: + static QString pixelInfoStr(QPoint pos, QRgb rgb); + +public: + lpImgViewer(QGraphicsScene *scene, QWidget *parent = 0); + lpImgViewer(QWidget* parent = 0); + virtual ~lpImgViewer(){ + if (mpImg) + delete mpImg; + } + + virtual QGraphicsPixmapItem* setImg(QImage& img); + void setInfoLabel(QLabel* ipLabel){ mpInfoLabel = ipLabel; } + const QImage* img() { return mpImg; } + QGraphicsPixmapItem* imgItem() { return mpImgItem; } + + virtual void wheelEvent(QWheelEvent *evt); + virtual void mouseMoveEvent(QMouseEvent *evt); + virtual void resizeEvent(QResizeEvent* evt); + virtual void mouseReleaseEvent(QMouseEvent *evt); + virtual void closeEvent(QCloseEvent *evt); + virtual void clear(); + + virtual const qreal getScale(void) const { return m_scale; }; + virtual void setChannel(const QString& channel) { m_channel = channel; } + virtual QString getChannel() const { return m_channel; } + bool isFixedSizeScale() const { return mIsFixedSizeScale; } + void setIsFixedSizeScale(bool val) { mIsFixedSizeScale = val; } + qreal fixedSizeScale() const { return mFixedSizeScale; } + void setFixedSizeScale(qreal val) { mFixedSizeScale = val; } + ImageScaleFun imgScaleFunc() const { return mImgScaleFunc; } + void setImgScaleFunc(ImageScaleFun val) { mImgScaleFunc = val; } + void setInitScale(qreal val); + + bool isAutoResetTransform() const { return mIsAutoResetTransform; } + void setIsAutoResetTransform(bool val) { mIsAutoResetTransform = val; } + bool isAutoResetToWindowSize() const { return mIsAutoResetToWindowSize; } + void setIsAutoResetToWindowSize(bool val) { mIsAutoResetToWindowSize = val; } + +signals: + void imgScaled(QTransform tf, QPointF center); + void imgMoved(QPointF pos); + void pixelSelected(QPoint pos); + void imgScaledWithFixedSize(qreal scale); + +public slots: + void scaleImg(QTransform tf, QPointF center); + void moveImg(QPointF pos); + void selectPixel(QPoint pos); + void scaleImageWithFixedViewSize(); + +protected: + QGraphicsPixmapItem* mpImgItem; + QSize mImgOriSize; + qreal mMinScaleThre; + QLabel* mpInfoLabel; + QImage* mpImg; + + void scaleImg2(qreal scale); + qreal m_scale; + QString m_channel; + + bool mIsFixedSizeScale; + qreal mFixedSizeScale; + bool mIsAutoResetTransform; + bool mIsAutoResetToWindowSize; + + ImageScaleFun mImgScaleFunc; +}; diff --git a/src/lpMain/lpMain.qrc b/src/lpMain/lpMain.qrc new file mode 100644 index 0000000..f619ec9 --- /dev/null +++ b/src/lpMain/lpMain.qrc @@ -0,0 +1,22 @@ + + + Resource/wanfeng.png + Resource/wanfeng2.png + + + Resource/toolBar/administrator.png + Resource/toolBar/background.png + Resource/toolBar/cali.png + Resource/toolBar/education24.png + Resource/toolBar/help.png + Resource/toolBar/model.png + Resource/toolBar/save.png + Resource/toolBar/setting.png + Resource/toolBar/test.png + Resource/toolBar/messenger.png + + + Resource/app.png + Resource/app2.png + + diff --git a/src/lpMain/sqliteDB/DetectDataDB.cpp b/src/lpMain/sqliteDB/DetectDataDB.cpp new file mode 100644 index 0000000..8f1aee3 --- /dev/null +++ b/src/lpMain/sqliteDB/DetectDataDB.cpp @@ -0,0 +1,399 @@ +#include "DetectDataDB.h" +#include "InfoFile.h" +#include "gensql.h" + +#define _MD_WARNINGTABLE "warningtable" +/*myself */ +#define _MD_PRIMARY_KEY "uid" +#define _MD_TABLE_FORMS_MANE "wftable" //数据表名称 + +#define _MD_MODEL "model" //model 模型名称 +#define _MD_DIAMETER "diameter" //直径 +#define _MD_HIGHT "hight" //高度 +#define _MD_CORRELATE "correlate" //相似度 +#define _MD_CHANNEL "channel" //通道 +#define _MD_PICPATH "picpath" //图片路径 +#define _MD_ROTATE "rotate"//辐条个数 +#define _MD_UPDATE_TIME "time" //最后更新的时间 +#define _MD_PIC "pic" //图片 + + +#define _MD_TIMETABLE "wftimetable" +#define _MD_TYPE "type" +#define _MD_STARTTIME "starttime" +#define _MD_ENDTIME "endtime" + +#define _MD_WARNINGTABLE "warningtable" +#define _MD_MESSAGE "message" +#define _MD_CLASS "class" +#define _INDEX_FORTABLE "CREATE INDEX idx_wftable ON wftable (time COLLATE BINARY ASC); " +DetectDataDB::DetectDataDB(const QString& dbName,const QString &dbType) :DataBaseSql(dbName,dbType) +{ +// db = QSqlDatabase::addDatabase("QSQLITE"); +// db.setDatabaseName(dbName); +// if (!dbUser.isEmpty()){ +// db.setUserName(dbUser); +// db.setPassword(dbPwb); +// } +} + +DetectDataDB::~DetectDataDB() +{ + +} +//m_value.insert("time1", "");//起始时间 +//m_value.insert("time2", "");//结束时间 +//m_value.insert("name", "");//模型名字 +//m_value.insert("Type", "");//查询类型 +QString DataBaseSql::genCheckStr(QVariantMap m_value) +{ + int nType = m_value.value(_CHECK_TYPE_).toInt(); + QString select_sql; + switch (nType) + { + case EMT_CHECK_BY_TIME: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString strFifter = QString(" where time ='%1'").arg(time1); + int IsCount = m_value.value(_CHECK_COUNT_).toInt(0); + if (IsCount==1) + select_sql = QString("select count(*) from ") + _MD_TABLE_FORMS_MANE + strFifter; + else + select_sql = QString("select * from ") + _MD_TABLE_FORMS_MANE + strFifter; + } + break; + case EMT_CHECK_BY_NAME: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString time2 = m_value.value(_CHECK_TIME2_).toString(); + QString modelname = m_value.value(_CHECK_NAME_).toString(); + QString strFifter = QString(" where time >'%1' and time < '%2' and model='%3'").arg(time1).arg(time2).arg(modelname); + int IsCount = m_value.value(_CHECK_COUNT_).toInt(0); + if (IsCount == 1) + select_sql = QString("select count(*) from ") + _MD_TABLE_FORMS_MANE + strFifter; + else + select_sql = QString("select * from ") + _MD_TABLE_FORMS_MANE + strFifter; + } + break; + case EMT_CHECK_BY_SAE: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString time2 = m_value.value(_CHECK_TIME2_).toString(); + QString checktypeStr = m_value.value("CheckType").toString(); + QString strFifter = QString(" where time >'%1' and time < '%2'").arg(time1).arg(time2); + int IsCount = m_value.value(_CHECK_COUNT_).toInt(0); + if (IsCount == 1) + select_sql = QString("select count(*) from ") + _MD_TABLE_FORMS_MANE + strFifter; + else{ + if (checktypeStr == "Count") + select_sql = QString("select %1 from ").arg(_MD_MODEL) + _MD_TABLE_FORMS_MANE + strFifter; + else + select_sql = QString("select * from ") + _MD_TABLE_FORMS_MANE + strFifter; + } + } + break; + case EMT_CHECK_BY_LOG: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString time2 = m_value.value(_CHECK_TIME2_).toString(); + int logType = m_value.value(_CHECK_LOGTYPE).toInt(); + QString strFifter = QString(" where time >'%1' and time < '%2' and class = '%3' order by time desc ").arg(time1).arg(time2).arg(logType); + int IsCount = m_value.value(_CHECK_COUNT_).toInt(0); + if (IsCount == 1) + select_sql = QString("select count(*) from ") + _MD_WARNINGTABLE + strFifter; + else + select_sql = QString("select * from ") + _MD_WARNINGTABLE + strFifter; + } + break; + case EMT_CHECK_BY_COUNT: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString time2 = m_value.value(_CHECK_TIME2_).toString(); + QString checktypeStr = m_value.value("CheckType").toString(); + QString strFifter = QString(" where time >'%1' and time < '%2'").arg(time1).arg(time2); + int IsCount = m_value.value(_CHECK_COUNT_).toInt(0); + if (IsCount == 1) + select_sql = QString("select count(*) from ") + _MD_TABLE_FORMS_MANE + strFifter; + else{ + QString strGroup = QString(" GROUP BY %1 ORDER BY %2").arg(_MD_MODEL).arg(_MD_MODEL); + if (checktypeStr == "Count") + select_sql = QString("select %1 from ").arg(_MD_MODEL).arg(_MD_MODEL) + _MD_TABLE_FORMS_MANE + strFifter;// +strGroup; + else + select_sql = QString("select * from ") + _MD_TABLE_FORMS_MANE + strFifter; + } + } + break; + default: + break; + } + return select_sql; +} + +bool DetectDataDB::checkoutData(QVariantMap m_value, QSqlQuery &sql) +{ + int nType = m_value.value(_CHECK_TYPE_).toInt(); + QString select_sql; + switch (nType) + { + case EMT_CHECK_BY_TIME: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString strFifter = QString(" where time ='%1'").arg(time1); + select_sql = QString("select * from ") + _MD_TABLE_FORMS_MANE + strFifter; + } + break; + case EMT_CHECK_BY_NAME: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString time2 = m_value.value(_CHECK_TIME2_).toString(); + QString modelname = m_value.value(_CHECK_NAME_).toString(); + QString strFifter = QString(" where time >'%1' and time < '%2' and model='%3'").arg(time1).arg(time2).arg(modelname); + select_sql = QString("select * from ") + _MD_TABLE_FORMS_MANE + strFifter; + } + break; + case EMT_CHECK_BY_SAE: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString time2 = m_value.value(_CHECK_TIME2_).toString(); + QString strFifter = QString(" where time >'%1' and time < '%2'").arg(time1).arg(time2); + select_sql = QString("select * from ") + _MD_TABLE_FORMS_MANE + strFifter; + } + break; + case EMT_CHECK_BY_LOG: + { + QString time1 = m_value.value(_CHECK_TIME1_).toString(); + QString time2 = m_value.value(_CHECK_TIME2_).toString(); + int logType = m_value.value(_CHECK_LOGTYPE).toInt(); + QString strFifter = QString(" where time >'%1' and time < '%2' and class = '%3' order by time desc ").arg(time1).arg(time2).arg(logType); + select_sql = QString("select * from ") + _MD_WARNINGTABLE + strFifter; + } + break; + default: + break; + } + + sql = db->exec(select_sql); + return true; +} + +bool DetectDataDB::checkoutData(QString selectStr, QSqlQuery &sql) +{ + sql = db->exec(selectStr); + return true; +} + +bool DetectDataDB::DelDatasByTime(QString minTime) +{ + QString StrTime = QString("time <'%1'").arg(minTime); + QString select_sql = QString("DELETE FROM %1 WHERE %2").arg(_MD_TABLE_FORMS_MANE).arg(StrTime); + db->exec(select_sql); + return true; +} + +bool DetectDataDB::DelAllDatas() +{ + QString strSqll = QString("delete from %1 ").arg(_MD_TABLE_FORMS_MANE); + db->exec(strSqll); + strSqll = QString("delete from %1 ").arg("warningtable"); + db->exec(strSqll); + return true; +} + +bool DetectDataDB::DelWarnDataByCount(int model /*=1*/, int nCount/*=100000*/) +{ + QString strLimit1 = QString("select count(uid) from %1 where class = '%3'").arg("warningtable").arg(model); + QString strLimit2 = QString("uid in(select uid from %1 where class = '%2' order by time desc limit(select count(uid) from %3 where class = '%4') offset %5)").arg("warningtable").arg(model).arg("warningtable").arg(model).arg(nCount); + QString strSqll = QString("delete from %1 where (%2)>%3 and %4").arg("warningtable").arg(strLimit1).arg(nCount).arg(strLimit2); + QSqlQuery sql = db->exec(strSqll); + return true; +} + +bool DetectDataDB::InitDatabase() +{ + if (!openDB()) + { + return false; + } + + QString strCheckTable = QString("select name from sqlite_master where type='table' order by name;"); + QSqlQuery sql = db->exec(strCheckTable);//查询数据库中所有的数据表 + QStringList tablenamelist; + while (sql.next()) + { + QString strname = sql.value(0).toString(); + tablenamelist.append(strname); + } + if (!tablenamelist.contains(_MD_TABLE_FORMS_MANE))//是否包含 table 否则创建新表 + { + QVariantMap vDataMap; + vDataMap.insert(_MD_MODEL, "VARCHAR(32)"); + vDataMap.insert(_MD_DIAMETER, "INT(100)");//直径 + vDataMap.insert(_MD_HIGHT, "INT(100)");//高度 + vDataMap.insert(_MD_CORRELATE, "INT(100)");//相似度 + vDataMap.insert(_MD_CHANNEL, "INT(100)"); + vDataMap.insert("detecttime", "VARCHAR(32)"); + vDataMap.insert(_MD_UPDATE_TIME, "DATETIME(32)"); + vDataMap.insert(_MD_PIC, "BLOB"); + if (!CreatTable(_MD_TABLE_FORMS_MANE, _MD_PRIMARY_KEY, vDataMap)) + { + //return false; + } + else + { + QString strIndex = QString("CREATE INDEX idx_wftable ON wftable (time COLLATE BINARY ASC); "); + db->exec(strIndex);//创建索引 + } + } + + if (!tablenamelist.contains(_MD_TIMETABLE)) + { + QVariantMap vTimeMap;//保存班次时间 + vTimeMap.insert(_MD_TYPE, "VARCHAR(32)"); + vTimeMap.insert(_MD_STARTTIME, "VARCHAR(32)"); + vTimeMap.insert(_MD_ENDTIME, "VARCHAR(32)"); + if (!CreatTable(_MD_TIMETABLE, _MD_PRIMARY_KEY, vTimeMap)) + { + //return false; + } + else{ + + } + } + + if (!tablenamelist.contains(_MD_WARNINGTABLE)) + { + QVariantMap vWarningMap;//报警信息 + vWarningMap.insert(_MD_MESSAGE, "VARCHAR(256)"); + vWarningMap.insert(_MD_CLASS, "VARCHAR(32)"); + vWarningMap.insert(_MD_UPDATE_TIME, "DATETIME(32)"); + if (!CreatTable(_MD_WARNINGTABLE, _MD_PRIMARY_KEY, vWarningMap)) + { + //return false; + + } + else{ + QString strIndex = QString("CREATE INDEX idx_warningtable ON warningtable (time COLLATE BINARY ASC); "); + db->exec(strIndex); + } + } + + return true; +} + +bool DetectDataDB::ReadOutTimeData(QMap &m_MapTimeMatch) +{ + QString select_sql = QString("select * from ") + _MD_TIMETABLE; + QSqlQuery sql = db->exec(select_sql); + QSqlError err = sql.lastError(); + int t = err.type(); + if (t != QSqlError::NoError) + { + return false; + } + while (sql.next()) + { + int nIndex = sql.value("uid").toInt(); + QString type = sql.value(_MD_TYPE).toString(); + QTime start = sql.value(_MD_STARTTIME).toTime(); + QTime end = sql.value(_MD_ENDTIME).toTime(); + TimeStruct m_timeStruct; + m_timeStruct.m_Index = nIndex; + m_timeStruct.m_name = type; + m_timeStruct.m_startTime = start; + m_timeStruct.m_endTime = end; + m_MapTimeMatch.insert(type, m_timeStruct); + } + return true; +} + +bool DetectDataDB::AddOneWarningMessage(WarnMessage m_messageInfo) +{ + QVariantMap m_map; + m_map.insert(_MD_MESSAGE, m_messageInfo.m_Message); + m_map.insert(_MD_UPDATE_TIME, m_messageInfo.m_Date); + m_map.insert(_MD_CLASS, m_messageInfo.m_class); + return InsertOneData(_MD_WARNINGTABLE, m_map); +} +bool DetectDataDB::AddOneTime(TimeStruct &m_timestruct) +{ + QVariantMap m_map; + m_map.insert(_MD_TYPE, m_timestruct.m_name); + m_map.insert(_MD_STARTTIME, m_timestruct.m_startTime); + m_map.insert(_MD_ENDTIME, m_timestruct.m_endTime); + return InsertOneData(_MD_TIMETABLE, m_map); +} +bool DetectDataDB::DelOneTime(TimeStruct &m_timestruct) +{ + QString m_map = gensql::genClass(_MD_TYPE, m_timestruct.m_name);//!>生成where条件语句 + QString strSql = gensql::genDeleteData(_MD_TIMETABLE, m_map); + db->exec(strSql); + if (db->lastError().isValid()) + { + return false; + } + return true; +} + +bool DetectDataDB::AddOneData(QVariantMap m_map) +{ + /*该部分数据是保存检测结果+结果图片*/ + + QString strInsert = "INSERT INTO %1(%2) VALUES(%3)"; + QString strHeader;// + QString strValue;// + + QStringList strKeys = m_map.keys(); + for (int i = 0; i < strKeys.size(); i++) { + strHeader += strKeys[i]; + if (i + 1 < strKeys.size()) + strHeader += ","; + strValue += ":" + strKeys[i] + ""; + + if (i + 1 < strKeys.size()) + strValue += ","; + } + strInsert = strInsert.arg(_MD_TABLE_FORMS_MANE, strHeader, strValue); + //return strInsert; + QSqlQuery query = db->exec(); + query.prepare(strInsert); + for (int i = 0; i < strKeys.size(); i++) + { + QString m_keys = strKeys.at(i); + QString str = ":" + m_keys; + query.bindValue(str, m_map.value(m_keys)); + } + query.exec(); + return true; +} + +bool DetectDataDB::AddOneData(QVariantMap m_map,QString strTableName) +{ + /*该部分数据是保存检测结果+结果图片*/ + + QString strInsert = "INSERT INTO %1(%2) VALUES(%3)"; + QString strHeader;// + QString strValue;// + + QStringList strKeys = m_map.keys(); + for (int i = 0; i < strKeys.size(); i++) { + strHeader += strKeys[i]; + if (i + 1 < strKeys.size()) + strHeader += ","; + strValue += ":" + strKeys[i] + ""; + + if (i + 1 < strKeys.size()) + strValue += ","; + } + strInsert = strInsert.arg(strTableName, strHeader, strValue); + QSqlQuery query = db->exec(); + query.prepare(strInsert); + for (int i = 0; i < strKeys.size(); i++) + { + QString m_keys = strKeys.at(i); + QString str = ":" + m_keys; + query.bindValue(str, m_map.value(m_keys)); + } + query.exec(); + return true; +} + diff --git a/src/lpMain/sqliteDB/DetectDataDB.h b/src/lpMain/sqliteDB/DetectDataDB.h new file mode 100644 index 0000000..ae393d4 --- /dev/null +++ b/src/lpMain/sqliteDB/DetectDataDB.h @@ -0,0 +1,34 @@ +#ifndef DETECTDATA_H +#define DETECTDATA_H +#include "databasesql.h" +#include +#include "InfoFile.h" +#include "QMutex" +/*DetectDataDB 用于记录检测数据 便于查询和插入*/ +class DetectDataDB : public DataBaseSql +{ + Q_OBJECT + +public: + DetectDataDB(const QString& dbName, const QString &dbType = QString("QSQLITE")); + ~DetectDataDB(); + virtual bool InitDatabase(); + + virtual bool checkoutData(QVariantMap m_value, QSqlQuery &sql); + virtual bool checkoutData(QString selectStr, QSqlQuery &sql); + virtual bool DelDatasByTime(QString minTime); + virtual bool DelAllDatas(); + virtual bool DelWarnDataByCount(int model /*=1*/, int nCount/*=100000*/); + bool AddOneWarningMessage(WarnMessage m_messageInfo); + + /*班次时间管理*/ + bool ReadOutTimeData(QMap &m_MapTimeMatch); + bool DelOneTime(TimeStruct &m_timestruct); + bool AddOneTime(TimeStruct &m_timestruct); + bool AddOneData(QVariantMap m_map); + bool AddOneData(QVariantMap m_map, QString strTableName); +private: + +}; + +#endif // DETECTDATA_H diff --git a/src/lpMain/sqliteDB/InfoFile.h b/src/lpMain/sqliteDB/InfoFile.h new file mode 100644 index 0000000..2d30418 --- /dev/null +++ b/src/lpMain/sqliteDB/InfoFile.h @@ -0,0 +1,53 @@ +#ifndef _INFOFILE_H_ +#define _INFOFILE_H_ +#include +#include +#include "qstringlist.h" +#define WS_PICSIZE 129 //!>缩放大小 +#define WS_PICSIZELIST 120 + +#define WF_HUBMODEL_ID "hubID" +#define WF_HUBMODEL_DIAMETER "diameter"//轮毂直径 +#define WF_HUBMODEL_HEIGHT "height"//轮毂厚度 +#define WF_HUBMODEL_CHANNELS "channels" +#define WF_HUBMODEL_FILEPATH "filepath" + +#define WF_LIFHT "Light" +#define WF_PLCPARA "PlcPara" +#define WF_PARASET "paraSetting"//PLC参数设置 +struct TimeStruct +{ + int m_Index; + QString m_name; + QTime m_startTime; + QTime m_endTime; +}; +struct WarnMessage +{ + QString m_Message; + QString m_Date; + QString m_class; +}; + +struct ChannelInfo +{ + QString m_ChannelName; + QString m_AboutName; + int m_value; + QStringList m_strModels; +}; +enum EM_LOG_TYPE{ + emTypeWaring = 1, + emTypeCameraState, + emTypeNetState, + emTypeRunState, + emTypeUseState +}; +enum emTypeBee{ + LIGHT_REDBEE = 0, + LIGHT_GREENBEE, + LIGHT_YELLOWBEE, + LIGHT_BEE +}; + +#endif diff --git a/src/lpMain/sqliteDB/QSqliteGeneral.cpp b/src/lpMain/sqliteDB/QSqliteGeneral.cpp new file mode 100644 index 0000000..ba55195 --- /dev/null +++ b/src/lpMain/sqliteDB/QSqliteGeneral.cpp @@ -0,0 +1,133 @@ +#include "QSqliteGeneral.h" + + +QSqliteGeneral::QSqliteGeneral(const QString& dbPath) + : QZkDbSqlite(dbPath) +{ +} + + +QSqliteGeneral::~QSqliteGeneral() +{ +} + +QString QSqliteGeneral::genInsertData(const QString & strTableName, const QString &strUniqueKey, const QString & strUniqueValue, const QVariantMap &vMap) +{ + QString strInsert = "INSERT INTO %1(%2) VALUES(%3)"; + QString strHeader = strUniqueKey; + QString strValue = "'" + strUniqueValue + "'"; + + QStringList strKeys = vMap.keys(); + for (int i = 0; i < strKeys.size(); i++) { + strHeader += ", " + strKeys[i]; + strValue += ", '" + vMap[strKeys[i]].toString() + "'"; + } + strInsert = strInsert.arg(strTableName, strHeader, strValue); + return strInsert; +} + +QString QSqliteGeneral::genSelect(const QString & strTableName, QStringList strSelectList, QString strClass) +{ + QString strDstSelect; + if (strClass.isEmpty()) { + strDstSelect = "SELECT %1 FROM %2"; + } + else { + strDstSelect = "SELECT %1 FROM %2 WHERE " + strClass; + } + + if (0 == strSelectList.size()) { + strDstSelect = strDstSelect.arg("*").arg(strTableName); + } + else { + QString strSeletTmp = strSelectList[0]; + for (int i = 1; i < strSelectList.size(); i++) { + strSeletTmp += "," + strSelectList[i]; + } + strDstSelect = strDstSelect.arg(strSeletTmp, strTableName); + } + + return strDstSelect; +} + +QString QSqliteGeneral::genClass(QString strName, QString strValue) +{ + QString strDst = ""; + strDst += strName + " = '" + strValue + "'"; + return strDst; +} + +QString QSqliteGeneral::genUpdate(const QString & strTableName, QVariantMap &vMap, QString strClass) +{ + //#define _WF_SQL_UPDATE_INFO_MODEL "UPDATE e_wh_models SET %1 = '%2' WHERE md_unique = '%3'" + QString strDst = "";// + if (strClass.isEmpty()) { + strDst = "UPDATE %1 SET %2"; + } + else { + strDst = "UPDATE %1 SET %2 WHERE %3"; + } + QString strUpdateTmp = ""; + if (vMap.size() != 0) { + QStringList strList = vMap.keys(); + strUpdateTmp += genClass(strList[0], vMap[strList[0]].toString()); + for (int i = 1; i < strList.size(); i++) { + strUpdateTmp += "," + genClass(strList[i], vMap[strList[i]].toString()); + } + } + + strDst = strDst.arg(strTableName, strUpdateTmp, strClass); + return strDst; +} + +QString QSqliteGeneral::genDeleteData(const QString & strTableName, QString strClass) +{ + //#define _WF_SQL_DELETE_MODEL "DELETE FROM e_wh_models WHERE md_unique = '%1'" + QString strDst; + if (strClass.isEmpty()) { + strDst = "DELETE FROM %1"; + } + else { + strDst = "DELETE FROM %1 WHERE %2"; + } + + strDst = strDst.arg(strTableName, strClass); + return strDst; +} + +QString QSqliteGeneral::genInsertColumn(const QString & strTableName, QString strName, QString strType) +{ + QString strDst = "ALTER TABLE %1 ADD COLUMN %2 %3"; + strDst = strDst.arg(strTableName).arg(strName).arg(strType); + + return strDst; +} + +QString QSqliteGeneral::genDeleteColumn(const QString & strTableName, QString strColumnName) +{ + QString strDst = "ALTER TABLE %1 DROP %2"; + strDst = strDst.arg(strTableName, strColumnName); + return strDst; +} + +QString QSqliteGeneral::genCreateTabel(const QString strTable, QString strPrimary, QString strType, QVariantMap &vMap) +{ + QString strDst = "CREATE TABLE IF NOT EXISTS %1 (%2)"; + QString strColumn = strPrimary + " " + strType; + QStringList strList = vMap.keys(); + for (int i = 0; i < strList.size(); i++) { + strColumn += ", " + strList[i] + " " + vMap[strList[i]].toString(); + } + strDst = strDst.arg(strTable, strColumn); + return strDst; +// +// "CREATE TABLE IF NOT EXISTS e_wh_models(md_unique VARCHAR(60) PRIMARY KEY \ +// , md_id VARCHAR(2) \ +// , md_index INT(100) \ +// , md_camKey VARCHAR(32) \ +// , md_model VARCHAR(20) \ +// , md_cali INT(1) DEFAULT '0' \ +// , md_stamp VARCHAR(32) \ +// , md_count INT(1) DEFAULT '0' \ +// , md_text TEXT)" +} \ No newline at end of file diff --git a/src/lpMain/sqliteDB/QSqliteGeneral.h b/src/lpMain/sqliteDB/QSqliteGeneral.h new file mode 100644 index 0000000..0649bd4 --- /dev/null +++ b/src/lpMain/sqliteDB/QSqliteGeneral.h @@ -0,0 +1,18 @@ +#pragma once +#include "QZkDbSqlite.h" +#include "QVariantMap" +class QSqliteGeneral : public QZkDbSqlite +{ +public: + QSqliteGeneral(const QString& dbPath); + ~QSqliteGeneral(); + QString genInsertData(const QString & strTableName, const QString &strUniqueKey, const QString & strUniqueValue, const QVariantMap &vMap); + QString genInsertColumn(const QString & strTableName, QString strName, QString strType); + QString genClass(QString strName, QString strValue); + QString genSelect(const QString & strTableName, QStringList strSelect = QStringList(), QString strClass = QString()); + QString genUpdate(const QString & strTableName, QVariantMap &vMap, QString strClass); + QString genDeleteData(const QString & strTableName, QString strClass = QString()); + QString genDeleteColumn(const QString & strTableName, QString strColumnName); + QString genCreateTabel(const QString strTable, QString strPrimary, QString strType, QVariantMap &vMap); +}; + diff --git a/src/lpMain/sqliteDB/QSqliteWheelHubWf.cpp b/src/lpMain/sqliteDB/QSqliteWheelHubWf.cpp new file mode 100644 index 0000000..269f11e --- /dev/null +++ b/src/lpMain/sqliteDB/QSqliteWheelHubWf.cpp @@ -0,0 +1,343 @@ +#include "QSqliteWheelHubWf.h" +#include "qmutex.h" + +#define _QSQLITE_WHEELHUBWF_NAME "wheelhubwf.db" + +#define _QSQLITE_CREATE_WHEELHUBWF_MODELS \ +"CREATE TABLE IF NOT EXISTS e_wh_models(md_unique VARCHAR(60) PRIMARY KEY \ +, md_id VARCHAR(2) \ +, md_index INT(100) \ +, md_camKey VARCHAR(32) \ +, md_model VARCHAR(20) \ +, md_cali INT(1) DEFAULT '0' \ +, md_stamp VARCHAR(32) \ +, md_count INT(1) DEFAULT '0' \ +, md_text TEXT)" + +#define _QSQLITE_CRETAE_WHEELHUBWF_R_MODEL_CAMERA \ +"CREATE TABLE IF NOT EXISTS r_wh_model_camera(model_id VARCHAR(16) \ +, camera_id INT(32) \ +, camera_key VARCHAR(255) \ +, add_stamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP \ +, mark_data BLOB \ +, PRIMARY KEY(model_id, camera_key))" +//#define _WF_SQL_SELECT_MODEL "SELECT * FROM e_wh_models WHERE md_unique = '%1'" +//#define _WF_SQL_SELECT_MODELS_ALL "SELECT md_id, md_stamp FROM e_wh_models ORDER BY md_stamp" +//#define _WF_SQL_INSERT_MODEL "INSERT INTO e_wh_models(md_unique, md_id, md_camKey, md_model, md_stamp) VALUES('%1', '%2', '%3', '%4', '%5')" +//#define _WF_SQL_INSERT_MODEL_PREFIX "INSERT INTO e_wh_models" +//#define _WF_SQL_DELETE_MODEL "DELETE FROM e_wh_models WHERE md_unique = '%1'" +//#define _WF_SQL_SELECT_INFO_MODEL "SELECT %1 FROM e_wh_models WHERE md_unique = '%2'" +// #define _WF_SQL_UPDATE_INFO_MODEL "UPDATE e_wh_models SET %1 = '%2' WHERE md_unique = '%3'" +//#define _WF_SQL_DELETE_HEADER "alter table e_wh_models drop %1" +// #define _WF_SQL_ADD_HEADER "alter table e_wh_models add column %1 %2" + + +/* +ijλϱ궨乤λ++ͺΨһġ +*/ + +QSqliteWheelHubWf::QSqliteWheelHubWf(const QString& dbPath) + : QSqliteGeneral(dbPath + _QSQLITE_WHEELHUBWF_NAME), m_mxLock(QMutex::Recursive) +{ + +} + +QSqliteWheelHubWf::~QSqliteWheelHubWf() +{ + +} + +bool QSqliteWheelHubWf::InitDatabase() +{ + if (!isOpen() && !open()) + { + return false; + } + //cerate default tables; + QVariantMap vMap; + vMap.insert(_WF_DB_MD_ID, "VARCHAR(2)"); + vMap.insert(_WF_DB_MD_INDEX, "INT(100)"); + vMap.insert(_WF_DB_MD_CAMKEY, "VARCHAR(32)"); + vMap.insert(_WF_DB_MD_MODEL, "VARCHAR(20)"); + vMap.insert(_WF_DB_MD_CALI, "INT(1) DEFAULT '0'"); + vMap.insert(_WF_DB_MD_STAMP, "VARCHAR(32)"); + vMap.insert(_WF_DB_MD_COUNT, "INT(1) DEFAULT '0'"); + vMap.insert(_WF_DB_MD_TEXT, "TEXT"); + + QString strSql = genCreateTabel(_WF_DB_MD_TABLE_NAME, _WF_DB_MD_PRIMARY_KEY, "VARCHAR(60) PRIMARY KEY", vMap); + QSqlQuery sql = exec(strSql); + QSqlError err = sql.lastError(); + int t = err.type(); + if (t != QSqlError::NoError) + { + return false; + } + + //sql = exec(_QSQLITE_CRETAE_WHEELHUBWF_R_MODEL_CAMERA); + //err = sql.lastError(); + //t = err.type(); + // if (t != QSqlError::NoError) + // { + // return false; + // } + return true; +} +void QSqliteWheelHubWf::FreeDatabase() +{ + +} + + + +QStringList QSqliteWheelHubWf::GetModelsByCamera(const QString& camKey) +{ + QStringList modelsList; + QSqlQuery sql = exec(QString("SELECT model_id, camera_id, camera_key FROM r_wh_model_camera WHERE camera_key = '%1'").arg(camKey)); + while (sql.next()) + { + QSqlRecord record = sql.record(); + modelsList.append(record.value("model_id").toString()); + } + return modelsList; +} + +QList QSqliteWheelHubWf::GetCamsOfModel(const QString& model) +{ + QList rmcList; + tagR_MODEL_CAMERA rmc; + bool bOk; + QSqlQuery sql = exec(QString("SELECT model_id, camera_id, camera_key FROM r_wh_model_camera WHERE model_id = '%1'").arg(model)); + while (sql.next()) + { + QSqlRecord record = sql.record(); + rmc.cameraId = record.value("camera_id").toInt(&bOk); + if (!bOk) + { + continue; + } + rmc.cameraKey = record.value("camera_key").toString(); + rmcList.append(rmc); + } + return rmcList; +} + +QStringList QSqliteWheelHubWf::getModelsByCamKey(const QString &camKey) +{ + QStringList modelsList; + + QString strClass = genClass(_WF_DB_MD_CAMKEY, camKey); + QString strSql = genSelect(_WF_DB_MD_TABLE_NAME, QStringList(_WF_DB_MD_MODEL), strClass); + QSqlQuery sql = exec(strSql); + + while (sql.next()) { + QSqlRecord record = sql.record(); + modelsList.append(record.value(_WF_DB_MD_MODEL).toString()); + } + return modelsList; +} + +// bool QSqliteWheelHubWf::addModel(const QString & strUnique, const QString & id, const QString &cam, const QString &model, QString &strTime) +// { +// if (cam.isEmpty() && model.isEmpty()) { +// return false; +// } +// +// QString strSql = QString(_WF_SQL_SELECT_MODEL).arg(strUnique); +// QSqlQuery sql = exec(strSql); +// if (sql.size() > 0) { +// return false; +// } +// strSql = QString(_WF_SQL_INSERT_MODEL).arg(strUnique, id, cam, model, strTime); +// exec(strSql); +// if (lastError().isValid()) +// { +// return false; +// } +// return true; +// } + + +bool QSqliteWheelHubWf::addModel(const QString & strUnique, QVariantMap &vMap) +{ + if (strUnique.isEmpty() || vMap.empty()) { + return false; + } + + QString strClass = genClass(_WF_DB_MD_PRIMARY_KEY, strUnique); + QString strSql = genSelect(_WF_DB_MD_TABLE_NAME, QStringList(), strClass); + + QSqlQuery sql = exec(strSql); + if (sql.size() > 0) { + return false; + } + + strSql = genInsertData(_WF_DB_MD_TABLE_NAME, _WF_DB_MD_PRIMARY_KEY, strUnique, vMap); + + exec(strSql); + if (lastError().isValid()) + { + return false; + } + return true; +} +bool QSqliteWheelHubWf::delModelByUnique(const QString &strUnique) +{ + //QString str = QString(_WF_SQL_DELETE_MODEL).arg(strUnique); + QString str = genDeleteData(_WF_DB_MD_TABLE_NAME, genClass(_WF_DB_MD_PRIMARY_KEY, strUnique));// QString(_WF_SQL_DELETE_MODEL).arg(strUnique); + + QSqlQuery sql = exec(str); + if (sql.size() == 0) { + return false; + } + + return true; +} + +bool QSqliteWheelHubWf::getModelInfoByUnique(const QString &strUnique, const QString & strContent, QVariant &result) +{ + //QString strSql = QString(_WF_SQL_SELECT_INFO_MODEL).arg(strContent).arg(strUnique); + QString strClass = genClass(_WF_DB_MD_PRIMARY_KEY, strUnique);// _WF_DB_PRIMARY_KEY + QString("=") + strUnique; + QString strSql = genSelect(_WF_DB_MD_TABLE_NAME, QStringList(strContent), strClass); + QSqlQuery sql = exec(strSql); + if (sql.size() == 0) { + return false; + } + + if (lastError().isValid()) { + return false; + } + while (sql.next()) + { + QSqlRecord records = sql.record(); + result = records.value(strContent); + return true; + } + + return false; +} + + +bool QSqliteWheelHubWf::setModelInfoByUnique(const QString &strUnique, const QString & strContent, QVariant v, int type) +{ + QMutexLocker locker(&m_mxLock); + QString strSql; + if (type == 0) { + QVariantMap vMap; + vMap.insert(strContent, v); + QString strClass = genClass(_WF_DB_MD_PRIMARY_KEY, strUnique); + strSql = genUpdate(_WF_DB_MD_TABLE_NAME, vMap, strClass); + } + + QSqlQuery sql = exec(strSql); + if (sql.size() == 0) { + return false; + } + + if (lastError().isValid()) { + return false; + } + + return true; +} + +bool QSqliteWheelHubWf::getRecordByUnique(const QString &strUnique, QSqlRecord &result) +{ + //QString strSql = QString(_WF_SQL_SELECT_MODEL).arg(strUnique); + QString strSql = genSelect(_WF_DB_MD_TABLE_NAME, QStringList(), genClass(_WF_DB_MD_PRIMARY_KEY, strUnique));// QString(_WF_SQL_SELECT_MODEL).arg(strUnique); + + QSqlQuery sql = exec(strSql); + if (sql.size() == 0) { + return false; + } + + if (lastError().isValid()) { + return false; + } + while (sql.next()) + { + QSqlRecord records = sql.record(); + result = records; + return true; + } + + return false; +} + +bool QSqliteWheelHubWf::addHeader(QString strColumnName, QString strColumnType) +{ + QString strSql = genSelect(_WF_DB_MD_TABLE_NAME);// QString("SELECT * FROM e_wh_models"); + QSqlQuery sql = exec(strSql); + if (sql.size() == 0) { + return false; + } + + if (lastError().isValid()) { + return false; + } + + while (sql.next()) { + QSqlRecord records = sql.record(); + int bExist = records.contains(strColumnName); + if (!bExist) { + //QString strSql = QString(_WF_SQL_ADD_HEADER).arg(strColumnName).arg(strColumnType); + QString strSql = genInsertColumn(_WF_DB_MD_TABLE_NAME, strColumnName, strColumnType); + QSqlQuery sql = exec(strSql); + + if (sql.size() == 0) { + return false; + } + + if (lastError().isValid()) { + return false; + } + } + return true; + } + + return true; +} + +bool QSqliteWheelHubWf::deleteHeader(QString strColumnName) +{ + QString strSql = genDeleteColumn(_WF_DB_MD_TABLE_NAME, strColumnName);// QString(_WF_SQL_DELETE_HEADER).arg(strColumnName); + QSqlQuery sql = exec(strSql); + + if (sql.size() == 0) { + return false; + } + + if (lastError().isValid()) { + return false; + } + return true; +} + +bool QSqliteWheelHubWf::increaseJob(QString strModel, QString strKey, QString strValue, QVariantMap &vMap) +{ + QString strTable = _WF_DB_MF_TABLE_PRIFIX + strModel; + { + QVariantMap vMapCreate; + QStringList strList = vMap.keys(); + for (int i = 0; i < strList.size(); i++) { + vMapCreate.insert(strList[i], "VARCHAR(20)"); + } + + QString strSql = genCreateTabel(strTable, _WF_DB_MF_PRIMARY_KEY, "VARCHAR(90) PRIMARY KEY", vMapCreate); + QSqlQuery sql = exec(strSql); + + QSqlError err = sql.lastError(); + int t = err.type(); + if (t != QSqlError::NoError) + { + return false; + } + } + + QString strInsert = genInsertData(strTable, strKey, strValue, vMap); + QSqlQuery sql = exec(strInsert); + + if (lastError().isValid()) { + return false; + } + return true; +} \ No newline at end of file diff --git a/src/lpMain/sqliteDB/QSqliteWheelHubWf.h b/src/lpMain/sqliteDB/QSqliteWheelHubWf.h new file mode 100644 index 0000000..6a7e64a --- /dev/null +++ b/src/lpMain/sqliteDB/QSqliteWheelHubWf.h @@ -0,0 +1,64 @@ +#ifndef QSQLITEWHEELHUBWF_H +#define QSQLITEWHEELHUBWF_H + +#include +#include +#include "QSqliteGeneral.h" +#include "qmutex" + +#define _WF_DB_MD_TABLE_NAME "e_wh_models" +#define _WF_DB_MD_PRIMARY_KEY "md_unique" +#define _WF_DB_MD_CAMKEY "md_camKey" +#define _WF_DB_MD_INDEX "md_index" +#define _WF_DB_MD_CALI "md_cali" +#define _WF_DB_MD_COUNT "md_count" +#define _WF_DB_MD_MODEL "md_model" +#define _WF_DB_MD_ID "md_id" +#define _WF_DB_MD_TEXT "md_text" +#define _WF_DB_MD_STAMP "md_stamp" + +#define _WF_DB_MF_TABLE_PRIFIX "e_wf_model_info" +#define _WF_DB_MF_PRIMARY_KEY "mf_time" +#define _WF_DB_MF_MODEL "mf_model" +#define _WF_DB_MF_UNIQUE "mf_unique" +#define _WF_DB_MF_ERROR_TYPE "mf_error" +#define _WF_DB_MF_ANGLE "md_angle" + +#define _WF_UNIQUE_SPLIT "_-_" +#define _WF_PRIMARY_SPLIT "__" + + + + +class QSqliteWheelHubWf : public QSqliteGeneral +{ +public: + struct tagR_MODEL_CAMERA{ + int cameraId; + QString cameraKey; + }; + QSqliteWheelHubWf(const QString& dbPath); + ~QSqliteWheelHubWf(); + + bool InitDatabase(); + void FreeDatabase(); + + QStringList GetModelsByCamera(const QString& camKey); + QList GetCamsOfModel(const QString& model); + + bool addModel(const QString & strUnique, const QString & id, + const QString &cam, const QString &model, QString &strTime); + bool addModel(const QString & strUnique, QVariantMap &vMap); + QStringList getModelsByCamKey(const QString &cam); + bool delModelByUnique(const QString &strUnique); + bool getModelInfoByUnique(const QString &strUnique, const QString & strContent, QVariant &result); + bool getRecordByUnique(const QString &strUnique, QSqlRecord &result); + bool setModelInfoByUnique(const QString &strUnique, const QString & strContent, QVariant result, int type = 0); + bool addHeader(QString strColumnName, QString strColumnType); + bool deleteHeader(QString strColumnName); + bool increaseJob(QString strModel, QString strKey, QString strValue, QVariantMap &vMap); +private: + QMutex m_mxLock; +}; + +#endif // QSQLITEWHEELHUBWF_H diff --git a/src/lpMain/sqliteDB/databasesql.cpp b/src/lpMain/sqliteDB/databasesql.cpp new file mode 100644 index 0000000..dd0f6a5 --- /dev/null +++ b/src/lpMain/sqliteDB/databasesql.cpp @@ -0,0 +1,165 @@ +#include "databasesql.h" +#include "gensql.h" +#define _MD_PRIMARY_KEY "uid" +#define _QMYSQL_ "QMYSQL" +#define _SQLITE_ "QSQLITE" + +DataBaseSql::DataBaseSql(const QString& dbName, const QString &dbType) +{ + m_DBType = dbType; + if (dbType == "QSQLITE") + { + db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", dbName)); + db->setDatabaseName(dbName); + //qry = QSqlQuery::QSqlQuery(db); + } + else if (dbType == "QMYSQL") + { + //if (!QSqlDatabase::contains(m_DBType)) + db = new QSqlDatabase(QSqlDatabase::addDatabase("QMYSQL", dbName)); + //db.setDatabaseName(dbName); + } +} + +DataBaseSql::~DataBaseSql() +{ + if (db->isOpen()) + { + db->close(); + } + delete db; + db = NULL; +} + +bool DataBaseSql::openDB() +{ + return db->open(); +} + +void DataBaseSql::closeDB() +{ + db->close(); +} + +bool DataBaseSql::InitDatabase() +{ + if (!db->open() && db->open()) + { + return false; + } + + return true; +} + +bool DataBaseSql::CreatTable(QString m_tableName, QString m_primaty, QVariantMap m_map) +{ + QString strSql; + if (m_DBType == _SQLITE_) + strSql = gensql::genCreateTabel(m_tableName, m_primaty, "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL", m_map); + else if (m_DBType == _QMYSQL_) + strSql = QString("CREATE TABLE `testtable2` (`id` int(11) NOT NULL,`name` varchar(60) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY(`id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8; "); + QSqlQuery sql = db->exec(strSql); + + QSqlError err = sql.lastError(); + int nType = err.type(); + if (nType != QSqlError::NoError) + return false; + return true; +} + +bool DataBaseSql::InsertOneData(QString m_tableName, QVariantMap m_map) +{ + + if (m_tableName.isEmpty() || m_map.empty()) { + return false; + } + QString strSql; + strSql = gensql::genInsertData(m_tableName, m_map); + qry = db->exec(strSql); + QSqlError bflags = qry.lastError(); + int nType = bflags.type(); + QString str = db->databaseName(); + if (db->lastError().isValid()) + { + return false; + } + return true; +} + +bool DataBaseSql::ModefyOneData(QString m_tableName, QVariantMap m_map) +{ + if (m_tableName.isEmpty() || m_map.empty()) { + return false; + } + int max_id = 0; + QString select_max_sql = "select max(md_unique) from e_report_forms"; + QSqlQuery sql = db->exec(select_max_sql); + if (db->lastError().isValid()) + { + return false; + } + else + { + while (sql.next()) + { + max_id = sql.value(0).toInt(); + } + } + QString m = gensql::genClass(_MD_PRIMARY_KEY, "0"); + QString strSql = gensql::genUpdate(m_tableName, m_map, m); + + db->exec(strSql); + if (db->lastError().isValid()) + { + return false; + } + return true; +} + +bool DataBaseSql::DeleteOneData(QString m_tableName, QString m_map) +{ + if (m_tableName.isEmpty() || m_map.isEmpty()) { + return false; + } + int max_id = 0; + QString select_max_sql = "select max(" + QString(_MD_PRIMARY_KEY) + ") from " + m_tableName; + QSqlQuery sql = db->exec(select_max_sql); + if (db->lastError().isValid()) + { + return false; + } + else + { + while (sql.next()) + { + max_id = sql.value(0).toInt(); + } + } + QString m;// = genClass(_MD_PRIMARY_KEY, "0"); + QString strSql = gensql::genDeleteData(m_tableName, m); + + db->exec(strSql); + if (db->lastError().isValid()) + { + return false; + } + return true; +} + +void DataBaseSql::SetDatabaseName(QString dbName) +{ + db->setDatabaseName(dbName); +} + +void DataBaseSql::SetDBPort(QString nAddr /*= QString("localhost")*/, int nPort /*= 3306*/) +{ + db->setHostName(nAddr); + db->setPort(nPort); +} + +void DataBaseSql::SetDBUser(QString dbUser /*= QString("root")*/, QString dbPwd /*= QString("hzleaper")*/) +{ + db->setUserName(dbUser); + db->setPassword(dbPwd); +} + diff --git a/src/lpMain/sqliteDB/databasesql.h b/src/lpMain/sqliteDB/databasesql.h new file mode 100644 index 0000000..1d82024 --- /dev/null +++ b/src/lpMain/sqliteDB/databasesql.h @@ -0,0 +1,73 @@ +#ifndef DATABASESQL_H +#define DATABASESQL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "QMutex" + +#define _CHECK_TIME1_ "time1" +#define _CHECK_TIME2_ "time2" +#define _CHECK_NAME_ "name" +#define _CHECK_TYPE_ "Type" +#define _CHECK_COUNT_ "Count" +#define _CHECK_LOGTYPE "logType" +enum EMTYPE_CHECKTYPE{ + EMT_CHECK_BY_TIME = 0,//只按时间查询 + EMT_CHECK_BY_NAME,//按照模型名查询 + EMT_CHECK_BY_SAE,//按照起始结束时间查询 + EMT_CHECK_BY_LOG, + EMT_CHECK_BY_COUNT +}; +class DataBaseSql : public QObject +{ + Q_OBJECT + +public: + DataBaseSql( const QString& dbName ,const QString &dbType = QString("QSQLITE")); + ~DataBaseSql(); + bool openDB(); + void closeDB(); + void SetDatabaseName(QString dbName); + void SetDBPort(QString nAddr = QString("localhost"), int nPort = 3306); + void SetDBUser(QString dbUser = QString("root"), QString dbPwd = QString("hzleaper")); + virtual bool InitDatabase(); + bool CreatTable(QString m_tableName, QString m_primaty, QVariantMap m_map); + + virtual bool InsertOneData(QString m_tableName, QVariantMap m_map); + virtual bool ModefyOneData(QString m_tableName, QVariantMap m_map); + virtual bool DeleteOneData(QString m_tableName, QString m_map); + /* + m_value.insert("time1","");//起始时间 + m_value.insert("time2","");//结束时间 + m_value.insert("name","");//模型名字 + m_value.insert("Type","");//查询类型 + m_value.insert("logType","");//报警信息查询 + */ + virtual bool AddOneData(QVariantMap m_map, QString strTableName)=0; + virtual bool checkoutData(QVariantMap m_value, QSqlQuery &sql) = 0; + virtual bool checkoutData(QString selectStr, QSqlQuery &sql) = 0; + virtual bool AddOneWarningMessage(struct WarnMessage m_messageInfo) = 0; + virtual bool DelWarnDataByCount(int model = 1, int nCount = 100000) = 0; + virtual bool DelAllDatas() = 0; + virtual bool DelDatasByTime(QString minTime) = 0; + virtual QString genCheckStr(QVariantMap m_value);//通过value数据生成查询语句 实现与checkoutdata相同 +public: + +protected: + QSqlDatabase *db; + QSqlQuery qry; + QString m_DBType; + +}; + +#endif // DATABASESQL_H diff --git a/src/lpMain/sqliteDB/gensql.cpp b/src/lpMain/sqliteDB/gensql.cpp new file mode 100644 index 0000000..fe955ad --- /dev/null +++ b/src/lpMain/sqliteDB/gensql.cpp @@ -0,0 +1,116 @@ +#include "gensql.h" +#include +#include "QStringList" + +QString gensql::genInsertData(const QString & strTableName, const QVariantMap &vMap) +{ + QString strInsert = "INSERT INTO %1(%2) VALUES(%3)"; + QString strHeader;// + QString strValue;// + + QStringList strKeys = vMap.keys(); + for (int i = 0; i < strKeys.size(); i++) { + strHeader += strKeys[i]; + if (i + 1 < strKeys.size()) + strHeader += ","; + strValue += "'" + vMap[strKeys[i]].toString() + "'"; + if (i + 1 < strKeys.size()) + strValue += ","; + } + strInsert = strInsert.arg(strTableName, strHeader, strValue); + return strInsert; +} + +QString gensql::genSelect(const QString & strTableName, QStringList strSelectList, QString strClass) +{ + QString strDstSelect; + if (strClass.isEmpty()) { + strDstSelect = "SELECT %1 FROM %2"; + } + else { + strDstSelect = "SELECT %1 FROM %2 WHERE " + strClass; + } + + if (0 == strSelectList.size()) { + strDstSelect = strDstSelect.arg("*").arg(strTableName); + } + else { + QString strSeletTmp = strSelectList[0]; + for (int i = 1; i < strSelectList.size(); i++) { + strSeletTmp += "," + strSelectList[i]; + } + strDstSelect = strDstSelect.arg(strSeletTmp, strTableName); + } + + return strDstSelect; +} + +QString gensql::genClass(QString strName, QString strValue) +{ + QString strDst = ""; + strDst += strName + " = '" + strValue + "'"; + return strDst; +} + +QString gensql::genUpdate(const QString & strTableName, QVariantMap &vMap, QString strClass) +{ + QString strDst = "";// + if (strClass.isEmpty()) { + strDst = "UPDATE %1 SET %2";//全部修改 + } + else { + strDst = "UPDATE %1 SET %2 WHERE %3";//指定修改 + } + QString strUpdateTmp = ""; + if (vMap.size() != 0) { + QStringList strList = vMap.keys(); + strUpdateTmp += gensql::genClass(strList[0], vMap[strList[0]].toString()); + for (int i = 1; i < strList.size(); i++) { + strUpdateTmp += "," + gensql::genClass(strList[i], vMap[strList[i]].toString()); + } + } + + strDst = strDst.arg(strTableName, strUpdateTmp, strClass); + return strDst; +} + +QString gensql::genDeleteData(const QString & strTableName, QString strClass) +{ + QString strDst; + if (strClass.isEmpty()) { + strDst = "DELETE FROM %1";//全部删除 + } + else { + strDst = "DELETE FROM %1 WHERE %2";//指定删除 + } + + strDst = strDst.arg(strTableName, strClass); + return strDst; +} + +QString gensql::genInsertColumn(const QString & strTableName, QString strName, QString strType) +{ + QString strDst = "ALTER TABLE %1 ADD COLUMN %2 %3"; + strDst = strDst.arg(strTableName).arg(strName).arg(strType); + + return strDst; +} + +QString gensql::genDeleteColumn(const QString & strTableName, QString strColumnName) +{ + QString strDst = "ALTER TABLE %1 DROP %2"; + strDst = strDst.arg(strTableName, strColumnName); + return strDst; +} + +QString gensql::genCreateTabel(const QString strTable, QString strPrimary, QString strType, QVariantMap &vMap) +{ + QString strDst = "CREATE TABLE IF NOT EXISTS %1 (%2)"; + QString strColumn = strPrimary + " " + strType; + QStringList strList = vMap.keys(); + for (int i = 0; i < strList.size(); i++) { + strColumn += ", " + strList[i] + " " + vMap[strList[i]].toString(); + } + strDst = strDst.arg(strTable, strColumn); + return strDst; +} diff --git a/src/lpMain/sqliteDB/gensql.h b/src/lpMain/sqliteDB/gensql.h new file mode 100644 index 0000000..0a72be0 --- /dev/null +++ b/src/lpMain/sqliteDB/gensql.h @@ -0,0 +1,15 @@ +#ifndef _GENSQL_H_ +#define _GENSQL_H_ +#include "QMetaType" +#include "QString" +namespace gensql{ + QString genInsertData(const QString & strTableName, const QVariantMap &vMap); + QString genSelect(const QString & strTableName, QStringList strSelectList, QString strClass); + QString genClass(QString strName, QString strValue); + QString genUpdate(const QString & strTableName, QVariantMap &vMap, QString strClass); + QString genDeleteData(const QString & strTableName, QString strClass); + QString genInsertColumn(const QString & strTableName, QString strName, QString strType); + QString genDeleteColumn(const QString & strTableName, QString strColumnName); + QString genCreateTabel(const QString strTable, QString strPrimary, QString strType, QVariantMap &vMap); +} +#endif diff --git a/src/lpMain/sqliteDB/qcheckdatadlg.cpp b/src/lpMain/sqliteDB/qcheckdatadlg.cpp new file mode 100644 index 0000000..7ba34b9 --- /dev/null +++ b/src/lpMain/sqliteDB/qcheckdatadlg.cpp @@ -0,0 +1,372 @@ +#include "qcheckdatadlg.h" +#include "qstandarditemmodel.h" +#include "qmessagebox.h" +#include "qfontmetrics.h" +#pragma execution_character_set("utf-8") +QCheckDataDlg::QCheckDataDlg(QWidget *parent) + : QDialog(parent), m_db(NULL), nPrevNum(0), nTotlaNumber(0) +{ + ui.setupUi(this); + m_ViewImg = new QTpGraphView; + connect(ui.m_pbCheck, SIGNAL(clicked()), this, SLOT(onCheckOutData())); + connect(ui.Prev_checkShengchang, SIGNAL(clicked()), this, SLOT(onChenckButton())); + connect(ui.Next_checkShengchang, SIGNAL(clicked()), this, SLOT(onChenckButton())); + connect(ui.m_pbCount, SIGNAL(clicked()), this, SLOT(onChenckButton())); + setWindowIcon(QIcon(":/leaper/app.png")); + pShowName_label = ui.check_fileName; + pShowName_label->clear(); + + check_showImg_label = ui.check_showImg_label; + check_showImg_label->clear(); + QGridLayout *pGrid = new QGridLayout(this); + pGrid->addWidget(m_ViewImg); + check_showImg_label->setLayout(pGrid); + + m_tableModel = new QStandardItemModel; + tableView = ui.tableView; + connect(tableView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTBUI_ItemClick(const QModelIndex &))); + tableView->setModel(m_tableModel); + textBrowser = ui.textBrowser; + + pStartTimeEdit = ui.dateTimeEdit_start; + pEndTimeEdit = ui.dateTimeEdit_end; + QDateTime nDateTime = QDateTime::currentDateTime(); + nDateTime.setTime(QTime(0, 0, 0)); + pStartTimeEdit->setDateTime(nDateTime); + pEndTimeEdit->setDateTime(QDateTime::currentDateTime()); + Qt::WindowFlags flags = Qt::Dialog; + flags |= Qt::WindowMinMaxButtonsHint; + flags |= Qt::WindowCloseButtonHint; + setWindowFlags(flags); + setPageShow(0, 0); +} + +QCheckDataDlg::~QCheckDataDlg() +{ + if (m_tableModel) + { + delete m_tableModel; + m_tableModel = NULL; + } + if (m_ViewImg){ + delete m_ViewImg; + m_ViewImg = NULL; + } +} + +void QCheckDataDlg::createHeader() +{ + QStringList listStr; + listStr.append(QObject::tr("时间")); + listStr.append(QObject::tr("型号")); + listStr.append(QObject::tr("matchScore")); + listStr.append(QObject::tr("BenchMark")); + listStr.append(QObject::tr("Angle")); + listStr.append(QObject::tr("errorType")); + listStr.append(QObject::tr("resultTip")); + listStr.append(QObject::tr("工位")); + m_tableModel->setHorizontalHeaderLabels(listStr); +} + +void QCheckDataDlg::updateModelShowLog(QSqlQuery &sql) +{ + m_tableModel->clear(); + m_strMap.clear(); + createHeader(); + tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);//自适应行宽 + int nIndex = 0; + while (sql.next()) + { + /*这里下面是统计查询到的通道对应的产品总数*/ + QString time = sql.value("time").toString(); + QString dAngle = sql.value("dAngle").toString(); + QString matchScore = sql.value("matchScore").toString().left(5); + QString errorType = sql.value("errorType").toString(); + QString resultTip = sql.value("resultTip").toString(); + QString threshBenchMark = sql.value("threshBenchMark").toString(); + QString stationName = sql.value("stationName").toString(); + QString strFilename = sql.value("value1").toString();//取出保存图片的路径名 + //QByteArray picByte = sql.value("image").toByteArray(); + QString strModelName = sql.value("value2").toString(); + //QImage img = QImage::fromData(picByte); + + m_tableModel->setItem(nIndex, 0, new QStandardItem(time)); + m_tableModel->setItem(nIndex, 1, new QStandardItem(strModelName)); + m_tableModel->setItem(nIndex, 2, new QStandardItem(matchScore)); + m_tableModel->setItem(nIndex, 3, new QStandardItem(threshBenchMark)); + m_tableModel->setItem(nIndex, 4, new QStandardItem(dAngle)); + m_tableModel->setItem(nIndex, 5, new QStandardItem(errorType)); + m_tableModel->setItem(nIndex, 6, new QStandardItem(resultTip)); + m_tableModel->setItem(nIndex, 7, new QStandardItem(stationName)); + m_strMap.insert(time, strFilename); + pShowName_label->setText(strFilename); + nIndex++; + } + + ui.showNum_Label_Checkdata->setText(QString(QObject::tr("共显示%1条记录")).arg(nIndex)); + tableView->setModel(m_tableModel); + + int page = nPrevNum / getLimitNumber(); + int totalPage = nTotlaNumber / getLimitNumber(); + setPageShow(page+1, totalPage+1); +} + +int QCheckDataDlg::getLimitNumber() +{ + int num = ui.LineNumber_checkShengchang->text().toInt(); + if (num > 1) + return num; + else + { + ui.LineNumber_checkShengchang->setText("50"); + return 50; + } + +} +int QCheckDataDlg::getTotalNumber() +{ + QString startTime = pStartTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); + QString endTime = pEndTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); + QString stationName = getStationName(); + int errorType = getErrorType(); + QString strSql = QString(); + if (stationName == QObject::tr("全部")&&errorType == 0) + strSql = QString("select count(uid) from RltTable where time >'%1' and time <'%2' ").arg(startTime).arg(endTime); + else if (stationName == QObject::tr("全部") && errorType == 1) + strSql = QString("select count(uid) from RltTable where time >'%1' and time <'%2' and errorType != '%3'").arg(startTime).arg(endTime).arg(0); + else if (stationName != QObject::tr("全部") && errorType == 1) + strSql = QString("select count(uid) from RltTable where time >'%1' and time <'%2' and errorType != '%3' and stationName == '%4' ").arg(startTime).arg(endTime).arg(0).arg(stationName); + else + strSql = QString("select count(uid) from RltTable where time >'%1' and time <'%2' and stationName == '%3'").arg(startTime).arg(endTime).arg(stationName); + + QSqlQuery sqlquery; + m_db->checkoutData(strSql, sqlquery); + int totalNumber =0; + while (sqlquery.next()) + { + QSqlRecord record = sqlquery.record(); + QString fieldName = record.fieldName(0); + QString num = sqlquery.value(0).toString(); + totalNumber = num.toInt(); + // /*这里下面是统计查询到的通道对应的产品总数*/ + } + return totalNumber; +} +Q_SLOT void QCheckDataDlg::onCheckOutData() +{ + nTotlaNumber = 0; + nPrevNum = 0; + int limitNum = getLimitNumber(); + nTotlaNumber = getTotalNumber(); + + onCheckOutData(limitNum, nPrevNum); +} + +void QCheckDataDlg::onCheckOutData(int nLimitNum, int nPrenum) +{ + QString startTime = pStartTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); + QString endTime = pEndTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); + QString stationName = getStationName(); + int errorType = getErrorType(); + QString strSql = QString(); + if (stationName == QObject::tr("全部")&&errorType == 0) + strSql=QString("select * from RltTable where time >'%1' and time <'%2' LIMIT %3 OFFSET %4 ").arg(startTime).arg(endTime).arg(nLimitNum).arg(nPrenum); + else if (stationName == QObject::tr("全部") && errorType == 1) + strSql = QString("select * from RltTable where time >'%1' and time <'%2' and errorType != '%3' LIMIT %4 OFFSET %5 ").arg(startTime).arg(endTime).arg(0).arg(nLimitNum).arg(nPrenum); + else if (stationName != QObject::tr("全部") && errorType == 1) + strSql = QString("select * from RltTable where time >'%1' and time <'%2' and errorType != '%3' and stationName == '%4' LIMIT %5 OFFSET %6 ").arg(startTime).arg(endTime).arg(0).arg(stationName).arg(nLimitNum).arg(nPrenum); + else + strSql = QString("select * from RltTable where time >'%1' and time <'%2' and stationName == '%3' LIMIT %4 OFFSET %5 ").arg(startTime).arg(endTime).arg(stationName).arg(nLimitNum).arg(nPrenum); + QSqlQuery sqlquery; + m_db->checkoutData(strSql, sqlquery); + updateModelShowLog(sqlquery); +} + +Q_SLOT void QCheckDataDlg::onChenckButton() +{ + QString strObjName = sender()->objectName(); + if (strObjName == "Next_checkShengchang") + { + if (nTotlaNumber <= 0) + return; + if (nPrevNum > nTotlaNumber) + return; + nPrevNum += getLimitNumber(); + if (nPrevNum > nTotlaNumber) + { + nPrevNum -= getLimitNumber(); + QMessageBox infobox(QMessageBox::Information, QObject::tr("提醒"), QObject::tr("已经是最后一页了"), QMessageBox::Ok, NULL); + infobox.setWindowIcon(QIcon(":/leaper/app.png")); + infobox.setButtonText(QMessageBox::Ok, QString(QObject::tr("确认"))); + infobox.exec(); + return; + } + onCheckOutData(getLimitNumber(), nPrevNum); + } + else if (strObjName == "Prev_checkShengchang") + { + if (nTotlaNumber <= 0) + return; + if (nPrevNum < 0) + return; + nPrevNum -= getLimitNumber(); + if (nPrevNum < 0) + { + nPrevNum = 0; + QMessageBox infobox(QMessageBox::Information, QObject::tr("提醒"), QObject::tr("已经是第一页了"), QMessageBox::Ok, NULL); + infobox.setWindowIcon(QIcon(":/leaper/app.png")); + infobox.setButtonText(QMessageBox::Ok, QString(QObject::tr("确认"))); + infobox.exec(); + return; + } + onCheckOutData(getLimitNumber(), nPrevNum); + } + else if (strObjName == "m_pbCount") + { + QString startTime = pStartTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); + QString endTime = pEndTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); + QString stationName = getStationName(); + QString strSql = QString(); + if (stationName == QObject::tr("全部")) + strSql = QString("select errorType,count(uid) from RltTable where time >'%1' and time <'%2' group by errorType order by errorType").arg(startTime).arg(endTime); + else + strSql = QString("select errorType,count(uid) from RltTable where time >'%1' and time <'%2' and stationName == '%3' group by errorType order by errorType").arg(startTime).arg(endTime).arg(stationName); + + QSqlQuery sqlquery; + m_db->checkoutData(strSql, sqlquery); + QMap nCountMap; + + while (sqlquery.next()) + { + QSqlRecord record = sqlquery.record(); + QString fieldName = record.fieldName(0); + QString nType = sqlquery.value(0).toString(); + int count = sqlquery.value(1).toInt(); + nCountMap.insert(nType, count); + } + int TotalNumber = 0; + int ErrorNumber = 0; + textBrowser->clear(); + for (QMap::iterator its = nCountMap.begin(); its != nCountMap.end();++its) + { + QString strKey = its.key(); + int nValue = nCountMap.value(strKey); + QString str; + if (strKey != "0") + str = QString("errorType:%1 Sum:%2;").arg(strKey).arg(nValue); + else + str = QString("goodType Sum:%2;").arg(nValue); + TotalNumber += nValue; + if (strKey != "0") + { + ErrorNumber += nValue; + } + textBrowser->append(str); + } + QString strEnd = QString("ErrorNumber:%1\nTotalNumber:%2").arg(ErrorNumber).arg(TotalNumber); + textBrowser->append(strEnd); + } +} + +void QCheckDataDlg::setStationNames(QStringList listNames) +{ + ui.comboBox_station->addItems(listNames); +} + +QString QCheckDataDlg::getStationName() +{ + return ui.comboBox_station->currentText(); +} + +int QCheckDataDlg::getErrorType() +{ + QString str = ui.comboBox_errorType->currentText(); + if (str == QObject::tr("全部")) + return 0; + else + return 1; +} + +void QCheckDataDlg::setPageShow(int page,int totalPage) +{ + QString str = QString(QObject::tr("第%1页 共%2页")).arg(page).arg(totalPage); + ui.showPage_Label_Checkdata->setText(str); +} + +Q_SLOT void QCheckDataDlg::onTBUI_ItemClick(const QModelIndex & index) +{ + QModelIndex nindex = m_tableModel->index(index.row(), 0); + QString str = nindex.data().toString(); + if (m_strMap.size() > 0) + { + QString strFileName = m_strMap.value(str); + + if (check_showImg_label) + { + QPixmap npixmap; + QString appPath = QApplication::applicationDirPath(); + npixmap.load(appPath+strFileName); + m_ViewImg->setImg(npixmap); + if (!npixmap.isNull()){ + if (pShowName_label) + { + QFontMetrics fontMetrics(this->font()); + int fontSize = fontMetrics.width(strFileName); + QString str = strFileName; + if (fontSize > pShowName_label->width()) + { + str = fontMetrics.elidedText(strFileName, Qt::ElideRight, pShowName_label->width()); + } + pShowName_label->setText(str); + } + } + else{ + if (pShowName_label){ + pShowName_label->setText(QObject::tr("无图片(或图片已过期/已被删除)")); + } + } + } + } + + int i = 0; +} + +Q_SLOT void QCheckDataDlg::onSlowPixmap(QSqlQuery sql) +{ + static bool nFlag = false; + if (nFlag == true) + return; + nFlag = true; + + sql.next(); + double dHight = sql.value("hight").toDouble(); + QByteArray pic = sql.value("pic").toByteArray(); + QImage img = QImage::fromData(pic); + QSize s = img.size(); + QPixmap m_Pix = QPixmap::fromImage(img); + + { + int dhight = m_Pix.height(); + int dwidth = m_Pix.width(); + double nRate = m_Pix.width()*1.0 / m_Pix.height(); + int scarew = 150; + int scareh = 150 * 1.0 / nRate; + + if (scareh > 150) + { + scarew = 150; + scareh = 150 * 1.0 / nRate; + } + + if (check_showImg_label) + { + m_ViewImg->setImg(m_Pix); + } + } + //label_6->setPixmap(m_Pix.scaled(WHEEL_PIC_SIZE, WHEEL_PIC_SIZE)); + nFlag = false; +} + + diff --git a/src/lpMain/sqliteDB/qcheckdatadlg.h b/src/lpMain/sqliteDB/qcheckdatadlg.h new file mode 100644 index 0000000..9fdaf8a --- /dev/null +++ b/src/lpMain/sqliteDB/qcheckdatadlg.h @@ -0,0 +1,58 @@ +#ifndef QCHECKDATADLG_H +#define QCHECKDATADLG_H + +#include +#include "ui_qcheckdatadlg.h" +#include "stationdb.h" +#include "qsqlquery.h" +#include "qstandarditemmodel.h" +#include "qtableview.h" +#include "qdatetimeedit.h" +#include "QTextBrowser.h" +#include "cunstomgraphview.h" +class QCheckDataDlg : public QDialog +{ + Q_OBJECT + +public: + QCheckDataDlg(QWidget *parent = 0); + ~QCheckDataDlg(); + void setDbPtr(StationDB *pDb){ m_db = pDb; }; + void setStationNames(QStringList listNames); + +private: + Q_SLOT void onChenckButton(); + Q_SLOT void onCheckOutData(); + Q_SLOT void onTBUI_ItemClick(const QModelIndex & index); + Q_SLOT void onSlowPixmap(QSqlQuery sql); +private: + int getLimitNumber(); + int getTotalNumber(); + int getErrorType(); + + QString getStationName(); + + void createHeader(); + void updateModelShowLog(QSqlQuery &sql); + void onCheckOutData(int nLimitNum, int nPrenum); + void setPageShow(int page, int totalPage); +private: + Ui::QCheckDataDlg ui; + + QLabel *pShowName_label; + QLabel *check_showImg_label; + QTextBrowser *textBrowser; + StationDB *m_db; + QStandardItemModel *m_tableModel; + QTableView *tableView; + QDateTimeEdit *pStartTimeEdit; + QDateTimeEdit *pEndTimeEdit; + + QMap m_strMap; + int nPrevNum; + int nTotlaNumber; + + QTpGraphView *m_ViewImg; +}; + +#endif // QCHECKDATADLG_H diff --git a/src/lpMain/sqliteDB/qcheckdatadlg.ui b/src/lpMain/sqliteDB/qcheckdatadlg.ui new file mode 100644 index 0000000..35b6ac5 --- /dev/null +++ b/src/lpMain/sqliteDB/qcheckdatadlg.ui @@ -0,0 +1,386 @@ + + + QCheckDataDlg + + + + 0 + 0 + 902 + 598 + + + + 检测记录查询 + + + + + + Qt::Horizontal + + + + + 4 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 3 + 0 + + + + Qt::Vertical + + + true + + + 1 + + + true + + + + + 0 + 2 + + + + + + + + + + 查询操作 + + + + + + + 全部 + + + + + error + + + + + + + + 结束时间: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + 全部 + + + + + + + + 显示: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 起始时间: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 工位: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + 查询 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 8 + + + + + + + + + + 显示数据数(条) + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 100 + 50 + + + + 50 + + + 4 + + + Qt::AlignCenter + + + + + + + 上一页 + + + + + + + TextLabel + + + + + + + 下一页 + + + + + + + Qt::Horizontal + + + + 151 + 20 + + + + + + + + TextLabel + + + + + + + + + + TextLabel + + + + + + + + + + + + + 6 + 0 + + + + + 250 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Qt::Vertical + + + + + 0 + 3 + + + + 统计 + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + 统计当前设置的查询时间记录总数 + + + 统计 + + + + + + + + + 0 + 6 + + + + + 300 + 300 + + + + + + + + + + + + + + + + + + diff --git a/src/lpMain/sqliteDB/stationdb.cpp b/src/lpMain/sqliteDB/stationdb.cpp new file mode 100644 index 0000000..9fea651 --- /dev/null +++ b/src/lpMain/sqliteDB/stationdb.cpp @@ -0,0 +1,94 @@ +#include "stationdb.h" +#define _TABLENAME_ "RltTable" +#define _PERITY_KEY_ "uid" +#define _ST_ANGLE_ "dAngle" +#define _ST_SCORE_ "matchScore" +#define _ST_ERRORTYPE_ "errorType" +#define _ST_RESULT_TIP_ "resultTip" +#define _ST_THREDBENCHMARK_ "threshBenchMark" +#define _ST_STATIONNAME_ "stationName" +#define _ST_VALUE1_ "value1" +#define _ST_VALUE2_ "value2" +#define _ST_VALUE3_ "value3" +#define _ST_TIME_ "time" +#define _ST_PIC_ "image" +#define _ST_NAME_ "modelName" +StationDB::StationDB(const QString& dbName, const QString &dbType /*= QString("QSQLITE")*/):DetectDataDB(dbName,dbType) +{ + +} + +StationDB::~StationDB() +{ + +} + +bool StationDB::InitDatabase() +{ + if (!openDB()) + { + return false; + } + QStringList strList = GetAllTableNamesByDB(); + if (!strList.contains(_TABLENAME_)) + { + QVariantMap vDataMap; + vDataMap.insert(_ST_ANGLE_, "INT(100)"); + vDataMap.insert(_ST_SCORE_, "INT(100)"); + vDataMap.insert(_ST_ERRORTYPE_, "INT(100)");//ƶ + vDataMap.insert(_ST_RESULT_TIP_, "VARCHAR(32)"); + vDataMap.insert(_ST_THREDBENCHMARK_, "VARCHAR(32)"); + vDataMap.insert(_ST_STATIONNAME_, "VARCHAR(32)"); + vDataMap.insert(_ST_VALUE1_, "VARCHAR(32)"); + vDataMap.insert(_ST_VALUE2_, "VARCHAR(32)"); + vDataMap.insert(_ST_VALUE3_, "VARCHAR(32)"); + vDataMap.insert(_ST_TIME_, "DATETIME(32)"); + //vDataMap.insert(_ST_PIC_, "BLOB"); + if (!CreatTable(_TABLENAME_, _PERITY_KEY_, vDataMap)) + { + //return false; + } + else + { + QString strIndex = QString("CREATE INDEX %1 ON %2 (time COLLATE BINARY ASC); ").arg(QString("idx_%1").arg(_TABLENAME_)).arg(_TABLENAME_); + db->exec(strIndex);// + } + } + return true; +} + + +QStringList StationDB::GetAllTableNamesByDB() +{ + QString strCheckTable = QString("select name from sqlite_master where type='table' order by name;"); + QSqlQuery sql = db->exec(strCheckTable);//ѯݿеݱ + QStringList tablenamelist; + while (sql.next()) + { + QString strname = sql.value(0).toString(); + tablenamelist.append(strname); + } + return tablenamelist; +} + +void StationDB::addData2DB(Struct2SaveData &data) +{ + QVariantMap m_map; + m_map.insert(_ST_ANGLE_, data.dAngle); + m_map.insert(_ST_SCORE_, data.matchScore); + m_map.insert(_ST_ERRORTYPE_, data.errorType); + m_map.insert(_ST_RESULT_TIP_, data.resultTip); + m_map.insert(_ST_THREDBENCHMARK_, data.threshBenchMark); + m_map.insert(_ST_STATIONNAME_, data.stationName); + m_map.insert(_ST_VALUE1_, data.value1); + m_map.insert(_ST_VALUE2_, data.value2); + m_map.insert(_ST_VALUE3_, data.value3); + m_map.insert(_ST_TIME_, QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz")); + //QByteArray ad; + //QBuffer buffer(&ad); + //data.m_pixRlt.save(&buffer, "jpg", 20); + //m_map.insert(_ST_PIC_, ad); + AddOneData(m_map, _TABLENAME_); + +} + diff --git a/src/lpMain/sqliteDB/stationdb.h b/src/lpMain/sqliteDB/stationdb.h new file mode 100644 index 0000000..05b5cb1 --- /dev/null +++ b/src/lpMain/sqliteDB/stationdb.h @@ -0,0 +1,35 @@ +#ifndef STATIONDB_H +#define STATIONDB_H + +#include +#include "DetectDataDB.h" +#include "QPixmap" +struct Struct2SaveData +{ + double dAngle; + int errorType; + double matchScore; + QString resultTip; + QString threshBenchMark; + QString stationName; + QString value1; + QString value2; + QString value3; + QPixmap m_pixRlt; +}; + +class StationDB : public DetectDataDB +{ + Q_OBJECT + +public: + StationDB(const QString& dbName, const QString &dbType = QString("QSQLITE")); + ~StationDB(); + virtual bool InitDatabase(); + QStringList GetAllTableNamesByDB(); + void addData2DB(Struct2SaveData &data); +private: + +}; + +#endif // STATIONDB_H diff --git a/tpvs17/Enchanter/Enchanter.qrc b/tpvs17/Enchanter/Enchanter.qrc new file mode 100644 index 0000000..7c87735 --- /dev/null +++ b/tpvs17/Enchanter/Enchanter.qrc @@ -0,0 +1,4 @@ + + + + diff --git a/tpvs17/Enchanter/Enchanter.vcxproj b/tpvs17/Enchanter/Enchanter.vcxproj new file mode 100644 index 0000000..b1efa29 --- /dev/null +++ b/tpvs17/Enchanter/Enchanter.vcxproj @@ -0,0 +1,139 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {7B76D75A-0E01-451E-880E-FB9AC63A914B} + Qt4VSv1.0 + 10.0.17763.0 + + + + Application + v141 + + + Application + v141 + + + + $(MSBuildProjectDirectory)\QtMsBuild + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(ProjectName)d + + + $(SolutionDir)$(Platform)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + true + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;%(AdditionalIncludeDirectories) + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + + + Windows + $(SolutionDir)..\runner17\$(TargetName)$(TargetExt) + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + true + qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;%(AdditionalDependencies) + + + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + Moc'ing %(Identity)... + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;%(AdditionalIncludeDirectories) + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) + + + Uic'ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h + + + Rcc'ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp + + + + + true + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;%(AdditionalIncludeDirectories) + + MultiThreadedDLL + true + + + Windows + $(OutDir)\$(ProjectName).exe + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + false + qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies) + + + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + Moc'ing %(Identity)... + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;%(AdditionalIncludeDirectories) + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) + + + Uic'ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h + + + Rcc'ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp + + + + + + + + + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tpvs17/Enchanter/Enchanter.vcxproj.filters b/tpvs17/Enchanter/Enchanter.vcxproj.filters new file mode 100644 index 0000000..0a7e78a --- /dev/null +++ b/tpvs17/Enchanter/Enchanter.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + moc;h;cpp + False + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/tpvs17/Enchanter/Enchanter.vcxproj.user b/tpvs17/Enchanter/Enchanter.vcxproj.user new file mode 100644 index 0000000..1cea243 --- /dev/null +++ b/tpvs17/Enchanter/Enchanter.vcxproj.user @@ -0,0 +1,15 @@ + + + + + D:\Qt\Qt5.9.4\5.9.4\msvc2017_64 + $(SolutionDir)..\runner17\$(TargetName)$(TargetExt) + $(SolutionDir)..\runner17\ + WindowsLocalDebugger + PATH=$(QTDIR)\bin%3b$(PATH) + + + D:\Qt\Qt5.9.4\5.9.4\msvc2017_64 + PATH=$(QTDIR)\bin%3b$(PATH) + + \ No newline at end of file diff --git a/tpvs17/Enchanter/IMainWidget.h b/tpvs17/Enchanter/IMainWidget.h new file mode 100644 index 0000000..f68415a --- /dev/null +++ b/tpvs17/Enchanter/IMainWidget.h @@ -0,0 +1,20 @@ +#ifndef _H_IMAINWIDGET_H_ +#define _H_IMAINWIDGET_H_ + +#include +#include +class IMainWidget : public QObject +{ + Q_OBJECT + +public: + IMainWidget(QObject *parent = nullptr) {}; + ~IMainWidget() {}; + virtual QWidget* getMainWidget() = 0; +}; + +#define IMAINWIDGET_API extern "C" __declspec(dllexport) +IMAINWIDGET_API IMainWidget* MainWidget_Create(); +IMAINWIDGET_API void MainWidget_Delete(IMainWidget* pwd); +typedef IMainWidget* (*_MainWidget_Create)(); +#endif diff --git a/tpvs17/Enchanter/main.cpp b/tpvs17/Enchanter/main.cpp new file mode 100644 index 0000000..847ad79 --- /dev/null +++ b/tpvs17/Enchanter/main.cpp @@ -0,0 +1,35 @@ +#include +#include +#include "IMainWidget.h" +#include + +#pragma execution_character_set("utf-8") +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication a(argc, argv); + IMainWidget* pMain = nullptr; + QWidget* pMainWid = nullptr; +#ifdef _DEBUG + QLibrary lib("lpMaind"); +#else + QLibrary lib("lpMain"); +#endif + _MainWidget_Create func = (_MainWidget_Create)lib.resolve("MainWidget_Create"); + if (func) + { + pMain = func(); + if (pMain) { + pMainWid = pMain->getMainWidget(); + } + } + if (pMainWid) + { + pMainWid->show(); + a.exec(); + } + else { + QMessageBox::information(0, "", QObject::tr("lpMainʧ")); + } + return 0; +} diff --git a/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj b/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj new file mode 100644 index 0000000..a95df4b --- /dev/null +++ b/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj @@ -0,0 +1,141 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {784071A9-BF94-4D27-B62E-588ACD7E0633} + Qt4VSv1.0 + 10.0.16299.0 + + + + DynamicLibrary + v141 + + + DynamicLibrary + v141 + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(ProjectName)d + + + $(SolutionDir)$(Platform)\$(Configuration)\ + + + + UNICODE;_UNICODE;WIN32;WIN64;QT_CORE_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_SQL_LIB;TPCORECTRL_LIB;TPCORECTRL_EXPORTS;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;..\..\src\lpCoreCtrl;..\..\src\lpCoreCtrl\tpCamera;..\..\src\lpCoreCtrl\tpImgProc;..\..\3part\libzkq\include;..\..\..\lpOpenssl\openssl-1.0.2n\vs13\include;..\..\src\lpBase;%(AdditionalIncludeDirectories) + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + + + Windows + $(SolutionDir)..\runner17\$(TargetName)$(TargetExt) + $(QTDIR)\lib;$(OutDir);..\..\..\lpOpenssl\openssl-1.0.2n\vs13\lib\x64;%(AdditionalLibraryDirectories) + true + qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Networkd.lib;Qt5SerialPortd.lib;Qt5Sqld.lib;tpBased.lib;libzkqd.lib;libeay32.lib;%(AdditionalDependencies) + + + + + UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_SQL_LIB;TPCORECTRL_LIB;TPCORECTRL_EXPORTS;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;..\..\include;..\..\src\tpBase;..\..\src\tpCoreCtrl;..\..\src\tpCamera;..\..\src\tpCommunicate;..\..\src\tpImgProc;..\..\3dpart\modbus\include;..\..\..\spider\libZK;..\..\src\crypto;..\..\..\lpOpenssl\openssl-1.0.2n\vs13\include;%(AdditionalIncludeDirectories) + ProgramDatabase + MultiThreadedDLL + true + + + Windows + $(SolutionDir)..\runner17\$(TargetName)$(TargetExt) + $(QTDIR)\lib;$(OutDir);..\..\..\lpOpenssl\openssl-1.0.2n\vs13\lib\x64;%(AdditionalLibraryDirectories) + true + qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Network.lib;Qt5SerialPort.lib;Qt5Sql.lib;tpBase.lib;libzkq.lib;libeay32.lib;%(AdditionalDependencies) + + + + + + + + + + + \ No newline at end of file diff --git a/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.filters b/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.filters new file mode 100644 index 0000000..43f9326 --- /dev/null +++ b/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.filters @@ -0,0 +1,212 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + moc;h;cpp + False + + + {98ba8cd6-7087-4a49-9a18-5b750011123a} + + + {17f92375-9526-49f4-8a95-2b5adfa2fb7e} + + + {2edd888e-9c88-49cf-a98d-e23d4409689d} + + + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCamera + + + tpCamera + + + tpCamera + + + tpCamera + + + tpCamera + + + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpImgProc + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCoreCtrl + + + tpCamera + + + tpCamera + + + tpCamera + + + tpCamera + + + \ No newline at end of file diff --git a/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.user b/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.user new file mode 100644 index 0000000..4195e6b --- /dev/null +++ b/tpvs17/lpCoreCtrl/lpCoreCtrl.vcxproj.user @@ -0,0 +1,12 @@ + + + + + D:\Qt\Qt5.9.4\5.9.4\msvc2017_64 + PATH=$(QTDIR)\bin%3b$(PATH) + + + D:\Qt\Qt5.9.4\5.9.4\msvc2017_64 + PATH=$(QTDIR)\bin%3b$(PATH) + + \ No newline at end of file diff --git a/tpvs17/lpMain/CMainWin.cpp b/tpvs17/lpMain/CMainWin.cpp new file mode 100644 index 0000000..6321043 --- /dev/null +++ b/tpvs17/lpMain/CMainWin.cpp @@ -0,0 +1,951 @@ +#include "CMainWin.h" +#include +#include "lpSysLog.h" +#include "IStation.h" +#include +#include +#include "WfCtrl.h" +#include +#include "quserinfo_global.h" +#include "Serialport_global.h" +#include "qcheckdatadlg.h" +#include "QAboutUI.h" + +#define LEAPER_LOGO ":/leaper/app.png" +#define DELETE_POINTER(p) if (p) {delete p; p = NULL;} +#pragma execution_character_set("utf-8") +CMainWin::CMainWin(QWidget *parent) + : QMainWindow(parent) +{ + setWindowIcon(QIcon(":/leaper/Resource/app.png")); + onInitCoreCtrl(); + + ui.setupUi(this); + lpSysLog::instance()->Init(); + readConfig(); + SetLanguage(m_strCurLanguage); + + { + QString strPath = QCoreApplication::applicationDirPath(); + QString DBFilePath = strPath + "\\DBFiles"; + QDir dbDir; + dbDir.mkpath(DBFilePath); + m_db = new StationDB(DBFilePath + "\\AntMan.db"); + m_db->InitDatabase(); + QSettings setting(strPath + "\\user\\systemfile.ini", QSettings::IniFormat); + connect(this, SIGNAL(sgShowLog(int, QString)), this, SLOT(onShowLog(int, QString))); + SYSLOG_STATUS << "ϵͳ"; + } + + m_pCameraTrig = new AutoTrigger; + connect(m_pCameraTrig, SIGNAL(sgTrig()), this, SLOT(onTrigImage())); + onInitStatus(); + + if (!m_DesignerMgr.Initialize(m_pDetectorEngine)) { + qWarning() << "Initialize is false"; + + } + connect(&m_DesignerMgr, SIGNAL(sgCloseWindow()), this, SLOT(onMainFrameClose())); + connect(&m_testWid, SIGNAL(sgTestMode(int)), this, SLOT(onTestMode(int))); + + m_pColossus = new WfColossus(m_pDetectorEngine); + m_pWfCtrl = new CWfCtrl(m_pCoreCtrl, m_pColossus); + + connect(m_pColossus, SIGNAL(sgSetModel(int, QString)), this, SLOT(onShowName(int, QString))); + m_mangeWid.onInitModelList(m_pWfCtrl); + + {//ûģ +#ifdef _DEBUG + QLibrary lib("QUserInfod"); +#else + QLibrary lib("QUserInfo"); +#endif + _UserCtrlCreate func = (_UserCtrlCreate)lib.resolve("UserCtrlCreate"); + if (func) { + m_pUserCtrl = func(); + connect(m_pUserCtrl, SIGNAL(sgCurrentUserInfo(QString, int, int)), this, SLOT(onLogInOut(QString, int, int))); + } + } + + {//豸 +#ifdef _DEBUG + QLibrary lib("SerialPortToold");//ļ +#else + QLibrary lib("SerialPortTool");//ļ +#endif + _SerialPortCreate func = (_SerialPortCreate)lib.resolve("SerialPortCreate"); + if (func) + m_pSerialPort = func(); + + //load seriport dll + if (m_pSerialPort)//ļ ʹԶĴ + { + m_pSerialPort->loadAnalysefunc(this, &CMainWin::onAppanalysis); + + //رտܵĴʹ + QString strDefaultPort = "COM6"; + + bool bOpen = m_pSerialPort->OpenCom(strDefaultPort, QString::number(115200)); + if (bOpen == false) + { + QMessageBox infobox(QMessageBox::Information, QString(QObject::tr("ʾ")), QString("error %1 %2 ").arg(strDefaultPort).arg(115200), QMessageBox::Yes | QMessageBox::No, NULL); + infobox.setWindowIcon(QIcon(":/image/leaper")); + infobox.exec(); + } + connect(&m_HeartBit, SIGNAL(timeout()), this, SLOT(onHeardBit())); + m_HeartBit.start(300); + } + } + + connect(ui.actionSetting, SIGNAL(triggered()), this, SLOT(onActionClicked())); + connect(ui.actionManage, SIGNAL(triggered()), this, SLOT(onActionClicked())); + connect(ui.actionTest, SIGNAL(triggered()), this, SLOT(onActionClicked())); + connect(ui.actionHelp, SIGNAL(triggered()), this, SLOT(onActionClicked())); + connect(ui.action_Check, SIGNAL(triggered()), this, SLOT(onActionClicked())); + connect(ui.action, SIGNAL(triggered()), this, SLOT(onActionClicked())); + connect(ui.main_Login_action, SIGNAL(triggered()), this, SLOT(onActionClicked())); + connect(ui.main_action_userManager, SIGNAL(triggered()), this, SLOT(onActionClicked())); + + connect(this, SIGNAL(sgShowImg(int, QImage)), this, SLOT(onShowImage(int, QImage))); + connect(this, SIGNAL(sgSelModel(int, QString)), this, SLOT(onSelModel(int, QString))); + m_TimerID_Status = startTimer(1000); + + ui.main_action_userManager->setVisible(false); + ui.action->setVisible(false); + + QTextDocument *pDoc = ui.wf_text_edit_result_1->document(); + pDoc->setMaximumBlockCount(200); + pDoc = ui.wf_text_edit_result_2->document(); + pDoc->setMaximumBlockCount(200); +} + +CMainWin::~CMainWin() +{ + if (m_pStatus) { + DELETE_POINTER(m_pStatusTrig); + DELETE_POINTER(m_pStatus); + } + DELETE_POINTER(m_pVersion); + DELETE_POINTER(m_pCameraTrig); + DELETE_POINTER(m_pWfCtrl); + DELETE_POINTER(m_pColossus); + if (m_db) + { + delete m_db; + m_db = NULL; + } + if (m_pUserCtrl) { + delete m_pUserCtrl; + m_pUserCtrl = NULL; + } + if (m_pSerialPort) { + m_HeartBit.stop(); + m_pSerialPort->CloseCom(); + delete m_pSerialPort; + m_pSerialPort = NULL; + } + rmTranslator(); + lpSysLog::uninstance(); +} + +void CMainWin::IVariantMapToUI(emTpUiDataType dataType, const QString& camKey, const QVariantMap& vMap) +{ + qDebug() << "variant 2 ui, start"; + if (!m_pWfCtrl) { + qWarning() << "VariantMapToUI, WFctrl is null"; + return; + } + QVariantMap mMap = vMap.value("algoRlt").toMap(); + IStation *pStation = m_pWfCtrl->IGetStationByKey(camKey); + if (pStation) { + QImage image = vMap.value("image").value(); + int nSize = image.height(); + if (!vMap.contains("angle")) { + qWarning() << "no angle result"; + } + double dAngle = vMap.contains("angle") ? vMap.value("angle").toDouble() : 365; + int errorType = vMap.contains("error") ? vMap.value("error").toInt() : 16; + double matchScore = vMap.value("score").toDouble() * 100; + + QString str = vMap.value("resultTip").toString(); + QStringList strList = str.split("/"); + QString strRusltTip = strList.first(); + QString threshBenchMark = strList.last(); + //QString strImgPath = vMap.value("imageName").toString(); + + QString strResult = QTime::currentTime().toString("hh:mm:ss zzz:") + strList.first(); + strResult += "angle result : " + QString::number(dAngle, 'f', 3); + pStation->setSerialPortPtr(m_pSerialPort); + if (dAngle >= 361) + dAngle = 361.0; + pStation->sendResult(dAngle); + if (image.isNull()) + int b = 0; + SYSLOG_STATUS << QString("λ%1յ㷨,ǰͺΪ[%2],ͽǶΪ:%3").arg(pStation->stationId()).arg(pStation->currentRunningModel()).arg(dAngle); + emit sgShowImg(pStation->stationId(),image); + emit sgShowLog(pStation->stationId(), strResult); + //emit pStation->sgPrint2Window(strResult); + pStation->revResult(); + + QString str2 = pStation->currentRunningModel(); + int ID = pStation->stationId(); + QString strModelName = QString("%1_%2").arg(ID).arg(str2); + QString strImgPath;// = genSavePath(strModelName, image); + Struct2SaveData nStructData; + nStructData.dAngle = dAngle; + nStructData.errorType = errorType; + nStructData.matchScore = matchScore; + nStructData.threshBenchMark = threshBenchMark; + nStructData.resultTip = strRusltTip; + nStructData.stationName = pStation->stationShowName(); + nStructData.value1 = strImgPath; + nStructData.value2 = str2; + qWarning() << "Add Result to DB,cam=" << camKey; + m_db->addData2DB(nStructData); + } + else { + qWarning() << "can not find station key=" << camKey; + qWarning() << "framework, camera kyes:" << m_pCoreCtrl->ICameraKeys(); + qWarning() << "wf ctrl, camera kyes:" << m_pWfCtrl->IGetStationKeys(); + } + qDebug() << "variant 2 ui, end"; +} + +QVariant CMainWin::IGetVariantById(int id) +{ + IStation *pStation = m_pWfCtrl->IGetStationById(id); + if (pStation) { + return pStation->getVariant(); + } + return QVariant(); +} + +QString CMainWin::genSavePath(QString modelName, QImage &img) +{ + QString strApp = QApplication::applicationDirPath(); + QString targetPath = "/DBFiles/Images"; + QString strData = QDateTime::currentDateTime().toString("yyyyMMdd"); + QString strFileName = QDateTime::currentDateTime().toString("yyyy_MM_dd_hhmmsszzz") + ".jpg"; + targetPath = targetPath + "/" + strData + "/" + modelName; + QDir dir; + dir.mkpath(strApp + targetPath); + targetPath = targetPath + "/" + strFileName; + + if (!img.isNull()) { + QString strImg = strApp + targetPath; + img.save(strImg, "jpg", 5); + } + return targetPath; +} + +Q_SLOT void CMainWin::onAppanalysis(SComFrame frame) +{ + //ݽ + int nCmd = frame.cmd; + if (0x43 == nCmd) + { + onHeartComm(frame); + return; + } + else if (0x50 == nCmd) { + int nCameraID = -1; + if (5 == frame.data1) { + nCameraID = 1; + } + else if (6 == frame.data1) { + nCameraID = 2; + } + + if (!m_pWfCtrl) { + qDebug() << "CommAchieved m_pWfCtrl is null"; + return; + } + IStation *pStation = m_pWfCtrl->IGetStationById(nCameraID); + if (!pStation) { + return; + } + if (m_pWfCtrl->IOnlineMode() == true) + { + if (nCameraID == 1) + { + if (m_StationInfo_1.m_bRunEnable == true) + { + pStation->trigImage(); + } + else { + QString strMsg = QString("%1:ͺID:%2ģͿвڣգ").arg(QTime::currentTime().toString("hh:mm:ss zzz:")).arg(m_StationInfo_1.m_PLCID); + emit sgShowLog(1, strMsg); + if (m_StationInfo_1.m_PLCID > 0) { + pStation->setSerialPortPtr(m_pSerialPort); + pStation->sendResult(999); + SYSLOG_STATUS << QString("λ%1:յź,Ϸû,999Ƕ,ͺΪ[%2],ͺIDΪ[%3],ʱ:%4") + .arg(nCameraID) + .arg(m_StationInfo_1.strModelName.isEmpty() == true ? "" : m_StationInfo_1.strModelName) + .arg(m_StationInfo_1.m_PLCID) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + else { + SYSLOG_STATUS << QString("λ%1:յź,Ϸû,ͺΪ[%2],ͺIDΪ[%3],ʱ:%4") + .arg(nCameraID) + .arg(m_StationInfo_1.strModelName.isEmpty() == true ? "" : m_StationInfo_1.strModelName) + .arg(m_StationInfo_1.m_PLCID) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + } + } + else if (nCameraID == 2) + { + if (m_StationInfo_2.m_bRunEnable == true) + { + pStation->trigImage(); + } + else + { + QString strMsg = QString("%1:ͺID:%2ģͿвڣգ").arg(QTime::currentTime().toString("hh:mm:ss zzz:")).arg(m_StationInfo_2.m_PLCID); + emit sgShowLog(2, strMsg); + if (m_StationInfo_2.m_PLCID > 0) { + pStation->setSerialPortPtr(m_pSerialPort); + pStation->sendResult(999); + SYSLOG_STATUS << QString("λ%1:յź,Ϸû,999Ƕ,ͺΪ[%2],ͺIDΪ[%3],ʱ:%4") + .arg(nCameraID) + .arg(m_StationInfo_2.strModelName.isEmpty() == true ? "" : m_StationInfo_2.strModelName) + .arg(m_StationInfo_2.m_PLCID) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + + } + else { + SYSLOG_STATUS << QString("λ%1:յź,Ϸû,ͺΪ[%2],ͺIDΪ[%3],ʱ:%4") + .arg(nCameraID) + .arg(m_StationInfo_2.strModelName.isEmpty() == true ? "" : m_StationInfo_2.strModelName) + .arg(m_StationInfo_2.m_PLCID) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + } + } + } + else + pStation->trigImage(); + } + else if (0xf1 == nCmd) + {//յλرд ػ + qApp->closeAllWindows(); + //shutDown(); + } +} + +void CMainWin::onHeartComm(SComFrame &frame) +{ + static int nS1 = -1; + static int nS2 = -1; + + if (m_pWfCtrl) + m_pWfCtrl->registerConnect(); + else + return; + + if (m_pWfCtrl->IOnlineMode() == true) + { + int nSta1 = frame.data1; + int nSta2 = frame.data2; + if (nS1 != nSta1 || nS2 != nSta2) + { + nS1 = nSta1; + nS2 = nSta2; + } + else + return; + + QStringList lstKeys = m_pWfCtrl->IGetStationKeys(); + for each (QString var in lstKeys) + { + IStation *pStation = m_pWfCtrl->IGetStationByKey(var); + if (!pStation) { + continue; + } + int nId = pStation->stationId(); + if (nId <= 0 || nId > 8) { + //error + qWarning() << "error, id is beyong"; + continue; + } + + int mmCmd = 0; + if (nId == 1) + mmCmd = frame.data1; + if (nId == 2) + mmCmd = frame.data2; + if (nId == 3) + mmCmd = frame.data3; + if (nId == 4) + mmCmd = frame.data4; + if (nId == 5) + mmCmd = frame.data5; + if (nId == 6) + mmCmd = frame.data6; + if (nId == 7) + mmCmd = frame.data7; + if (nId == 8) + mmCmd = frame.data8; + QString strModel = pStation->modelByPlcCmd(mmCmd); + if (!strModel.isEmpty()) + { + emit(sgSelModel(nId, strModel)); + if (nId == 1) + { + if (m_StationInfo_1.m_PLCID != mmCmd) + { + m_StationInfo_1.m_PLCID = mmCmd; + m_StationInfo_1.strModelName = strModel; + SYSLOG_STATUS << QString("﹤λ%1:лͺΪ[%2],ͺIDΪ[%3],ʱ:%4") + .arg(nId) + .arg(strModel) + .arg(mmCmd) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + m_StationInfo_1.m_bRunEnable = true; + } + else if (nId == 2) + { + if (m_StationInfo_2.m_PLCID != mmCmd) { + m_StationInfo_2.m_PLCID = mmCmd; + m_StationInfo_2.strModelName = strModel; + SYSLOG_STATUS << QString("﹤λ%1:лͺΪ[%2],ͺIDΪ[%3],ʱ:%4") + .arg(nId) + .arg(strModel) + .arg(mmCmd) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + m_StationInfo_2.m_bRunEnable = true; + } + } + else { + if (nId == 1) + { + if (m_StationInfo_1.m_PLCID != mmCmd) { + m_StationInfo_1.m_PLCID = mmCmd; + m_StationInfo_1.strModelName = strModel; + if (mmCmd <= 0) + SYSLOG_STATUS << QString("﹤λ%1:ͺ,ͺIDΪ[%2],ʱ:%3") + .arg(nId) + .arg(mmCmd) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + else + SYSLOG_STATUS << QString("﹤λ%1:ͺлʧ,Ӧ,ͺIDΪ[%2],ʱ:%3") + .arg(nId) + .arg(mmCmd) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + + m_StationInfo_1.m_bRunEnable = false; + if (mmCmd > 0) + { + QString strMsg = QString("%1:ͺʧ,ģͿв%2").arg(QTime::currentTime().toString("hh:mm:ss zzz:")).arg(mmCmd); + emit sgShowLog(1, strMsg); + } + } + else if (nId == 2) + { + if (m_StationInfo_2.m_PLCID != mmCmd) { + m_StationInfo_2.m_PLCID = mmCmd; + m_StationInfo_2.strModelName = strModel; + if (mmCmd <= 0) + SYSLOG_STATUS << QString("﹤λ%1:ͺ,ͺIDΪ[%2],ʱ:%3") + .arg(nId) + .arg(mmCmd) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + else + SYSLOG_STATUS << QString("﹤λ%1:ͺлʧ,Ӧ,ͺIDΪ[%2],ʱ:%3") + .arg(nId) + .arg(mmCmd) + .arg(QDateTime::currentDateTime().toString("hh:mm:ss zzz")); + } + m_StationInfo_2.m_bRunEnable = false; + + if (mmCmd > 0) { + QString strMsg = QString("%1:ͺʧܣģͿв%2").arg(QTime::currentTime().toString("hh:mm:ss zzz:")).arg(mmCmd); + emit sgShowLog(2, strMsg); + } + } + } + } + } +} + +Q_SLOT void CMainWin::onHeardBit() +{ + if (m_pSerialPort) { + SComFrame frame; + memset(&frame, 0, sizeof(SComFrame)); + frame.cmd = 0x43; + frame.data7 = 200; + frame.data8 = 0x50; + m_pSerialPort->enquequeData(frame); + } +} + +Q_SLOT void CMainWin::onActionClicked() +{ + QString strObj = sender()->objectName(); + if ("actionSetting" == strObj) {//궨 + IDetectorUI* pDetectorUI = GetDesignerInterface(); + if (pDetectorUI) { + pDetectorUI->ShowMainFrame(); + } + } + else if ("actionManage" == strObj) {//ģ + m_mangeWid.setParent(this); + m_mangeWid.setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + m_mangeWid.setWindowModality(Qt::ApplicationModal); + m_mangeWid.setAttribute(Qt::WA_ShowModal, true); + m_mangeWid.show(); + } + else if ("actionTest" == strObj) {// + m_testWid.setParent(this); + m_testWid.setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + m_testWid.setWindowModality(Qt::ApplicationModal); + m_testWid.setAttribute(Qt::WA_ShowModal, true); + m_testWid.show(); + } + else if ("actionHelp" == strObj) {// + QAboutUI dlg(this); + dlg.setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + dlg.setWindowModality(Qt::ApplicationModal); + dlg.setAttribute(Qt::WA_ShowModal, true); + dlg.show(); + } + else if ("action_Check" == strObj) {//ʷ¼ѯ + QCheckDataDlg dlg(this); + dlg.setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + dlg.setDbPtr(m_db); + QStringList strList = m_pWfCtrl->IGetStationKeys(); + QStringList pList; + for (int nIndex = 0; nIndex < strList.size(); nIndex++) + { + IStation *pStation = m_pWfCtrl->IGetStationByKey(strList.at(nIndex)); + pList.append(pStation->stationShowName()); + } + dlg.setStationNames(pList); + dlg.exec(); + } + else if ("action" == strObj) {//ϵͳ + + } + else if ("main_Login_action" == strObj) {//û½ + if (m_pUserCtrl) { + if (m_pUserCtrl->getLoginState() == EM_LOGIN) + { + QMessageBox infobox(QMessageBox::Information, QString(QObject::tr("ʾ")), QString(QObject::tr("ȷҪע%1 ?")).arg(m_pUserCtrl->CurUser()), QMessageBox::Yes | QMessageBox::No, NULL); + infobox.setWindowIcon(QIcon(":/image/leaper")); + infobox.setButtonText(QMessageBox::Yes, QString("ȷ")); + infobox.setButtonText(QMessageBox::No, QString("ȡ")); + if (infobox.exec() == QMessageBox::Yes) { + m_pUserCtrl->LogOutUser(); + } + } + else + m_pUserCtrl->CheckLogin(); + } + else + { + QMessageBox infobox(QMessageBox::Information, QString(QObject::tr("ʾ")), QString(QObject::tr("ùδ.")), QMessageBox::Yes, NULL); + infobox.setWindowIcon(QIcon(":/image/leaper")); + infobox.setButtonText(QMessageBox::Yes, QString(QObject::tr("ȷ"))); + infobox.exec(); + } + } + else if ("main_action_userManager" == strObj) {//û + if (m_pUserCtrl) { + m_pUserCtrl->ShowUserMgrDlg(); + } + else + { + QMessageBox infobox(QMessageBox::Information, QString(QObject::tr("ʾ")), QString(QObject::tr("ùδ.")), QMessageBox::Yes, NULL); + infobox.setWindowIcon(QIcon(":/image/leaper")); + infobox.setButtonText(QMessageBox::Yes, QString(QObject::tr("ȷ"))); + infobox.exec(); + } + } +} + +void CMainWin::timerEvent(QTimerEvent *event) +{ + if (m_TimerID_Status == event->timerId()) + { + onUpdateStatus(); + } +} + +void CMainWin::closeEvent(QCloseEvent *event) +{ + QMessageBox info(this); + info.setWindowIcon(QIcon(LEAPER_LOGO)); + info.setWindowTitle(QObject::tr("")); + info.setText(QObject::tr("ϵͳУҪرգ")); + info.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + info.setButtonText(QMessageBox::Ok, QObject::tr("ȷ")); + info.setButtonText(QMessageBox::Cancel, QObject::tr("ȡ")); + if (info.exec() != QMessageBox::Ok) + { + event->ignore(); + } + else + event->accept(); +} + +//ʼ״̬ +void CMainWin::onInitStatus() +{ + const int c_nWidth = 170; + m_pLbCurrentTime = new QLabel(QObject::tr("ϵͳʱ")); + m_pLbCurrentTime->setMinimumHeight(40); + m_pLbCurrentTime->setMinimumWidth(c_nWidth); + + m_pLbOnLine = new class QLabel(QObject::tr("ģʽ:")); + m_pLbOnLine->setMinimumWidth(c_nWidth); + + m_pLbUser = new class QLabel(QObject::tr("û:")); + m_pLbUser->setMinimumWidth(c_nWidth); + + m_pLbConnect = new class QLabel(QObject::tr("״̬:")); + m_pLbConnect->setMinimumWidth(c_nWidth); + + m_pLbDiskSpace = new class QLabel(QObject::tr("Ӳʣռ:xxx.xG")); + m_pLbDiskSpace->setMinimumWidth(c_nWidth); + + ui.statusBar->addWidget(m_pLbOnLine); + ui.statusBar->addWidget(m_pLbConnect); + ui.statusBar->addWidget(m_pLbUser); + ui.statusBar->addWidget(m_pLbCurrentTime); + ui.statusBar->addWidget(m_pLbDiskSpace); +} + +void CMainWin::onUpdateStatus() +{ + if (m_pLbCurrentTime) { + QString m_currentTimerString = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + m_pLbCurrentTime->setText(QObject::tr("ϵͳʱ: ") + m_currentTimerString + " "); + //m_pLbCurrentTime->setStyleSheet("font: 14px;"); + } + +// if (m_pLbOnLine && m_pCtrl) { +// QString strOnlineState = QString(QObject::tr("ģʽ")) +// + (m_pCtrl->IOnlineMode() == true ? QObject::tr("ģʽ") : QObject::tr("ģʽ")); +// m_pLbOnLine->setText(strOnlineState); +// // m_pLbOnLine->setStyleSheet("font: bold 14px;"); +// } + + if (m_pLbConnect) { + QString strOnlineState = QString(QObject::tr("״̬")) + + (true == true ? QObject::tr("") : QObject::tr("쳣")); + m_pLbConnect->setText(strOnlineState); + } + + if (m_pLbUser) { + QString strUser; + m_pLbUser->setText(QObject::tr("û ") + strUser); + } + if (m_pLbDiskSpace) + { + QString strDiskSpace = QString(QObject::tr("Ӳʣռ:%1G")).arg(100 / 1024.0); + m_pLbDiskSpace->setText(strDiskSpace); + } +} + +bool CMainWin::onInitCoreCtrl() +{ + //load engine + if (NULL == m_pDllDetectorEngine) + { + m_pDllDetectorEngine = new CDllDetectorEngine(); + if (NULL == m_pDllDetectorEngine) + { + return false; + } + m_pDetectorEngine = m_pDllDetectorEngine->m_pDE; + } + //load coretrl + if (NULL == m_pDllCoreCtrl) + { + m_pDllCoreCtrl = new CDllCoreCtrl(QStringList(), this, m_pDetectorEngine); + if (NULL == m_pDllCoreCtrl) + { + return false; + } + m_pCoreCtrl = m_pDllCoreCtrl->m_pCoreCtrl; + } + + m_pDllDetectorEngine->Initialize(m_pCoreCtrl); + return true; +} + +bool CMainWin::onInitDevice() +{ + QStringList strCamKeys = m_pCoreCtrl->ICameraKeys(); + for (int nIndex = 0; nIndex < strCamKeys.size(); nIndex++) + { + QString camKey = strCamKeys.at(nIndex); + m_pCoreCtrl->IOpenCamera(camKey); + m_pCoreCtrl->IStartCamera(camKey); + } + return true; +} +//======ϵͳ +void CMainWin::SearchQmFile(const QString & strDir) +{ + QDir dir(strDir); + if (!dir.exists()) + { + return; + } + dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + dir.setSorting(QDir::DirsFirst); // ļ + // תһList + QFileInfoList list = dir.entryInfoList(); + if (list.size() < 1) + { + return; + } + int i = 0; + do + { + QFileInfo fileInfo = list.at(i); + QString tt = fileInfo.fileName(); + // ļ + bool bisDir = fileInfo.isDir(); + if (bisDir) + { + SearchQmFile(fileInfo.filePath()); + } + else + { + bool bQm = fileInfo.fileName().endsWith(".qm"); + SetTranslator(fileInfo.filePath()); + } + i++; + } while (i < list.size()); +} + +void CMainWin::SetTranslator(const QString strPath) +{ + if (strPath.isEmpty()) + { + return; + } + QTranslator *pTrans = new QTranslator; + if (pTrans->load(strPath)) // سɹ + { + qApp->installTranslator(pTrans); + m_VecTranPtr.append(pTrans); + } + else + { + delete pTrans; + pTrans = NULL; + } +} + +void CMainWin::SetLanguage(QString strLangage) +{ + QString strDirPath = QString(QCoreApplication::applicationDirPath() + "/language/"); + QString translatorFileName = strLangage; + if (!translatorFileName.isEmpty()) + { + rmTranslator(); + QLocale::setDefault(QLocale(translatorFileName)); + + QString transDir = strDirPath + translatorFileName; + SearchQmFile(transDir); + } +} + +void CMainWin::rmTranslator() +{ + if (m_VecTranPtr.size() > 0) + { + while (m_VecTranPtr.size()) + { + QTranslator *pVa = m_VecTranPtr.takeFirst(); + qApp->removeTranslator(pVa); + delete pVa; + pVa = NULL; + } + } +} + +void CMainWin::readConfig() +{ + QSettings setting("language.ini", QSettings::IniFormat); + m_strCurLanguage = setting.value("language", "Chinese").toString(); +} + +void CMainWin::writeConfig() +{ + QSettings setting("language.ini", QSettings::IniFormat); + setting.setValue("language", m_strCurLanguage); +} +//====== + +IDetectorUI* CMainWin::GetDesignerInterface() const +{ + return m_DesignerMgr.GetDesignerInterface(); +} + +void CMainWin::saveSolution() +{ + qDebug() << "start save solution"; + if (m_pColossus && m_pWfCtrl) { + m_pColossus->saveTask(m_pWfCtrl->IGetModelInfos()); + } + qDebug() << "finish save solution"; +} + +Q_SLOT void CMainWin::onMainFrameClose() +{ + if (m_pWfCtrl) { + m_pWfCtrl->IUpdateModelInfo(); + } +} + +Q_SLOT void CMainWin::onSnapImage(int nCamera /*= -1*/) +{ + if (!m_pWfCtrl) + return; + IStation *pStation = m_pWfCtrl->IGetStationById(nCamera); + if (!pStation) { + return; + } + else { + pStation->trigImage(); + } +} + +Q_SLOT void CMainWin::onTrigImage() +{ + onSnapImage(1); + onSnapImage(2); +} + +Q_SLOT void CMainWin::onSelModel(int nId, QString strModel) +{ + if (m_pWfCtrl) { + m_pWfCtrl->ISelModel(nId, strModel); + } +} + +Q_SLOT void CMainWin::onChangeUI(QString strUsr, int nLevel) +{ + switch (nLevel) { + case 9: + case 8: + case 7: + case 6: + ui.main_action_userManager->setVisible(true); + ui.action->setEnabled(true); + ui.actionTest->setEnabled(true); + break; + case 5: + ui.main_action_userManager->setVisible(true); + ui.action->setEnabled(true); + ui.actionTest->setEnabled(true); + break; + case 4: + case 3: + case 2: + case 1: + case 0: + ui.main_action_userManager->setVisible(false); + ui.action->setEnabled(false); + ui.actionTest->setEnabled(false); + break; + default: + break; + } +} +Q_SLOT void CMainWin::onLogInOut(QString strName, int level, int state) +{ + if (m_pWfCtrl) { + m_pWfCtrl->ISetUserInfo(strName, level); + onChangeUI(strName, level); + } + + if (state == 0) { + ui.main_Login_action->setText(QObject::tr("ע ")); + } + else + { + ui.main_Login_action->setText(QObject::tr(" ¼")); + } +} + +Q_SLOT void CMainWin::onTestMode(int val) +{ + if (val == 0) { + m_pCameraTrig->start(1000); + } + else if(val ==1){ + m_pCameraTrig->stop(); + } + else if (val == 2) { + onSnapImage(1); + } + else if (val == 3) { + onSnapImage(2); + } +} +//չʾͼƬ +Q_SLOT void CMainWin::onShowImage(int ID, QImage img) +{ + auto ShowImg=[&](QLabel* pLab ,QImage& img) { + if (!pLab) { + qDebug() << "label image is null"; + return; + } + if (img.isNull()) + { + QString str = QObject::tr("⵽: ģͼߴ粻ƥ䣬±궨ģ!!!"); + pLab->setText(str); + } + else + { + QRect rt = ui.wf_lb_image_show_1->rect(); + int h = img.height(); + int w = img.width(); + float d = w * 1.0 / h; + if (d > 0) + { + int h2 = rt.width() / d; + rt.setHeight(h2); + } + QImage imgShow = img.scaled(QSize(rt.size())); + pLab->setPixmap(QPixmap::fromImage(imgShow)); + } + }; + if (ID == 1) + { + ShowImg(ui.wf_lb_image_show_1,img); + } + else if (ID == 2) + { + ShowImg(ui.wf_lb_image_show_2, img); + } +} +//չʾڼͺ +Q_SLOT void CMainWin::onShowName(int ID, QString strName) +{ + if (ID == 1) { + ui.wf_lb_station_name_1->setText(strName); + } + else if (ID == 2) { + ui.wf_lb_station_name_2->setText(strName); + } +} +//չʾlog +Q_SLOT void CMainWin::onShowLog(int nID, QString strMsg) +{ + if (nID == 1) { + ui.wf_text_edit_result_1->append(strMsg); + } + else if (nID == 2) { + ui.wf_text_edit_result_2->append(strMsg); + } +} \ No newline at end of file diff --git a/tpvs17/lpMain/CMainWin.h b/tpvs17/lpMain/CMainWin.h new file mode 100644 index 0000000..4e86ecd --- /dev/null +++ b/tpvs17/lpMain/CMainWin.h @@ -0,0 +1,135 @@ +#ifndef _H_CMAINWIN_H_ +#define _H_CMAINWIN_H_ + +#include +#include "ui_CMainWin.h" +#include "QUserBase.h" +#include "BaseSerialport.h" +#include +#include +#include "CDllCoreCtrl.h" +#include "CDllDetectorEngine.h" +#include "stationdb.h" +#include "IWfCtrl.h" +#include "WfColossus.h" +#include "AutoTrigger.h" +#include "QDetectorDesignerMgr.h" +#include "QTestModeWid.h" +#include "iCoreCtrl.h" +#include "QModelMangerUI.h" + +struct StationInfo +{ + int m_PLCID{ 0 }; + bool m_bRunEnable{ false }; + QString strModelName; +}; +class CMainWin : public QMainWindow, public IGuiCallback +{ + Q_OBJECT + +public: + CMainWin(QWidget *parent = Q_NULLPTR); + ~CMainWin(); + + virtual void IOnReady() {} + virtual void IUpdateShow(const QString& skey) {}; + virtual void INewCameraImage(const QVariantMap& vMap) {}; + virtual void IAddWindows(const QString& sWinName, const QString& sShowId) {}; + virtual void ISendDataToUI(emTpUiDataType dataType, const QString& camKey, void* pData) {}//ãIVariantMapToUI() + virtual void ICameraTrigger(bool bStartOrStop) {};//ãICommInterval() + virtual void IDrawDefectToScene(const QString& szWinName, IImageObject::emTpDefectType defectType, QByteArray& data) {}; + virtual void ISafeDataToUI(emTpUiDataType dataType, const QString& camKey, const QByteArray& data) {}; + virtual void IWarning(int nWarningCode, void* data, int nDataLen) {}; + virtual void ICommInterval(const char* szCom, int nCmd, BYTE* pData, int nDataLen) {}; + virtual void IAlgorithmResult(const QVariantMap& varMap) {}; + virtual void IVariantMapToUI(emTpUiDataType dataType, const QString& camKey, const QVariantMap& vMap); + virtual QVariant IGetVariantById(int id); + virtual void IIoStatesChanged(int nOldState, int nNewState) {}; + virtual void ICommAchieved(const char* szCom, int nCmd, BYTE* pData, int nDataLen) {}; + virtual WORD IGetWorkState() { return 0; };//з͵Ƿ񴥷ֵ01 +public: + Q_SLOT void onAppanalysis(SComFrame frame); + void onHeartComm(SComFrame &frame); + Q_SLOT void onHeardBit(); + Q_SLOT void onShowLog(int nID, QString strMsg); + Q_SLOT void onActionClicked(); + QString genSavePath(QString modelName, QImage &img); +protected: + virtual void timerEvent(QTimerEvent *event); + virtual void closeEvent(QCloseEvent *event); +protected: + void onInitStatus(); + void onUpdateStatus(); + + bool onInitCoreCtrl(); + bool onInitDevice(); +signals: + void sgSelModel(int, QString); + void sgSendChangeUI(QString, QString); + void sgShowLog(int, QString); + void sgShowImg(int, QImage); +private://Ӣ + void SearchQmFile(const QString & strDir); + void SetTranslator(const QString strPath); + void SetLanguage(QString strLangage); + void rmTranslator(); + void readConfig(); + void writeConfig(); + +private: + IDetectorUI* GetDesignerInterface() const; + QDetectorDesignerMgr m_DesignerMgr; + void saveSolution(); + int lastNum(QString str); + Q_SLOT void onMainFrameClose(); + + Q_SLOT void onSnapImage(int nCamera = -1); + Q_SLOT void onTrigImage(); + Q_SLOT void onSelModel(int, QString); + Q_SLOT void onChangeUI(QString strUsr, int nLevel); + Q_SLOT void onLogInOut(QString strName, int level, int state); + Q_SLOT void onTestMode(int); + Q_SLOT void onShowImage(int ID, QImage img); + Q_SLOT void onShowName(int ID, QString strName); +private: + Ui::CMainWin ui; + + IUserCtrl *m_pUserCtrl{ nullptr }; + ISerialPortTool *m_pSerialPort{ nullptr }; + QTimer m_HeartBit; + + QVector m_VecTranPtr; + QString m_strCurLanguage{ "Chinese" }; + + StationInfo m_StationInfo_1; + StationInfo m_StationInfo_2; + + //status ״̬ + int m_TimerID_Status{ 0 }; + class QLabel *m_pLbCurrentTime; + class QLabel *m_pLbOnLine; + class QLabel *m_pLbConnect; + class QLabel *m_pLbUser; + class QLabel *m_pLbDiskSpace; +private: + CDllCoreCtrl* m_pDllCoreCtrl{nullptr}; + CDllDetectorEngine* m_pDllDetectorEngine{nullptr}; + + ICoreCtrl* m_pCoreCtrl{nullptr}; + IDetectorEngine* m_pDetectorEngine{nullptr}; + + StationDB *m_db{nullptr}; + class AutoTrigger *m_pCameraTrig; + class AutoTrigger *m_pStatusTrig; + class ModelLists *m_pModelList; + class IWfCtrl *m_pWfCtrl; + class WfColossus *m_pColossus; + class WfStatus *m_pStatus; + class CVersion *m_pVersion; + + QTestModeWid m_testWid; + QModelMangerUI m_mangeWid; +}; + +#endif diff --git a/tpvs17/lpMain/CMainWin.ui b/tpvs17/lpMain/CMainWin.ui new file mode 100644 index 0000000..a8d0aea --- /dev/null +++ b/tpvs17/lpMain/CMainWin.ui @@ -0,0 +1,386 @@ + + + CMainWin + + + + 0 + 0 + 1028 + 686 + + + + CMainWin + + + + + + + + 0 + 0 + + + + + 0 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 20 + 20 + + + + + 240 + 80 + + + + + + + :/wanfengLogo/Resource/wanfeng2.png + + + true + + + false + + + + + + + + 宋体 + 22 + + + + 浙江万丰科技开发股份有限公司 + + + Qt::AutoText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QFrame::Box + + + QFrame::Raised + + + + 3 + + + 3 + + + + + + + + 0 + 40 + + + + + 55555 + 40 + + + + + 微软雅黑 + 10 + + + + <html><head/><body><p><span style=" font-size:11pt; color:#ff0000;">工位1</span></p></body></html> + + + + + + + + 492 + 372 + + + + + 492 + 372 + + + + QFrame::Panel + + + image + + + Qt::AlignCenter + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 40 + + + + + 16777215 + 40 + + + + + 微软雅黑 + 10 + + + + <html><head/><body><p><span style=" font-size:11pt; color:#ff0000;">工位2</span></p></body></html> + + + + + + + + 492 + 372 + + + + + 492 + 372 + + + + QFrame::Panel + + + image + + + Qt::AlignCenter + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 12 + + + + Qt::ToolButtonTextUnderIcon + + + TopToolBarArea + + + false + + + + + + + + + + + + + + + :/toolbar/Resource/toolBar/cali.png:/toolbar/Resource/toolBar/cali.png + + + 标定 + + + + + + :/toolbar/Resource/toolBar/model.png:/toolbar/Resource/toolBar/model.png + + + 模型管理 + + + + + + :/toolbar/Resource/toolBar/test.png:/toolbar/Resource/toolBar/test.png + + + 测试 + + + + + + :/toolbar/Resource/toolBar/help.png:/toolbar/Resource/toolBar/help.png + + + 帮助 + + + + + + :/toolbar/Resource/toolBar/education24.png:/toolbar/Resource/toolBar/education24.png + + + 查询记录 + + + + + + :/toolbar/Resource/toolBar/setting.png:/toolbar/Resource/toolBar/setting.png + + + 工位配置 + + + + + + :/toolbar/Resource/toolBar/administrator.png:/toolbar/Resource/toolBar/administrator.png + + + 登 录 + + + + + + :/toolbar/Resource/toolBar/messenger.png:/toolbar/Resource/toolBar/messenger.png + + + 用户管理 + + + + + + + + + diff --git a/tpvs17/lpMain/IMainWidget.h b/tpvs17/lpMain/IMainWidget.h new file mode 100644 index 0000000..f68415a --- /dev/null +++ b/tpvs17/lpMain/IMainWidget.h @@ -0,0 +1,20 @@ +#ifndef _H_IMAINWIDGET_H_ +#define _H_IMAINWIDGET_H_ + +#include +#include +class IMainWidget : public QObject +{ + Q_OBJECT + +public: + IMainWidget(QObject *parent = nullptr) {}; + ~IMainWidget() {}; + virtual QWidget* getMainWidget() = 0; +}; + +#define IMAINWIDGET_API extern "C" __declspec(dllexport) +IMAINWIDGET_API IMainWidget* MainWidget_Create(); +IMAINWIDGET_API void MainWidget_Delete(IMainWidget* pwd); +typedef IMainWidget* (*_MainWidget_Create)(); +#endif diff --git a/tpvs17/lpMain/QAboutUI.cpp b/tpvs17/lpMain/QAboutUI.cpp new file mode 100644 index 0000000..01a6519 --- /dev/null +++ b/tpvs17/lpMain/QAboutUI.cpp @@ -0,0 +1,12 @@ +#include "QAboutUI.h" +#pragma execution_character_set("utf-8") +QAboutUI::QAboutUI(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); + +} + +QAboutUI::~QAboutUI() +{ +} diff --git a/tpvs17/lpMain/QAboutUI.h b/tpvs17/lpMain/QAboutUI.h new file mode 100644 index 0000000..a48c100 --- /dev/null +++ b/tpvs17/lpMain/QAboutUI.h @@ -0,0 +1,19 @@ +#ifndef _H_QABOUTUI_H_ +#define _H_QABOUTUI_H_ + +#include +#include "ui_QAboutUI.h" + +class QAboutUI : public QWidget +{ + Q_OBJECT + +public: + QAboutUI(QWidget *parent = Q_NULLPTR); + ~QAboutUI(); + +private: + Ui::QAboutUI ui; +}; + +#endif diff --git a/tpvs17/lpMain/QAboutUI.ui b/tpvs17/lpMain/QAboutUI.ui new file mode 100644 index 0000000..ad828bd --- /dev/null +++ b/tpvs17/lpMain/QAboutUI.ui @@ -0,0 +1,34 @@ + + + QAboutUI + + + + 0 + 0 + 436 + 284 + + + + 关于 + + + + + + + 11 + + + + <html><head/><body><p>Main Version: 1.5.0228</p><p>Tadpole Version: 2.0.99</p><p>Baumer Driver: v2.8</p><p>Final update date:20200228 11:00:00</p><p>Final Test data:11</p><p>单工位汽轮定位型号判别</p><p>CopyRight (C) 2016-2022 HangZhou Leaper </p><p>摩汽配件产品部</p><p><br/></p></body></html> + + + + + + + + + diff --git a/tpvs17/lpMain/QModelMangerUI.cpp b/tpvs17/lpMain/QModelMangerUI.cpp new file mode 100644 index 0000000..a269fb2 --- /dev/null +++ b/tpvs17/lpMain/QModelMangerUI.cpp @@ -0,0 +1,203 @@ +#include "QModelMangerUI.h" +#include +#include +#include +#include +#include "QPLCIndexUI.h" +#include "WfModel.h" + +#pragma execution_character_set("utf-8") +QModelMangerUI::QModelMangerUI(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); + connect(ui.wf_model_select_button, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + connect(ui.wf_model_add_button, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + connect(ui.wf_model_mod_button, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + connect(ui.wf_model_delete_button, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + QRegExp regx("[a-zA-Z0-9]+$"); + QSharedPointer validator = QSharedPointer(new QRegExpValidator(regx)); + ui.wf_model_input_edit->setValidator(validator.data()); + + connect(ui.wf_chkbox_read_mode_from_plc, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxChange(int))); +} + + +QModelMangerUI::~QModelMangerUI() +{ +} + +Q_SLOT void QModelMangerUI::onButtonClicked() +{ + QString strObj = sender()->objectName(); + if ("wf_model_select_button" == strObj) { + if (!m_pCtrl->IOnlineMode()) { + IStation *pStation = getStation(); + if (pStation) { + QString strModel = pStation->currentSelectModel(); + if (!strModel.isEmpty()) { + m_pCtrl->ISelModel(currentTab(), strModel); + ui.mLblModelState->setText(tr("current %1 OK!").arg(strModel)); + m_pCtrl->ISetModifyModel(true); + } + } + } + } + else if ("wf_model_add_button" == strObj) { + QString strModel = ui.wf_model_input_edit->text(); + if (!strModel.isEmpty() && !strModel.contains(_WF_UNIQUE_SPLIT) && !strModel.contains("##") + && strModel.size() >= 3 && strModel.size() <= 20 && !strModel.contains(" ")) { + int nIndex = currentTab(); + bool bFlag = m_pCtrl->IAddModel(nIndex, strModel); + if (bFlag) { + ui.mLblModelState->setText(tr("add %1 successful!").arg(strModel)); + ui.wf_model_input_edit->setText(""); + } + else { + ui.mLblModelState->setText(tr("add %1 failed!").arg(strModel)); + } + } + else { + ui.mLblModelState->setText(QObject::tr("ģͺвַܰոַ320֮!")); + } + QTimer::singleShot(5000, [&]() { + ui.mLblModelState->setText(""); + }); + } + else if ("wf_model_mod_button" == strObj) { + + } + else if ("wf_model_delete_button" == strObj) { + IStation *pStation = getStation(); + if (pStation) { + QString strModel = pStation->currentRunningModel(); + if (!strModel.isEmpty()) { + QString strName = m_pCtrl->IGetCurrentRuningModel(currentTab()); + if (strModel == strName) + { + QMessageBox msgWarning(QMessageBox::Warning, QObject::tr("󾯸"), strModel + QObject::tr(" ʹãܱɾ"), QMessageBox::Yes); + msgWarning.setWindowIcon(QIcon(":/leaper/app.png")); + msgWarning.setButtonText(QMessageBox::Yes, QObject::tr("ȷ")); + msgWarning.exec(); + return ; + } + QMessageBox msgBox(QMessageBox::Warning, QObject::tr("ͺɾ"), QObject::tr("ɾ") + strModel + "?", QMessageBox::Yes | QMessageBox::No); + msgBox.setWindowIcon(QIcon(":/leaper/app.png")); + if (QMessageBox::Yes == msgBox.exec()) { + bool b = m_pCtrl->IDeleteModel(currentTab(), strModel); + if (!b) { + ui.mLblModelState->setText(tr("delete %1 failed!").arg(strModel)); + } + else { + ui.mLblModelState->setText(tr("delete %1 successful!").arg(strModel)); + } + } + } + } + } +} +int QModelMangerUI::lastNum(QString str) +{ + return str.right(1).toInt(); +} +Q_SLOT bool QModelMangerUI::OnCellDoubleClicked(const QModelIndex &index) +{ + QString strListName = sender()->objectName(); + if (1 != index.column()) { + return false; + } + int stationID = lastNum(strListName); + IStation *pStation = getStation(stationID); + if (pStation) { + QString strModel = pStation->model(index.row()); + QString strName = QObject::tr("ģͺ:") + strModel; + WfModel *pModel = pStation->wfModel(strModel); + if (!pModel) { + return false; + } + QPLCIndexUI dlg(this); + dlg.setModelName(strName); + dlg.setModelIndex(pModel->nIndex); + if (dlg.exec() == QDialog::Accepted) + { + int nIndex = dlg.getModelIndex(); + pModel->nIndex = nIndex; + } + } + + return true; +} + +Q_SLOT void QModelMangerUI::onCheckBoxChange(int state) +{ + if (state > 0) + { + ui.wf_model_select_button->setDisabled(true); + if (m_pCtrl) + m_pCtrl->ISetOnlineModel(true); + } + else { + ui.wf_model_select_button->setDisabled(false); + if (m_pCtrl) + m_pCtrl->ISetOnlineModel(false); + } +} + +IStation * QModelMangerUI::getStation(int nIndex) +{ + if (nIndex == -1) { + QWidget *pWidget = ui.tabWidgetStation->currentWidget(); + if (pWidget) { + nIndex = lastNum(pWidget->objectName()); + } + } + return m_pCtrl->IGetStationById(nIndex); +} + +void QModelMangerUI::showEvent(QShowEvent *event) +{ + if (m_pCtrl) { + if (m_pCtrl->IOnlineMode() == true) + { + ui.wf_chkbox_read_mode_from_plc->setChecked(true); + } + else { + ui.wf_chkbox_read_mode_from_plc->setChecked(false); + } + } +} + +void QModelMangerUI::closeEvent(QCloseEvent *event) +{ + +} + + +void QModelMangerUI::onInitModelList(IWfCtrl *pCtrl) +{ + m_pCtrl = pCtrl; + if (m_pCtrl) + { + ui.tabWidgetStation->clear(); + QStringList lst = m_pCtrl->IGetStationKeys(); + for (int i = 0; i < lst.size(); i++) { + QTableView *pW = new QTableView(ui.tabWidgetStation); + IStation *pStation = m_pCtrl->IGetStationByKey(lst.at(i)); + QString strName = "wf_cam_listwidget_" + QString::number(pStation->stationId()); + pW->setObjectName(strName); + int nIndex = pStation->stationId()-1; + ui.tabWidgetStation->insertTab(nIndex, (QWidget*)pW, pStation->stationShowName()); + pStation->setWidget(strName, pW); + connect(pW, SIGNAL(doubleClicked(const QModelIndex &)),this, SLOT(OnCellDoubleClicked(const QModelIndex &))); + } + } +} + +int QModelMangerUI::currentTab() +{ + QWidget *pWidget = ui.tabWidgetStation->currentWidget(); + if (pWidget) { + return lastNum(pWidget->objectName()); + } + return -1; +} \ No newline at end of file diff --git a/tpvs17/lpMain/QModelMangerUI.h b/tpvs17/lpMain/QModelMangerUI.h new file mode 100644 index 0000000..f6997e6 --- /dev/null +++ b/tpvs17/lpMain/QModelMangerUI.h @@ -0,0 +1,34 @@ +#ifndef _H_QMODELMANGERUI_H_ +#define _H_QMODELMANGERUI_H_ + +#include +#include "ui_QModelMangerUI.h" +#include "IWfCtrl.h" +#include "IStation.h" + +class QModelMangerUI : public QWidget +{ + Q_OBJECT + +public: + QModelMangerUI(QWidget *parent = Q_NULLPTR); + ~QModelMangerUI(); + + void onInitModelList(IWfCtrl *pCtrl); + int currentTab(); + Q_SLOT void onButtonClicked(); + int lastNum(QString str); + Q_SLOT bool OnCellDoubleClicked(const QModelIndex &index); + Q_SLOT void onCheckBoxChange(int state); +private: + IStation * getStation(int nIndex = -1); +protected: + virtual void showEvent(QShowEvent *event); + virtual void closeEvent(QCloseEvent *event); + +private: + Ui::QModelMangerUI ui; + IWfCtrl *m_pCtrl{ nullptr }; +}; + +#endif diff --git a/tpvs17/lpMain/QModelMangerUI.ui b/tpvs17/lpMain/QModelMangerUI.ui new file mode 100644 index 0000000..aa89ac8 --- /dev/null +++ b/tpvs17/lpMain/QModelMangerUI.ui @@ -0,0 +1,199 @@ + + + QModelMangerUI + + + + 0 + 0 + 671 + 450 + + + + QModelMangerUI + + + + + + + 0 + 0 + + + + + 0 + 23 + + + + + 16777215 + 44444 + + + + + 微软雅黑 + 10 + + + + QTabWidget::North + + + QTabWidget::Rounded + + + -1 + + + + + + + + 16777215 + 50 + + + + QFrame::Box + + + QFrame::Raised + + + 2 + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 微软雅黑 + 10 + + + + 模型号: + + + + + + + + 0 + 25 + + + + + + + + + 0 + 25 + + + + 添加 + + + + + + + + 0 + 25 + + + + 修改 + + + + + + + + 0 + 25 + + + + 删除 + + + + + + + + 0 + 25 + + + + 设置为当前运行模型 + + + + + + + + 0 + 25 + + + + + 微软雅黑 + 10 + + + + 从PLC获取当前模型 + + + + + + + + + + + 16777215 + 25 + + + + + + + + + + + + + diff --git a/tpvs17/lpMain/QPLCIndexUI.cpp b/tpvs17/lpMain/QPLCIndexUI.cpp new file mode 100644 index 0000000..973ef80 --- /dev/null +++ b/tpvs17/lpMain/QPLCIndexUI.cpp @@ -0,0 +1,40 @@ +#include "QPLCIndexUI.h" +#pragma execution_character_set("utf-8") +QPLCIndexUI::QPLCIndexUI(QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + connect(ui.BtnPCLIndexOK, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + connect(ui.BtnPCLIndexCancel, SIGNAL(clicked()), this, SLOT(onButtonClicked())); +} + +QPLCIndexUI::~QPLCIndexUI() +{ +} + +void QPLCIndexUI::setModelName(QString strName) +{ + +} + +void QPLCIndexUI::setModelIndex(int val) +{ + ui.spBxPLCEdit->setValue(val); +} + +int QPLCIndexUI::getModelIndex() +{ + int ret = ui.spBxPLCEdit->value(); + return ret; +} + +Q_SLOT void QPLCIndexUI::onButtonClicked() +{ + QString strObj = sender()->objectName(); + if ("BtnPCLIndexOK" == strObj) { + QDialog::accept(); + } + else if ("BtnPCLIndexCancel" == strObj) { + QDialog::reject(); + } +} diff --git a/tpvs17/lpMain/QPLCIndexUI.h b/tpvs17/lpMain/QPLCIndexUI.h new file mode 100644 index 0000000..31f14c6 --- /dev/null +++ b/tpvs17/lpMain/QPLCIndexUI.h @@ -0,0 +1,23 @@ +#ifndef _H_QPLCINDEXUI_H_ +#define _H_QPLCINDEXUI_H_ + +#include +#include "ui_QPLCIndexUI.h" + +class QPLCIndexUI : public QDialog +{ + Q_OBJECT + +public: + QPLCIndexUI(QWidget *parent = Q_NULLPTR); + ~QPLCIndexUI(); + + void setModelName(QString strName); + void setModelIndex(int val); + int getModelIndex(); + Q_SLOT void onButtonClicked(); +private: + Ui::QPLCIndexUI ui; +}; + +#endif diff --git a/tpvs17/lpMain/QPLCIndexUI.ui b/tpvs17/lpMain/QPLCIndexUI.ui new file mode 100644 index 0000000..ef562a5 --- /dev/null +++ b/tpvs17/lpMain/QPLCIndexUI.ui @@ -0,0 +1,109 @@ + + + QPLCIndexUI + + + + 0 + 0 + 230 + 91 + + + + + 11 + + + + QPLCIndexUI + + + + + + + 0 + 0 + + + + + 11 + + + + 确认 + + + + + + + + 0 + 0 + + + + + 11 + + + + 取消 + + + + + + + + 11 + + + + PLC索引号设置 + + + + + + + + + + 11 + + + + PLC索引值 + + + + + + + + 0 + 0 + + + + + 11 + + + + 100 + + + + + + + + + + + diff --git a/tpvs17/lpMain/QTestModeWid.cpp b/tpvs17/lpMain/QTestModeWid.cpp new file mode 100644 index 0000000..6a346b8 --- /dev/null +++ b/tpvs17/lpMain/QTestModeWid.cpp @@ -0,0 +1,33 @@ +#include "QTestModeWid.h" +#pragma execution_character_set("utf-8") +QTestModeWid::QTestModeWid(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); + + connect(ui.wf_test_btn_start, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + connect(ui.wf_test_btn_stop, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + connect(ui.wf_test_btn_snap_cam1, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + connect(ui.wf_test_btn_snap_cam2, SIGNAL(clicked()), this, SLOT(onButtonClicked())); +} + +QTestModeWid::~QTestModeWid() +{ +} + +Q_SLOT void QTestModeWid::onButtonClicked() +{ + QString strObj = sender()->objectName(); + if ("wf_test_btn_start" == strObj) { + emit sgTestMode(EM_START); + } + else if("wf_test_btn_stop" == strObj) { + emit sgTestMode(EM_STOP); + } + else if ("wf_test_btn_snap_cam1" == strObj) { + emit sgTestMode(EM_CAM1_Trig); + } + else if ("wf_test_btn_snap_cam2" == strObj) { + emit sgTestMode(EM_CAM2_Trig); + } +} diff --git a/tpvs17/lpMain/QTestModeWid.h b/tpvs17/lpMain/QTestModeWid.h new file mode 100644 index 0000000..089f4c5 --- /dev/null +++ b/tpvs17/lpMain/QTestModeWid.h @@ -0,0 +1,28 @@ +#ifndef _H_QTESTMODEWID_H_ +#define _H_QTESTMODEWID_H_ + +#include +#include "ui_QTestModeWid.h" +enum EM_TestMode { + EM_START = 0, + EM_STOP = 1, + EM_CAM1_Trig = 2, + EM_CAM2_Trig = 3 +}; +class QTestModeWid : public QWidget +{ + Q_OBJECT + +public: + QTestModeWid(QWidget *parent = Q_NULLPTR); + ~QTestModeWid(); + + Q_SLOT void onButtonClicked(); +signals: + void sgTestMode(int); +private: + Ui::QTestModeWid ui; + +}; + +#endif diff --git a/tpvs17/lpMain/QTestModeWid.ui b/tpvs17/lpMain/QTestModeWid.ui new file mode 100644 index 0000000..4e4cff0 --- /dev/null +++ b/tpvs17/lpMain/QTestModeWid.ui @@ -0,0 +1,110 @@ + + + QTestModeWid + + + + 0 + 0 + 250 + 103 + + + + QTestModeWid + + + + + + + 0 + 30 + + + + + 11 + + + + 模拟运行 + + + 1001 + + + + + + + + 0 + 30 + + + + + 11 + + + + 相机2捕获 + + + + + + + + 0 + 30 + + + + + 11 + + + + 相机1捕获 + + + + + + + + 0 + 30 + + + + + 11 + + + + 停止 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + diff --git a/tpvs17/lpMain/lpMain.cpp b/tpvs17/lpMain/lpMain.cpp new file mode 100644 index 0000000..63a9a6e --- /dev/null +++ b/tpvs17/lpMain/lpMain.cpp @@ -0,0 +1,29 @@ +#include "lpMain.h" +#pragma execution_character_set("utf-8") + +IMAINWIDGET_API IMainWidget* MainWidget_Create() +{ + return new lpMain(); +} +IMAINWIDGET_API void MainWidget_Delete(IMainWidget* pwd) +{ + if (pwd) { + delete pwd; + pwd = nullptr; + } +} + +lpMain::lpMain(QObject *parent /*= nullptr*/) +{ + +} + +lpMain::~lpMain() +{ + +} + +QWidget * lpMain::getMainWidget() +{ + return &m_wid; +} diff --git a/tpvs17/lpMain/lpMain.h b/tpvs17/lpMain/lpMain.h new file mode 100644 index 0000000..0e2a140 --- /dev/null +++ b/tpvs17/lpMain/lpMain.h @@ -0,0 +1,18 @@ +#ifndef _H_LPMAIN_H_ +#define _H_LPMAIN_H_ + +#include "IMainWidget.h" +#include "CMainWin.h" +class lpMain :public IMainWidget +{ + Q_OBJECT +public: + lpMain(QObject *parent = nullptr); + ~lpMain(); + virtual QWidget *getMainWidget(); + +public: + CMainWin m_wid; +}; + +#endif diff --git a/tpvs17/lpMain/lpMain.vcxproj b/tpvs17/lpMain/lpMain.vcxproj new file mode 100644 index 0000000..c836e7e --- /dev/null +++ b/tpvs17/lpMain/lpMain.vcxproj @@ -0,0 +1,326 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {783A56EA-8A72-4BC9-B8DF-23B76D4FE0E0} + Qt4VSv1.0 + 10.0.17763.0 + + + + DynamicLibrary + v141 + + + DynamicLibrary + v141 + + + + $(MSBuildProjectDirectory)\QtMsBuild + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(ProjectName)d + + + $(SolutionDir)$(Platform)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + true + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;LPMAIN_LIB;QT_WIDGETS_LIB;QT_SERIALBUS_LIB;QT_SERIALPORT_LIB;QT_SQL_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;..\..\3part\SerialPortTool\include;..\..\src\lpMain;..\..\src\lpMain\algela;..\..\src\lpMain\QDiskCleanThread;..\..\src\lpMain\sqliteDB;..\..\src\lpMain\UI;..\..\3part\opencv3.4.1\include;..\..\3part\opencv3.4.1\include\opencv;..\..\3part\opencv3.4.1\include\opencv2;..\..\3part\libzkq\include;..\..\3part\lpSyslog\inc;..\..\3part\customgui\include;..\..\src\userCtrl;..\..\src\lpMain\CoreCtrl;..\..\src\lpBase;%(AdditionalIncludeDirectories) + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + + + Windows + $(SolutionDir)..\runner17\$(TargetName)$(TargetExt) + $(QTDIR)\lib;..\..\3part\opencv3.4.1\x64\vc15\lib;..\..\3part\lpSyslog\lib;..\..\3part\customgui\lib_x64;..\..\3part\libzkq\lib;%(AdditionalLibraryDirectories) + true + qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5SerialBusd.lib;Qt5SerialPortd.lib;Qt5Sqld.lib;customguid.lib;opencv_world341d.lib;lpSyslogd.lib;libzkqd.lib;%(AdditionalDependencies) + + + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;LPMAIN_LIB;QT_WIDGETS_LIB;QT_SERIALBUS_LIB;QT_SERIALPORT_LIB;QT_SQL_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;..\..\3part\SerialPortTool\include;..\..\src\lpMain;..\..\src\lpMain\algela;..\..\src\lpMain\QDiskCleanThread;..\..\src\lpMain\sqliteDB;..\..\src\lpMain\UI;..\..\3part\opencv3.4.1\include;..\..\3part\opencv3.4.1\include\opencv;..\..\3part\opencv3.4.1\include\opencv2;..\..\3part\libzkq\include;..\..\3part\lpSyslog\inc;..\..\3part\customgui\include;..\..\src\userCtrl;..\..\src\lpMain\CoreCtrl;..\..\src\lpBase;%(AdditionalIncludeDirectories) + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + Moc'ing %(Identity)... + + + Uic'ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h + + + Rcc'ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp + + + + + true + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;LPMAIN_LIB;QT_WIDGETS_LIB;QT_SERIALBUS_LIB;QT_SERIALPORT_LIB;QT_SQL_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;%(AdditionalIncludeDirectories) + + MultiThreadedDLL + true + + + Windows + $(OutDir)\$(ProjectName).dll + $(QTDIR)\lib;%(AdditionalLibraryDirectories) + false + qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5SerialBus.lib;Qt5SerialPort.lib;Qt5Sql.lib;%(AdditionalDependencies) + + + UNICODE;_UNICODE;WIN32;WIN64;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;LPMAIN_LIB;QT_WIDGETS_LIB;QT_SERIALBUS_LIB;QT_SERIALPORT_LIB;QT_SQL_LIB;%(PreprocessorDefinitions) + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;%(AdditionalIncludeDirectories) + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + Moc'ing %(Identity)... + + + Uic'ing %(Identity)... + .\GeneratedFiles\ui_%(Filename).h + + + Rcc'ing %(Identity)... + .\GeneratedFiles\qrc_%(Filename).cpp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets + + + + + + + + + + + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl;.\..\..\src\lpBase + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + + + + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql;.\..\..\3part\SerialPortTool\include;.\..\..\src\lpMain;.\..\..\src\lpMain\algela;.\..\..\src\lpMain\QDiskCleanThread;.\..\..\src\lpMain\sqliteDB;.\..\..\src\lpMain\UI;.\..\..\3part\opencv3.4.1\include;.\..\..\3part\opencv3.4.1\include\opencv;.\..\..\3part\opencv3.4.1\include\opencv2;.\..\..\3part\libzkq\include;.\..\..\3part\lpSyslog\inc;.\..\..\3part\customgui\include;.\..\..\src\userCtrl;.\..\..\src\lpMain\CoreCtrl + .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSerialBus;$(QTDIR)\include\QtSerialPort;$(QTDIR)\include\QtSql + + + + + + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tpvs17/lpMain/lpMain.vcxproj.filters b/tpvs17/lpMain/lpMain.vcxproj.filters new file mode 100644 index 0000000..227fd3c --- /dev/null +++ b/tpvs17/lpMain/lpMain.vcxproj.filters @@ -0,0 +1,300 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + moc;h;cpp + False + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + true + + + {b8a7fc0f-77f9-4dfc-ab46-43a76501dc86} + + + {dc879e95-c05e-4810-b5cc-f05b2d5ae753} + + + {788788e3-dd4d-4754-9097-7ab652d7049b} + + + {d4ee84cc-3ae8-4d69-b15a-24ed868a2f7a} + + + {aeffe6ee-5f98-4bbf-b0fc-bada2f76bf5d} + + + {460a87f9-e992-4ca0-8545-eea6c2194b2b} + + + + + Source Files + + + CoreCtrl + + + CoreCtrl + + + Source Files + + + CoreCtrl + + + lpMain\algela + + + lpMain\algela + + + lpMain\diskclean + + + lpMain\diskclean + + + lpMain\db + + + lpMain\db + + + lpMain\db + + + lpMain\db + + + lpMain\db + + + lpMain\db + + + lpMain\algela + + + lpMain + + + lpMain + + + lpMain + + + lpMain + + + lpMain\db + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + CoreCtrl + + + lpMain\algela + + + lpMain\algela + + + lpMain\diskclean + + + lpMain\diskclean + + + lpMain\db + + + lpMain\db + + + lpMain\db + + + lpMain\algela + + + lpMain + + + lpMain + + + lpMain + + + lpMain + + + lpMain + + + lpMain\db + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Form Files + + + lpMain\db + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + + + CoreCtrl + + + CoreCtrl + + + lpMain\diskclean + + + lpMain\db + + + lpMain\db + + + lpMain\db + + + lpMain\db + + + lpMain + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/tpvs17/lpMain/lpMain.vcxproj.user b/tpvs17/lpMain/lpMain.vcxproj.user new file mode 100644 index 0000000..60c58f5 --- /dev/null +++ b/tpvs17/lpMain/lpMain.vcxproj.user @@ -0,0 +1,15 @@ + + + + + D:\Qt\Qt5.9.4\5.9.4\msvc2017_64 + $(SolutionDir)..\runner17\ + WindowsLocalDebugger + $(SolutionDir)..\runner17\Enchanterd.exe + PATH=$(QTDIR)\bin%3b$(PATH) + + + D:\Qt\Qt5.9.4\5.9.4\msvc2017_64 + PATH=$(QTDIR)\bin%3b$(PATH) + + \ No newline at end of file diff --git a/tpvs17/tpMain/toolBar/messenger.png b/tpvs17/tpMain/toolBar/messenger.png new file mode 100644 index 0000000000000000000000000000000000000000..bd0dffb717ad24f40ace9d0c2969f35f4706f47e GIT binary patch literal 2987 zcmbVOdpJ~iA3rm0m1-VXh1_jLVpDzl9=E#>}`iH#38w)VMBDhH9n4?5=py zE}K}aM50n#Tak!DYNwP$uAA7=X5aUZefN*|JkL45%lUpjpYQGWoRb&e=dP}@Sp@(9 zbx#j+pllSa-4I3D^SUGTj%?T@po9v7IMIS+DvtrU&^b{Im?w)G!w6(h=_v_43wAlg%dxiSW<71lfFT8wrPfHW3^k z!vAtA)HeX;%Hc6!HVBjz4TVF&aA*V?Yi(<5YYD@k&?qDdgG6Jj(6$6DhJZrBzFcsb z8jl`L2qe3G(UPr*@K}L>OF$x%lamq2SOkX`gGA%;_%#j;#!6;k#ZO@is6s0?-*}yY z%;3{_aa=(hhYed}q(*TP1w^>a)4xJsaeaONCT8=$BubVtq>#!*q7f)0i?tTlXKTJ7 zkntZk{?VEroWf-w0~vfyB9A7kN3`)eSeCng7g{rvc|-8$#mS07Ws*6xL>7ZB@FWxA zvNs5N9G!r5L)+oVBsU7lmPA3J(RL^@h2)AQqup>gES7{`=lCZq22Hl1;N4u&cv}n_ zO~K%CZZ0llvMm@Fqmco1h9c7Y6k;FY+!)UReb$r0~g(ZnAiEq-W(E@5q1{=B`%6Q;Hd=d?|`9hVQVCLbQeZM#PMkqUu) zywkzgrsbk9fnQ;L4i8r8l%Fpds>+UOL2PmiG#4p8uxCL=OPedyMh&8^i0*^)E$WpK z08MxQVr&Q2*|{mQblGc*_hsPS-qfD_C}v4f`z!Hs{jutufI>d9xp(LL3-NW$W!`52 zX3Oe`+&ia?tmWIMVHKvJ@XNDNA59k6+AdV((zFs&&_M1=liWLEzIb^i;C_)Jh~8aj z^NAEA7kCdTR*`IT_#Qj(L_@5RdpmL5y{B;uvd85sJwfTVdJ|xa6Lem|(zEtRfy0)H z#8TMWma4JyO2VVY=-HM#(aB zmtE^wn|N?`*xdAyjLl?=71ngU)%RE17j_0=b)@2q@bh*3HSgewDH-woHK zf=(zFy9^co0Ap?#bT<=il8#kVWnH3|hU_}^W?-Xw+p68y1Eq` zG7}Ng7C8ti;ut6%CaP&oz^+mXdQJS_#yi)Jrf0^KlA2UBi&PqZwa!eYK~d-a?^j;RBlT`*qMR3dk~*^oIqy`#}CB z{WO!`vCd+Ljzj6-C004qsk*uOp;^B{d{H0bN&ZF{NwQ?Fia4r_6~zgj2S7XIc{6q9 z^NUwPoy-d7oY|oUW2WfkYsB3(&n4>U8^7f$TwvzLwCtNcVwkHIpQ!;}++;reK72>F zj%rjW@*rF%wAMSN2t2)FOVc2e?9o^(0(bQr^q9sDN+;2nO^mf_ooKcfT}njq*j`S_O0bTbT+EVXn1D}(JbNey?u}I@wy0m zWX=|?aXWp%QhAwvRobggIi~%@9EzQjM&*kKKSIw>EggFWhJIjt<&^Yog0|3bNfyouK2{O0iKhnPpkA zqGzkPAhX{J@5-IqKGO)h+378nXm>yps?{^Es)NLdU5fU(+m9Cub;AlW>wa|l^!OToWf+yTXO&V>nUCI7lD9@F