commit fb543a3b0de203136686424227661bb4a2e20c25 Author: 熊玮 Date: Thu Nov 21 11:39:52 2024 +0800 1、update diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..803bd27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,403 @@ +### PyCharm+iml template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +#*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### PyCharm+all template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# db +*.db + +vendors/image-framework/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..82960fe --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,20 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:D:\xwd\cnnc\detect-gui\detect.db + $ProjectFileDir$ + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar + + + + + \ No newline at end of file diff --git a/.idea/detect-gui.iml b/.idea/detect-gui.iml new file mode 100644 index 0000000..80f67ae --- /dev/null +++ b/.idea/detect-gui.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..f083c02 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..2af8ee4 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,58 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..66b3d02 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..43473f2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bd7e59e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "type_traits": "cpp" + }, + "cmake.ignoreCMakeListsMissing": true +} \ No newline at end of file diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..726a14b --- /dev/null +++ b/README.MD @@ -0,0 +1,15 @@ +## 相机SDK +```bash +/opt/HuarayTech/MVviewer/lib/libMVSDK.so +``` + +## GPIO +### 设置gpio功能 +pin11控制雷达 +```bash +bspmm 0x0102F00FC 0x0201 +``` +pin12控制相机 +```bash +bspmm 0x0102F010C 0x0201 +``` \ No newline at end of file diff --git a/assets/bg.png b/assets/bg.png new file mode 100644 index 0000000..089d636 Binary files /dev/null and b/assets/bg.png differ diff --git a/assets/fonts/NotoSansSC-Regular.otf b/assets/fonts/NotoSansSC-Regular.otf new file mode 100644 index 0000000..fc0fda9 Binary files /dev/null and b/assets/fonts/NotoSansSC-Regular.otf differ diff --git a/assets/icon_analyse.png b/assets/icon_analyse.png new file mode 100644 index 0000000..b181d4d Binary files /dev/null and b/assets/icon_analyse.png differ diff --git a/assets/icon_bim.png b/assets/icon_bim.png new file mode 100644 index 0000000..bcc759d Binary files /dev/null and b/assets/icon_bim.png differ diff --git a/assets/icon_cali.png b/assets/icon_cali.png new file mode 100644 index 0000000..66b407f Binary files /dev/null and b/assets/icon_cali.png differ diff --git a/assets/icon_charge_finish.png b/assets/icon_charge_finish.png new file mode 100644 index 0000000..42b6914 Binary files /dev/null and b/assets/icon_charge_finish.png differ diff --git a/assets/icon_charging.png b/assets/icon_charging.png new file mode 100644 index 0000000..8055e82 Binary files /dev/null and b/assets/icon_charging.png differ diff --git a/assets/icon_com.png b/assets/icon_com.png new file mode 100644 index 0000000..a031d8c Binary files /dev/null and b/assets/icon_com.png differ diff --git a/assets/icon_device.png b/assets/icon_device.png new file mode 100644 index 0000000..477c25e Binary files /dev/null and b/assets/icon_device.png differ diff --git a/assets/icon_setting.png b/assets/icon_setting.png new file mode 100644 index 0000000..e000c66 Binary files /dev/null and b/assets/icon_setting.png differ diff --git a/assets/icon_task.png b/assets/icon_task.png new file mode 100644 index 0000000..0c975dc Binary files /dev/null and b/assets/icon_task.png differ diff --git a/assets/img1.jpg b/assets/img1.jpg new file mode 100644 index 0000000..bb52fd3 Binary files /dev/null and b/assets/img1.jpg differ diff --git a/assets/img2.png b/assets/img2.png new file mode 100644 index 0000000..a37b59c Binary files /dev/null and b/assets/img2.png differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..3146a8e Binary files /dev/null and b/assets/logo.png differ diff --git a/components/__init__.py b/components/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/camera.py b/components/camera.py new file mode 100644 index 0000000..343503b --- /dev/null +++ b/components/camera.py @@ -0,0 +1,864 @@ +from dynaconf.base import Settings + +from core.edge_component import EdgeComponent, device, service +from device.mv.ImageConvert import * +from device.mv.MVSDK import * +import struct +import time +import datetime +import logging +from typing import List +from core.logging import logger + +from PyQt5.QtCore import QObject, pyqtSignal + + +g_cameraStatusUserInfo = b"statusInfo" + +class BITMAPFILEHEADER(Structure): + _fields_ = [ + ('bfType', c_ushort), + ('bfSize', c_uint), + ('bfReserved1', c_ushort), + ('bfReserved2', c_ushort), + ('bfOffBits', c_uint), + ] + +class BITMAPINFOHEADER(Structure): + _fields_ = [ + ('biSize', c_uint), + ('biWidth', c_int), + ('biHeight', c_int), + ('biPlanes', c_ushort), + ('biBitCount', c_ushort), + ('biCompression', c_uint), + ('biSizeImage', c_uint), + ('biXPelsPerMeter', c_int), + ('biYPelsPerMeter', c_int), + ('biClrUsed', c_uint), + ('biClrImportant', c_uint), + ] + +# 调色板,只有8bit及以下才需要 +class RGBQUAD(Structure): + _fields_ = [ + ('rgbBlue', c_ubyte), + ('rgbGreen', c_ubyte), + ('rgbRed', c_ubyte), + ('rgbReserved', c_ubyte), + ] + + +# 相机设备类 +class CameraDevice: + def __init__(self, camera_id, key, vendor_name, model_name, serial_number): + self.camera_id = camera_id + self.key = key + self.vendor_name = vendor_name + self.model_name = model_name + self.serial_number = serial_number + + def __repr__(self): + return (f"CameraDevice(camera_id={self.camera_id}, key={self.key}, " + f"vendor_name={self.vendor_name}, model_name={self.model_name}, " + f"serial_number={self.serial_number})") + + +class CameraSignals(QObject): + # QT 信号 + on_device_list = pyqtSignal(list) + on_device_link = pyqtSignal(int, str) + on_device_change = pyqtSignal() + on_device_error = pyqtSignal() + on_device_data = pyqtSignal(object) + + +# pyqt的相机管理类 +@device("camera", auto_start=True) +class Camera(EdgeComponent): + signals = CameraSignals() + + def __init__(self, context): + super().__init__(context) + global mv_camera + mv_camera = self + self.devices = None + self.current_index = 0 + self.streamSource = None + + def configure(self, setting: Settings) -> None: + self.logger.info(f"Camera configure done.") + + @service() + def start(self): + if MVSDKdll is None: + raise RuntimeError("相机设备SDK未正确加载") + + camera_count = self.start_discovery() + if camera_count <= 0: + self.logger.info('no cameras found!') + else: + self.open_camera() + super().start() + self.logger.info("Camera start!") + + @service() + def stop(self): + self.stop_camera() + super().stop() + self.logger.info("Camera stop!") + + def start_discovery(self): + cameraCnt, cameraList = enumCameras() + if cameraCnt is None: + return -1 + + self.devices = cameraList + camera_devices = [] + # 显示相机信息 + for index in range(0, cameraCnt): + camera = cameraList[index] + self.logger.info("Camera Id = " + str(index)) + self.logger.info("Key = " + str(camera.getKey(camera))) + self.logger.info("vendor name = " + str(camera.getVendorName(camera))) + self.logger.info("Model name = " + str(camera.getModelName(camera))) + self.logger.info("Serial number = " + str(camera.getSerialNumber(camera))) + camera_devices.append(CameraDevice(index, str(camera.getKey(camera)), str(camera.getVendorName(camera)), + str(camera.getModelName(camera)), str(camera.getSerialNumber(camera)))) + self.signals.on_device_list.emit(camera_devices) + + return cameraCnt + + def open_camera(self, index = 0): + # 打开相机 + camera = self.devices[index] + nRet = openCamera(camera) + if nRet != 0: + self.logger.error("openCamera fail.") + return -1 + + # 创建流对象 + streamSourceInfo = GENICAM_StreamSourceInfo() + streamSourceInfo.channelId = 0 + streamSourceInfo.pCamera = pointer(camera) + + self.streamSource = pointer(GENICAM_StreamSource()) + nRet = GENICAM_createStreamSource(pointer(streamSourceInfo), byref(self.streamSource)) + if nRet != 0: + self.logger.error("create StreamSource fail!") + return -1 + + # 注册相机连接状态回调 + nRet = subscribeCameraStatus(camera) + if nRet != 0: + self.logger.error("subscribeCameraStatus fail!") + return -1 + + self.current_index = index + return 0 + + def stop_camera(self): + camera = self.devices[self.current_index] + # 关闭相机 + nRet = closeCamera(camera) + if nRet != 0: + self.logger.error("closeCamera fail") + # 释放相关资源 + if self.streamSource is not None: + self.streamSource.contents.release(self.streamSource) + return -1 + + # 反向注册相机连接状态回调 + nRet = unsubscribeCameraStatus(camera) + if nRet != 0: + self.logger.error("unsubscribeCameraStatus fail!") + return -1 + + # 释放相关资源 + if self.streamSource is not None: + self.streamSource.contents.release(self.streamSource) + + def set_exposure_time(self, exposure_time: int): + # 设置曝光时间 + camera = self.devices[self.current_index] + setExposureTime(camera, exposure_time) + + def set_soft_trigger(self): + # 设置软触发 + camera = self.devices[self.current_index] + setSoftTriggerConf(camera) + + def set_line_trigger(self): + # 设置外触发 + camera = self.devices[self.current_index] + setLineTriggerConf(camera) + + def set_roi(self, offset_x, offset_y, width, height): + # 设置相机ROI + camera = self.devices[self.current_index] + setROI(camera, offset_x, offset_y, width, height) + + def grab_one(self): + # 设置软触发 + camera = self.devices[self.current_index] + nRet = setSoftTriggerConf(camera) + if nRet != 0: + self.logger.error("set SoftTriggerConf fail!") + # 释放相关资源 + self.streamSource.contents.release(self.streamSource) + return -1 + else: + self.logger.info("set SoftTriggerConf success!") + + # 开始拉流 + nRet = self.streamSource.contents.startGrabbing(self.streamSource, c_ulonglong(0), + c_int(GENICAM_EGrabStrategy.grabStrartegySequential)) + if nRet != 0: + self.logger.error("startGrabbing fail!") + # 释放相关资源 + self.streamSource.contents.release(self.streamSource) + return -1 + time.sleep(1) + # 软触发取一张图 + nRet = grabOne(camera) + if nRet != 0: + self.logger.error("grabOne fail!") + # 释放相关资源 + self.streamSource.contents.release(self.streamSource) + return -1 + else: + self.logger.info("trigger time: " + str(datetime.datetime.now())) + + # 主动取图 + frame = pointer(GENICAM_Frame()) + nRet = self.streamSource.contents.getFrame(self.streamSource, byref(frame), c_uint(100000)) + if nRet != 0: + self.logger.error("SoftTrigger getFrame fail! timeOut [100000]ms") + # 释放相关资源 + self.streamSource.contents.release(self.streamSource) + return -1 + else: + self.logger.info("SoftTrigger getFrame success BlockId = " + str(frame.contents.getBlockId(frame))) + self.logger.info("get frame time: " + str(datetime.datetime.now())) + + nRet = frame.contents.valid(frame) + if nRet != 0: + self.logger.error("frame is invalid!") + # 释放驱动图像缓存资源 + frame.contents.release(frame) + # 释放相关资源 + self.streamSource.contents.release(self.streamSource) + return -1 + + # 将裸数据图像拷出 + imageSize = frame.contents.getImageSize(frame) + self.logger.info(f"imageSize is {imageSize}!") + buffAddr = frame.contents.getImage(frame) + frameBuff = c_buffer(b'\0', imageSize) + memmove(frameBuff, c_char_p(buffAddr), imageSize) + + # 给转码所需的参数赋值 + convertParams = IMGCNV_SOpenParam() + convertParams.dataSize = imageSize + convertParams.height = frame.contents.getImageHeight(frame) + convertParams.width = frame.contents.getImageWidth(frame) + convertParams.paddingX = frame.contents.getImagePaddingX(frame) + convertParams.paddingY = frame.contents.getImagePaddingY(frame) + convertParams.pixelForamt = frame.contents.getImagePixelFormat(frame) + + # 释放驱动图像缓存 + frame.contents.release(frame) + + # 保存bmp图片 + bmpInfoHeader = BITMAPINFOHEADER() + bmpFileHeader = BITMAPFILEHEADER() + + uRgbQuadLen = 0 + rgbQuad = (RGBQUAD * 256)() # 调色板信息 + rgbBuff = c_buffer(b'\0', convertParams.height * convertParams.width * 3) + + # 如果图像格式是 Mono8 不需要转码 + if convertParams.pixelForamt == EPixelType.gvspPixelMono8: + # 初始化调色板rgbQuad 实际应用中 rgbQuad 只需初始化一次 + for i in range(0, 256): + rgbQuad[i].rgbBlue = rgbQuad[i].rgbGreen = rgbQuad[i].rgbRed = i + + uRgbQuadLen = sizeof(RGBQUAD) * 256 + bmpFileHeader.bfSize = sizeof(bmpFileHeader) + sizeof(bmpInfoHeader) + uRgbQuadLen + convertParams.dataSize + bmpInfoHeader.biBitCount = 8 + else: + # 转码 => BGR24 + rgbSize = c_int() + nRet = IMGCNV_ConvertToBGR24(cast(frameBuff, c_void_p), byref(convertParams), + cast(rgbBuff, c_void_p), byref(rgbSize)) + + if nRet != 0: + mv_camera.logger.error("image convert fail! errorCode = " + str(nRet)) + # 释放相关资源 + self.streamSource.contents.release(self.streamSource) + return -1 + + bmpFileHeader.bfSize = sizeof(bmpFileHeader) + sizeof(bmpInfoHeader) + rgbSize.value + bmpInfoHeader.biBitCount = 24 + + bmpFileHeader.bfType = 0x4D42 # 文件头类型 'BM'(42 4D) + bmpFileHeader.bfReserved1 = 0 # 保留字 + bmpFileHeader.bfReserved2 = 0 # 保留字 + bmpFileHeader.bfOffBits = 54 + uRgbQuadLen # 位图像素数据的起始位置 + + bmpInfoHeader.biSize = 40 # 信息头所占字节数 + bmpInfoHeader.biWidth = convertParams.width + bmpInfoHeader.biHeight = -convertParams.height + bmpInfoHeader.biPlanes = 1 # 位图平面数 + + bmpInfoHeader.biCompression = 0 # 压缩类型,0 即不压缩 + bmpInfoHeader.biSizeImage = 0 + bmpInfoHeader.biXPelsPerMeter = 0 + bmpInfoHeader.biYPelsPerMeter = 0 + bmpInfoHeader.biClrUsed = 0 + bmpInfoHeader.biClrImportant = 0 + + fileName = './image.bmp' + imageFile = open(fileName, 'wb+') + + imageFile.write(struct.pack('H', bmpFileHeader.bfType)) + imageFile.write(struct.pack('I', bmpFileHeader.bfSize)) + imageFile.write(struct.pack('H', bmpFileHeader.bfReserved1)) + imageFile.write(struct.pack('H', bmpFileHeader.bfReserved2)) + imageFile.write(struct.pack('I', bmpFileHeader.bfOffBits)) + + imageFile.write(struct.pack('I', bmpInfoHeader.biSize)) + imageFile.write(struct.pack('i', bmpInfoHeader.biWidth)) + imageFile.write(struct.pack('i', bmpInfoHeader.biHeight)) + imageFile.write(struct.pack('H', bmpInfoHeader.biPlanes)) + imageFile.write(struct.pack('H', bmpInfoHeader.biBitCount)) + imageFile.write(struct.pack('I', bmpInfoHeader.biCompression)) + imageFile.write(struct.pack('I', bmpInfoHeader.biSizeImage)) + imageFile.write(struct.pack('i', bmpInfoHeader.biXPelsPerMeter)) + imageFile.write(struct.pack('i', bmpInfoHeader.biYPelsPerMeter)) + imageFile.write(struct.pack('I', bmpInfoHeader.biClrUsed)) + imageFile.write(struct.pack('I', bmpInfoHeader.biClrImportant)) + + if convertParams.pixelForamt == EPixelType.gvspPixelMono8: + # 写入调色板信息 + for i in range(0, 256): + imageFile.write(struct.pack('B', rgbQuad[i].rgbBlue)) + imageFile.write(struct.pack('B', rgbQuad[i].rgbGreen)) + imageFile.write(struct.pack('B', rgbQuad[i].rgbRed)) + imageFile.write(struct.pack('B', rgbQuad[i].rgbReserved)) + + imageFile.writelines(frameBuff) + else: + imageFile.writelines(rgbBuff) + + imageFile.close() + self.logger.info("save " + fileName + " success.") + self.logger.info("save bmp time: " + str(datetime.datetime.now())) + + # 停止拉流 + nRet = self.streamSource.contents.stopGrabbing(self.streamSource) + if nRet != 0: + self.logger.error("stopGrabbing fail!") + # 释放相关资源 + self.streamSource.contents.release(self.streamSource) + return -1 + +# 相机管理类实例 +mv_camera = None + +# 取流回调函数 +def onGetFrame(frame): + nRet = frame.contents.valid(frame) + if nRet != 0: + mv_camera.logger.error("frame is invalid!") + # 释放驱动图像缓存资源 + frame.contents.release(frame) + return + + mv_camera.logger.info("BlockId = %d" %(frame.contents.getBlockId(frame))) + #此处客户应用程序应将图像拷贝出使用 + ''' + ''' + # 释放驱动图像缓存资源 + frame.contents.release(frame) + +# 取流回调函数Ex +def onGetFrameEx(frame, userInfo): + nRet = frame.contents.valid(frame) + if ( nRet != 0): + mv_camera.logger.error("frame is invalid!") + # 释放驱动图像缓存资源 + frame.contents.release(frame) + return + + mv_camera.logger.info("BlockId = %d userInfo = %s" %(frame.contents.getBlockId(frame), c_char_p(userInfo).value)) + #此处客户应用程序应将图像拷贝出使用 + ''' + ''' + # 释放驱动图像缓存资源 + frame.contents.release(frame) + +# 相机连接状态回调函数 +def deviceLinkNotify(connectArg, linkInfo): + mv_camera.on_device_link.emit(connectArg.contents.m_event, c_char_p(linkInfo).value.decode('utf-8')) + if EVType.offLine == connectArg.contents.m_event: + mv_camera.logger.info("camera has off line, userInfo [%s]" %(c_char_p(linkInfo).value)) + elif EVType.onLine == connectArg.contents.m_event: + mv_camera.logger.info("camera has on line, userInfo [%s]" %(c_char_p(linkInfo).value)) + + +connectCallBackFuncEx = connectCallBackEx(deviceLinkNotify) +frameCallbackFunc = callbackFunc(onGetFrame) +frameCallbackFuncEx = callbackFuncEx(onGetFrameEx) + +# 注册相机连接状态回调 +def subscribeCameraStatus(camera): + # 注册上下线通知 + eventSubscribe = pointer(GENICAM_EventSubscribe()) + eventSubscribeInfo = GENICAM_EventSubscribeInfo() + eventSubscribeInfo.pCamera = pointer(camera) + nRet = GENICAM_createEventSubscribe(byref(eventSubscribeInfo), byref(eventSubscribe)) + if nRet != 0: + mv_camera.logger.error("create eventSubscribe fail!") + return -1 + + nRet = eventSubscribe.contents.subscribeConnectArgsEx(eventSubscribe, connectCallBackFuncEx, g_cameraStatusUserInfo) + if nRet != 0: + mv_camera.logger.error("subscribeConnectArgsEx fail!") + # 释放相关资源 + eventSubscribe.contents.release(eventSubscribe) + return -1 + + # 不再使用时,需释放相关资源 + eventSubscribe.contents.release(eventSubscribe) + return 0 + +# 反注册相机连接状态回调 +def unsubscribeCameraStatus(camera): + # 反注册上下线通知 + eventSubscribe = pointer(GENICAM_EventSubscribe()) + eventSubscribeInfo = GENICAM_EventSubscribeInfo() + eventSubscribeInfo.pCamera = pointer(camera) + nRet = GENICAM_createEventSubscribe(byref(eventSubscribeInfo), byref(eventSubscribe)) + if nRet != 0: + mv_camera.logger.error("create eventSubscribe fail!") + return -1 + + nRet = eventSubscribe.contents.unsubscribeConnectArgsEx(eventSubscribe, connectCallBackFuncEx, g_cameraStatusUserInfo) + if nRet != 0: + mv_camera.logger.error("unsubscribeConnectArgsEx fail!") + # 释放相关资源 + eventSubscribe.contents.release(eventSubscribe) + return -1 + + # 不再使用时,需释放相关资源 + eventSubscribe.contents.release(eventSubscribe) + return 0 + +# 设置软触发 +def setSoftTriggerConf(camera): + # 创建control节点 + acqCtrlInfo = GENICAM_AcquisitionControlInfo() + acqCtrlInfo.pCamera = pointer(camera) + acqCtrl = pointer(GENICAM_AcquisitionControl()) + nRet = GENICAM_createAcquisitionControl(pointer(acqCtrlInfo), byref(acqCtrl)) + if nRet != 0: + mv_camera.logger.error("create AcquisitionControl fail!") + return -1 + + # 设置触发源为软触发 + trigSourceEnumNode = acqCtrl.contents.triggerSource(acqCtrl) + nRet = trigSourceEnumNode.setValueBySymbol(byref(trigSourceEnumNode), b"Software") + if nRet != 0: + mv_camera.logger.error("set TriggerSource value [Software] fail!") + # 释放相关资源 + trigSourceEnumNode.release(byref(trigSourceEnumNode)) + acqCtrl.contents.release(acqCtrl) + return -1 + + # 需要释放Node资源 + trigSourceEnumNode.release(byref(trigSourceEnumNode)) + + # 设置触发方式 + trigSelectorEnumNode = acqCtrl.contents.triggerSelector(acqCtrl) + nRet = trigSelectorEnumNode.setValueBySymbol(byref(trigSelectorEnumNode), b"FrameStart") + if nRet != 0: + mv_camera.logger.error("set TriggerSelector value [FrameStart] fail!") + # 释放相关资源 + trigSelectorEnumNode.release(byref(trigSelectorEnumNode)) + acqCtrl.contents.release(acqCtrl) + return -1 + + # 需要释放Node资源 + trigSelectorEnumNode.release(byref(trigSelectorEnumNode)) + + # 打开触发模式 + trigModeEnumNode = acqCtrl.contents.triggerMode(acqCtrl) + nRet = trigModeEnumNode.setValueBySymbol(byref(trigModeEnumNode), b"On") + if nRet != 0: + mv_camera.logger.error("set TriggerMode value [On] fail!") + # 释放相关资源 + trigModeEnumNode.release(byref(trigModeEnumNode)) + acqCtrl.contents.release(acqCtrl) + return -1 + + # 需要释放相关资源 + trigModeEnumNode.release(byref(trigModeEnumNode)) + acqCtrl.contents.release(acqCtrl) + + return 0 + +# 设置外触发 +def setLineTriggerConf(camera): + # 创建control节点 + acqCtrlInfo = GENICAM_AcquisitionControlInfo() + acqCtrlInfo.pCamera = pointer(camera) + acqCtrl = pointer(GENICAM_AcquisitionControl()) + nRet = GENICAM_createAcquisitionControl(pointer(acqCtrlInfo), byref(acqCtrl)) + if nRet != 0: + mv_camera.logger.error("create AcquisitionControl fail!") + return -1 + + # 设置触发源为软触发 + trigSourceEnumNode = acqCtrl.contents.triggerSource(acqCtrl) + nRet = trigSourceEnumNode.setValueBySymbol(byref(trigSourceEnumNode), b"Line1") + if nRet != 0: + mv_camera.logger.error("set TriggerSource value [Line1] fail!") + # 释放相关资源 + trigSourceEnumNode.release(byref(trigSourceEnumNode)) + acqCtrl.contents.release(acqCtrl) + return -1 + + # 需要释放Node资源 + trigSourceEnumNode.release(byref(trigSourceEnumNode)) + + # 设置触发方式 + trigSelectorEnumNode = acqCtrl.contents.triggerSelector(acqCtrl) + nRet = trigSelectorEnumNode.setValueBySymbol(byref(trigSelectorEnumNode), b"FrameStart") + if nRet != 0: + mv_camera.logger.error("set TriggerSelector value [FrameStart] fail!") + # 释放相关资源 + trigSelectorEnumNode.release(byref(trigSelectorEnumNode)) + acqCtrl.contents.release(acqCtrl) + return -1 + + # 需要释放Node资源 + trigSelectorEnumNode.release(byref(trigSelectorEnumNode)) + + # 打开触发模式 + trigModeEnumNode = acqCtrl.contents.triggerMode(acqCtrl) + nRet = trigModeEnumNode.setValueBySymbol(byref(trigModeEnumNode), b"On") + if nRet != 0: + mv_camera.logger.error("set TriggerMode value [On] fail!") + # 释放相关资源 + trigModeEnumNode.release(byref(trigModeEnumNode)) + acqCtrl.contents.release(acqCtrl) + return -1 + + # 需要释放Node资源 + trigModeEnumNode.release(byref(trigModeEnumNode)) + + # 设置触发沿 + trigActivationEnumNode = acqCtrl.contents.triggerActivation(acqCtrl) + nRet = trigActivationEnumNode.setValueBySymbol(byref(trigActivationEnumNode), b"RisingEdge") + if nRet != 0: + mv_camera.logger.error("set TriggerActivation value [RisingEdge] fail!") + # 释放相关资源 + trigActivationEnumNode.release(byref(trigActivationEnumNode)) + acqCtrl.contents.release(acqCtrl) + return -1 + + # 需要释放Node资源 + trigActivationEnumNode.release(byref(trigActivationEnumNode)) + acqCtrl.contents.release(acqCtrl) + return 0 + +# 打开相机 +def openCamera(camera): + # 连接相机 + nRet = camera.connect(camera, c_int(GENICAM_ECameraAccessPermission.accessPermissionControl)) + if nRet != 0: + mv_camera.logger.error("camera connect fail!") + return -1 + else: + mv_camera.logger.info("camera connect success.") + + # # 注册相机连接状态回调 + # nRet = subscribeCameraStatus(camera) + # if nRet != 0: + # mv_camera.logger.error("subscribeCameraStatus fail!") + # return -1 + + return 0 + +# 关闭相机 +def closeCamera(camera): + # 反注册相机连接状态回调 + nRet = unsubscribeCameraStatus(camera) + if nRet != 0: + mv_camera.logger.error("unsubscribeCameraStatus fail!") + return -1 + + # 断开相机 + # nRet = camera.disConnect(byref(camera)) + # if nRet != 0: + # mv_camera.logger.error("disConnect camera fail!") + # return -1 + + return 0 + +# 设置曝光 +def setExposureTime(camera, dVal): + # 通用属性设置:设置曝光 --根据属性类型,直接构造属性节点。如曝光是 double类型,构造doubleNode节点 + exposureTimeNode = pointer(GENICAM_DoubleNode()) + exposureTimeNodeInfo = GENICAM_DoubleNodeInfo() + exposureTimeNodeInfo.pCamera = pointer(camera) + exposureTimeNodeInfo.attrName = b"ExposureTime" + nRet = GENICAM_createDoubleNode(byref(exposureTimeNodeInfo), byref(exposureTimeNode)) + if nRet != 0: + mv_camera.logger.error("create ExposureTime Node fail!") + return -1 + + # 设置曝光时间 + nRet = exposureTimeNode.contents.setValue(exposureTimeNode, c_double(dVal)) + if nRet != 0: + mv_camera.logger.error("set ExposureTime value [%f]us fail!" % (dVal)) + # 释放相关资源 + exposureTimeNode.contents.release(exposureTimeNode) + return -1 + else: + mv_camera.logger.info("set ExposureTime value [%f]us success." % (dVal)) + + # 释放节点资源 + exposureTimeNode.contents.release(exposureTimeNode) + return 0 + +# 枚举相机 +def enumCameras(): + # 获取系统单例 + system = pointer(GENICAM_System()) + nRet = GENICAM_getSystemInstance(byref(system)) + if nRet != 0: + mv_camera.logger.error("getSystemInstance fail!") + return None, None + + # 发现相机 + cameraList = pointer(GENICAM_Camera()) + cameraCnt = c_uint() + nRet = system.contents.discovery(system, byref(cameraList), byref(cameraCnt), c_int(GENICAM_EProtocolType.typeAll)); + if nRet != 0: + mv_camera.logger.error("discovery fail!") + return None, None + elif cameraCnt.value < 1: + mv_camera.logger.info("discovery no camera!") + return None, None + else: + mv_camera.logger.info("cameraCnt: " + str(cameraCnt.value)) + return cameraCnt.value, cameraList + +def grabOne(camera): + # 创建流对象 + streamSourceInfo = GENICAM_StreamSourceInfo() + streamSourceInfo.channelId = 0 + streamSourceInfo.pCamera = pointer(camera) + + streamSource = pointer(GENICAM_StreamSource()) + nRet = GENICAM_createStreamSource(pointer(streamSourceInfo), byref(streamSource)) + if nRet != 0: + mv_camera.logger.error("create StreamSource fail!") + return -1 + + # 创建control节点 + acqCtrlInfo = GENICAM_AcquisitionControlInfo() + acqCtrlInfo.pCamera = pointer(camera) + acqCtrl = pointer(GENICAM_AcquisitionControl()) + nRet = GENICAM_createAcquisitionControl(pointer(acqCtrlInfo), byref(acqCtrl)) + if nRet != 0: + mv_camera.logger.error("create AcquisitionControl fail!") + # 释放相关资源 + streamSource.contents.release(streamSource) + return -1 + + # 执行一次软触发 + trigSoftwareCmdNode = acqCtrl.contents.triggerSoftware(acqCtrl) + nRet = trigSoftwareCmdNode.execute(byref(trigSoftwareCmdNode)) + if nRet != 0: + mv_camera.logger.error("Execute triggerSoftware fail!") + # 释放相关资源 + trigSoftwareCmdNode.release(byref(trigSoftwareCmdNode)) + acqCtrl.contents.release(acqCtrl) + streamSource.contents.release(streamSource) + return -1 + + # 释放相关资源 + trigSoftwareCmdNode.release(byref(trigSoftwareCmdNode)) + acqCtrl.contents.release(acqCtrl) + streamSource.contents.release(streamSource) + + return 0 + +# 设置感兴趣区域 --- 感兴趣区域的宽高 和 xy方向的偏移量 入参值应符合对应相机的递增规则 +def setROI(camera, OffsetX, OffsetY, nWidth, nHeight): + #获取原始的宽度 + widthMaxNode = pointer(GENICAM_IntNode()) + widthMaxNodeInfo = GENICAM_IntNodeInfo() + widthMaxNodeInfo.pCamera = pointer(camera) + widthMaxNodeInfo.attrName = b"WidthMax" + nRet = GENICAM_createIntNode(byref(widthMaxNodeInfo), byref(widthMaxNode)) + if nRet != 0: + mv_camera.logger.error("create WidthMax Node fail!") + return -1 + + oriWidth = c_longlong() + nRet = widthMaxNode.contents.getValue(widthMaxNode, byref(oriWidth)) + if nRet != 0: + mv_camera.logger.error("widthMaxNode getValue fail!") + # 释放相关资源 + widthMaxNode.contents.release(widthMaxNode) + return -1 + + # 释放相关资源 + widthMaxNode.contents.release(widthMaxNode) + + # 获取原始的高度 + heightMaxNode = pointer(GENICAM_IntNode()) + heightMaxNodeInfo = GENICAM_IntNodeInfo() + heightMaxNodeInfo.pCamera = pointer(camera) + heightMaxNodeInfo.attrName = b"HeightMax" + nRet = GENICAM_createIntNode(byref(heightMaxNodeInfo), byref(heightMaxNode)) + if nRet != 0: + mv_camera.logger.error("create HeightMax Node fail!") + return -1 + + oriHeight = c_longlong() + nRet = heightMaxNode.contents.getValue(heightMaxNode, byref(oriHeight)) + if nRet != 0: + mv_camera.logger.error("heightMaxNode getValue fail!") + # 释放相关资源 + heightMaxNode.contents.release(heightMaxNode) + return -1 + + # 释放相关资源 + heightMaxNode.contents.release(heightMaxNode) + + # 检验参数 + if (oriWidth.value < (OffsetX + nWidth)) or (oriHeight.value < (OffsetY + nHeight)): + mv_camera.logger.error("please check input param!") + return -1 + + # 设置宽度 + widthNode = pointer(GENICAM_IntNode()) + widthNodeInfo = GENICAM_IntNodeInfo() + widthNodeInfo.pCamera = pointer(camera) + widthNodeInfo.attrName = b"Width" + nRet = GENICAM_createIntNode(byref(widthNodeInfo), byref(widthNode)) + if nRet != 0: + mv_camera.logger.error("create Width Node fail!") + return -1 + + nRet = widthNode.contents.setValue(widthNode, c_longlong(nWidth)) + if nRet != 0: + mv_camera.logger.error("widthNode setValue [%d] fail!" % (nWidth)) + # 释放相关资源 + widthNode.contents.release(widthNode) + return -1 + + # 释放相关资源 + widthNode.contents.release(widthNode) + + # 设置高度 + heightNode = pointer(GENICAM_IntNode()) + heightNodeInfo = GENICAM_IntNodeInfo() + heightNodeInfo.pCamera = pointer(camera) + heightNodeInfo.attrName = b"Height" + nRet = GENICAM_createIntNode(byref(heightNodeInfo), byref(heightNode)) + if nRet != 0: + mv_camera.logger.error("create Height Node fail!") + return -1 + + nRet = heightNode.contents.setValue(heightNode, c_longlong(nHeight)) + if nRet != 0: + mv_camera.logger.error("heightNode setValue [%d] fail!" % (nHeight)) + # 释放相关资源 + heightNode.contents.release(heightNode) + return -1 + + # 释放相关资源 + heightNode.contents.release(heightNode) + + # 设置OffsetX + OffsetXNode = pointer(GENICAM_IntNode()) + OffsetXNodeInfo = GENICAM_IntNodeInfo() + OffsetXNodeInfo.pCamera = pointer(camera) + OffsetXNodeInfo.attrName = b"OffsetX" + nRet = GENICAM_createIntNode(byref(OffsetXNodeInfo), byref(OffsetXNode)) + if nRet != 0: + mv_camera.logger.error("create OffsetX Node fail!") + return -1 + + nRet = OffsetXNode.contents.setValue(OffsetXNode, c_longlong(OffsetX)) + if nRet != 0: + mv_camera.logger.error("OffsetX setValue [%d] fail!" % (OffsetX)) + # 释放相关资源 + OffsetXNode.contents.release(OffsetXNode) + return -1 + + # 释放相关资源 + OffsetXNode.contents.release(OffsetXNode) + + # 设置OffsetY + OffsetYNode = pointer(GENICAM_IntNode()) + OffsetYNodeInfo = GENICAM_IntNodeInfo() + OffsetYNodeInfo.pCamera = pointer(camera) + OffsetYNodeInfo.attrName = b"OffsetY" + nRet = GENICAM_createIntNode(byref(OffsetYNodeInfo), byref(OffsetYNode)) + if nRet != 0: + mv_camera.logger.error("create OffsetY Node fail!") + return -1 + + nRet = OffsetYNode.contents.setValue(OffsetYNode, c_longlong(OffsetY)) + if nRet != 0: + mv_camera.logger.error("OffsetY setValue [%d] fail!" % (OffsetY)) + # 释放相关资源 + OffsetYNode.contents.release(OffsetYNode) + return -1 + + # 释放相关资源 + OffsetYNode.contents.release(OffsetYNode) + return 0 + +def demo(): + # 发现相机 + cameraCnt, cameraList = enumCameras() + if cameraCnt is None: + return -1 + + # 显示相机信息 + for index in range(0, cameraCnt): + camera = cameraList[index] + print("\nCamera Id = " + str(index)) + print("Key = " + str(camera.getKey(camera))) + print("vendor name = " + str(camera.getVendorName(camera))) + print("Model name = " + str(camera.getModelName(camera))) + print("Serial number = " + str(camera.getSerialNumber(camera))) + + camera = cameraList[0] + return 0 + + +if __name__=="__main__": + pass + # camera_count = mv_camera.start_discovery() + # if camera_count <= 0: + # mv_camera.logger.info('no cameras found!') + # else: + # mv_camera.open() + # mv_camera.set_roi(0, 0, 3000, 3000) + # mv_camera.grab_one() + # + # time.sleep(2) + # + # mv_camera.set_exposure_time(20000) + # mv_camera.stop() + # # 3s exit + # time.sleep(3) \ No newline at end of file diff --git a/components/dat_task.py b/components/dat_task.py new file mode 100644 index 0000000..5f97c36 --- /dev/null +++ b/components/dat_task.py @@ -0,0 +1,171 @@ +import json +import sqlite3 + +from dynaconf.base import Settings +from exceptiongroup import catch + +from core.edge_component import EdgeComponent, action, service + + +@action("dat_task", auto_start=True) +class TaskTable(EdgeComponent): + + def __init__(self, context): + super(TaskTable, self).__init__(context) + self.db_path = None + + def configure(self, setting: Settings) -> None: + self.db_path = self.context.get_component("_database").db_path + + @service() + def start(self) -> None: + super().start() + + @service() + def stop(self) -> None: + super().stop() + + @service() + def insert_task(self, entity): + cmd_str = "INSERT INTO dat_task (" + value_str = " VALUES (" + data = [] + for k, v in entity.items(): + if v is not None and k != "id": + cmd_str += " {},".format(k) + value_str += " ?," + data.append(v) + cmd_str = cmd_str[:len(cmd_str) - 1] + value_str = value_str[:len(value_str) - 1] + cmd_str += ")" + value_str + " )" + + conn = None + cursor = None + try: + conn = sqlite3.connect(self.db_path) + cursor = self.conn.cursor() + cursor.execute(cmd_str, data) + self.conn.commit() + except Exception as e: + pass + finally: + if cursor is not None: + cursor.close() + if conn is not None: + conn.close() + + @service() + def update_task(self, entity): + if entity["id"] is None: + return + cmd_str = "UPDATE dat_task SET" + data = [] + for k, v in entity.items(): + if v is not None and k != "id": + cmd_str += " {}=?,".format(k) + data.append(v) + cmd_str = cmd_str[:len(cmd_str) - 1] + if cmd_str.find('?') < 0: + return + cmd_str += " WHERE id=?" + data.append(entity["id"]) + + conn = None + cursor = None + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute(cmd_str, data) + conn.commit() + except Exception as e: + pass + finally: + if cursor is not None: + cursor.close() + if conn is not None: + conn.close() + + @service() + def list_task(self, state): + conn = None + cursor = None + l = None + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("SELECT * FROM dat_task WHERE state <= ? order by create_time", (state,)) + l = cursor.fetchall() + except Exception as e: + pass + finally: + if cursor is not None: + cursor.close() + if conn is not None: + conn.close() + return l + + @service() + def add_server_tasks(self, tasks): + """ + 接收来自PC的任务 + :return: + """ + if tasks is None or len(tasks) == 0: + self.logger.warn("没有接收到任务数据!") + return + + conn = None + cursor = None + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + for task in tasks: + cursor.execute("SELECT * FROM dat_task WHERE id = ? and device_sn = ?", (task["id"], task["deviceSn"])) + rows = cursor.fetchall() + if len(rows) == 0: + cursor.execute( + "INSERT INTO dat_task (id, name, device_sn, param_json, state, create_time, update_time) VALUES (?, ?, ?, ?, ?, ?, ?)", + (task['id'], task['name'], task['deviceSn'], task['paramJson'], task['state'], task['createTime'], task['updateTime']) + ) + conn.commit() + except Exception as e: + self.logger.error(e) + finally: + if cursor is not None: + cursor.close() + if conn is not None: + conn.close() + + @service() + def sync_client_tasks(self, device_id, device_sn): + """ + 上传本地任务数据 + :return: + """ + result = None + if device_id is None or device_sn is None == 0: + self.logger.warn("没有接收到任务数据!") + return result + + conn = None + cursor = None + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("SELECT id, device_sn, result_json, start_time, create_time, state FROM dat_task WHERE id = ? and device_sn = ?", (device_id, device_sn)) + row = cursor.fetchone() + if row is not None: + result = dict() + result["id"] = row[0] + result["result_json"] = row[2] + result["start_time"] = row[3] + result["end_time"] = row[4] + result["state"] = row[5] + except Exception as e: + self.logger.error(e) + finally: + if cursor is not None: + cursor.close() + if conn is not None: + conn.close() + return result \ No newline at end of file diff --git a/components/gpio.py b/components/gpio.py new file mode 100644 index 0000000..ce2d39d --- /dev/null +++ b/components/gpio.py @@ -0,0 +1,133 @@ +import logging +from PyQt5.QtCore import QObject, pyqtSignal + +from dynaconf.base import Settings +from core.edge_component import device, EdgeComponent, service + + +class GpioSignals(QObject): + gpio_lidar_opened = pyqtSignal(int) + gpio_lidar_closed = pyqtSignal(int) + gpio_camera_opened = pyqtSignal(int) + gpio_camera_closed = pyqtSignal(int) + +@device("gpio", auto_start=True) +class GPIOManager(EdgeComponent): + signals = GpioSignals() + + def __init__(self, context): + super(GPIOManager, self).__init__(context) + self.lidar_pin = 80 + self.camera_pin = 91 + self.export_path = '/sys/class/gpio/export' + self.unexport_path = '/sys/class/gpio/unexport' + self.lidar_value_path = None + self.lidar_direction_path = None + self.camera_value_path = None + self.camera_direction_path = None + + def configure(self, setting: Settings) -> None: + self.lidar_pin = setting.get('gpio.lidar.pin', 80) + self.camera_pin = setting.get('gpio.camera.pin', 91) + + self.lidar_value_path = f'/sys/class/gpio/gpio{self.lidar_pin}/value' + self.lidar_direction_path = f'/sys/class/gpio/gpio{self.lidar_pin}/direction' + self.camera_value_path = f'/sys/class/gpio/gpio{self.camera_pin}/value' + self.camera_direction_path = f'/sys/class/gpio/gpio{self.camera_pin}/direction' + + self.logger.info(f"Gpio configure done.") + + @service() + def start(self) -> None: + """导出 GPIO 引脚并设置为输出""" + self.stop() + try: + with open(self.export_path, 'w') as f: + f.write(str(self.lidar_pin)) + with open(self.lidar_direction_path, 'w') as f: + f.write('out') + self.logger.info(f"Export GPIO {self.lidar_pin}") + + with open(self.export_path, 'w') as f: + f.write(str(self.camera_pin)) + with open(self.camera_direction_path, 'w') as f: + f.write('out') + self.logger.info(f"Export GPIO {self.camera_pin}") + except IOError as e: + self.logger.error(f"Error exporting GPIO {self.lidar_pin}/{self.camera_pin}: {e}") + + @service() + def stop(self) -> None: + """释放 GPIO 引脚""" + try: + with open(self.unexport_path, 'w') as f: + f.write(str(self.lidar_pin)) + self.logger.info(f"Release GPIO {self.lidar_pin}") + + with open(self.unexport_path, 'w') as f: + f.write(str(self.camera_pin)) + self.logger.info(f"Release GPIO {self.camera_pin}") + except IOError as e: + self.logger.error(f"Error unexporting GPIO {self.lidar_pin}/{self.camera_pin}: {e}") + + @property + def lidar_value(self): + try: + with open(self.lidar_value_path, 'r') as f: + return int(f.read().strip()) + except Exception as e: + self.logger.error(f"Error reading GPIO value: {e}") + return -1 + + @lidar_value.setter + def lidar_value(self, val): + try: + with open(self.lidar_value_path, 'w') as f: + f.write(str(val)) + except Exception as e: + self.logger.error(f"Error setting GPIO value: {e}") + + @property + def camera_value(self): + try: + with open(self.camera_value_path, 'r') as f: + return int(f.read().strip()) + except Exception as e: + self.logger.error(f"Error reading GPIO value: {e}") + return -1 + + @camera_value.setter + def camera_value(self, val): + try: + with open(self.camera_value_path, 'w') as f: + f.write(str(val)) + except Exception as e: + self.logger.error(f"Error setting GPIO value: {e}") + + @service() + def open_lidar(self): + """设置 GPIO 为低电平并发出信号""" + self.lidar_value = 0 # 设置为低电平 + self.signals.gpio_lidar_opened.emit(self.lidar_pin) + self.logger.info(f"Setting GPIO {self.lidar_pin}: LOW") + + @service() + def close_lidar(self): + """设置 GPIO 为高电平并发出信号""" + self.lidar_value = 1 # 设置为高电平 + self.signals.gpio_lidar_closed.emit(self.lidar_pin) + self.logger.info(f"Setting GPIO {self.lidar_pin}: HIGH") + + @service() + def open_camera(self): + """设置 GPIO 为低电平并发出信号""" + self.camera_value = 0 # 设置为低电平 + self.signals.gpio_camera_opened.emit(self.camera_pin) + self.logger.info(f"Setting GPIO {self.camera_pin}: LOW") + + @service() + def close_camera(self): + """设置 GPIO 为高电平并发出信号""" + self.camera_value = 1 # 设置为高电平 + self.signals.gpio_camera_closed.emit(self.camera_pin) + self.logger.info(f"Setting GPIO {self.camera_pin}: HIGH") diff --git a/components/image_framework.py b/components/image_framework.py new file mode 100644 index 0000000..ea461eb --- /dev/null +++ b/components/image_framework.py @@ -0,0 +1,135 @@ +import os +from ctypes import * +import numpy as np +from PyQt5.QtCore import QObject, pyqtSignal +from dynaconf.base import Settings +from numpy.ctypeslib import as_array +import platform +import time + +from core.edge_component import action, EdgeComponent, service +from util import get_system_and_library_suffix + + +# 给回调函数使用 +class _SubRoiData_(Structure): + _fields_ = [ + ('mPparentId', c_long), + ('mId', c_long), + ('mInfo', c_char * 256), + ('isGood', c_bool), + ('vpOffset', c_float * 2), + ('mRoiPoints', c_float * 8), # 4 * 2 + ('mInnerConners', c_float * 8), # 4 * 2 + ('mInnerConners3D', c_float * 12), # 4 * 3 + ('edges', c_float * 4), + ('center', c_float * 2), + ('cloud_size', c_int), + ('mVc2D', POINTER(c_float)), + ('mVc3D', POINTER(c_float)), + ('H', c_float * 9), + ] + +SubRoiData = _SubRoiData_ + +class _CallbackInfo_(Structure): + _fields_ = [ + ('code', c_int), + ('errorInfo', c_char * 256), + ('bGetData', c_bool), + ('mId', c_long), + ('mInfo', c_char * 256), + ('mImPath', c_char * 256), + ('mIm2D3DPtr', c_char * 256), + ('roi_size', c_int), + ('subRois', SubRoiData * 16), + ] + +CallbackInfo = _CallbackInfo_ +# 回调函数类型定义 +CallbackType = CFUNCTYPE(None, POINTER(CallbackInfo)) + + +class ImageFrameworkSignals(QObject): + # QT 信号 + on_image_result = pyqtSignal(object) + + +@action("image-framework", auto_start=True) +class ImageFramework(EdgeComponent): + signals = ImageFrameworkSignals() + + def __init__(self, context): + super().__init__(context) + self.image_framework_sdk = None + self.data_callback = None + + def configure(self, setting: Settings) -> None: + self.init_image_framework_sdk() + self.logger.info(f"Image framework configure done.") + + def init_image_framework_sdk(self): + try: + # 加载C++库 + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + (arch, suffix) = get_system_and_library_suffix() + self.image_framework_sdk = CDLL(os.path.join(current_file_dir, f'../vendors/image-framework/{arch}/image_framework.{suffix}')) + # 设置回调函数 + self.init_callback() + except Exception as e: + self.logger.error(f"Load Image framework sdk failed: {str(e)}") + + def init_callback(self): + def _callback(data): + contents = data.contents + self.logger.info(f"image framework callback data, bGetData:{contents.bGetData}") + if contents.bGetData: + record = CallbackInfo() + self.image_framework_sdk.LibapGetRecord(byref(record)) + self.logger.info(f"image framework callback data, roi_size:{record.roi_size}") + self.logger.info(f"image framework callback data, code:{record.code}") + self.logger.info(f"image framework callback data, errorInfo:{record.errorInfo}") + for i in range(record.roi_size): + roi_data = record.subRois[i] + self.logger.info(f"roi data, roi_data[{i}].mId:{roi_data.mId}") + self.logger.info(f"roi data, roi_data[{i}].isGood:{roi_data.isGood}") + self.logger.info(f"roi data, roi_data[{i}].mRoiPoints:{list(roi_data.mRoiPoints)}") + + self.logger.info("image framework callback done.") + + self.data_callback = CallbackType(_callback) + + @service() + def start(self): + super().start() + if self.image_framework_sdk is None: + raise RuntimeError("Image framework SDK未正确加载!") + + ret = self.image_framework_sdk.LibapiInit(1, "", self.data_callback) + if ret != 0: + raise RuntimeError("Image framework 初始化失败!") + + self.logger.info("Image framework started.") + + @service() + def stop(self): + self.image_framework_sdk.stop_sdk() + self.logger.info("Image framework stopped.") + + @service() + def start_detect(self): + ret = self.image_framework_sdk.LibapiStartDetection() + if ret != 0: + raise RuntimeError("启动检测失败!") + + @service() + def continue_detect(self): + ret = self.image_framework_sdk.LibapiContinuetDetection() + if ret != 0: + raise RuntimeError("继续检测失败!") + + @service() + def stop_detect(self): + ret = self.image_framework_sdk.LibapStopDetection() + if ret != 0: + raise RuntimeError("停止检测失败!") \ No newline at end of file diff --git a/components/lidar.py b/components/lidar.py new file mode 100644 index 0000000..b9d24db --- /dev/null +++ b/components/lidar.py @@ -0,0 +1,282 @@ +import os +import ctypes +from ctypes import * +import numpy as np +from dynaconf.base import Settings + +from core.edge_component import device, EdgeComponent, service +from core.logging import logger +import time + +from PyQt5.QtCore import QObject, pyqtSignal + +from core.config import settings + + +# 定义 livox_status 的返回值类型 +livox_status = ctypes.c_int +# 预定义的常量 +kBroadcastCodeSize = 16 # 广播码长度 + +# 定义枚举类型 +class DeviceType(c_int): + kDeviceTypeHub = 0 + kDeviceTypeLidarMid40 = 1 + kDeviceTypeLidarTele = 2 + kDeviceTypeLidarHorizon = 3 + kDeviceTypeLidarMid70 = 6 + kDeviceTypeLidarAvia = 7 + +class LidarState(c_int): + kLidarStateInit = 0 + kLidarStateNormal = 1 + kLidarStatePowerSaving = 2 + kLidarStateStandBy = 3 + kLidarStateError = 4 + kLidarStateUnknown = 5 + +class LidarMode(c_int): + kLidarModeNormal = 1 + kLidarModePowerSaving = 2 + kLidarModeStandby = 3 + +class LidarFeature(c_int): + kLidarFeatureNone = 0 + kLidarFeatureRainFog = 1 + +class LidarIpMode(c_int): + kLidarDynamicIpMode = 0 + kLidarStaticIpMode = 1 + +class LidarScanPattern(c_int): + kNoneRepetitiveScanPattern = 0 + kRepetitiveScanPattern = 1 + +class LivoxStatus(c_int): + kStatusSendFailed = -9 + kStatusHandlerImplNotExist = -8 + kStatusInvalidHandle = -7 + kStatusChannelNotExist = -6 + kStatusNotEnoughMemory = -5 + kStatusTimeout = -4 + kStatusNotSupported = -3 + kStatusNotConnected = -2 + kStatusFailure = -1 + kStatusSuccess = 0 + +# 定义结构体和联合体 +class LidarErrorCode(Structure): + _fields_ = [ + ("temp_status", c_uint32, 2), + ("volt_status", c_uint32, 2), + ("motor_status", c_uint32, 2), + ("dirty_warn", c_uint32, 2), + ("firmware_err", c_uint32, 1), + ("pps_status", c_uint32, 1), + ("device_status", c_uint32, 1), + ("fan_status", c_uint32, 1), + ("self_heating", c_uint32, 1), + ("ptp_status", c_uint32, 1), + ("time_sync_status", c_uint32, 3), + ("rsvd", c_uint32, 13), + ("system_status", c_uint32, 2), + ] + +class HubErrorCode(Structure): + _fields_ = [ + ("sync_status", c_uint32, 2), + ("temp_status", c_uint32, 2), + ("lidar_status", c_uint32, 1), + ("lidar_link_status", c_uint32, 1), + ("firmware_err", c_uint32, 1), + ("rsvd", c_uint32, 23), + ("system_status", c_uint32, 2), + ] + +class ErrorMessage(Union): + _fields_ = [ + ("error_code", c_uint32), + ("lidar_error_code", LidarErrorCode), + ("hub_error_code", HubErrorCode) + ] + +class StatusUnion(Union): + _fields_ = [ + ("error_code", c_uint32), # 假设这个字段为示例 + ("lidar_error_code", LidarErrorCode), + ("hub_error_code", HubErrorCode) + ] + +# 定义 DeviceInfo 结构体 +class DeviceInfo(Structure): + _fields_ = [ + ("broadcast_code", c_char * kBroadcastCodeSize), # 广播码,最大15个字符,带终止符 + ("handle", c_uint8), # 设备句柄 + ("slot", c_uint8), # 插槽编号 + ("id", c_uint8), # LiDAR id + ("type", DeviceType), # 设备类型 + ("data_port", c_uint16), # 点云数据UDP端口 + ("cmd_port", c_uint16), # 控制命令UDP端口 + ("sensor_port", c_uint16), # IMU数据UDP端口 + ("ip", c_char * 16), # IP地址 + ("state", LidarState), # LiDAR状态 + ("feature", LidarFeature), # LiDAR特性 + ("status", StatusUnion), # LiDAR工作状态 + ("firmware_version", c_uint8 * 4) # 固件版本 + ] + +# 定义 BroadcastDeviceInfo 结构体 +class BroadcastDeviceInfo(Structure): + _fields_ = [ + ("broadcast_code", c_char * kBroadcastCodeSize), # 广播码,最多15个字符,带终止符 + ("dev_type", c_uint8), # 设备类型,参考 DeviceType + ("reserved", c_uint16), # 保留字段 + ("ip", c_char * 16) # 设备 IP 地址 + ] + +# 定义 LivoxEthPacket 结构体 +class LivoxEthPacket(Structure): + _fields_ = [ + ("version", c_uint8), # Packet protocol version. + ("slot", c_uint8), # Slot number used for connecting LiDAR. + ("id", c_uint8), # LiDAR id. + ("rsvd", c_uint8), # Reserved. + ("err_code", c_uint32), # Device error status indicator information. + ("timestamp_type", c_uint8), # Timestamp type. + ("data_type", c_uint8), # Point cloud coordinate format. + ("timestamp", c_uint8 * 8), # Nanosecond or UTC format timestamp. + ("data", c_uint8 * 1) # Point cloud data (can be extended as needed). + ] + +# Extend cartesian coordinate format. +class LivoxExtendRawPoint(Structure): + _fields_ = [ + ('x', c_int32), # X axis, Unit: mm + ('y', c_int32), # Y axis, Unit: mm + ('z', c_int32), # Z axis, Unit: mm + ('reflectivity', c_uint8), # Reflectivity + ('tag', c_uint8) # Tag + ] + +# 回调函数类型定义 +DataCallbackType = CFUNCTYPE(None, c_uint8, POINTER(c_int32), c_uint32) +ErrorCallbackType = CFUNCTYPE(None, c_uint8, c_uint8, POINTER(ErrorMessage)) +DeviceChangeCallbackType = CFUNCTYPE(None, POINTER(DeviceInfo), c_uint8) +DeviceBroadcastCallbackType = CFUNCTYPE(None, POINTER(BroadcastDeviceInfo)) +CommonCommandCallback = CFUNCTYPE(None, c_int, c_uint8, c_uint8) + + +class LidarSignals(QObject): + # QT 信号 + on_device_broadcast = pyqtSignal(str) + # 0 - 已连接,1 - 已断开 + on_device_change = pyqtSignal(int) + on_device_error = pyqtSignal(ErrorMessage) + on_device_data = pyqtSignal(object) + + +@device("lidar", auto_start=True) +class LidarDevice(EdgeComponent): + signals = LidarSignals() + + def __init__(self, context): + super().__init__(context) + self.livox_sdk = None + self.data_callback = None + self.error_callback = None + self.device_change_callback = None + self.device_broadcast_callback = None + self.common_command_callback = None + + def configure(self, setting: Settings) -> None: + self.init_livox_sdk() + self.logger.info(f"Lidar configure done.") + + def init_livox_sdk(self): + try: + # 加载C++库 + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + self.livox_sdk = ctypes.CDLL(os.path.join(current_file_dir, '../vendors/livox/liblivox_sdk_wrapper.so')) + self.livox_sdk.connect.argtypes = [c_char_p] # 设置参数类型 + self.livox_sdk.connect.restype = c_int # 设置返回值类型 + # 设置回调函数 + self.init_livox_callback() + except Exception as e: + self.logger.error(f"load livox sdk failed: {str(e)}") + + def init_livox_callback(self): + # 数据回调函数 + def data_callback(handle, points, data_num): + # 将 ctypes 指针转换为 numpy 数组,点数 * 4 个 uint32 值 (x, y, z, reflectivity_and_tag) + np_data = np.ctypeslib.as_array(points, shape=(data_num * 4,)) + # 将 x, y, z 组成新的二维 numpy 数组 + xyz = np_data.reshape(data_num, 4)[:, :3] # 提取前 3 个数值 (x, y, z) + # 结果 + self.signals.on_device_data.emit(xyz) + + # 通用指令回调函数 + def common_command_callback(status, handle, response): + logger.info(f"Common from handle {handle}, status: {status}, response: {response}") + + # 错误回调函数 + def error_callback(status, handle, error_message): + logger.error(f"Error from handle {handle}, status: {status}") + + # 设备状态变化回调函数 + def device_change_callback(info, event_type): + logger.info( + f"Device {bytes(info.contents.broadcast_code).decode('utf-8')} changed state, event type: {event_type}") + self.signals.on_device_change.emit(event_type) + + # 设备广播回调函数 + def device_broadcast_callback(info): + broadcast_code_bytes = bytes(info.contents.broadcast_code) + broadcast_code = broadcast_code_bytes.decode('utf-8') + logger.info(f"New device broadcast received: {broadcast_code}") + if broadcast_code == settings.get("lidar.target.barcode", "3GGDLCM00201561"): + logger.info(f"Connect device: {broadcast_code}") + self.livox_sdk.connect(broadcast_code_bytes) + self.signals.on_device_broadcast.emit(broadcast_code) + + self.data_callback = DataCallbackType(data_callback) + self.error_callback = ErrorCallbackType(data_callback) + self.device_change_callback = DeviceChangeCallbackType(device_change_callback) + self.device_broadcast_callback = DeviceBroadcastCallbackType(device_broadcast_callback) + self.common_command_callback = CommonCommandCallback(common_command_callback) + # 注册回调函数 + self.livox_sdk.register_data_callback(self.data_callback) + self.livox_sdk.register_error_callback(self.error_callback) + self.livox_sdk.register_device_change_callback(self.device_change_callback) + self.livox_sdk.register_device_broadcast_callback(self.device_broadcast_callback) + self.livox_sdk.register_common_command_callback(self.common_command_callback) + + @service() + def start(self): + if self.livox_sdk is None: + raise RuntimeError("雷达设备SDK未正确加载") + + if self.livox_sdk.init_sdk(): + self.logger.info("Livox SDK initialized.") + if self.livox_sdk.start_discovery(): + self.logger.info("Started discovering Livox devices.") + else: + self.logger.error("Failed to discover devices.") + else: + self.logger.error("Failed to initialize Livox SDK.") + + @service() + def set_mode(self, mode): + """ + 设置雷达模式:1-normal,2-power saving + """ + self.logger.info(f'set mode: {mode}') + return self.livox_sdk.set_mode(mode) + + @service() + def stop(self): + self.livox_sdk.stop_sdk() + self.logger.info("Livox SDK stopped.") + + +if __name__ == '__main__': + pass diff --git a/components/ups.py b/components/ups.py new file mode 100644 index 0000000..299356a --- /dev/null +++ b/components/ups.py @@ -0,0 +1,135 @@ +from threading import Timer +from time import sleep + +from dynaconf.base import Settings +from serial import Serial, SerialException + +from PyQt5.QtCore import QObject, pyqtSignal + +from core.edge_component import device, EdgeComponent, service + + +def get_crc(pc_rec, i_len): + i_crc = 0xFFFF # 初始化CRC值 + if i_len <= 0: + return None # 长度为0时返回None + + for ix in range(i_len): + i_crc ^= pc_rec[ix] # 进行异或运算 + for iy in range(8): + if (i_crc & 1) != 0: + i_crc = (i_crc >> 1) ^ 0xA001 # CRC多项式 + else: + i_crc >>= 1 # 右移一位 + + # 返回高字节和低字节 + return [(i_crc >> 8) & 0xFF, i_crc & 0xFF] + + +def read_packet(addr): + pack = bytearray([0xAB, 0xCD, 0x00, 0x03, addr, 0x00, 0x00]) + crc = get_crc(pack, 7) + pack.append(crc[0]) + pack.append(crc[1]) + return pack + + +class UpsSignals(QObject): + battery_change = pyqtSignal(int) + state_change = pyqtSignal(int) + + +@device("ups", auto_start=True) +class Ups(EdgeComponent): + + signals = UpsSignals() + + def __init__(self, context): + super(Ups, self).__init__(context) + self.port = '/dev/ttyUSB0' + self.bytesize = 8 + self.parity = 'N' + self.stopbits = 1 + self.baudrate = 115200 + self.serial = None + self.read_timer = None + self.cmd_read_delay = 0.3 + + def configure(self, setting: Settings) -> None: + self.port = setting.get('ups.port', '/dev/ttyUSB0') + self.baudrate = setting.get('ups.baudrate', 115200) + self.bytesize = setting.get('ups.bytesize', 8) + self.stopbits = setting.get('ups.stopbits', 1) + self.parity = setting.get('ups.parity', 'N') + self.cmd_read_delay = setting.get('ups.cmd.read.delay', 0.3) + + self.logger.info(f"Ups configure done.") + + @service() + def start(self) -> None: + # 打开串口 + try: + self.serial = Serial(port=self.port, baudrate=self.baudrate, stopbits=self.stopbits, parity=self.parity, + bytesize=self.bytesize, timeout=2.0) + self.logger.info(f"open serial port: {self.port} baudrate:{self.baudrate} stopbits:{self.stopbits} bytesize:{self.bytesize}") + except SerialException as e: + self.logger.error(f"Failed to open serial port: {e}") + + # 启动读取数据 + self.read_battery() + self.logger.info('Ups start!') + super().start() + + def read_battery(self): + if self.serial is not None and self.serial.isOpen(): + # 读取状态 + read_state_pack = read_packet(0x05) + self.serial.write(read_state_pack) + # self.logger.info(f"发送查询状态指令: {read_state_pack.hex()}") + sleep(self.cmd_read_delay) + if self.serial.in_waiting >= 9: + # 返回指令释义: + # AB CD 00 03 05 64 00 00 00 + data = self.serial.read(9) + # self.logger.info(f'收到查询状态数据:{data.hex()}') + if data[0] == 0xAB and data[1] == 0xCD: + state = data[6] + # self.logger.info(f'当前状态:{state}') + self.signals.state_change.emit(state) + + # 读取电量 + read_battery_pack = read_packet(0x01) + self.serial.write(read_battery_pack) + # self.logger.info(f"发送查询电量指令: {read_battery_pack.hex()}") + sleep(self.cmd_read_delay) + if self.serial.in_waiting >= 9: + # 返回指令释义: + # abcd000301003c4042 + data = self.serial.read(9) + # self.logger.info(f'收到查询电量数据:{data.hex()}') + if data[0] == 0xAB and data[1] == 0xCD: + battery = data[6] + # self.logger.info(f'当前电量:{battery}') + self.signals.battery_change.emit(battery) + + self.read_timer = Timer(3, self.read_battery) + self.read_timer.start() + + @service() + def stop(self) -> None: + if self.read_timer is not None: + self.read_timer.cancel() + + if self.serial is not None: + self.serial.close() + self.serial = None + + self.logger.info('Ups stop!') + super().stop() + + +if __name__=="__main__": + ups = Ups() + ups.start() + sleep(30) + ups.stop() diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..2f0de86 --- /dev/null +++ b/config.ini @@ -0,0 +1,42 @@ +# 2D config +[2D] +w=9344 +h=7000 +fx=11241.983 +fy=11244.0599 +cx=4553.03821 +cy=3516.9118 +k1=-0.04052072 +k2=0.22211572 +p1=0.00042405 +p2=-0.00367346 +k3=-0.15639485 +yolo_label=0 +yolo_prob=0.25 +yolo_modol_path=./best_rgb_ymj_csc_80_2.om + +# 3D config +[3D] +r=-0.321605 +p=-1.5938 +y=1.91032 +tx=0.109253 +ty=0.0710213 +tz=0.0175978 +dminx=-2.5 +dmaxx=2.5 +dminy=-2.5 +dmaxy=2.5 +dminz=0.0 +dmaxz=3.8 +kk=0.02 + +# system config +[sys] +fake=true +camera_cap_fake=true +lidar_cap_fake=true +npu_fake=true +conners_detect_fake=true +fake_image_fpath=./vendors/image-framework/output.png +fake_lidar_fpath=./vendors/image-framework/20241027112343190.ply diff --git a/config/app.toml b/config/app.toml new file mode 100644 index 0000000..2772274 --- /dev/null +++ b/config/app.toml @@ -0,0 +1,5 @@ +[default] +logging = { level = "DEBUG", directory = "./logs", console = true } +rest = { port = 8000, static = 'local' } +tcp = { enable = true, port = 13000 } +device = { name = "预埋件检测设备", model = "DT-01", sn = "DT01000001" } \ No newline at end of file diff --git a/config/dev.toml b/config/dev.toml new file mode 100644 index 0000000..01926f8 --- /dev/null +++ b/config/dev.toml @@ -0,0 +1,26 @@ +[dev] +mqtt.server="146.56.195.129" +mqtt.port=1883 +mqtt.username="cps-device-detect-1" +mqtt.password="" + +sqlite3.path='detect.db' +sqlite3.sql='sql' + +sched.db='sqlite:///detect-sched.db' +sched.coalesce=false +sched.max_instances=3 +sched.thread_pool_workers=10 +sched.process_pool_workers=3 + +lidar.target.baracode='3GGDLCM00201561' + +ups.port='/dev/ttyUSB0' +ups.baudrate=115200 +ups.bytesize=8 +ups.stopbits=1 +ups.parity='N' +ups.cmd.read.delay=0.3 + +gpio.lidar.pin=80 +gpio.camera.pin=84 diff --git a/config/prod.toml b/config/prod.toml new file mode 100644 index 0000000..79b6975 --- /dev/null +++ b/config/prod.toml @@ -0,0 +1,26 @@ +[prod] +mqtt.server="146.56.195.129" +mqtt.port=1883 +mqtt.username="cps-device-detect-1" +mqtt.password="" + +sqlite3.path='detect.db' +sqlite3.sql='sql' + +sched.db='sqlite:///detect-sched.db' +sched.coalesce=false +sched.max_instances=3 +sched.thread_pool_workers=10 +sched.process_pool_workers=3 + +lidar.target.baracode='3GGDLCM00201561' + +ups.port='/dev/ttyUSB0' +ups.baudrate=115200 +ups.bytesize=8 +ups.stopbits=1 +ups.parity='N' +ups.cmd.read.delay=0.3 + +gpio.lidar.pin=80 +gpio.camera.pin=91 \ No newline at end of file diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/config.py b/core/config.py new file mode 100644 index 0000000..4e7847f --- /dev/null +++ b/core/config.py @@ -0,0 +1,16 @@ +import os + +from dynaconf import Dynaconf + +current_file_dir = os.path.dirname(os.path.abspath(__file__)) +# 设置环境变量 +os.environ['ENV_FOR_DYNACONF'] = 'dev' +settings = Dynaconf( + envvar_prefix="DYNACONF", + settings_files=[ + os.path.join(current_file_dir, '../config/app.toml'), + os.path.join(current_file_dir, '../config/dev.toml'), + os.path.join(current_file_dir, '../config/prod.toml') + ], + environments=True, +) diff --git a/core/context.py b/core/context.py new file mode 100644 index 0000000..8ecb36d --- /dev/null +++ b/core/context.py @@ -0,0 +1,26 @@ + + +class AppContext: + """ + 存放一些全局服务对象 + """ + # 显示比例 + _display_ratio = 1 + # EdgeContext + _edge_context = None + + @staticmethod + def set_ratio(ratio): + AppContext._display_ratio = ratio + + @staticmethod + def get_ratio(): + return AppContext._display_ratio + + @staticmethod + def set_edge_context(ctx): + AppContext._edge_context = ctx + + @staticmethod + def get_edge_context(): + return AppContext._edge_context \ No newline at end of file diff --git a/core/edge_component.py b/core/edge_component.py new file mode 100644 index 0000000..a376996 --- /dev/null +++ b/core/edge_component.py @@ -0,0 +1,130 @@ +import asyncio +import inspect +import os +from abc import ABC, abstractmethod +import logging +from functools import wraps +from typing import Any + +from dynaconf.base import Settings + +from core.config import settings +from core.edge_logger import LoggingMixin + + +def device(name, auto_start=False): + def decorator(cls): + cls.component_name = name + cls.component_type = "device" + cls.component_auto_start = auto_start + return cls + + return decorator + + +def action(name, auto_start=True): + def decorator(cls): + cls.component_name = name + cls.component_type = "action" + cls.component_auto_start = auto_start + return cls + + return decorator + + +def service(): + """ + 定义标记服务的装饰器,只有有此装饰器的函数才可以被远程调用 + :return: + """ + def decorator(func): + func._component_service = True + if not inspect.iscoroutinefunction(func): + # 如果原函数不是协程函数,则直接返回原函数 + return func + + @wraps(func) + async def wrapper(*args, **kwargs): + return await func(*args, **kwargs) + + return wrapper + + return decorator + + +class EdgeComponent(LoggingMixin, ABC): + + component_auto_start = False + + def __init__(self, context): + super().__init__() + self.context = context + self.is_started = False + + async def execute(self, func_name, *args, **kwargs): + func = getattr(self, func_name, None) + if func is None: + raise RuntimeError(f"Function {func_name} not found in {self.__class__.__name__}") + + if hasattr(func, '_component_service') and func._component_service: + if callable(func): + method_signature = inspect.signature(func) + method_params = method_signature.parameters + call_args = [] + for param_name, param_info in method_params.items(): + if param_name == 'self' or param_info.kind == 4: + continue + param_type = param_info.annotation + if param_name in kwargs: + # 尝试将参数值转换为指定类型 + try: + if param_type is inspect.Parameter.empty or param_type is Any: + # 如果参数类型未注明或为 Any 类型,则不进行强制类型转换 + call_args.append(kwargs[param_name]) + else: + # 否则,尝试将参数值转换为指定类型 + call_args.append(param_type(kwargs[param_name])) + except (TypeError, ValueError) as e: + raise ValueError(f"Invalid value '{kwargs[param_name]}' for parameter '{param_name}': {e}") + elif param_info.default is inspect.Parameter.empty: + # 如果参数没有默认值且未在 kwargs 中指定,则抛出异常 + raise ValueError(f"Missing required parameter '{param_name}' for method '{func_name}'.") + else: + # 否则,使用参数的默认值 + call_args.append(param_info.default) + + func_kwargs = {key: value for key, value in kwargs.items() if + key not in [param_name for param_name, _ in method_params.items()]} + if inspect.iscoroutinefunction(func): + return await func(*tuple(call_args), **func_kwargs) + else: + return await asyncio.to_thread(func, *tuple(call_args), **func_kwargs) + else: + raise RuntimeError(f"Function {func.__name__} not found in {self.__class__.__name__}") + else: + raise RuntimeError(f"Function {func.__name__} does not have the required annotation.") + + @abstractmethod + def start(self): + """ + 启动 + :return: + """ + self.is_started = True + + @abstractmethod + def configure(self, setting: Settings) -> None: + """ + 配置 + :param setting: + :return: + """ + pass + + @abstractmethod + def stop(self): + """ + 停止 + :return: + """ + self.is_started = False diff --git a/core/edge_context.py b/core/edge_context.py new file mode 100644 index 0000000..018a20d --- /dev/null +++ b/core/edge_context.py @@ -0,0 +1,473 @@ +import asyncio +import importlib +import inspect +import json +import os +import pkgutil +import re +from typing import Type, Dict, Optional, Any, List + +from fastapi import FastAPI, Request +from fastapi.staticfiles import StaticFiles +from httpx import delete +from starlette.responses import JSONResponse +from uvicorn import Server, Config + +from core.edge_component import EdgeComponent, service +from core.config import settings +from core.edge_internal import Database, Scheduler +from core.edge_logger import LoggingMixin + +import paho.mqtt.client as mqtt + +from core.edge_routes import setup_routes +from core.edge_task import EdgeTask, EdgeTaskStep +from core.edge_util import camel_to_snake + +MQTT_TELEMETRY_TOPIC = "v1/devices/me/telemetry" +MQTT_RPC_TOPIC_PREFIX = "v1/devices/me/rpc/request" +MQTT_RPC_TOPIC_RESP_PREFIX = "v1/devices/me/rpc/response" +MQTT_RPC_TOPIC = f"{MQTT_RPC_TOPIC_PREFIX}/+" + + +class EdgeContext(LoggingMixin): + + def __init__(self): + super().__init__() + # 初始化mqtt客户端 + self.client = mqtt.Client() + self.client.on_connect = self.on_connect + self.client.on_disconnect = self.on_disconnect + self.client.on_message = self.on_message + # 搜索边缘组件并实例化 + self.components: Dict[str, EdgeComponent] = self._load_components() + self._init_internal_components() + self._init_components() + self.logger.info(f"当前边缘组件:{','.join(self.components.keys())}") + # loop + self.loop = None + # tcp server + self.tcp_server = None + self.tcp_clients = set() # 保存客户端连接 + # rest + rest_port = settings.get("rest.port", 8000) + self.app = FastAPI() + # 挂载静态文件目录 + local_dir = settings.get('rest.static', 'local') + os.makedirs(local_dir, exist_ok=True) + self.app.mount("/local", StaticFiles(directory=local_dir), name="local") + self.config = Config(app=self.app, host="0.0.0.0", port=rest_port) + self.server = Server(self.config) + setup_routes(self, self.app) + + def _load_components(self) -> Dict[str, EdgeComponent]: + package_name = 'components' + components = {} + # 动态导入 components 目录下的所有模块 + package = importlib.import_module(package_name) + for _, module_name, _ in pkgutil.iter_modules(package.__path__, package_name + "."): + module = importlib.import_module(module_name) + # 查找模块中的所有 EdgeComponent 子类 + for name, cls in inspect.getmembers(module, inspect.isclass): + if issubclass(cls, EdgeComponent) and cls is not EdgeComponent: + # 确保是非抽象类且没有子类 + if not inspect.isabstract(cls) and not cls.__subclasses__() and hasattr(cls, 'component_name'): + instance = cls(self) + components[cls.component_name] = instance + + return components + + def _init_internal_components(self): + database = Database(self) + scheduler = Scheduler(self) + database.configure(settings) + scheduler.configure(settings) + database.start() + scheduler.start() + self.components[database.__class__.component_name] = database + self.components[scheduler.__class__.component_name] = scheduler + + def _init_components(self): + for componentKey in self.components.keys(): + if componentKey in [Database.component_name, Scheduler.component_name]: + continue + component = self.components[componentKey] + component.configure(settings) + if component.__class__.component_auto_start: + try: + if not component.is_started: + component.start() + except Exception as e: + self.logger.error(f"{componentKey}启动失败", e) + + def _get_non_abstract_subclasses(self, cls: Type[EdgeComponent]) -> Dict[str, EdgeComponent]: + subclasses = set(cls.__subclasses__()) + non_abstract_subclasses = {} + + for subclass in subclasses: + if not inspect.isabstract(subclass) and not subclass.__subclasses__() and hasattr(subclass, 'component_name'): + instance = subclass() + instance.configure(settings) + if subclass.component_auto_start: + instance.start() + non_abstract_subclasses[subclass.component_name] = instance + else: + non_abstract_subclasses.update(self._get_non_abstract_subclasses(subclass)) + + return non_abstract_subclasses + + async def execute(self, func_name, *args, **kwargs): + func = getattr(self, func_name, None) + if func is None: + raise RuntimeError(f"Function {func_name} not found in {self.__class__.__name__}") + + if hasattr(func, '_component_service') and func._component_service: + if callable(func): + method_signature = inspect.signature(func) + method_params = method_signature.parameters + call_args = [] + for param_name, param_info in method_params.items(): + if param_name == 'self' or param_info.kind == 4: + continue + param_type = param_info.annotation + if param_name in kwargs: + # 尝试将参数值转换为指定类型 + try: + if param_type is inspect.Parameter.empty or param_type is Any: + # 如果参数类型未注明或为 Any 类型,则不进行强制类型转换 + call_args.append(kwargs[param_name]) + else: + # 否则,尝试将参数值转换为指定类型 + call_args.append(param_type(kwargs[param_name])) + except (TypeError, ValueError) as e: + raise ValueError(f"Invalid value '{kwargs[param_name]}' for parameter '{param_name}': {e}") + elif param_info.default is inspect.Parameter.empty: + # 如果参数没有默认值且未在 kwargs 中指定,则抛出异常 + raise ValueError(f"Missing required parameter '{param_name}' for method '{func_name}'.") + else: + # 否则,使用参数的默认值 + call_args.append(param_info.default) + + func_kwargs = {key: value for key, value in kwargs.items() if + key not in [param_name for param_name, _ in method_params.items()]} + if inspect.iscoroutinefunction(func): + return await func(*tuple(call_args), **func_kwargs) + else: + return await asyncio.to_thread(func, *tuple(call_args), **func_kwargs) + else: + raise RuntimeError(f"Function {func.__name__} not found in {self.__class__.__name__}") + else: + raise RuntimeError(f"Function {func.__name__} does not have the required annotation.") + + def get_component(self, name: str) -> EdgeComponent: + component_instance = self.components.get(name) + if component_instance: + return component_instance + else: + raise ValueError(f"Component {name} not found") + + async def handle_message(self, client, userdata, message): + self.logger.info(f"Receive: topic={message.topic} content={message.payload}") + # 'v1/devices/me/rpc/request/0' + topic = message.topic + if topic.startswith(MQTT_RPC_TOPIC_PREFIX): + rpc_id = topic.rsplit('/', 1)[-1] + payload = json.loads(message.payload) + response_topic = f"{MQTT_RPC_TOPIC_RESP_PREFIX}/{rpc_id}" + + requestId = payload["requestId"] + response = { + "requestId": requestId, + "code": 0, + "message": "success", + } + reqeust_type = payload["type"] + if not reqeust_type in ['service', 'task']: + response['type'] = reqeust_type + response['code'] = -1 + response['message'] = 'invalid reqeust type!' + else: + if reqeust_type == 'service': + method = payload.get('method') + component = payload.get('component') + method = camel_to_snake(method) + if payload.get("method") is None: + response = {"requestId": requestId, "type": reqeust_type, "code": -1, + "message": "没有method参数!"} + else: + if component == "" or component is None: + executor = self + else: + executor = self.get_component(component) + if executor: + try: + kwargs = payload.get("params") + if kwargs is None: + kwargs = {} + result = await executor.execute(method, **kwargs) + response = {"requestId": requestId, "type": reqeust_type, "code": 0, + "message": "success", "result": result} + except Exception as e: + response = {"requestId": requestId, "type": reqeust_type, "code": -1, "message": str(e)} + else: + response = {"requestId": requestId, "type": reqeust_type, "code": -2, + "message": f"Executor {component} not found"} + else: + try: + kwargs = payload.get("params") + if kwargs is None: + kwargs = {} + result = await self.execute_task(**kwargs) + response = {"requestId": requestId, "type": reqeust_type, "code": 0, "message": "success", + "result": result} + except Exception as e: + response = {"requestId": requestId, "type": reqeust_type, "code": -1, "message": str(e)} + + self.client.publish(response_topic, json.dumps(response)) + + def on_connect(self, client, userdata, flags, rc): + if rc == 0: + self.logger.info("Connected to MQTT Broker") + client.subscribe(MQTT_RPC_TOPIC) + else: + self.logger.info("Failed to connect, return code %d\n", rc) + + def on_disconnect(self, client, userdata, rc): + self.logger.info("MQTT disconnect, return code %d\n", rc) + + def on_message(self, client, userdata, message): + asyncio.run(self.handle_message(client, userdata, message)) + + def _stop_components(self): + for componentKey in self.components.keys(): + component = self.components[componentKey] + component.configure(settings) + if component.__class__.component_auto_start: + try: + if component.is_started: + component.stop() + except Exception as e: + self.logger.error(f"{componentKey}停止失败", e) + + def start(self): + # mqtt + mqtt_server = settings.get("mqtt.server", "127.0.0.1") + mqtt_port = settings.get("mqtt.port", 1883) + mqtt_username = settings.get("mqtt.username", "") + mqtt_password = settings.get("mqtt.password", "") + self.logger.info(f"mqtt: {mqtt_server}:{mqtt_port}:{mqtt_username}") + self.client.username_pw_set(mqtt_username, mqtt_password) + self.client.connect(mqtt_server, mqtt_port, 60) + # self.client.loop_start() + # 在单独的事件循环中启动 TCP 服务器和 FastAPI 服务器 + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) + tasks = [] + # tcp server + tcp_enable = settings.get("tcp.enable", False) + if tcp_enable: + tasks.append(self.loop.create_task(self.start_tcp_server())) + # 启动 FastAPI 服务器 + tasks.append(self.loop.create_task(self.server.serve())) + self.logger.info('edge context start!') + self.loop.run_until_complete(asyncio.gather(*tasks)) + # 保持事件循环的持续运行 + self.loop.run_forever() + + def stop(self): + self._stop_components() + + self.client.loop_stop() + self.client.disconnect() + + # rest + # 停止 FastAPI 服务器 + asyncio.run(self.stop_rest_server()) + # tcp server + tcp_enable = settings.get("tcp.enable", False) + if tcp_enable: + asyncio.run(self.stop_tcp_server()) + + if self.loop: + self.loop.stop() + # 确保事件循环停止后再关闭 + if not self.loop.is_running(): + self.loop.close() + + self.logger.info("Edge context stopped!") + + async def handle_tcp(self, reader, writer): + addr = writer.get_extra_info('peername') + self.logger.info(f"Connected by {addr}") + self.tcp_clients.add(writer) # 添加客户端连接 + try: + while True: + data = await reader.read(4096) + if not data: + break + + requestId = None + try: + req = json.loads(data.decode('utf-8')) + self.logger.info(f"Received JSON request: {req}") + requestId = req["requestId"] + response = { + "requestId": requestId, + "code": 0, + "message": "success", + } + reqeust_type = req["type"] + if not reqeust_type in ['service', 'task']: + response['type'] = reqeust_type + response['code'] = -1 + response['message'] = 'invalid reqeust type!' + else: + if reqeust_type == 'service': + method = req.get('method') + component = req.get('component') + method = camel_to_snake(method) + if req.get("method") is None: + response = {"requestId": requestId, "type": reqeust_type, "code": -1, + "message": "没有method参数!"} + else: + if component == "" or component is None: + executor = self + else: + executor = self.get_component(component) + if executor: + try: + kwargs = req.get("params") + if kwargs is None: + kwargs = {} + result = await executor.execute(method, **kwargs) + response = {"requestId": requestId, "type": reqeust_type, "code": 0, + "message": "success", "result": result} + except Exception as e: + response = {"requestId": requestId, "type": reqeust_type, "code": -1, + "message": str(e)} + else: + response = {"requestId": requestId, "type": reqeust_type, "code": -2, + "message": f"Executor {component} not found"} + else: + try: + kwargs = req.get("params") + if kwargs is None: + kwargs = {} + result = await self.execute_task(**kwargs) + response = {"requestId": requestId, "type": reqeust_type, "code": 0, + "message": "success", "result": result} + except Exception as e: + response = {"requestId": requestId, "type": reqeust_type, "code": -1, "message": str(e)} + writer.write(json.dumps(response).encode('utf-8')) + await writer.drain() + except json.JSONDecodeError: + self.logger.error("Received invalid JSON") + error_response = json.dumps({"requestId": requestId, "code": -9, "message": "Invalid JSON"}) + writer.write(error_response.encode('utf-8')) + await writer.drain() + break + except asyncio.CancelledError: + self.logger.error(f"Connection with {addr} cancelled.") + finally: + # 移除客户端连接 + self.tcp_clients.remove(writer) + self.logger.info(f"Closing connection with {addr}") + writer.close() + await writer.wait_closed() + + async def start_tcp_server(self): + # Start the server + tcp_port = settings.get("tcp.port", 13000) + self.tcp_server = await asyncio.start_server(self.handle_tcp, '0.0.0.0', tcp_port) + addr = self.tcp_server.sockets[0].getsockname() + self.logger.info(f"Tcp Serving on {addr}") + + # Create a future to keep the server running + async with self.tcp_server: + await self.tcp_server.serve_forever() + + async def broadcast(self, message): + # 向所有连接的客户端发送消息 + for client in self.tcp_clients: + client.write(message.encode()) + await client.drain() + + async def stop_tcp_server(self): + self.logger.info("Stopping tcp server...") + self.tcp_server.close() # Stop accepting new connections + await self.tcp_server.wait_closed() # Wait for the server to close existing connections + for client in self.tcp_clients: + client.close() + await client.wait_closed() + self.tcp_clients.clear() + self.logger.info("Tcp Server stopped.") + + async def stop_rest_server(self): + self.logger.info("Stopping fastapi server...") + self.server.should_exit = True + # await self.server.shutdown() + await asyncio.sleep(5) + self.logger.info("Fastapi Server stopped.") + + @service() + async def execute_task(self, **kwargs): + """ + 执行任务 + :return: + """ + task = EdgeTask(**kwargs) + + async def execute_step(s: EdgeTaskStep): + try: + if s.component is None or s.component == "": + component = self + else: + component = self.get_component(s.component) + res = await component.execute(s.method, **s.params) + return {"status": "success", "result": res} + except Exception as e: + return {"status": "error", "error": str(e)} + + async def execute_steps(steps: List[EdgeTaskStep]): + tasks = [execute_step(sub_step) for sub_step in steps] + return await asyncio.gather(*tasks) + + self.logger.info(f"执行任务{task.name}({task.id})开始") + results = [] + if len(task.steps) > 0: + for step in task.steps: + if isinstance(step, list): + # 如果是 List[EdgeTaskStep],并行执行这些步骤 + result = await execute_steps(step) + else: + # 否则,顺序执行单个步骤 + result = await execute_step(step) + results.append(result) + + self.logger.info(f"执行任务{task.name}({task.id})结束") + return results + + @service() + def list_components(self): + """ + 列出当前的边缘组件 + :return: + """ + return list(self.components.keys()) + + @service() + def list_component_services(self, comp=None): + """ + 列出指定边缘组件的服务函数 + :return: + """ + comp = self if comp is None else self.get_component(comp) + if comp is None: + raise RuntimeError(f"Component {comp} not found") + + methods = inspect.getmembers(comp.__class__, predicate=inspect.isfunction) + services = [] + for name, method in methods: + if hasattr(method, '_component_service') and method._component_service: + services.append(name) + return services diff --git a/core/edge_internal.py b/core/edge_internal.py new file mode 100644 index 0000000..98ec9e9 --- /dev/null +++ b/core/edge_internal.py @@ -0,0 +1,345 @@ +import asyncio +import json +import os +import sqlite3 +import traceback +from sqlite3 import Error +from typing import Any + +from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor +from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.schedulers.base import STATE_RUNNING +from dynaconf.base import Settings + +from core.edge_component import EdgeComponent, action, service +from core.logging import logger + + +ctx = None + +def execute_action(component, method, **args): + try: + comp = ctx.get_component(component) + result = asyncio.run(comp.execute(method, **args)) + logger.info(result) + logger.info(f'执行定时任务【{component}.{method}】') + except Exception as e: + logger.error(f'执行定时任务【{component}.{method}】出错:{str(e)}') + logger.error(traceback.format_exc()) + +def job2dict(job): + return {'id': job.id, 'name': job.name, 'trigger': str(job.trigger), 'next_run_time': job.next_run_time, + 'args': job.args, 'kwargs': job.kwargs} + +# 检查表是否存在 +def check_table_exists(cursor, table_name): + cursor.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';") + return cursor.fetchone() is not None + + +@action("_database", auto_start=True) +class Database(EdgeComponent): + def __init__(self, context): + super().__init__(context) + self.device_info = None + self.sql_path = None + self.db_path = None + + def start(self): + self.logger.info("Database started") + self.init_db() + super().start() + + def configure(self, setting: Settings) -> None: + self.db_path = setting.get("sqlite3.path") + self.sql_path = setting.get("sqlite3.sql") + self.device_info = setting.get("device") + self.logger.info(f"Database configure done.") + + def stop(self): + super().stop() + self.logger.info("Database stopped") + + # 读取SQL文件并执行建表语句 + def init_db(self): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + sql_files = sorted(os.listdir(self.sql_path)) + executed_sqls = [] + cursor = conn.cursor() + if check_table_exists(cursor, 'sys_system'): + self.logger.info(f"Table sys_system already exists, no need to initialize.") + cursor.execute("select file from sys_sql_history order by id") + rows = cursor.fetchall() + executed_sqls = [row[0] for row in rows] + for sql in sql_files: + if not sql in executed_sqls: + sql_file = os.path.join(self.sql_path, sql) + with open(sql_file, 'r') as file: + sql_script = file.read() + cursor.executescript(sql_script) + self.logger.info(f"Execute SQL:{sql_file}") + cursor.execute(f"INSERT INTO sys_sql_history (file) VALUES (?)", (sql,)) + conn.commit() + self.logger.info(f"SQL[{sql_file}] initialized successfully.") + + self.logger.info("Database initialized successfully.") + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + # 系统信息 + @service() + def system_info(self): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("select code, val from sys_system") + rows = cursor.fetchall() + self.logger.info("List system info successfully.") + sys_info = dict(rows) + if self.device_info is not None: + sys_info.update(self.device_info) + return sys_info + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + # 插入用户数据 + @service() + def insert_user(self, username, password): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute( + f"INSERT INTO sys_user (username, password) VALUES (?, ?)", + (username, password) + ) + conn.commit() + self.logger.info("User inserted successfully.") + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + # 根据用户名删除用户 + @service() + def delete_user(self, username): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("DELETE FROM sys_user WHERE username = ?", (username,)) + conn.commit() + self.logger.info(f"User {username} deleted successfully.") + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + @service() + def list_users(self, username=None): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + if username is None: + cursor.execute("SELECT id, username, created_at FROM sys_user order by id") + else: + cursor.execute("SELECT id, username, created_at FROM sys_user WHERE username = ?", (username,)) + rows = cursor.fetchall() + keys = ['id', 'username', 'created_at'] + return [dict(zip(keys, values)) for values in rows] + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + # 根据用户名更新用户密码 + @service() + def update_user(self, username, password): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("UPDATE sys_user set password = ? WHERE username = ?", (password, username,)) + conn.commit() + self.logger.info(f"User {username} update successfully.") + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + # 新增任务记录 + @service() + def add_task(self, name, steps, result=None, state=0, creator='admin'): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + id = cursor.execute( + "insert into sys_task(name, creator, steps, result, state)values(?,?,?,?,?)", + (name, creator, json.dumps(steps), json.dumps(result), state) + ) + conn.commit() + self.logger.info(f"Task {id} insert successfully.") + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + # 列出任务记录 + @service() + def list_tasks(self, id=None): + global conn, cursor + try: + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + if id is None: + cursor.execute("SELECT id, name, creator, steps, result, state, created_at FROM sys_task order by id " + "desc") + else: + cursor.execute("SELECT id, name, creator, steps, result, state, created_at FROM sys_task WHERE id = ?", (id,)) + rows = cursor.fetchall() + keys = ['id', 'name', 'creator', 'steps', 'result', 'state', 'created_at'] + return [dict(zip(keys, values)) for values in rows] + except Error as e: + self.logger.error(f"Error occurred: {e}") + finally: + if cursor: + cursor.close() + if conn: + conn.close() + + +@action("_scheduler", auto_start=True) +class Scheduler(EdgeComponent): + def __init__(self, context): + super().__init__(context) + global ctx + self.scheduler = None + ctx = self.context + + def start(self): + try: + self.scheduler.start() + except Exception as e: + self.logger.error(f'定时器启动失败:{str(e)}') + raise e + super().start() + self.logger.info("Scheduler started") + + def configure(self, setting: Settings) -> None: + db_url = setting.get('sched.db') + job_stores = { + 'default': SQLAlchemyJobStore(url=db_url) + } + executors = { + 'default': ThreadPoolExecutor(setting.get('sched.thread_pool_workers', 10)), + 'processpool': ProcessPoolExecutor(setting.get('sched.process_pool_workers', 3)) + } + job_defaults = { + 'coalesce': setting.get('sched.coalesce', False), + 'max_instances': setting.get('sched.max_instances', 3) + } + self.scheduler = BackgroundScheduler(jobstores=job_stores, executors=executors, job_defaults=job_defaults, daemon=True) + self.logger.info(f"Scheduler configure done.") + + def stop(self): + if self.scheduler is not None and self.scheduler.state == STATE_RUNNING: + self.scheduler.shutdown() + self.logger.info("Scheduler stopped") + super().stop() + + @service() + def add_job(self, schedule_comp: str, schedule_method: str, trigger: str = 'cron', + schedule_kwargs: dict[str, Any] = None, **kwargs) -> None: + """ + 设置定时任务 + :param schedule_comp: + :param schedule_method: + :param trigger: + :param schedule_kwargs: + :param kwargs: + :return: + """ + if not self.scheduler.running: + raise RuntimeError('定时器未启动') + + self.logger.debug(f'设置定时任务:{schedule_comp}.{schedule_method}:{json.dumps(schedule_kwargs)}') + job = self.scheduler.add_job(func=execute_action, args=[schedule_comp, schedule_method], kwargs=schedule_kwargs, + trigger=trigger, **kwargs) + return job.id + + @service() + def get_jobs(self): + if not self.scheduler.running: + raise RuntimeError('定时器未启动') + + return [job2dict(job) for job in self.scheduler.get_jobs()] + + @service() + def get_job(self, job_id): + if not self.scheduler.running: + raise RuntimeError('定时器未启动') + + job = self.scheduler.get_job(job_id=job_id) + return job2dict(job) + + @service() + def pause_job(self, job_id): + if not self.scheduler.running: + raise RuntimeError('定时器未启动') + + job = self.scheduler.pause_job(job_id=job_id) + return job2dict(job) + + @service() + def resume_job(self, job_id): + if not self.scheduler.running: + raise RuntimeError('定时器未启动') + + job = self.scheduler.resume_job(job_id=job_id) + return job2dict(job) + + @service() + def remove_job(self, job_id): + if not self.scheduler.running: + raise RuntimeError('定时器未启动') + + self.scheduler.remove_job(job_id=job_id) + + @service() + def remove_jobs(self): + if not self.scheduler.running: + raise RuntimeError('定时器未启动') + + self.scheduler.remove_all_jobs() + diff --git a/core/edge_logger.py b/core/edge_logger.py new file mode 100644 index 0000000..a3b4147 --- /dev/null +++ b/core/edge_logger.py @@ -0,0 +1,6 @@ +from core.logging import logger + + +class LoggingMixin: + def __init__(self): + self.logger = logger diff --git a/core/edge_routes.py b/core/edge_routes.py new file mode 100644 index 0000000..d6c34cb --- /dev/null +++ b/core/edge_routes.py @@ -0,0 +1,62 @@ +import json + +from starlette.requests import Request +from starlette.responses import JSONResponse + +from core.edge_util import camel_to_snake +from core.config import settings + + +def setup_routes(context, app): + @app.get("/") + async def system_info(): + info = context.get_component("_database").system_info() + return {"code": 0, "message": "success", "result": info} + + @app.get("/components") + async def components(): + return {"code": 0, "message": "success", "result": list(context.components.keys())} + + @app.middleware("http") + async def add_process_time_header(request: Request, call_next): + if request.url.path.startswith("/service"): + req_body = await request.body() + req_body = req_body.decode("utf-8") + if req_body == '': + req_body = "{}" + req = json.loads(req_body) + req.update(request.query_params) + method = req.get('method') + component = req.get('component') + method = camel_to_snake(method) + if req.get("method") is None: + response = {"code": -1, "type": "service", "message": "没有method参数!"} + else: + if component == "" or component is None: + executor = context + else: + executor = context.get_component(component) + if executor: + try: + kwargs = {key: value for key, value in req.items() if key not in ["method", "component"]} + result = await executor.execute(method, **kwargs) + response = {"code": 0, "type": "service", "message": "success", "result": result} + except Exception as e: + response = {"code": -1, "type": "service", "message": str(e)} + else: + response = {"code": -2, "type": "service", "message": f"Executor {component} not found"} + return JSONResponse(response) + elif request.url.path.startswith("/task"): + req_body = await request.body() + req_body = req_body.decode("utf-8") + if req_body == '': + req_body = "{}" + req = json.loads(req_body) + try: + result = await context.execute_task(**req) + response = {"code": 0, "type": "task", "message": "success", "result": result} + except Exception as e: + response = {"code": -1, "type": "task", "message": str(e)} + return JSONResponse(response) + else: + return await call_next(request) \ No newline at end of file diff --git a/core/edge_task.py b/core/edge_task.py new file mode 100644 index 0000000..270c801 --- /dev/null +++ b/core/edge_task.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel +from typing import Union, Dict, Any, List, Optional +from datetime import datetime + + +class EdgeTaskStep(BaseModel): + component: Optional[str] + method: str + params: Dict[str, Any] + + +class EdgeTask(BaseModel): + id: int + name: str + creator: str + createTime: datetime + steps: List[Union[EdgeTaskStep, List[EdgeTaskStep]]] diff --git a/core/edge_util.py b/core/edge_util.py new file mode 100644 index 0000000..7132e5a --- /dev/null +++ b/core/edge_util.py @@ -0,0 +1,9 @@ +import re + + +def camel_to_snake(name): + # 匹配大写字母并在前面添加下划线,然后将结果转换为小写 + s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + # 处理驼峰形式中的连续大写字母 + s2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1) + return s2.lower() \ No newline at end of file diff --git a/core/logging.py b/core/logging.py new file mode 100644 index 0000000..3b66167 --- /dev/null +++ b/core/logging.py @@ -0,0 +1,28 @@ +import logging +import os +from core.config import settings + +def setup_logger(): + log_dir = settings.logging.directory + log_level = settings.logging.level + os.makedirs(log_dir, exist_ok=True) + # 创建并配置全局 logger + logger_name = 'app' + _logger = logging.getLogger('app') + if len(_logger.handlers) == 0: + handler = logging.FileHandler(os.path.join(log_dir, f"{logger_name}.log"), encoding='utf-8') + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + _logger.addHandler(handler) + if settings.get("logging.console", False): + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + _logger.addHandler(console_handler) + _logger.setLevel(getattr(logging, log_level.upper(), logging.DEBUG)) + + # 关闭日志传播,防止重复处理 + _logger.propagate = False + return _logger + +# 程序启动时调用一次 logger 配置 +logger = setup_logger() diff --git a/device/__init__.py b/device/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/device/mv/ImageConvert.py b/device/mv/ImageConvert.py new file mode 100644 index 0000000..2921271 --- /dev/null +++ b/device/mv/ImageConvert.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# coding: utf-8 +''' +Created on 2017-10-26 + +@author: +''' +from ctypes import * + +# 加载ImageConvert库 +ImageConvertdll = None +try: + ImageConvertdll = cdll.LoadLibrary("/opt/HuarayTech/MVviewer/lib/libImageConvert.so") +except Exception as e: + print(e) + +#定义枚举类型 +def enum(**enums): + return type('Enum', (), enums) + +# ImageConvert.h => enum tagIMGCNV_EErr +IMGCNV_EErr = enum( + IMGCNV_SUCCESS = 0, + IMGCNV_ILLEGAL_PARAM = 1, + IMGCNV_ERR_ORDER = 2, + IMGCNV_NO_MEMORY = 3, + IMGCNV_NOT_SUPPORT = 4, + ) + +# ImageConvert.h => struct tagIMGCNV_SOpenParam +class IMGCNV_SOpenParam(Structure): + _fields_ = [ + ('width', c_int), + ('height', c_int), + ('paddingX', c_int), + ('paddingY', c_int), + ('dataSize', c_int), + ('pixelForamt', c_uint), + ] +# ImageConvert.h => enum tagIMGCNV_EBayerDemosaic +IMGCNV_EErr = enum( + IMGCNV_DEMOSAIC_NEAREST_NEIGHBOR = 0, + IMGCNV_DEMOSAIC_BILINEAR = 1, + IMGCNV_DEMOSAIC_EDGE_SENSING = 2, + IMGCNV_DEMOSAIC_NOT_SUPPORT = 255, + ) + +# ImageConvert.h => IMGCNV_ConvertToBGR24(unsigned char* pSrcData, IMGCNV_SOpenParam* pOpenParam, unsigned char* pDstData, int* pDstDataSize) +IMGCNV_ConvertToBGR24 = None if ImageConvertdll is None else ImageConvertdll.IMGCNV_ConvertToBGR24 + +# ImageConvert.h => IMGCNV_ConvertToRGB24(unsigned char* pSrcData, IMGCNV_SOpenParam* pOpenParam, unsigned char* pDstData, int* pDstDataSize) +IMGCNV_ConvertToRGB24 = None if ImageConvertdll is None else ImageConvertdll.IMGCNV_ConvertToRGB24 + +# ImageConvert.h => IMGCNV_ConvertToMono8(unsigned char* pSrcData, IMGCNV_SOpenParam* pOpenParam, unsigned char* pDstData, int* pDstDataSize) +IMGCNV_ConvertToMono8 = None if ImageConvertdll is None else ImageConvertdll.IMGCNV_ConvertToMono8 + +#ImageConvert.h => IMGCNV_ConvertToBGR24_Ex(unsigned char* pSrcData, IMGCNV_SOpenParam* pOpenParam, unsigned char* pDstData, int* pDstDataSize, IMGCNV_EBayerDemosaic eBayerDemosaic) +IMGCNV_ConvertToBGR24_Ex = None if ImageConvertdll is None else ImageConvertdll.IMGCNV_ConvertToBGR24_Ex + +#ImageConvert.h => IMGCNV_ConvertToRGB24_Ex(unsigned char* pSrcData, IMGCNV_SOpenParam* pOpenParam, unsigned char* pDstData, int* pDstDataSize, IMGCNV_EBayerDemosaic eBayerDemosaic) +IMGCNV_ConvertToRGB24_Ex = None if ImageConvertdll is None else ImageConvertdll.IMGCNV_ConvertToRGB24_Ex + +#ImageConvert.h => CALLMETHOD IMGCNV_ConvertToMono8_Ex(unsigned char* pSrcData, IMGCNV_SOpenParam* pOpenParam, unsigned char* pDstData, int* pDstDataSize, IMGCNV_EBayerDemosaic eBayerDemosaic) +IMGCNV_ConvertToMono8_Ex = None if ImageConvertdll is None else ImageConvertdll.IMGCNV_ConvertToMono8_Ex + +#ImageConvert.h => IMGCNV_ConvertToBGRA32_Ex(unsigned char* pSrcData, IMGCNV_SOpenParam* pOpenParam, unsigned char* pDstData, int* pDstDataSize, IMGCNV_EBayerDemosaic eBayerDemosaic) +IMGCNV_ConvertToBGRA32_Ex = None if ImageConvertdll is None else ImageConvertdll.IMGCNV_ConvertToBGRA32_Ex diff --git a/device/mv/MVSDK.py b/device/mv/MVSDK.py new file mode 100644 index 0000000..3010667 --- /dev/null +++ b/device/mv/MVSDK.py @@ -0,0 +1,1309 @@ +#!/usr/bin/env python +# coding: utf-8 +''' +Created on 2017-10-18 + +@author: +''' +from ctypes import * + +#定义枚举类型 +def enum(**enums): + return type('Enum', (), enums) + +#加载SDK动态库 +MVSDKdll = None +try: + MVSDKdll = cdll.LoadLibrary("/opt/HuarayTech/MVviewer/lib/libMVSDK.so") +except Exception as e: + print(e) + +#SDK.h => define 宏定义 +MAX_PARAM_CNT = 1000 +MAX_STRING_LENTH = 256 +MAX_PAYLOAD_TYPE_CNT = 20 + +GVSP_PIX_MONO = 0x01000000 +GVSP_PIX_RGB = 0x02000000 +GVSP_PIX_COLOR = 0x02000000 +GVSP_PIX_CUSTOM = 0x80000000 +GVSP_PIX_COLOR_MASK = 0xFF000000 + +GVSP_PIX_OCCUPY1BIT = 0x00010000 +GVSP_PIX_OCCUPY2BIT = 0x00020000 +GVSP_PIX_OCCUPY4BIT = 0x00040000 +GVSP_PIX_OCCUPY8BIT = 0x00080000 +GVSP_PIX_OCCUPY12BIT = 0x000C0000 +GVSP_PIX_OCCUPY16BIT = 0x00100000 +GVSP_PIX_OCCUPY24BIT = 0x00180000 +GVSP_PIX_OCCUPY32BIT = 0x00200000 +GVSP_PIX_OCCUPY36BIT = 0x00240000 +GVSP_PIX_OCCUPY48BIT = 0x00300000 +GVSP_PIX_EFFECTIVE_PIXEL_SIZE_MASK = 0x00FF0000 +GVSP_PIX_EFFECTIVE_PIXEL_SIZE_SHIFT = 16 + +GVSP_PIX_ID_MASK = 0x0000FFFF +GVSP_PIX_COUNT = 0x46 + +MAX_ATTR_NAME_LEN = 1024 + + +#SDK.h => enum EPixelType +EPixelType = enum( + pixelTypeUndefined = -1, + gvspPixelMono1p = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY1BIT | 0x0037), + gvspPixelMono2p = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY2BIT | 0x0038), + gvspPixelMono4p = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY4BIT | 0x0039), + gvspPixelMono8 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY8BIT | 0x0001), + gvspPixelMono8S = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY8BIT | 0x0002), + gvspPixelMono10 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0003), + gvspPixelMono10Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x0004), + gvspPixelMono12 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0005), + gvspPixelMono12Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x0006), + gvspPixelMono14 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0025), + gvspPixelMono16 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0007), + + # Bayer Format + gvspPixelBayGR8 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY8BIT | 0x0008), + gvspPixelBayRG8 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY8BIT | 0x0009), + gvspPixelBayGB8 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY8BIT | 0x000A), + gvspPixelBayBG8 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY8BIT | 0x000B), + gvspPixelBayGR10 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x000C), + gvspPixelBayRG10 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x000D), + gvspPixelBayGB10 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x000E), + gvspPixelBayBG10 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x000F), + gvspPixelBayGR12 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0010), + gvspPixelBayRG12 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0011), + gvspPixelBayGB12 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0012), + gvspPixelBayBG12 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0013), + gvspPixelBayGR10Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x0026), + gvspPixelBayRG10Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x0027), + gvspPixelBayGB10Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x0028), + gvspPixelBayBG10Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x0029), + gvspPixelBayGR12Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x002A), + gvspPixelBayRG12Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x002B), + gvspPixelBayGB12Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x002C), + gvspPixelBayBG12Packed = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY12BIT | 0x002D), + gvspPixelBayGR16 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x002E), + gvspPixelBayRG16 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x002F), + gvspPixelBayGB16 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0030), + gvspPixelBayBG16 = (GVSP_PIX_MONO | GVSP_PIX_OCCUPY16BIT | 0x0031), + + # RGB Format + gvspPixelRGB8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY24BIT | 0x0014), + gvspPixelBGR8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY24BIT | 0x0015), + gvspPixelRGBA8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY32BIT | 0x0016), + gvspPixelBGRA8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY32BIT | 0x0017), + gvspPixelRGB10 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x0018), + gvspPixelBGR10 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x0019), + gvspPixelRGB12 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x001A), + gvspPixelBGR12 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x001B), + gvspPixelRGB16 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x0033), + gvspPixelRGB10V1Packed = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY32BIT | 0x001C), + gvspPixelRGB10P32 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY32BIT | 0x001D), + gvspPixelRGB12V1Packed = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY36BIT | 0X0034), + gvspPixelRGB565P = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x0035), + gvspPixelBGR565P = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0X0036), + + # YVR Format + gvspPixelYUV411_8_UYYVYY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY12BIT | 0x001E), + gvspPixelYUV422_8_UYVY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x001F), + gvspPixelYUV422_8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x0032), + gvspPixelYUV8_UYV = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY24BIT | 0x0020), + gvspPixelYCbCr8CbYCr = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY24BIT | 0x003A), + gvspPixelYCbCr422_8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x003B), + gvspPixelYCbCr422_8_CbYCrY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x0043), + gvspPixelYCbCr411_8_CbYYCrYY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY12BIT | 0x003C), + gvspPixelYCbCr601_8_CbYCr = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY24BIT | 0x003D), + gvspPixelYCbCr601_422_8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x003E), + gvspPixelYCbCr601_422_8_CbYCrY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x0044), + gvspPixelYCbCr601_411_8_CbYYCrYY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY12BIT | 0x003F), + gvspPixelYCbCr709_8_CbYCr = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY24BIT | 0x0040), + gvspPixelYCbCr709_422_8 = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x0041), + gvspPixelYCbCr709_422_8_CbYCrY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY16BIT | 0x0045), + gvspPixelYCbCr709_411_8_CbYYCrYY = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY12BIT | 0x0042), + + # RGB Planar + gvspPixelRGB8Planar = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY24BIT | 0x0021), + gvspPixelRGB10Planar = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x0022), + gvspPixelRGB12Planar = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x0023), + gvspPixelRGB16Planar = (GVSP_PIX_COLOR | GVSP_PIX_OCCUPY48BIT | 0x0024), + + # BayerRG10p和BayerRG12p格式,针对特定项目临时添加,请不要使用 + gvspPixelBayRG10p = 0x010A0058, + gvspPixelBayRG12p = 0x010c0059, + + # mono1c格式,自定义格式 + gvspPixelMono1c = 0x012000FF, + + # mono1e格式,自定义格式,用来显示连通域 + gvspPixelMono1e = 0x01080FFF + ) + +#SDK.h => enum GENICAM_ECameraAccessPermission +GENICAM_ECameraAccessPermission = enum( + accessPermissionOpen = 0, + accessPermissionExclusive = 1, + accessPermissionControl = 2, + accessPermissionControlWithSwitchover = 3, + accessPermissionUnknown = 254, + accessPermissionUndefined = 255, + ) + +#SDK.h => enum GENICAM_EProtocolType +GENICAM_EProtocolType = enum( + typeGigE = 0, + typeUsb3 = 1, + typeCL = 2, + typePCIe = 3, + typeAll = 255 + ) + +#SDK.h => struct GENICAM_Camera +class GENICAM_Camera(Structure): + pass + +GENICAM_Camera_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera)) #返回值 参数1 参数2 参数3 ...... +GENICAM_Camera_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera)) +GENICAM_Camera_getType = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera)) +GENICAM_Camera_getName = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_getKey = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_connect = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera), c_int) +GENICAM_Camera_disConnect = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera)) +GENICAM_Camera_isConnect = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera)) +GENICAM_Camera_getInterfaceName = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_getInterfaceType = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera)) +GENICAM_Camera_downLoadGenICamXML = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Camera), c_char_p) +GENICAM_Camera_getVendorName = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_getModelName = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_getSerialNumber = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_getDeviceVersion = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_getManufactureInfo = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_Camera)) +GENICAM_Camera_saveDeviceCfg = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Camera),c_char_p) +GENICAM_Camera_loadDeviceCfg = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Camera),c_char_p,POINTER(c_char*MAX_STRING_LENTH),POINTER(c_uint)) +GENICAM_Camera_writeUARTData = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Camera),c_void_p,c_uint) +GENICAM_Camera_readUARTData=eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Camera),c_void_p,c_uint) +GENICAM_Camera._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_Camera_addRef), + ('release', GENICAM_Camera_release), + ('getType', GENICAM_Camera_getType), + ('getName', GENICAM_Camera_getName), + ('getKey', GENICAM_Camera_getKey), + ('connect', GENICAM_Camera_connect), + ('disConnect', GENICAM_Camera_disConnect), + ('isConnect', GENICAM_Camera_isConnect), + ('getInterfaceName', GENICAM_Camera_getInterfaceName,), + ('getInterfaceType', GENICAM_Camera_getInterfaceType), + ('downLoadGenICamXML', GENICAM_Camera_downLoadGenICamXML), + ('getVendorName', GENICAM_Camera_getVendorName), + ('getModelName', GENICAM_Camera_getModelName), + ('getSerialNumber', GENICAM_Camera_getSerialNumber), + ('getDeviceVersion', GENICAM_Camera_getDeviceVersion), + ('getManufactureInfo', GENICAM_Camera_getManufactureInfo), + ('saveDeviceCfg',GENICAM_Camera_saveDeviceCfg), + ('loadDeviceCfg',GENICAM_Camera_loadDeviceCfg), + ('writeUARTData',GENICAM_Camera_writeUARTData), + ('readUARTData',GENICAM_Camera_readUARTData), + ('reserved', c_uint * 11), + ] +#SDK.h => struct GigeDeviceInfo +class GENICAM_GigeDeviceInfo(Structure): + _fields_ =[ + ('eAccessStatus',c_int), + ('nreserved', c_uint * 7), + ('strMACAddr',c_char*MAX_STRING_LENTH), + ('strIPAddr',c_char*MAX_STRING_LENTH), + ('strSubnetMask',c_char*MAX_STRING_LENTH), + ('strDefaultGateWay', c_char * MAX_STRING_LENTH), + ('strProtocolVersion', c_char * MAX_STRING_LENTH), + ('strIPConfiguration', c_char * MAX_STRING_LENTH), + ('strReserved', c_char * MAX_STRING_LENTH*10), + ] +#SDK.h => struct UsbDeviceInfo +class GENICAM_UsbDeviceInfo(Structure): + _fields_ = [ + ('bLowSpeedSupported',c_int), + ('bFullSpeedSupported', c_int), + ('bHighSpeedSupported', c_int), + ('bSuperSpeedSupported', c_int), + ('bDriverInstalled', c_int), + ('nreserved', c_uint * 3), + ('strConfigurationValid', c_char * MAX_STRING_LENTH), + ('strGenCPVersion', c_char * MAX_STRING_LENTH), + ('strU3VVersion', c_char * MAX_STRING_LENTH), + ('strDeviceGUID', c_char * MAX_STRING_LENTH), + ('strFamilyName', c_char * MAX_STRING_LENTH), + ('strU3VSerialNumber', c_char * MAX_STRING_LENTH), + ('strSpeed', c_char * MAX_STRING_LENTH), + ('strMaxPower', c_char * MAX_STRING_LENTH), + ('strReserved', c_char * MAX_STRING_LENTH*8), + + ] +class DeviceSpecificInfo(Union): + _anonymous_=('G','U') + _fields_ = [ + ('U',GENICAM_UsbDeviceInfo), + ('G',GENICAM_GigeDeviceInfo), + ] +class GENICAM_DeviceInfo (Structure): + _anonymous_=('u') + _fields_ = [ + ('nType', c_int), + ('reserved', c_uint * 7),##reserve + ('strKey', c_char * MAX_STRING_LENTH), + ('strUserDefinedName', c_char * MAX_STRING_LENTH), + ('strSerialNumber', c_char * MAX_STRING_LENTH), + ('strVendor', c_char * MAX_STRING_LENTH), + ('strModel', c_char * MAX_STRING_LENTH), + ('strManufactureInfo', c_char * MAX_STRING_LENTH), + ('strDeviceVersion', c_char * MAX_STRING_LENTH), + ('strReserved', c_char * MAX_STRING_LENTH*9), + ('u',DeviceSpecificInfo), + ] +#SDK.h => struct GENICAM_System +class GENICAM_System(Structure): + pass + +GENICAM_System_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_System)) +GENICAM_System_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_System)) +GENICAM_System_discovery = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_System), POINTER(POINTER(GENICAM_Camera)), \ + POINTER(c_uint), c_int) +GENICAM_System_getCamera = eval('CFUNCTYPE')(POINTER(GENICAM_Camera), POINTER(GENICAM_System), c_char_p) +GENICAM_System_getCameraByDeviceUserID = eval('CFUNCTYPE')(POINTER(GENICAM_Camera), POINTER(GENICAM_System), c_char_p) +GENICAM_System_getCameraByIP = eval('CFUNCTYPE')(POINTER(GENICAM_Camera), POINTER(GENICAM_System), c_char_p) +GENICAM_System_getVersion = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_System)) +GENICAM_System_enumDevicesInfo = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_System), POINTER(POINTER(GENICAM_DeviceInfo)), \ + POINTER(c_uint), c_int) +GENICAM_System_createDevice = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_System), POINTER(GENICAM_DeviceInfo), \ + POINTER(POINTER(GENICAM_Camera))) +GENICAM_System._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_System_addRef), + ('release', GENICAM_System_release), + ('discovery', GENICAM_System_discovery), + ('getCamera', GENICAM_System_getCamera), + ('getCameraByDeviceUserID',GENICAM_System_getCameraByDeviceUserID), + ('getCameraByIP',GENICAM_System_getCameraByIP), + ('getVersion', GENICAM_System_getVersion), + ('enumDevicesInfo',GENICAM_System_enumDevicesInfo), + ('createDevice',GENICAM_System_createDevice), + ('reserved', c_uint * 22), + ] + +#SDK.h => GENICAM_getSystemInstance(GENICAM_System** ppSystem); +GENICAM_getSystemInstance = None if MVSDKdll is None else MVSDKdll.GENICAM_getSystemInstance + +#SDK.h => enum GENICAM_EPayloadType +GENICAM_EPayloadType = enum( + payloadImage = 1, + payloadRawdata = 2, + payloadFile = 3, + payloadChunkData = 4, + payloadExtChunkData = 5, + payloadDevSpecBase = 0x8000, + payloadUndefined = 0x8001, + ) + +#SDK.h => struct GENICAM_Frame +class GENICAM_Frame(Structure): + pass + +GENICAM_Frame_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Frame)) +GENICAM_Frame_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Frame)) +GENICAM_Frame_clone = eval('CFUNCTYPE')(POINTER(GENICAM_Frame), POINTER(GENICAM_Frame)) +GENICAM_Frame_reset = eval('CFUNCTYPE')(None, POINTER(GENICAM_Frame)) +GENICAM_Frame_valid = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Frame)) +GENICAM_Frame_getImage = eval('CFUNCTYPE')(c_void_p, POINTER(GENICAM_Frame)) +GENICAM_Frame_getFrameStatus = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Frame)) +GENICAM_Frame_getImageWidth = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Frame)) +GENICAM_Frame_getImageHeight = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Frame)) +GENICAM_Frame_getImageSize = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Frame)) +GENICAM_Frame_getImagePixelFormat = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Frame)) +GENICAM_Frame_getImageTimeStamp = eval('CFUNCTYPE')(c_ulonglong, POINTER(GENICAM_Frame)) +GENICAM_Frame_getBlockId = eval('CFUNCTYPE')(c_ulonglong, POINTER(GENICAM_Frame)) +GENICAM_Frame_getPayLoadTypes = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Frame), c_int * MAX_PAYLOAD_TYPE_CNT, POINTER(c_uint)) +GENICAM_Frame_getChunkCount = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Frame)) +GENICAM_Frame_getChunkDataByIndex = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_Frame), c_uint, POINTER(c_uint), \ + c_char* MAX_STRING_LENTH * MAX_PARAM_CNT, POINTER(c_uint)) +GENICAM_Frame_getImagePaddingX = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Frame)) +GENICAM_Frame_getImagePaddingY = eval('CFUNCTYPE')(c_uint, POINTER(GENICAM_Frame)) + +GENICAM_Frame._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_Frame_addRef), + ('release', GENICAM_Frame_release), + ('clone', GENICAM_Frame_clone), + ('reset', GENICAM_Frame_reset), + ('valid', GENICAM_Frame_valid), + ('getImage', GENICAM_Frame_getImage), + ('getFrameStatus', GENICAM_Frame_getFrameStatus), + ('getImageWidth', GENICAM_Frame_getImageWidth), + ('getImageHeight', GENICAM_Frame_getImageHeight), + ('getImageSize', GENICAM_Frame_getImageSize), + ('getImagePixelFormat', GENICAM_Frame_getImagePixelFormat), + ('getImageTimeStamp', GENICAM_Frame_getImageTimeStamp), + ('getBlockId', GENICAM_Frame_getBlockId), + ('getPayLoadTypes', GENICAM_Frame_getPayLoadTypes), + ('getChunkCount', GENICAM_Frame_getChunkCount), + ('getChunkDataByIndex', GENICAM_Frame_getChunkDataByIndex), + ('getImagePaddingX', GENICAM_Frame_getImagePaddingX), + ('getImagePaddingY', GENICAM_Frame_getImagePaddingY), + ('reserved', c_uint * 13), + ] + + +#SDK.h => enum GENICAM_EGrabStrategy +GENICAM_EGrabStrategy = enum( + grabStrartegySequential = 0, + grabStrartegyLatestImage = 1, + grabStrartegyUpcomingImage = 2, + grabStrartegyUndefined = 3, + ) + +#SDK.h => void(*callbackFun)(GENICAM_Frame* pFrame) 回调函数原型 +callbackFunc = eval('CFUNCTYPE')(None, POINTER(GENICAM_Frame)) + +#SDK.h => void(*callbackFunEx)(GENICAM_Frame* pFrame, void* pUser); +callbackFuncEx = eval('CFUNCTYPE')(None, POINTER(GENICAM_Frame), c_void_p) + +#SDK.h => struct GENICAM_PCIEStreamStatsInfo +class GENICAM_PCIEStreamStatsInfo(Structure): + _fields_ =[ + ('imageError',c_uint), + ('lostPacketBlock', c_uint), + ('overrideBlock',c_uint), + ('reserved0', c_uint * 9), + ('imageReceived',c_uint), + ('fps',c_double), + ('bandwidth', c_double), + ('reserved', c_uint * 8), + + ] + +#SDK.h => struct GENICAM_U3VStreamStatsInfo +class GENICAM_U3VStreamStatsInfo(Structure): + _fields_ =[ + ('imageError',c_uint), + ('lostPacketBlock', c_uint), + ('overrideBlock',c_uint), + ('reserved0', c_uint * 9), + ('imageReceived',c_uint), + ('fps',c_double), + ('bandwidth', c_double), + ('reserved', c_uint * 8), + + ] + +#SDK.h => struct GENICAM_GigEStreamStatsInfo +class GENICAM_GigEStreamStatsInfo(Structure): + _fields_ =[ + ('reserved0', c_uint * 10), + ('imageError',c_uint), + ('lostPacketBlock', c_uint), + ('overrideBlock',c_uint), + ('reserved1', c_uint * 3), + ('reserved2', c_uint * 5), + ('imageReceived',c_uint), + ('fps',c_double), + ('bandwidth', c_double), + ('reserved', c_uint * 4), + + ] + +#SDK.h => struct GENICAM_StreamStatisticsInfo +class StreamStatisticsInfo(Union): + _anonymous_=('P','U','G') + _fields_ = [ + ('P',GENICAM_PCIEStreamStatsInfo), + ('U',GENICAM_U3VStreamStatsInfo), + ('G',GENICAM_GigEStreamStatsInfo), + ] +class GENICAM_StreamStatisticsInfo (Structure): + _anonymous_=('u') + _fields_ = [ + ('nCameraType', c_uint), + ('u',StreamStatisticsInfo), + ] + + +#SDK.h => struct GENICAM_StreamSource +class GENICAM_StreamSource(Structure): + pass + +GENICAM_StreamSource_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource)) +GENICAM_StreamSource_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource)) +GENICAM_StreamSource_startGrabbing = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), c_ulonglong, c_int) +GENICAM_StreamSource_stopGrabbing = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource)) +GENICAM_StreamSource_isGrabbing = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource)) +GENICAM_StreamSource_getFrame = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), POINTER(POINTER(GENICAM_Frame)), c_uint) +GENICAM_StreamSource_attachGrabbing = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), callbackFunc) +GENICAM_StreamSource_detachGrabbing = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), callbackFunc) +GENICAM_StreamSource_setBufferCount = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), c_uint) +GENICAM_StreamSource_attachGrabbingEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), callbackFuncEx, c_void_p) +GENICAM_StreamSource_detachGrabbingEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), callbackFuncEx, c_void_p) +GENICAM_StreamSource_setInterPacketTimeout= eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), c_uint) +GENICAM_StreamSource_setSingleResendMaxPacketNum= eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), c_uint) +GENICAM_StreamSource_setMaxLostPacketNum= eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), c_uint) +GENICAM_StreamSource_getStatisticsInfo= eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource), POINTER(POINTER(GENICAM_StreamStatisticsInfo))) +GENICAM_StreamSource_resetStatisticsInfo= eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StreamSource)) +GENICAM_StreamSource._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_StreamSource_addRef), + ('release', GENICAM_StreamSource_release), + ('startGrabbing', GENICAM_StreamSource_startGrabbing), + ('stopGrabbing', GENICAM_StreamSource_stopGrabbing), + ('isGrabbing', GENICAM_StreamSource_isGrabbing), + ('getFrame', GENICAM_StreamSource_getFrame), + ('attachGrabbing', GENICAM_StreamSource_attachGrabbing), + ('detachGrabbing', GENICAM_StreamSource_detachGrabbing), + ('setBufferCount', GENICAM_StreamSource_setBufferCount), + ('attachGrabbingEx', GENICAM_StreamSource_attachGrabbingEx), + ('detachGrabbingEx', GENICAM_StreamSource_detachGrabbingEx), + ('setInterPacketTimeout', GENICAM_StreamSource_setInterPacketTimeout), + ('setSingleResendMaxPacketNum',GENICAM_StreamSource_setSingleResendMaxPacketNum), + ('setMaxLostPacketNum',GENICAM_StreamSource_setMaxLostPacketNum), + ('getStatisticsInfo',GENICAM_StreamSource_getStatisticsInfo), + ('resetStatisticsInfo',GENICAM_StreamSource_resetStatisticsInfo), + ('reserved', c_uint * 15), + ] + +#SDK.h => struct GENICAM_StreamSourceInfo +class GENICAM_StreamSourceInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('channeId', c_uint), + ('reserved', c_uint), + ] + +#SDK.h => GENICAM_createStreamSource(const GENICAM_StreamSourceInfo* pStreamSourceInfo, GENICAM_StreamSource** ppStreamSource); +GENICAM_createStreamSource = None if MVSDKdll is None else MVSDKdll.GENICAM_createStreamSource + +#SDK.h => EVType +EVType = enum( + offLine = 0, + onLine = 1, + ) + +#SDK.h => struct GENICAM_SConnectArg +class GENICAM_SConnectArg(Structure): + _fields_ = [ + ('m_event', c_int), + ('reserve', c_uint * 15), + ] + +#SDK.h => struct GENICAM_SParamUpdataArg +class GENICAM_SParamUpdataArg(Structure): + _fields_ = [ + ('isPoll', c_int), + ('reserve', c_uint * 10), + ('paramNames', c_char * MAX_STRING_LENTH * MAX_PARAM_CNT), + ('referenceParamCnt', c_uint), + ] + +#SDK.h => enum GENICAM_EEventStatus +GENICAM_EEventStatus = enum( + streamEventNormal = 1, + streamEventLostFrame = 2, + streamEventLostPacket = 3, + streamEventImageError = 4, + streamEventStreamChannelError = 5, + ) + +#SDK.h => struct GENICAM_SStreamArg +class GENICAM_SStreamArg(Structure): + _fields_ = [ + ('channel', c_uint), + ('blockID', c_ulonglong), + ('timestamp', c_ulonglong), + ('eStreamEventStatus', c_int), + ('status', c_uint), + ('reserve', c_uint), + ] + +#SDK => 宏定义 消息通道事件ID列表 +MSG_EVENT_ID_EXPOSURE_END = 0x9001 +MSG_EVENT_ID_FRAME_TRIGGER = 0x9002 +MSG_EVENT_ID_FRAME_START = 0x9003 +MSG_EVENT_ID_ACQ_START = 0x9004 +MSG_EVENT_ID_ACQ_TRIGGER = 0x9005 +MSG_EVENT_ID_DATA_READ_OUT = 0x9006 + +#SDK.h => struct GENICAM_SMsgChannelArg +class GENICAM_SMsgChannelArg(Structure): + _fields_ = [ + ('eventID', c_ushort), + ('channelID', c_ushort), + ('blockID', c_ulonglong), + ('timeStamp', c_ulonglong), + ('reserve', c_uint * 8), + ('paramNames', c_char * MAX_STRING_LENTH * MAX_PARAM_CNT), + ('referenceParamCnt', c_uint), + ] + +#SDK.h => void (*connectCallBack)(const GENICAM_SConnectArg* pConnectArg) +connectCallBack = eval('CFUNCTYPE')(None, POINTER(GENICAM_SConnectArg)) + +#SDK.h => void (*connectCallBackEx)(const GENICAM_SConnectArg* pConnectArg, void* pUser) +connectCallBackEx = eval('CFUNCTYPE')(None, POINTER(GENICAM_SConnectArg), c_void_p) + +#SDK.h => void (*paramUpdateCallBack)(const GENICAM_SParamUpdataArg* pParamUpdateArg) +paramUpdateCallBack = eval('CFUNCTYPE')(None, POINTER(GENICAM_SParamUpdataArg)) + +#SDK.h => void (*paramUpdateCallBackEx)(const GENICAM_SParamUpdataArg* pParamUpdateArg, void* pUser) +paramUpdateCallBackEx = eval('CFUNCTYPE')(None, POINTER(GENICAM_SParamUpdataArg), c_void_p) + +#SDK.h => void (*streamCallBack)(const GENICAM_SStreamArg* pStreamArg) +streamCallBack = eval('CFUNCTYPE')(None, POINTER(GENICAM_SStreamArg)) + +#SDK.h => void (*streamCallBackEx)(const GENICAM_SStreamArg* pStreamArg, void *pUser) +streamCallBackEx = eval('CFUNCTYPE')(None, POINTER(GENICAM_SStreamArg), c_void_p) + +#SDK.h => void (*msgChannelCallBackEx)(const GENICAM_SMsgChannelArg* pMsgChannelArg, void *pUser) +msgChannelCallBackEx = eval('CFUNCTYPE')(None, POINTER(GENICAM_SMsgChannelArg), c_void_p) + +#SDK.h => struct GENICAM_EventSubscribe +class GENICAM_EventSubscribe(Structure): + pass + +GENICAM_EventSubscribe_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe)) +GENICAM_EventSubscribe_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe)) +GENICAM_EventSubscribe_subscribeConnectArgs = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), connectCallBack) +GENICAM_EventSubscribe_unsubscribeConnectArgs = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), connectCallBack) +GENICAM_EventSubscribe_subscribeParamUpdate = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), paramUpdateCallBack) +GENICAM_EventSubscribe_unsubscribeParamUpdate = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), paramUpdateCallBack) +GENICAM_EventSubscribe_subscribeStreamArg = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), streamCallBack) +GENICAM_EventSubscribe_unsubscribeStreamArg = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), streamCallBack) +GENICAM_EventSubscribe_subscribeConnectArgsEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), connectCallBackEx, c_void_p) +GENICAM_EventSubscribe_unsubscribeConnectArgsEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), connectCallBackEx, c_void_p) +GENICAM_EventSubscribe_subscribeParamUpdateEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), paramUpdateCallBackEx, c_void_p) +GENICAM_EventSubscribe_unsubscribeParamUpdateEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), paramUpdateCallBackEx, c_void_p) +GENICAM_EventSubscribe_subscribeStreamArgEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), streamCallBackEx, c_void_p) +GENICAM_EventSubscribe_unsubscribeStreamArgEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), streamCallBackEx, c_void_p) +GENICAM_EventSubscribe_subscribeMsgChannelEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), msgChannelCallBackEx, c_void_p) +GENICAM_EventSubscribe_unsubscribeMsgChannelEx = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EventSubscribe), msgChannelCallBackEx, c_void_p) + +GENICAM_EventSubscribe._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_EventSubscribe_addRef), + ('release', GENICAM_EventSubscribe_release), + ('subscribeConnectArgs', GENICAM_EventSubscribe_subscribeConnectArgs), + ('unsubscribeConnectArgs', GENICAM_EventSubscribe_unsubscribeConnectArgs), + ('subscribeParamUpdate', GENICAM_EventSubscribe_subscribeParamUpdate), + ('unsubscribeParamUpdate', GENICAM_EventSubscribe_unsubscribeParamUpdate), + ('subscribeStreamArg', GENICAM_EventSubscribe_subscribeStreamArg), + ('unsubscribeStreamArg', GENICAM_EventSubscribe_unsubscribeStreamArg), + ('subscribeConnectArgsEx', GENICAM_EventSubscribe_subscribeConnectArgsEx), + ('unsubscribeConnectArgsEx', GENICAM_EventSubscribe_unsubscribeConnectArgsEx), + ('subscribeParamUpdateEx', GENICAM_EventSubscribe_subscribeParamUpdateEx), + ('unsubscribeParamUpdateEx', GENICAM_EventSubscribe_unsubscribeParamUpdateEx), + ('subscribeStreamArgEx', GENICAM_EventSubscribe_subscribeStreamArgEx), + ('unsubscribeStreamArgEx', GENICAM_EventSubscribe_unsubscribeStreamArgEx), + ('subscribeMsgChannelEx', GENICAM_EventSubscribe_subscribeMsgChannelEx), + ('unsubscribeMsgChannelEx', GENICAM_EventSubscribe_unsubscribeMsgChannelEx), + ('reserve', c_uint * 15), + ] + +#SDK.h => struct GENICAM_EventSubscribeInfo +class GENICAM_EventSubscribeInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createEventSubscribe(const GENICAM_EventSubscribeInfo* pEventSubscribeInfo, GENICAM_EventSubscribe** ppEventSubscribe) +GENICAM_createEventSubscribe = None if MVSDKdll is None else MVSDKdll.GENICAM_createEventSubscribe + +#SDK.h => struct GENICAM_GigECamera +class GENICAM_GigECamera(Structure): + pass + +GENICAM_GigECamera_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_getIpAddress = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_getSubnetMask = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_getGateway = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_getMacAddress = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_forceIpAddress = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_GigECamera), c_char_p, c_char_p, c_char_p) +GENICAM_GigECamera_getAccessPermission = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_getProtocolVersion = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigECamera)) +GENICAM_GigECamera_getIPConfiguration = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigECamera)) + +GENICAM_GigECamera._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_GigECamera_addRef), + ('release', GENICAM_GigECamera_release), + ('getIpAddress', GENICAM_GigECamera_getIpAddress), + ('getSubnetMask', GENICAM_GigECamera_getSubnetMask), + ('getGateway', GENICAM_GigECamera_getGateway), + ('getMacAddress', GENICAM_GigECamera_getMacAddress), + ('forceIpAddress', GENICAM_GigECamera_forceIpAddress), + ('getAccessPermission', GENICAM_GigECamera_getAccessPermission), + ('getProtocolVersion', GENICAM_GigECamera_getProtocolVersion), + ('getIPConfiguration', GENICAM_GigECamera_getIPConfiguration), + ('reserve', c_uint * 21), + ] + +#SDK.h => struct GENICAM_GigECameraInfo +class GENICAM_GigECameraInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => createGigECamera(GENICAM_GigECameraInfo* pGigECameraInfo, GENICAM_GigECamera** ppGigECamera) +GENICAM_createGigECamera = None if MVSDKdll is None else MVSDKdll.GENICAM_createGigECamera + +#SDK.h => struct GENICAM_GigEInterface +class GENICAM_GigEInterface(Structure): + pass + +GENICAM_GigEInterface_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_GigEInterface)) +GENICAM_GigEInterface_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_GigEInterface)) +GENICAM_GigEInterface_getDescription = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigEInterface)) +GENICAM_GigEInterface_getIpAddress = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigEInterface)) +GENICAM_GigEInterface_getSubnetMask = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigEInterface)) +GENICAM_GigEInterface_getGateway = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigEInterface)) +GENICAM_GigEInterface_getMacAddress = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_GigEInterface)) + +GENICAM_GigEInterface._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_GigEInterface_addRef), + ('release', GENICAM_GigEInterface_release), + ('getDescription', GENICAM_GigEInterface_getDescription), + ('getIpAddress', GENICAM_GigEInterface_getIpAddress), + ('getSubnetMask', GENICAM_GigEInterface_getSubnetMask), + ('getGateway', GENICAM_GigEInterface_getGateway), + ('getMacAddress', GENICAM_GigEInterface_getMacAddress), + ('reserve', c_uint * 24), + ] + +#SDK.h => struct GENICAM_GigEInterfaceInfo +class GENICAM_GigEInterfaceInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createGigEInterface(GENICAM_GigEInterfaceInfo*pGigEInterfaceInfo, GENICAM_GigEInterface** ppGigEInterface) +GENICAM_createGigEInterface = None if MVSDKdll is None else MVSDKdll.GENICAM_createGigEInterface + +#SDK.h => struct GENICAM_UsbCamera +class GENICAM_UsbCamera(Structure): + pass + +GENICAM_UsbCamera_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getConfigurationValid = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getGenCPVersion = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getU3VVersion = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getDeviceGUID = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getFamilyName = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getU3VSerialNumber = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_isLowSpeedSupported = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_isFullSpeedSupported = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_isHighSpeedSupported = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_isSuperSpeedSupported = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getSpeed = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_getMaxPower = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbCamera)) +GENICAM_UsbCamera_isDriverInstalled = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbCamera)) + +GENICAM_UsbCamera._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_UsbCamera_addRef), + ('release', GENICAM_UsbCamera_release), + ('getConfigurationValid', GENICAM_UsbCamera_getConfigurationValid), + ('getGenCPVersion', GENICAM_UsbCamera_getGenCPVersion), + ('getU3VVersion', GENICAM_UsbCamera_getU3VVersion), + ('getDeviceGUID', GENICAM_UsbCamera_getDeviceGUID), + ('getFamilyName', GENICAM_UsbCamera_getFamilyName), + ('getU3VSerialNumber', GENICAM_UsbCamera_getU3VSerialNumber), + ('isLowSpeedSupported', GENICAM_UsbCamera_isLowSpeedSupported), + ('isFullSpeedSupported', GENICAM_UsbCamera_isFullSpeedSupported), + ('isHighSpeedSupported', GENICAM_UsbCamera_isHighSpeedSupported), + ('isSuperSpeedSupported', GENICAM_UsbCamera_isSuperSpeedSupported), + ('getSpeed', GENICAM_UsbCamera_getSpeed), + ('getMaxPower', GENICAM_UsbCamera_getMaxPower), + ('isDriverInstalled', GENICAM_UsbCamera_isDriverInstalled), + ('reserve', c_uint * 16), + ] + +#SDK.h => struct GENICAM_UsbCameraInfo +class GENICAM_UsbCameraInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createUsbCamera(GENICAM_UsbCameraInfo* pUsbCameraInfo, GENICAM_UsbCamera** ppUsbCamera); +GENICAM_createUsbCamera = None if MVSDKdll is None else MVSDKdll.GENICAM_createUsbCamera + +#SDK.h => struct GENICAM_UsbInterface +class GENICAM_UsbInterface(Structure): + pass + +GENICAM_UsbInterface_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbInterface)) +GENICAM_UsbInterface_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UsbInterface)) +GENICAM_UsbInterface_getDescription = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbInterface)) +GENICAM_UsbInterface_getVendorID = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbInterface)) +GENICAM_UsbInterface_getDeviceID = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbInterface)) +GENICAM_UsbInterface_getSubsystemID = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbInterface)) +GENICAM_UsbInterface_getRevision = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbInterface)) +GENICAM_UsbInterface_getSpeed = eval('CFUNCTYPE')(c_char_p, POINTER(GENICAM_UsbInterface)) + +GENICAM_UsbInterface._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_UsbInterface_addRef), + ('release', GENICAM_UsbInterface_release), + ('getDescription', GENICAM_UsbInterface_getDescription), + ('getVendorID', GENICAM_UsbInterface_getVendorID), + ('getDeviceID', GENICAM_UsbInterface_getDeviceID), + ('getSubsystemID', GENICAM_UsbInterface_getSubsystemID), + ('getRevision', GENICAM_UsbInterface_getRevision), + ('getSpeed', GENICAM_UsbInterface_getSpeed), + ('reserve', c_uint * 23), + ] + +#SDK.h => struct GENICAM_UsbInterfaceInfo +class GENICAM_UsbInterfaceInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createUsbInterface(GENICAM_UsbInterfaceInfo*pUsbInterfaceInfo, GENICAM_UsbInterface** ppUsbInterface); +GENICAM_createUsbInterface = None if MVSDKdll is None else MVSDKdll.GENICAM_createUsbInterface + +#SDK.h => struct GENICAM_IntNode +class GENICAM_IntNode(Structure): + pass + +GENICAM_IntNode_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode)) +GENICAM_IntNode_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode)) +GENICAM_IntNode_getValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode), POINTER(c_longlong)) +GENICAM_IntNode_setValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode), c_longlong) +GENICAM_IntNode_getMinVal = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode), POINTER(c_longlong)) +GENICAM_IntNode_getMaxVal = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode), POINTER(c_longlong)) +GENICAM_IntNode_isValid = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode)) +GENICAM_IntNode_isAvailable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode)) +GENICAM_IntNode_isReadable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode)) +GENICAM_IntNode_isWriteable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode)) +GENICAM_IntNode_getIncrement = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_IntNode), POINTER(c_longlong)) + +GENICAM_IntNode._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_IntNode_addRef), + ('release', GENICAM_IntNode_release), + ('getValue', GENICAM_IntNode_getValue), + ('setValue', GENICAM_IntNode_setValue), + ('getMinVal', GENICAM_IntNode_getMinVal), + ('getMaxVal', GENICAM_IntNode_getMaxVal), + ('isValid', GENICAM_IntNode_isValid), + ('isAvailable', GENICAM_IntNode_isAvailable), + ('isReadable', GENICAM_IntNode_isReadable), + ('isWriteable', GENICAM_IntNode_isWriteable), + ('getIncrement', GENICAM_IntNode_getIncrement), + ('reserve', c_uint * 20), + ] + +#SDK.h => struct GENICAM_IntNodeInfo +class GENICAM_IntNodeInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('attrName', c_char * MAX_ATTR_NAME_LEN), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createIntNode(GENICAM_IntNodeInfo* pIntNodeInfo, GENICAM_IntNode** ppIntNode) +GENICAM_createIntNode = None if MVSDKdll is None else MVSDKdll.GENICAM_createIntNode + +#SDK.h => struct GENICAM_DoubleNode +class GENICAM_DoubleNode(Structure): + pass + +GENICAM_DoubleNode_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode)) +GENICAM_DoubleNode_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode)) +GENICAM_DoubleNode_getValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode), POINTER(c_double)) +GENICAM_DoubleNode_setValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode), c_double) +GENICAM_DoubleNode_getMinVal = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode), POINTER(c_double)) +GENICAM_DoubleNode_getMaxVal = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode), POINTER(c_double)) +GENICAM_DoubleNode_isValid = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode)) +GENICAM_DoubleNode_isAvailable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode)) +GENICAM_DoubleNode_isReadable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode)) +GENICAM_DoubleNode_isWriteable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DoubleNode)) + +GENICAM_DoubleNode._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_DoubleNode_addRef), + ('release', GENICAM_DoubleNode_release), + ('getValue', GENICAM_DoubleNode_getValue), + ('setValue', GENICAM_DoubleNode_setValue), + ('getMinVal', GENICAM_DoubleNode_getMinVal), + ('getMaxVal', GENICAM_DoubleNode_getMaxVal), + ('isValid', GENICAM_DoubleNode_isValid), + ('isAvailable', GENICAM_DoubleNode_isAvailable), + ('isReadable', GENICAM_DoubleNode_isReadable), + ('isWriteable', GENICAM_DoubleNode_isWriteable), + ('reserve', c_uint * 21), + ] + +#SDK.h => struct GENICAM_DoubleNodeInfo +class GENICAM_DoubleNodeInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('attrName', c_char * MAX_ATTR_NAME_LEN), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createDoubleNode(GENICAM_DoubleNodeInfo* pDoubleNodeInfo, GENICAM_DoubleNode** ppDoubleNode) +GENICAM_createDoubleNode = None if MVSDKdll is None else MVSDKdll.GENICAM_createDoubleNode + +#SDK.h => struct GENICAM_EnumNode +class GENICAM_EnumNode(Structure): + pass + +GENICAM_EnumNode_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode)) +GENICAM_EnumNode_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode)) +GENICAM_EnumNode_getValueSymbol = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode), c_char_p, POINTER(c_uint)) +GENICAM_EnumNode_setValueBySymbol = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode), c_char_p) +GENICAM_EnumNode_getEnumSymbolList = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode), POINTER(c_char * 256), POINTER(c_uint)) #??? +GENICAM_EnumNode_isValid = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode)) +GENICAM_EnumNode_isAvailable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode)) +GENICAM_EnumNode_isReadable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode)) +GENICAM_EnumNode_isWriteable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode)) +GENICAM_EnumNode_setValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode),c_uint64) +GENICAM_EnumNode_getValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode),POINTER(c_uint64)) +GENICAM_EnumNode_getValueBySymbol = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode), c_char_p, POINTER(c_uint64)) +GENICAM_EnumNode_getSymbolByValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_EnumNode), c_uint64, c_char_p,POINTER(c_uint)) + +GENICAM_EnumNode._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_EnumNode_addRef), + ('release', GENICAM_EnumNode_release), + ('getValueSymbol', GENICAM_EnumNode_getValueSymbol), + ('setValueBySymbol', GENICAM_EnumNode_setValueBySymbol), + ('getEnumSymbolList', GENICAM_EnumNode_getEnumSymbolList), + ('isValid', GENICAM_EnumNode_isValid), + ('isAvailable', GENICAM_EnumNode_isAvailable), + ('isReadable', GENICAM_EnumNode_isReadable), + ('isWriteable', GENICAM_EnumNode_isWriteable), + ('setValue',GENICAM_EnumNode_setValue), + ('getValue',GENICAM_EnumNode_getValue), + ('getValueBySymbol',GENICAM_EnumNode_getValueBySymbol), + ('getSymbolByValue',GENICAM_EnumNode_getSymbolByValue), + ('reserve', c_uint * 18), + ] + +#SDK.h => struct GENICAM_EnumNodeInfo +class GENICAM_EnumNodeInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('attrName', c_char * MAX_ATTR_NAME_LEN), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createEnumNode(GENICAM_EnumNodeInfo* pEnumNodeInfo, GENICAM_EnumNode** ppEnumNode) +GENICAM_createEnumNode = None if MVSDKdll is None else MVSDKdll.GENICAM_createEnumNode + +#SDK.h => struct GENICAM_BoolNode +class GENICAM_BoolNode(Structure): + pass + +GENICAM_BoolNode_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode)) +GENICAM_BoolNode_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode)) +GENICAM_BoolNode_getValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode), POINTER(c_uint)) +GENICAM_BoolNode_setValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode), c_uint) +GENICAM_BoolNode_isValid = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode)) +GENICAM_BoolNode_isAvailable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode)) +GENICAM_BoolNode_isReadable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode)) +GENICAM_BoolNode_isWriteable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_BoolNode)) + +GENICAM_BoolNode._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_BoolNode_addRef), + ('release', GENICAM_BoolNode_release), + ('getValue', GENICAM_BoolNode_getValue), + ('setValue', GENICAM_BoolNode_setValue), + ('isValid', GENICAM_BoolNode_isValid), + ('isAvailable', GENICAM_BoolNode_isAvailable), + ('isReadable', GENICAM_BoolNode_isReadable), + ('isWriteable', GENICAM_BoolNode_isWriteable), + ('reserve', c_uint * 23), + ] + +#SDK.h => struct GENICAM_BoolNodeInfo +class GENICAM_BoolNodeInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('attrName', c_char * MAX_ATTR_NAME_LEN), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createBoolNode(GENICAM_BoolNodeInfo* pBoolNodeInfo, GENICAM_BoolNode** ppBoolNode) +GENICAM_createBoolNode = None if MVSDKdll is None else MVSDKdll.GENICAM_createBoolNode + +#SDK.h => struct GENICAM_CmdNode +class GENICAM_CmdNode(Structure): + pass + +GENICAM_CmdNode_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_CmdNode)) +GENICAM_CmdNode_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_CmdNode)) +GENICAM_CmdNode_execute = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_CmdNode)) +GENICAM_CmdNode_isValid = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_CmdNode)) +GENICAM_CmdNode_isAvailable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_CmdNode)) +GENICAM_CmdNode_isReadable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_CmdNode)) +GENICAM_CmdNode_isWriteable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_CmdNode)) + +GENICAM_CmdNode._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_CmdNode_addRef), + ('release', GENICAM_CmdNode_release), + ('execute', GENICAM_CmdNode_execute), + ('isValid', GENICAM_CmdNode_isValid), + ('isAvailable', GENICAM_CmdNode_isAvailable), + ('isReadable', GENICAM_CmdNode_isReadable), + ('isWriteable', GENICAM_CmdNode_isWriteable), + ('reserve', c_uint * 24), + ] + +#SDK.h => struct GENICAM_CmdNodeInfo +class GENICAM_CmdNodeInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('attrName', c_char * MAX_ATTR_NAME_LEN), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createCmdNode(GENICAM_CmdNodeInfo* pCmdNodeInfo, GENICAM_CmdNode** ppCmdNode) +GENICAM_createCmdNode = None if MVSDKdll is None else MVSDKdll.GENICAM_createCmdNode + +#SDK.h => struct GENICAM_StringNode +class GENICAM_StringNode(Structure): + pass + +GENICAM_StringNode_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode)) +GENICAM_StringNode_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode)) +GENICAM_StringNode_getValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode), c_char_p, POINTER(c_uint)) +GENICAM_StringNode_setValue = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode), c_char_p) +GENICAM_StringNode_isValid = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode)) +GENICAM_StringNode_isAvailable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode)) +GENICAM_StringNode_isReadable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode)) +GENICAM_StringNode_isWriteable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_StringNode)) + +GENICAM_StringNode._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_StringNode_addRef), + ('release', GENICAM_StringNode_release), + ('getValue', GENICAM_StringNode_getValue), + ('setValue', GENICAM_StringNode_setValue), + ('isValid', GENICAM_StringNode_isValid), + ('isAvailable', GENICAM_StringNode_isAvailable), + ('isReadable', GENICAM_StringNode_isReadable), + ('isWriteable', GENICAM_StringNode_isWriteable), + ('reserve', c_uint * 23), + ] + +#SDK.h => struct GENICAM_StringNodeInfo +class GENICAM_StringNodeInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('attrName', c_char * MAX_ATTR_NAME_LEN), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createStringNode(GENICAM_StringNodeInfo* pStringNodeInfo, GENICAM_StringNode** ppStringNode) +GENICAM_createStringNode = None if MVSDKdll is None else MVSDKdll.GENICAM_createStringNode + +#SDK.h => struct GENICAM_AcquisitionControl +class GENICAM_AcquisitionControl(Structure): + pass + +GENICAM_AcquisitionControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_acquisitionFrameCount = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_acquisitionFrameRate = eval('CFUNCTYPE')(GENICAM_DoubleNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_acquisitionFrameRateEnable = eval('CFUNCTYPE')(GENICAM_BoolNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_acquisitionMode = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_exposureAuto = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_exposureMode = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_exposureTime = eval('CFUNCTYPE')(GENICAM_DoubleNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_triggerActivation = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_triggerDelay = eval('CFUNCTYPE')(GENICAM_DoubleNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_triggerMode = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_triggerSelector = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_triggerSource = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AcquisitionControl)) +GENICAM_AcquisitionControl_triggerSoftware = eval('CFUNCTYPE')(GENICAM_CmdNode, POINTER(GENICAM_AcquisitionControl)) + +GENICAM_AcquisitionControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_AcquisitionControl_addRef), + ('release', GENICAM_AcquisitionControl_release), + ('acquisitionFrameCount', GENICAM_AcquisitionControl_acquisitionFrameCount), + ('acquisitionFrameRate', GENICAM_AcquisitionControl_acquisitionFrameRate), + ('acquisitionFrameRateEnable', GENICAM_AcquisitionControl_acquisitionFrameRateEnable), + ('acquisitionMode', GENICAM_AcquisitionControl_acquisitionMode), + ('exposureAuto', GENICAM_AcquisitionControl_exposureAuto), + ('exposureMode', GENICAM_AcquisitionControl_exposureMode), + ('exposureTime', GENICAM_AcquisitionControl_exposureTime), + ('triggerActivation', GENICAM_AcquisitionControl_triggerActivation), + ('triggerDelay', GENICAM_AcquisitionControl_triggerDelay), + ('triggerMode', GENICAM_AcquisitionControl_triggerMode), + ('triggerSelector', GENICAM_AcquisitionControl_triggerSelector), + ('triggerSource', GENICAM_AcquisitionControl_triggerSource), + ('triggerSoftware', GENICAM_AcquisitionControl_triggerSoftware), + ('reserve', c_uint * 17), + ] + +#SDK.h => struct GENICAM_AcquisitionControlInfo +class GENICAM_AcquisitionControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createAcquisitionControl(GENICAM_AcquisitionControlInfo*pAcquisitionControlInfo, GENICAM_AcquisitionControl** ppAcquisitionControl) +GENICAM_createAcquisitionControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createAcquisitionControl + +#SDK.h => enum GENICAM_EConfigSet +GENICAM_EConfigSet = enum( + userSet1 = 1, + userSet2 = 2, + userSetInvalid = 3, + ) + +#SDK.h => struct GENICAM_UserSetControl +class GENICAM_UserSetControl(Structure): + pass + +GENICAM_UserSetControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UserSetControl)) +GENICAM_UserSetControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UserSetControl)) +GENICAM_UserSetControl_restoreDefault = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UserSetControl)) +GENICAM_UserSetControl_setCurrentUserSet = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UserSetControl), c_int) +GENICAM_UserSetControl_saveUserSet = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UserSetControl), c_int) +GENICAM_UserSetControl_getCurrentUserSet = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UserSetControl)) +GENICAM_UserSetControl_isAvailable = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_UserSetControl)) + +GENICAM_UserSetControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_UserSetControl_addRef), + ('release', GENICAM_UserSetControl_release), + ('restoreDefault', GENICAM_UserSetControl_restoreDefault), + ('setCurrentUserSet', GENICAM_UserSetControl_setCurrentUserSet), + ('saveUserSet', GENICAM_UserSetControl_saveUserSet), + ('getCurrentUserSet', GENICAM_UserSetControl_getCurrentUserSet), + ('isAvailable', GENICAM_UserSetControl_isAvailable), + ('reserve', c_uint * 24), + ] + +#SDK.h => struct GENICAM_UserSetControlInfo +class GENICAM_UserSetControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createUserSetControl(GENICAM_UserSetControlInfo* pUserSetControlInfo, GENICAM_UserSetControl** ppUserSetControl) +GENICAM_createUserSetControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createUserSetControl + +#SDK.h => struct GENICAM_ISPControl +class GENICAM_ISPControl(Structure): + pass + +GENICAM_ISPControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_brightness = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_sharpness = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_sharpnessAuto = eval('CFUNCTYPE')(GENICAM_BoolNode, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_sharpnessEnable = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_contrast = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_hue = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ISPControl)) +GENICAM_ISPControl_saturation = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ISPControl)) + +GENICAM_ISPControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_ISPControl_addRef), + ('release', GENICAM_ISPControl_release), + ('brightness', GENICAM_ISPControl_brightness), + ('sharpness', GENICAM_ISPControl_sharpness), + ('sharpnessAuto', GENICAM_ISPControl_sharpnessAuto), + ('sharpnessEnable', GENICAM_ISPControl_sharpnessEnable), + ('contrast', GENICAM_ISPControl_contrast), + ('hue', GENICAM_ISPControl_hue), + ('saturation', GENICAM_ISPControl_saturation), + ('reserved', c_uint * 22), + ] + +#SDK.h => struct GENICAM_ISPControlInfo +class GENICAM_ISPControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createISPControl(GENICAM_ISPControlInfo* pISPControlInfo, GENICAM_ISPControl** ppISPControl) +GENICAM_createISPControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createISPControl + +#SDK.h => struct GENICAM_AnalogControl +class GENICAM_AnalogControl(Structure): + pass + +GENICAM_AnalogControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_blackLevelSelector = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_blackLevelAuto = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_blackLevel = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_gainAuto = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_gainRaw = eval('CFUNCTYPE')(GENICAM_DoubleNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_gamma = eval('CFUNCTYPE')(GENICAM_DoubleNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_balanceRatioSelector = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_balanceWhiteAuto = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_AnalogControl)) +GENICAM_AnalogControl_balanceRatio = eval('CFUNCTYPE')(GENICAM_DoubleNode, POINTER(GENICAM_AnalogControl)) + +GENICAM_AnalogControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_AnalogControl_addRef), + ('release', GENICAM_AnalogControl_release), + ('blackLevelSelector', GENICAM_AnalogControl_blackLevelSelector), + ('blackLevelAuto', GENICAM_AnalogControl_blackLevelAuto), + ('blackLevel', GENICAM_AnalogControl_blackLevel), + ('gainAuto', GENICAM_AnalogControl_gainAuto), + ('gainRaw', GENICAM_AnalogControl_gainRaw), + ('gamma', GENICAM_AnalogControl_gamma), + ('balanceRatioSelector', GENICAM_AnalogControl_balanceRatioSelector), + ('balanceWhiteAuto', GENICAM_AnalogControl_balanceWhiteAuto), + ('balanceRatio', GENICAM_AnalogControl_balanceRatio), + ('reserve', c_uint * 20), + ] + +#SDK.h => struct GENICAM_AnalogControlInfo +class GENICAM_AnalogControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createAnalogControl(GENICAM_AnalogControlInfo* pAnalogControlInfo, GENICAM_AnalogControl** ppAnalogControl) +GENICAM_createAnalogControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createAnalogControl + +#SDK.h => struct GENICAM_DeviceControl +class GENICAM_DeviceControl(Structure): + pass + +GENICAM_DeviceControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DeviceControl)) +GENICAM_DeviceControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DeviceControl)) +GENICAM_DeviceControl_deviceUserID = eval('CFUNCTYPE')(GENICAM_StringNode, POINTER(GENICAM_DeviceControl)) + +GENICAM_DeviceControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_DeviceControl_addRef), + ('release', GENICAM_DeviceControl_release), + ('deviceUserID', GENICAM_DeviceControl_deviceUserID), + ('reserve', c_uint * 28), + ] + +#SDK.h => struct GENICAM_DeviceControlInfo +class GENICAM_DeviceControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createDeviceControl(GENICAM_DeviceControlInfo* pDeviceControlInfo, GENICAM_DeviceControl** ppDeviceControl) +GENICAM_createDeviceControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createDeviceControl + +#SDK.h => struct GENICAM_DigitalIOControl +class GENICAM_DigitalIOControl(Structure): + pass + +GENICAM_DigitalIOControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DigitalIOControl)) +GENICAM_DigitalIOControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_DigitalIOControl)) +GENICAM_DigitalIOControl_lineSelector = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_DigitalIOControl)) +GENICAM_DigitalIOControl_lineDebouncerTimeAbs = eval('CFUNCTYPE')(GENICAM_DoubleNode, POINTER(GENICAM_DigitalIOControl)) +GENICAM_DigitalIOControl_userOutputSelector = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_DigitalIOControl)) +GENICAM_DigitalIOControl_userOutputValue = eval('CFUNCTYPE')(GENICAM_BoolNode, POINTER(GENICAM_DigitalIOControl)) + +GENICAM_DigitalIOControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_DigitalIOControl_addRef), + ('release', GENICAM_DigitalIOControl_release), + ('lineSelector', GENICAM_DigitalIOControl_lineSelector), + ('lineDebouncerTimeAbs', GENICAM_DigitalIOControl_lineDebouncerTimeAbs), + ('userOutputSelector', GENICAM_DigitalIOControl_userOutputSelector), + ('userOutputValue', GENICAM_DigitalIOControl_userOutputValue), + ('reserve', c_uint * 25), + ] + +#SDK.h => struct GENICAM_DigitalIOControlInfo +class GENICAM_DigitalIOControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createDigitalIOControl(GENICAM_DigitalIOControlInfo* pDigitalIOControlInfo, GENICAM_DigitalIOControl** ppDigitalIOControl) +GENICAM_createDigitalIOControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createDigitalIOControl + +#SDK.h => struct GENICAM_TransportLayerControl +class GENICAM_TransportLayerControl(Structure): + pass + +GENICAM_TransportLayerControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_TransportLayerControl)) +GENICAM_TransportLayerControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_TransportLayerControl)) +GENICAM_TransportLayerControl_gevSCPD = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_TransportLayerControl)) + +GENICAM_TransportLayerControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_TransportLayerControl_addRef), + ('release', GENICAM_TransportLayerControl_release), + ('gevSCPD', GENICAM_TransportLayerControl_gevSCPD), + ('reserve', c_uint * 28), + ] + +#SDK.h => struct GENICAM_TransportLayerControlInfo +class GENICAM_TransportLayerControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createTransportLayerControl(GENICAM_TransportLayerControlInfo* pTransportControlInfo, GENICAM_TransportLayerControl** ppTransportControl) +GENICAM_createTransportLayerControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createTransportLayerControl + + +#SDK.h => struct GENICAM_ImageFormatControl +class GENICAM_ImageFormatControl(Structure): + pass + +GENICAM_ImageFormatControl_addRef = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_release = eval('CFUNCTYPE')(c_int, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_height = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_width = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_offsetX = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_offsetY = eval('CFUNCTYPE')(GENICAM_IntNode, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_pixelFormat = eval('CFUNCTYPE')(GENICAM_EnumNode, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_reverseX = eval('CFUNCTYPE')(GENICAM_BoolNode, POINTER(GENICAM_ImageFormatControl)) +GENICAM_ImageFormatControl_reverseY = eval('CFUNCTYPE')(GENICAM_BoolNode, POINTER(GENICAM_ImageFormatControl)) + +GENICAM_ImageFormatControl._fields_ = [ + ('priv', c_void_p), + ('addRef', GENICAM_ImageFormatControl_addRef), + ('release', GENICAM_ImageFormatControl_release), + ('height', GENICAM_ImageFormatControl_height), + ('width', GENICAM_ImageFormatControl_width), + ('offsetX', GENICAM_ImageFormatControl_offsetX), + ('offsetY', GENICAM_ImageFormatControl_offsetY), + ('pixelFormat', GENICAM_ImageFormatControl_pixelFormat), + ('reverseX', GENICAM_ImageFormatControl_reverseX), + ('reverseY', GENICAM_ImageFormatControl_reverseY), + ('reserve', c_uint * 22), + ] + +#SDK.h => struct GENICAM_ImageFormatControlInfo +class GENICAM_ImageFormatControlInfo(Structure): + _fields_ = [ + ('pCamera', POINTER(GENICAM_Camera)), + ('reserved', c_uint * 31), + ] + +#SDK.h => GENICAM_createImageFormatControl(GENICAM_ImageFormatControlInfo* pImageFormatControlInfo, GENICAM_ImageFormatControl** ppImageFormatControl) +GENICAM_createImageFormatControl = None if MVSDKdll is None else MVSDKdll.GENICAM_createImageFormatControl diff --git a/device/mv/__init__.py b/device/mv/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/image.bmp b/image.bmp new file mode 100644 index 0000000..238167a Binary files /dev/null and b/image.bmp differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..565a7be --- /dev/null +++ b/main.py @@ -0,0 +1,177 @@ +import asyncio +import sys + +from PyQt5.QtCore import Qt, QDate, QThread +from PyQt5.QtGui import QPixmap, QFontDatabase, QFont, QPainter, \ + QLinearGradient, QColor +from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QStackedWidget, \ + QFrame, QSpacerItem, QSizePolicy, QProgressBar + +from core.edge_context import EdgeContext +from core.logging import logger +from core.context import AppContext +from util import load_stylesheet +from widget.device import DeviceWidget +from widget.task_list import TaskListWidget + +class DetectWindow(QMainWindow): + def __init__(self): + super().__init__() + + self.ratio = AppContext.get_ratio() + # 电池状态 + self.battery = 40 + self.battery_state = 0 + + # 设置主窗口标题 + self.setWindowTitle('预埋件检测系统') + + # 创建主窗口的中央Widget + central_widget = QWidget() + self.setCentralWidget(central_widget) + # self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) # 去掉窗口边框 + + # 创建主布局 + main_layout = QVBoxLayout(central_widget) + main_layout.setContentsMargins(0, 0, 0, 0) # 设置边距为零 + main_layout.setSpacing(0) # 设置间隔为零 + # ---------- 顶部标题栏 ---------- + header_widget = QWidget() + header_widget.setObjectName("headerWidget") + header_widget.setFixedHeight(int(self.ratio * 64)) + header_layout = QHBoxLayout(header_widget) + header_layout.setContentsMargins(int(self.ratio * 20), 0, int(self.ratio * 20), 0) + header_layout.setSpacing(0) + # 左侧标题 + header_left_widget = QWidget() + header_left_layout = QHBoxLayout(header_left_widget) + header_left_layout.setContentsMargins(0, 0, 0, 0) # 设置边距为零 + header_left_layout.setSpacing(0) # 设置间隔为零 + # logo + logo_label = QLabel() + pixmap = QPixmap("assets/logo.png") + scaled_pixmap = pixmap.scaled(int(self.ratio * 64) - 4, int(self.ratio * 64) - 4, True) + logo_label.setPixmap(scaled_pixmap) + header_left_layout.addWidget(logo_label) + header_left_layout.addSpacing(int(self.ratio * 5)) + # title + self.title_label = QLabel("预埋件检测系统") + self.title_label.setStyleSheet(f"color: white;font-size: {int(self.ratio * 24)}px;") + header_left_layout.addWidget(self.title_label) + header_layout.addWidget(header_left_widget, alignment=Qt.AlignLeft) + # 右侧日期和电量 + header_right_widget = QWidget() + header_right_layout = QHBoxLayout(header_right_widget) + self.date_label = QLabel(QDate.currentDate().toString('yyyy年MM月dd日')) + self.date_label.setStyleSheet(f"color: #898AC5;font-size: {int(self.ratio * 14)}px;") + header_right_layout.addWidget(self.date_label) + header_right_layout.addSpacing(int(self.ratio * 10)) + # 充电图标 + self.chargeIcon = QLabel(self) + self.chargeIcon.setPixmap(QPixmap("assets/icon_charge_finish.png").scaled(36, 36)) + self.chargeIcon.setVisible(False) + # 电量 + self.batteryBar = QProgressBar(self) + self.batteryBar.setMinimum(0) + self.batteryBar.setMaximum(100) + self.batteryBar.setValue(self.battery) + self.batteryBar.setFixedHeight(int(self.ratio * 24)) + self.batteryBar.setFixedWidth(int(self.ratio * 64)) + # 设置电池显示样式 + self.batteryBar.setAlignment(Qt.AlignCenter) + self.batteryBar.setFormat("%p%") + self.batteryBar.setStyleSheet("QProgressBar::chunk { background-color: #1afa29; }") + header_right_layout.addWidget(self.chargeIcon) + header_right_layout.addWidget(self.batteryBar) + header_layout.addWidget(header_right_widget, alignment=Qt.AlignRight) + # 添加标题栏 + main_layout.addWidget(header_widget) + + # 任务列表 + task_list_content = TaskListWidget() + task_list_content.setObjectName("taskListWidget") + task_list_content.setStyleSheet("background-color: #161B3B") + main_layout.addWidget(task_list_content) + + # 信号 + self.init_signals() + self.battery_change(self.battery) + + # 设置窗口默认全屏 + self.resize(1024, 768) + # self.showFullScreen() + # + def init_signals(self): + ups = AppContext.get_edge_context().get_component('ups') + ups.signals.battery_change.connect(self.battery_change) + ups.signals.state_change.connect(self.battery_state_change) + # + def battery_change(self, battery): + """更新电池电量""" + self.battery = battery + self.batteryBar.setValue(battery) + if self.battery < 20: + self.batteryBar.setStyleSheet("QProgressBar::chunk { background-color: red; }") + elif self.battery < 50: + self.batteryBar.setStyleSheet("QProgressBar::chunk { background-color: yellow; }") + else: + self.batteryBar.setStyleSheet("QProgressBar::chunk { background-color: #1afa29; }") + + def battery_state_change(self, battery_state): + """更新充电状态""" + self.battery_state = battery_state + if self.battery_state & 0x08 == 0x08: + self.chargeIcon.setPixmap(QPixmap("assets/icon_charging.png").scaled(36, 36)) + self.chargeIcon.setVisible(True) + elif self.battery_state & 0x04 == 0x04: + self.chargeIcon.setPixmap(QPixmap("assets/icon_charge_finish.png").scaled(36, 36)) + self.chargeIcon.setVisible(True) + else: + self.chargeIcon.setVisible(False) + + def showEvent(self, event): + super().showEvent(event) + +def on_quit(): + AppContext.get_edge_context().stop() + edge_worker.quit() + logger.info("Application quit.") + + +class EdgeContextWorker(QThread): + def __init__(self, context): + super().__init__() + self.edge_context = context + + def run(self): + self.edge_context.start() + + +if __name__ == '__main__': + app = QApplication(sys.argv) + app.aboutToQuit.connect(on_quit) + + screen = app.primaryScreen() + dpi = screen.logicalDotsPerInch() + AppContext.set_ratio(dpi / 96.0) + + stylesheet = load_stylesheet("styles/global.qss") + app.setStyleSheet(stylesheet) + + # 加载自定义字体 + font_id = QFontDatabase.addApplicationFont("assets/fonts/NotoSansSC-Regular.otf") + if font_id != -1: + font_family = QFontDatabase.applicationFontFamilies(font_id)[0] + custom_font = QFont(font_family, 14) # 使用字体族名创建字体,并设置大小 + app.setFont(custom_font) # 设置全局字体 + + # 启动 EdgeContext 服务 + edge_context = EdgeContext() + AppContext.set_edge_context(edge_context) + edge_worker = EdgeContextWorker(edge_context) + edge_worker.start() + + window = DetectWindow() + window.show() + + sys.exit(app.exec_()) diff --git a/model/__init__.py b/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/model/base_model.py b/model/base_model.py new file mode 100644 index 0000000..8832855 --- /dev/null +++ b/model/base_model.py @@ -0,0 +1,6 @@ +from PyQt5.QtCore import QObject, pyqtSignal + + +class DataModel(QObject): + # 定义一个信号,当数据变化时发射该信号 + property_changed = pyqtSignal(str, object) # 接受属性名称和新值 diff --git a/req.txt b/req.txt new file mode 100644 index 0000000..6141723 --- /dev/null +++ b/req.txt @@ -0,0 +1,11 @@ +PyOpenGL==3.1.1a1 +numpy==1.26.4 +openpyxl==3.1.5 +pandas==2.0.3 +pyserial==3.5 +APScheduler==3.10.4 +paho-mqtt==2.1.0 +dynaconf==3.2.5 +fastapi==0.111.0 +uvicorn==0.30.1 +SQLAlchemy==2.0.34 \ No newline at end of file diff --git a/sql/1.0.0__init.sql b/sql/1.0.0__init.sql new file mode 100644 index 0000000..1536754 --- /dev/null +++ b/sql/1.0.0__init.sql @@ -0,0 +1,38 @@ +-- sql history +CREATE TABLE IF NOT EXISTS sys_sql_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file TEXT NOT NULL, + updated_at TIMESTAMP DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'localtime')) +); + +-- system +CREATE TABLE IF NOT EXISTS sys_system ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code TEXT NOT NULL, + val TEXT NOT NULL, + updated_at TIMESTAMP DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'localtime')) +); +insert into sys_system(code, val) values ('software_version', '1.0.0'); +insert into sys_system(code, val) values ('firmware_version', '1.0.0'); +insert into sys_system(code, val) values ('algorithm_version', '1.0.0'); +insert into sys_system(code, val) values ('model_version', '1.0.0'); + +-- user +CREATE TABLE IF NOT EXISTS sys_user ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + password TEXT NOT NULL, + created_at TIMESTAMP DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'localtime')) +); +insert into sys_user(username, password) values ('admin', 'winner!'); + +-- task +CREATE TABLE IF NOT EXISTS sys_task ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + creator TEXT NULL, + steps TEXT NOT NULL, + result TEXT NULL, + state INTEGER default 0, + created_at TIMESTAMP DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'localtime')) +); diff --git a/sql/1.0.1__add_dat_task_tables.sql b/sql/1.0.1__add_dat_task_tables.sql new file mode 100644 index 0000000..6c41e1a --- /dev/null +++ b/sql/1.0.1__add_dat_task_tables.sql @@ -0,0 +1,34 @@ +-- task +CREATE TABLE IF NOT EXISTS dat_task +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + device_sn TEXT NOT NULL, + param_json TEXT NOT NULL, + result_json TEXT NULL, + state INTEGER DEFAULT 0, + start_time DATETIME NULL, + end_time DATETIME NULL, + create_time DATETIME NULL, + update_time DATETIME NULL +); + +-- task data +CREATE TABLE IF NOT EXISTS dat_task_progress +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id INTEGER NOT NULL, + device_sn TEXT NOT NULL, + task_data_json TEXT NOT NULL, + create_time TIMESTAMP DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'localtime')) +); + +-- task log +CREATE TABLE IF NOT EXISTS dat_task_log +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id INTEGER NOT NULL, + device_sn TEXT NOT NULL, + content TEXT NOT NULL, + create_time TIMESTAMP DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'localtime')) +); \ No newline at end of file diff --git a/styles/global.qss b/styles/global.qss new file mode 100644 index 0000000..193ca71 --- /dev/null +++ b/styles/global.qss @@ -0,0 +1,246 @@ +QMainWindow { + background-color: transparent; +} + +QLabel { + color: #D9E1E7; + background-color: transparent; + border: 0 solid transparent; +} + +MenuButton { + background-color: transparent; +} + +QWidget#menuWidget { + background-color: #1E244D; +} + +QWidget#headerWidget { + background-color: #2A2F55; +} + +TaskListWidget QLabel, +TaskListWidget QPlainTextEdit { + font-size: 14px; +} + +TaskListWidget QLabel#THeader { + font-size: 16px; + font-weight: bold; +} +TaskListWidget QPushButton { + color: #D9E1E7; + border-radius: 8px; + font-size: 16px; +} + +QPushButton { + color: #D9E1E7; + border-radius: 8px; + background-color: #3A36DB; +} + +QPushButton::disabled { + color: #999999; + background-color: rgba(58,54,219,0.4); +} + +QLineEdit { + color: #D9E1E7; + border: 1px solid #D9E1E7; + border-radius: 8px; + padding: 3 10; + background-color: transparent; +} + +QWidget#jsonWidget { + border: 1px solid #D9E1E7; + border-radius: 8px; +} + +QPlainTextEdit { + color: #D9E1E7; + border: none; + background-color: transparent; +} + +QAbstractScrollArea#mapArea { + border: none; + background: transparent; + border: 1px solid #D9E1E7; +} + +QAbstractScrollArea QWidget { + border: none; + background: transparent; +} + +/* === QScrollBar:horizontal === */ +QScrollBar:horizontal { + background: rgba(58,54,219,0.45); + height: 5px; + margin: 0px; + border-radius: 2px; +} + +QScrollBar::handle:horizontal { + background: rgba(58,54,219,0.9); + min-width: 20px; + border-radius: 2px; +} + +QScrollBar::add-line:horizontal { + width: 0px; +} + +QScrollBar::sub-line:horizontal { + width: 0px; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background: none; +} + +/* === QScrollBar:vertical === */ +QScrollBar:vertical { + background: rgba(58,54,219,0.45); + width: 5px; + margin: 0px; + border-radius: 2px; +} + +QScrollBar::handle:vertical { + background: rgba(58,54,219,0.9); + min-height: 20px; + border-radius: 2px; +} + +QScrollBar::add-line:vertical { + height: 0px; +} + +QScrollBar::sub-line:vertical { + height: 0px; +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; +} + +TaskRunDialog { + background-color: #161B3B; +} + +QWidget#viewWidget { + background-color: #222222; +} + +QPushButton#stopTaskButton { + background-color: #FF4444; +} + +EmbedItem QLabel { + color: #999999; + font-size: 10px; + font-weight: bold; + border: 1px solid transparent; +} + +EmbedItem QPushButton { + border: 1px solid #999999; + border-radius: 0; + font-size: 10px; + font-weight: bold; +} + +EmbedItem QPushButton#STD { + border: 1px solid transparent; + background-color: #FF4444; +} + +QWidget#checkWidget, +QWidget#viewCheckWidget { + border: 1px dashed #6cff6c; +} + +QWidget#legendCheckWidget { + width: 20px; + min-width: 20px; + max-width: 20px; + height: 20px; + min-height: 20px; + max-height: 20px; + border: 1px dashed #6cff6c; +} + +QWidget#legendCheckLabel { + color: #6cff6c; +} + +QWidget#legendStdWidget { + width: 20px; + min-width: 20px; + max-width: 20px; + height: 20px; + min-height: 20px; + max-height: 20px; + background-color: #FF4444; +} + +QWidget#legendStdLabel { + color: #FF4444; +} + +QWidget#legendOkWidget { + width: 20px; + min-width: 20px; + max-width: 20px; + height: 20px; + min-height: 20px; + max-height: 20px; + border: 1px solid #6cff6c; +} + +QWidget#legendOkLabel { + color: #6cff6c; +} + +QWidget#legendNgWidget { + width: 20px; + min-width: 20px; + max-width: 20px; + height: 20px; + min-height: 20px; + max-height: 20px; + border: 1px solid #FF4444; +} + +QWidget#legendNgLabel { + color: #FF4444; +} + +QWidget#legendNormalWidget { + width: 20px; + min-width: 20px; + max-width: 20px; + height: 20px; + min-height: 20px; + max-height: 20px; + border: 1px solid #999999; +} + +QWidget#legendNormalLabel { + color: #999999; +} + +EmbedDetail { + background-color: #161B3B; + border: 1px solid #999999; +} + +EmbedDetail QPushButton { + padding: 5px; + font-size: 16px; + border-radius: 2px; +} \ No newline at end of file diff --git a/test/gpio_test.bash b/test/gpio_test.bash new file mode 100644 index 0000000..f5dc569 --- /dev/null +++ b/test/gpio_test.bash @@ -0,0 +1,30 @@ +#!/bin/bash + +# 导出 GPIO 80 +echo 80 > /sys/class/gpio/export + +# 设置为输出 +echo "out" > /sys/class/gpio/gpio80/direction + +# 设置为高电平 +echo "1" > /sys/class/gpio/gpio80/value +echo "GPIO 80 set to HIGH" +sleep 5 # 等待 5 秒 + +# 设置为低电平 +echo "0" > /sys/class/gpio/gpio80/value +echo "GPIO 80 set to LOW" +sleep 5 # 等待 5 秒 + +# 再次设置为高电平 +echo "1" > /sys/class/gpio/gpio80/value +echo "GPIO 80 set to HIGH again" +sleep 5 # 等待 5 秒 + +# 最后设置为低电平 +echo "0" > /sys/class/gpio/gpio80/value +echo "GPIO 80 set to LOW again" + +# 释放 GPIO 80 +echo 80 > /sys/class/gpio/unexport + diff --git a/test/point_cloud_test.py b/test/point_cloud_test.py new file mode 100644 index 0000000..c5fff50 --- /dev/null +++ b/test/point_cloud_test.py @@ -0,0 +1,100 @@ +import sys +import random +from PyQt5.QtWidgets import QApplication, QMainWindow, QOpenGLWidget +from PyQt5.QtCore import Qt, pyqtSignal, QObject +from OpenGL.GL import * +from OpenGL.GLUT import * +from OpenGL.GLU import * + +class PointCloudWidget(QOpenGLWidget): + def __init__(self, parent=None): + super(PointCloudWidget, self).__init__(parent) + self.point_cloud = [] # 初始化点云数据为空 + self.point_size = 5 # 点的大小 + + def update_point_cloud(self, new_point_cloud): + """更新点云数据并触发刷新""" + self.point_cloud = new_point_cloud + self.update() # 触发重绘 + + def initializeGL(self): + """初始化OpenGL参数""" + glClearColor(0.0, 0.0, 0.0, 1.0) # 背景颜色:黑色 + glEnable(GL_DEPTH_TEST) # 启用深度测试 + glPointSize(self.point_size) # 设置点的大小 + + def paintGL(self): + """绘制点云""" + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # 清除颜色和深度缓冲 + glLoadIdentity() # 重置当前矩阵 + gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0) # 设置观察视角 + + glBegin(GL_POINTS) # 开始绘制点 + for point in self.point_cloud: + x, y, z, r, g, b = point + glColor3f(r, g, b) # 设置点的颜色 + glVertex3f(x, y, z) # 设置点的位置 + glEnd() # 结束绘制 + + glFlush() # 刷新绘图 + + def resizeGL(self, w, h): + """调整窗口大小时重置视口和投影矩阵""" + glViewport(0, 0, w, h) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluPerspective(45.0, w / h, 1.0, 100.0) + glMatrixMode(GL_MODELVIEW) + + +class PointCloudManager(QObject): + """负责发送点云数据的管理类""" + point_cloud_signal = pyqtSignal(list) # 信号,用于发送点云数据 + + def __init__(self): + super().__init__() + + def generate_and_send_point_cloud(self): + """模拟生成点云数据并通过信号发送""" + new_point_cloud = self.generate_point_cloud(1000) # 生成1000个随机点 + self.point_cloud_signal.emit(new_point_cloud) # 发射信号 + + def generate_point_cloud(self, num_points): + """生成随机点云数据""" + points = [] + for _ in range(num_points): + x = random.uniform(-1.0, 1.0) # X坐标 + y = random.uniform(-1.0, 1.0) # Y坐标 + z = random.uniform(-1.0, 1.0) # Z坐标 + r = random.random() # 红色通道 + g = random.random() # 绿色通道 + b = random.random() # 蓝色通道 + points.append((x, y, z, r, g, b)) + return points + + +class MainWindow(QMainWindow): + def __init__(self): + super(MainWindow, self).__init__() + self.setWindowTitle("PyQt5 + OpenGL 实时点云显示") + self.setGeometry(100, 100, 800, 600) + + # 创建OpenGL显示组件 + self.gl_widget = PointCloudWidget() + self.setCentralWidget(self.gl_widget) + + # 创建点云管理器 + self.point_cloud_manager = PointCloudManager() + + # 将信号连接到PointCloudWidget的更新槽 + self.point_cloud_manager.point_cloud_signal.connect(self.gl_widget.update_point_cloud) + + # 模拟点云数据生成与发送 + self.point_cloud_manager.generate_and_send_point_cloud() + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec_()) diff --git a/test/tcp_server_test.py b/test/tcp_server_test.py new file mode 100644 index 0000000..23a69ea --- /dev/null +++ b/test/tcp_server_test.py @@ -0,0 +1,24 @@ +import asyncio +import json + +HOST = '127.0.0.1' +PORT = 13000 + +async def send_json_request(data): + reader, writer = await asyncio.open_connection(HOST, PORT) + + # 发送JSON数据 + writer.write(json.dumps(data).encode('utf-8')) + await writer.drain() + + # 异步接收响应数据 + response_data = await reader.read(1024) + print(f"Received response: {response_data.decode('utf-8')}") + + # 关闭连接 + writer.close() + await writer.wait_closed() + +if __name__ == "__main__": + request_data = {"requestId": "asdasdasd", "type": "service", "component": "_database", "method": "system_info"} + asyncio.run(send_json_request(request_data)) diff --git a/ui.png b/ui.png new file mode 100644 index 0000000..03988d5 Binary files /dev/null and b/ui.png differ diff --git a/util/__init__.py b/util/__init__.py new file mode 100644 index 0000000..0328767 --- /dev/null +++ b/util/__init__.py @@ -0,0 +1,29 @@ +import platform +from PyQt5.QtCore import QFile, QTextStream + + +def load_stylesheet(file_path): + """加载 QSS 文件""" + file = QFile(file_path) + if not file.open(QFile.ReadOnly | QFile.Text): + print(f"无法打开样式表文件 {file_path}") + return "" + stream = QTextStream(file) + stylesheet = stream.readAll() + file.close() + return stylesheet + + +def get_system_and_library_suffix(): + system = platform.system().lower() + machine = platform.machine().lower() + + if system == 'windows': + return 'win', 'dll' + elif system == 'linux': + if 'linux_arm64' in machine or 'aarch64' in machine: + return 'linux_arm64', 'so' + else: + return 'linux', 'so' + else: + raise ValueError(f"Unsupported system: {system}") \ No newline at end of file diff --git a/vendors/livox/CMakeLists.txt b/vendors/livox/CMakeLists.txt new file mode 100644 index 0000000..02843ab --- /dev/null +++ b/vendors/livox/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.10) +project(LivoxSDKWrapper) + +set(CMAKE_CXX_STANDARD 11) + +include_directories(/usr/local/include) +link_directories(/usr/local/lib) + +add_library(livox_sdk_wrapper SHARED livox_sdk_wrapper.cpp) + +target_link_libraries(livox_sdk_wrapper livox_sdk_static pthread) diff --git a/vendors/livox/README.MD b/vendors/livox/README.MD new file mode 100644 index 0000000..c4dcd3c --- /dev/null +++ b/vendors/livox/README.MD @@ -0,0 +1,4 @@ +找到 CMakeLists.txt 中编译静态库的地方,确保添加 -fPIC 标志。可以通过以下方式添加: +```cmake +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +``` diff --git a/vendors/livox/build.sh b/vendors/livox/build.sh new file mode 100644 index 0000000..b430c2a --- /dev/null +++ b/vendors/livox/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +rm -rf build +mkdir build +cd build +cmake .. +make +cp liblivox_sdk_wrapper.so ../ diff --git a/vendors/livox/liblivox_sdk_wrapper.so b/vendors/livox/liblivox_sdk_wrapper.so new file mode 100644 index 0000000..b46855a Binary files /dev/null and b/vendors/livox/liblivox_sdk_wrapper.so differ diff --git a/vendors/livox/livox_sdk_wrapper.cpp b/vendors/livox/livox_sdk_wrapper.cpp new file mode 100644 index 0000000..78a1136 --- /dev/null +++ b/vendors/livox/livox_sdk_wrapper.cpp @@ -0,0 +1,275 @@ +#include +#include +#include +#include +#include "livox_sdk.h" + +typedef enum { + kDeviceStateDisconnect = 0, + kDeviceStateConnect = 1, + kDeviceStateSampling = 2, +} DeviceState; + +typedef struct { + uint8_t handle; + DeviceState device_state; + DeviceInfo info; +} DeviceItem; + +DeviceItem devices[kMaxLidarCount]; +int lidar_count = 0; + +/** 回调函数声明 */ +void OnLidarErrorStatusCallback(livox_status status, uint8_t handle, ErrorMessage *message); +void GetLidarData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *client_data); +void OnSampleCallback(livox_status status, uint8_t handle, uint8_t response, void *data); +void OnCommonCommandCallback(livox_status status, uint8_t handle, uint8_t response, void *data); +void OnStopSampleCallback(livox_status status, uint8_t handle, uint8_t response, void *data); +void OnDeviceInfoChange(const DeviceInfo *info, DeviceEvent type); +void OnDeviceBroadcast(const BroadcastDeviceInfo *info); + +/** 用于Python的回调函数指针 */ +void (*python_data_callback)(uint8_t, uint32_t *, uint32_t) = NULL; +void (*python_error_callback)(livox_status, uint8_t, ErrorMessage*) = NULL; +void (*python_device_change_callback)(const DeviceInfo*, DeviceEvent) = NULL; +void (*python_device_broadcast_callback)(const BroadcastDeviceInfo*) = NULL; +void (*python_common_command_callback)(livox_status, uint8_t, uint8_t) = NULL; + +extern "C" { + +/** 初始化Livox SDK */ +bool init_sdk() { + printf("Livox SDK initializing.\n"); + if (!Init()) { + return false; + } + printf("Livox SDK has been initialized.\n"); + LivoxSdkVersion _sdkversion; + GetLivoxSdkVersion(&_sdkversion); + printf("Livox SDK version %d.%d.%d .\n", _sdkversion.major, _sdkversion.minor, _sdkversion.patch); + + memset(devices, 0, sizeof(devices)); + return true; +} + +/** 启动设备扫描 */ +bool start_discovery() { + SetBroadcastCallback(OnDeviceBroadcast); + SetDeviceStateUpdateCallback(OnDeviceInfoChange); + + if (!Start()) { + printf("Failed to start device discovery.\n"); + Uninit(); + return false; + } + printf("Started device discovery.\n"); + return true; +} + +/** 停止SDK并清理 */ +void stop_sdk() { + printf("Stopping Livox SDK.\n"); + for (int i = 0; i < kMaxLidarCount; ++i) { + if (devices[i].device_state == kDeviceStateSampling) { + LidarStopSampling(devices[i].handle, OnStopSampleCallback, NULL); + } + } + Uninit(); +} + +int connect(const char *broadcast_code) { + uint8_t handle = 0; + livox_status result = AddLidarToConnect(broadcast_code, &handle); + if (result == kStatusSuccess) { + /** Set the point cloud data for a specific Livox LiDAR. */ + SetDataCallback(handle, GetLidarData, NULL); + } + return result; +} + +int start_sampling(uint8_t handle) { + livox_status result = LidarStartSampling(handle, OnSampleCallback, NULL); + devices[handle].device_state = kDeviceStateSampling; + return result; +} + +int stop_sampling(uint8_t handle) { + livox_status result = LidarStopSampling(handle, OnSampleCallback, NULL); + devices[handle].device_state = kDeviceStateSampling; + return result; +} + +int set_mode(LidarMode mode) { + return LidarSetMode(devices[0].handle, mode, OnCommonCommandCallback, NULL); +} + +/** 注册数据回调函数给Python调用 */ +void register_data_callback(void (*data_callback)(uint8_t handle, uint32_t *data, uint32_t data_num)) { + python_data_callback = data_callback; +} + +/** 注册错误回调函数给Python调用 */ +void register_error_callback(void (*error_callback)(livox_status status, uint8_t handle, ErrorMessage *message)) { + python_error_callback = error_callback; +} + +/** 注册设备状态变化回调给Python调用 */ +void register_device_change_callback(void (*device_change_callback)(const DeviceInfo* info, DeviceEvent type)) { + python_device_change_callback = device_change_callback; +} + +/** 注册设备广播回调给Python调用 */ +void register_device_broadcast_callback(void (*device_broadcast_callback)(const BroadcastDeviceInfo *info)) { + python_device_broadcast_callback = device_broadcast_callback; +} + +/** 注册设备广播回调给Python调用 */ +void register_common_command_callback(void (*common_command_callback)(livox_status status, uint8_t handle, uint8_t response)) { + python_common_command_callback = common_command_callback; +} + +} + +/** 回调函数的定义 */ +void OnLidarErrorStatusCallback(livox_status status, uint8_t handle, ErrorMessage *message) { + if (python_error_callback) { + python_error_callback(status, handle, message); + } +} + +void OnCommonCommandCallback(livox_status status, uint8_t handle, uint8_t response, void *data) { + if (python_common_command_callback) { + python_common_command_callback(status, handle, response); + } +} + +void GetLidarData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *client_data) { + if (data) { + /** Parsing the timestamp and the point cloud data. */ + uint64_t cur_timestamp = *((uint64_t *)(data->timestamp)); + if(data ->data_type == kCartesian) { + LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data; + }else if ( data ->data_type == kSpherical) { + LivoxSpherPoint *p_point_data = (LivoxSpherPoint *)data->data; + }else if ( data ->data_type == kExtendCartesian) { + LivoxExtendRawPoint *p_point_data = (LivoxExtendRawPoint *)data->data; + LivoxExtendRawPoint point_array[data_num]; + uint32_t *converted_data = (uint32_t *)malloc(data_num * 4 * sizeof(uint32_t)); + for (uint32_t i = 0; i < data_num; ++i) { + LivoxExtendRawPoint *point = &p_point_data[i]; + // 将结构体的 x, y, z 放入 uint32_t 数组中 + converted_data[i * 4] = point->x; + converted_data[i * 4 + 1] = point->y; + converted_data[i * 4 + 2] = point->z; + // 将 reflectivity 和 tag 合并成一个 uint32_t(高16位和低16位) + converted_data[i * 4 + 3] = (uint32_t)(point->reflectivity << 8 | point->tag); + } + // 调用 Python 回调函数 + if (python_data_callback) { + python_data_callback(handle, converted_data, data_num); + } + }else if ( data ->data_type == kExtendSpherical) { + LivoxExtendSpherPoint *p_point_data = (LivoxExtendSpherPoint *)data->data; + }else if ( data ->data_type == kDualExtendCartesian) { + LivoxDualExtendRawPoint *p_point_data = (LivoxDualExtendRawPoint *)data->data; + }else if ( data ->data_type == kDualExtendSpherical) { + LivoxDualExtendSpherPoint *p_point_data = (LivoxDualExtendSpherPoint *)data->data; + }else if ( data ->data_type == kImu) { + LivoxImuPoint *p_point_data = (LivoxImuPoint *)data->data; + }else if ( data ->data_type == kTripleExtendCartesian) { + LivoxTripleExtendRawPoint *p_point_data = (LivoxTripleExtendRawPoint *)data->data; + }else if ( data ->data_type == kTripleExtendSpherical) { + LivoxTripleExtendSpherPoint *p_point_data = (LivoxTripleExtendSpherPoint *)data->data; + } + // printf("data_type %d\n", data->data_type); + } +} + +void OnSampleCallback(livox_status status, uint8_t handle, uint8_t response, void *data) { + if (status == kStatusSuccess && response != 0) { + devices[handle].device_state = kDeviceStateConnect; + } else if (status == kStatusTimeout) { + devices[handle].device_state = kDeviceStateConnect; + } +} + +void OnStopSampleCallback(livox_status status, uint8_t handle, uint8_t response, void *data) { + // 停止采样时的回调处理 +} + +/** Query the firmware version of Livox LiDAR. */ +void OnDeviceInformation(livox_status status, uint8_t handle, DeviceInformationResponse *ack, void *data) { + if (status != kStatusSuccess) { + printf("Device Query Informations Failed %d\n", status); + } + if (ack) { + printf("firm ver: %d.%d.%d.%d\n", + ack->firmware_version[0], + ack->firmware_version[1], + ack->firmware_version[2], + ack->firmware_version[3]); + } +} + +void LidarConnect(const DeviceInfo *info) { + uint8_t handle = info->handle; + QueryDeviceInformation(handle, OnDeviceInformation, NULL); + if (devices[handle].device_state == kDeviceStateDisconnect) { + devices[handle].device_state = kDeviceStateConnect; + devices[handle].info = *info; + } +} + +void LidarDisConnect(const DeviceInfo *info) { + uint8_t handle = info->handle; + devices[handle].device_state = kDeviceStateDisconnect; +} + +void LidarStateChange(const DeviceInfo *info) { + uint8_t handle = info->handle; + devices[handle].info = *info; +} + +void OnDeviceInfoChange(const DeviceInfo *info, DeviceEvent type) { + if (info == NULL) { + return; + } + + uint8_t handle = info->handle; + if (handle >= kMaxLidarCount) { + return; + } + if (type == kEventConnect) { + LidarConnect(info); + printf("[WARNING] Lidar sn: [%s] Connect!!!\n", info->broadcast_code); + } else if (type == kEventDisconnect) { + LidarDisConnect(info); + printf("[WARNING] Lidar sn: [%s] Disconnect!!!\n", info->broadcast_code); + } else if (type == kEventStateChange) { + LidarStateChange(info); + printf("[WARNING] Lidar sn: [%s] StateChange!!!\n", info->broadcast_code); + } + printf("Device Working State %d\n", devices[handle].info.state); + if (devices[handle].device_state == kDeviceStateConnect) { + if (devices[handle].info.state == kLidarStateInit) { + printf("Device State Change Progress %u\n", devices[handle].info.status.progress); + } else { + printf("Device State Error Code 0X%08x\n", devices[handle].info.status.status_code.error_code); + } + printf("Device feature %d\n", devices[handle].info.feature); + SetErrorMessageCallback(handle, OnLidarErrorStatusCallback); + if (devices[handle].info.state == kLidarStateNormal) { + LidarStartSampling(handle, OnSampleCallback, NULL); + devices[handle].device_state = kDeviceStateSampling; + } + } + if (python_device_change_callback) { + python_device_change_callback(info, type); + } +} + +void OnDeviceBroadcast(const BroadcastDeviceInfo *info) { + if (python_device_broadcast_callback) { + python_device_broadcast_callback(info); + } +} diff --git a/vendors/livox/sdk_test.py b/vendors/livox/sdk_test.py new file mode 100644 index 0000000..87144ea --- /dev/null +++ b/vendors/livox/sdk_test.py @@ -0,0 +1,212 @@ +import ctypes +from ctypes import * + +# 加载C++库 +livox_sdk = ctypes.CDLL('./liblivox_sdk_wrapper.so') + +# 预定义的常量 +kBroadcastCodeSize = 16 # 广播码长度 +# 定义枚举类型 +class DeviceType(c_int): + kDeviceTypeHub = 0 + kDeviceTypeLidarMid40 = 1 + kDeviceTypeLidarTele = 2 + kDeviceTypeLidarHorizon = 3 + kDeviceTypeLidarMid70 = 6 + kDeviceTypeLidarAvia = 7 + +class LidarState(c_int): + kLidarStateInit = 0 + kLidarStateNormal = 1 + kLidarStatePowerSaving = 2 + kLidarStateStandBy = 3 + kLidarStateError = 4 + kLidarStateUnknown = 5 + +class LidarMode(c_int): + kLidarModeNormal = 1 + kLidarModePowerSaving = 2 + kLidarModeStandby = 3 + +class LidarFeature(c_int): + kLidarFeatureNone = 0 + kLidarFeatureRainFog = 1 + +class LidarIpMode(c_int): + kLidarDynamicIpMode = 0 + kLidarStaticIpMode = 1 + +class LidarScanPattern(c_int): + kNoneRepetitiveScanPattern = 0 + kRepetitiveScanPattern = 1 + +class LivoxStatus(c_int): + kStatusSendFailed = -9 + kStatusHandlerImplNotExist = -8 + kStatusInvalidHandle = -7 + kStatusChannelNotExist = -6 + kStatusNotEnoughMemory = -5 + kStatusTimeout = -4 + kStatusNotSupported = -3 + kStatusNotConnected = -2 + kStatusFailure = -1 + kStatusSuccess = 0 + +# 定义结构体和联合体 +class LidarErrorCode(Structure): + _fields_ = [ + ("temp_status", c_uint32, 2), + ("volt_status", c_uint32, 2), + ("motor_status", c_uint32, 2), + ("dirty_warn", c_uint32, 2), + ("firmware_err", c_uint32, 1), + ("pps_status", c_uint32, 1), + ("device_status", c_uint32, 1), + ("fan_status", c_uint32, 1), + ("self_heating", c_uint32, 1), + ("ptp_status", c_uint32, 1), + ("time_sync_status", c_uint32, 3), + ("rsvd", c_uint32, 13), + ("system_status", c_uint32, 2), + ] + +class HubErrorCode(Structure): + _fields_ = [ + ("sync_status", c_uint32, 2), + ("temp_status", c_uint32, 2), + ("lidar_status", c_uint32, 1), + ("lidar_link_status", c_uint32, 1), + ("firmware_err", c_uint32, 1), + ("rsvd", c_uint32, 23), + ("system_status", c_uint32, 2), + ] + +class ErrorMessage(Union): + _fields_ = [ + ("error_code", c_uint32), + ("lidar_error_code", LidarErrorCode), + ("hub_error_code", HubErrorCode) + ] + +class StatusUnion(Union): + _fields_ = [ + ("error_code", c_uint32), # 假设这个字段为示例 + ("lidar_error_code", LidarErrorCode), + ("hub_error_code", HubErrorCode) + ] + +# 定义 DeviceInfo 结构体 +class DeviceInfo(Structure): + _fields_ = [ + ("broadcast_code", c_char * kBroadcastCodeSize), # 广播码,最大15个字符,带终止符 + ("handle", c_uint8), # 设备句柄 + ("slot", c_uint8), # 插槽编号 + ("id", c_uint8), # LiDAR id + ("type", DeviceType), # 设备类型 + ("data_port", c_uint16), # 点云数据UDP端口 + ("cmd_port", c_uint16), # 控制命令UDP端口 + ("sensor_port", c_uint16), # IMU数据UDP端口 + ("ip", c_char * 16), # IP地址 + ("state", LidarState), # LiDAR状态 + ("feature", LidarFeature), # LiDAR特性 + ("status", StatusUnion), # LiDAR工作状态 + ("firmware_version", c_uint8 * 4) # 固件版本 + ] + +# 定义 BroadcastDeviceInfo 结构体 +class BroadcastDeviceInfo(Structure): + _fields_ = [ + ("broadcast_code", c_char * kBroadcastCodeSize), # 广播码,最多15个字符,带终止符 + ("dev_type", c_uint8), # 设备类型,参考 DeviceType + ("reserved", c_uint16), # 保留字段 + ("ip", c_char * 16) # 设备 IP 地址 + ] + +# 定义 LivoxEthPacket 结构体 +class LivoxEthPacket(Structure): + _fields_ = [ + ("version", c_uint8), # Packet protocol version. + ("slot", c_uint8), # Slot number used for connecting LiDAR. + ("id", c_uint8), # LiDAR id. + ("rsvd", c_uint8), # Reserved. + ("err_code", c_uint32), # Device error status indicator information. + ("timestamp_type", c_uint8), # Timestamp type. + ("data_type", c_uint8), # Point cloud coordinate format. + ("timestamp", c_uint8 * 8), # Nanosecond or UTC format timestamp. + ("data", c_uint8 * 1) # Point cloud data (can be extended as needed). + ] + +# 定义 LivoxExtendRawPoint 结构体 +class LivoxExtendRawPoint(Structure): + _fields_ = [ + ('x', c_int32), # X axis, Unit: mm + ('y', c_int32), # Y axis, Unit: mm + ('z', c_int32), # Z axis, Unit: mm + ('reflectivity', c_uint8), # Reflectivity + ('tag', c_uint8) # Tag + ] + +# 回调函数类型定义 +DataCallbackType = CFUNCTYPE(None, c_uint8, POINTER(LivoxEthPacket), c_uint32) +ErrorCallbackType = CFUNCTYPE(None, c_uint8, c_uint8, POINTER(ErrorMessage)) +DeviceChangeCallbackType = CFUNCTYPE(None, POINTER(DeviceInfo), c_uint8) +DeviceBroadcastCallbackType = CFUNCTYPE(None, POINTER(BroadcastDeviceInfo)) + + +point_size = sizeof(LivoxExtendRawPoint) + +# 数据回调函数 +@DataCallbackType +def data_callback(handle, packet, data_num): + # print(f"Received data from handle {handle}, data num: {data_num}") + # 1. 计算数据的总字节数 + total_data_size = data_num * point_size + # 2. 将 packet.data 转换为指向 LivoxExtendRawPoint 数组的指针 + point_data_array_type = LivoxExtendRawPoint * data_num # 定义类型 + point_data_pointer = cast(packet.data, POINTER(point_data_array_type)) # 将data转换为LivoxExtendRawPoint指针 + # 3. 获取具体的点云数据 + point_data_array = point_data_pointer.contents # 访问数组内容 + # 4. 遍历每个 LivoxExtendRawPoint 并打印数据 + for i in range(data_num): + point = point_data_array[i] + print(f"Point {i}: X={point.x}, Y={point.y}, Z={point.z}, Reflectivity={point.reflectivity}, Tag={point.tag}") + +# 错误回调函数 +@ErrorCallbackType +def error_callback(status, handle, error_message): + print(f"Error from handle {handle}, status: {status}") + +# 设备状态变化回调函数 +@DeviceChangeCallbackType +def device_change_callback(info, event_type): + print(f"Device {bytes(info.contents.broadcast_code).decode('utf-8')} changed state, event type: {event_type}") + +# 设备广播回调函数 +@DeviceBroadcastCallbackType +def device_broadcast_callback(info): + print(f"New device broadcast received: {bytes(info.contents.broadcast_code).decode('utf-8')}") + +# 注册回调函数 +livox_sdk.register_data_callback(data_callback) +livox_sdk.register_error_callback(error_callback) +livox_sdk.register_device_change_callback(device_change_callback) +livox_sdk.register_device_broadcast_callback(device_broadcast_callback) + +# 调用初始化和开始发现设备的函数 +if livox_sdk.init_sdk(): + print("Livox SDK initialized.") +else: + print("Failed to initialize Livox SDK.") + +if livox_sdk.start_discovery(): + print("Started discovering Livox devices.") +else: + print("Failed to discover devices.") + +# 等待数据回调 +try: + while True: + pass +except KeyboardInterrupt: + livox_sdk.stop_sdk() + print("SDK stopped.") diff --git a/widget/__init__.py b/widget/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/widget/device.py b/widget/device.py new file mode 100644 index 0000000..3cb8466 --- /dev/null +++ b/widget/device.py @@ -0,0 +1,133 @@ +import logging + +from PyQt5.QtWidgets import ( + QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QFrame +) +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor + +from core.context import AppContext + + +# 相机组件 +class CameraWidget(QWidget): + def __init__(self): + super().__init__() + self.ratio = AppContext.get_ratio() + + # 设备信息部分 + device_info = QWidget() + device_info.setFixedHeight(self.ratio * 80) + device_layout = QHBoxLayout() + self.serial_label = QLabel("高清相机: DH51215AAK00001") + self.status_label = QLabel("状态: 未连接") + self.power_button = QPushButton("通电") + self.connect_button = QPushButton("连接") + self.capture_button = QPushButton("拍照") + self.power_button.setFixedHeight(self.ratio * 42) + self.power_button.setFixedWidth(self.ratio * 64) + self.connect_button.setFixedHeight(self.ratio * 42) + self.connect_button.setFixedWidth(self.ratio * 64) + self.capture_button.setFixedHeight(self.ratio * 42) + self.capture_button.setFixedWidth(self.ratio * 64) + device_layout.addWidget(self.serial_label) + device_layout.addWidget(self.status_label) + device_layout.addWidget(self.power_button) + device_layout.addWidget(self.connect_button) + device_layout.addWidget(self.capture_button) + device_info.setLayout(device_layout) + + # 事件 + self.power_button.clicked.connect(self.on_power) + self.on_power() # init + + # 信号 + AppContext.get_edge_context().get_component('gpio').signals.gpio_camera_opened.connect( + lambda pin: self.power_button.setText("断电")) + AppContext.get_edge_context().get_component('gpio').signals.gpio_camera_closed.connect( + lambda pin: self.power_button.setText("通电")) + + # 数据展示部分 + data_display = QFrame() + data_display.setStyleSheet("background-color: white;") + data_display.setFrameShape(QFrame.StyledPanel) + + # 垂直布局 + layout = QVBoxLayout() + layout.addWidget(device_info) + layout.addWidget(data_display) + self.setLayout(layout) + + def on_power(self): + gpio_manager = AppContext.get_edge_context().get_component('gpio') + if gpio_manager.camera_value == 0: + gpio_manager.close_camera() + else: + gpio_manager.open_camera() + + +# 雷达组件 +class LidarWidget(QWidget): + def __init__(self): + super().__init__() + self.ratio = AppContext.get_ratio() + + # 设备信息部分 + device_info = QWidget() + device_info.setFixedHeight(self.ratio * 80) + device_layout = QHBoxLayout() + self.serial_label = QLabel("激光雷达: 3GGDLCM00201561") + self.status_label = QLabel("状态: 未连接") + self.power_button = QPushButton("通电") + self.connect_button = QPushButton("启动") + self.power_button.setFixedHeight(self.ratio * 42) + self.power_button.setFixedWidth(self.ratio * 64) + self.connect_button.setFixedHeight(self.ratio * 42) + self.connect_button.setFixedWidth(self.ratio * 64) + device_layout.addWidget(self.serial_label) + device_layout.addWidget(self.status_label) + device_layout.addWidget(self.power_button) + device_layout.addWidget(self.connect_button) + device_info.setLayout(device_layout) + + # 事件 + self.power_button.clicked.connect(self.on_power) + self.on_power() # init + + # 信号 + AppContext.get_edge_context().get_component('gpio').signals.gpio_lidar_opened.connect( + lambda pin: self.power_button.setText("断电")) + AppContext.get_edge_context().get_component('gpio').signals.gpio_lidar_closed.connect( + lambda pin: self.power_button.setText("通电")) + + # 数据展示部分 + data_display = QFrame() + data_display.setStyleSheet("background-color: white;") + data_display.setFrameShape(QFrame.StyledPanel) + + # 垂直布局 + layout = QVBoxLayout() + layout.addWidget(device_info) + layout.addWidget(data_display) + self.setLayout(layout) + + def on_power(self): + gpio_manager = AppContext.get_edge_context().get_component('gpio') + if gpio_manager.lidar_value == 0: + gpio_manager.close_lidar() + else: + gpio_manager.open_lidar() + + +class DeviceWidget(QWidget): + def __init__(self): + super().__init__() + + layout = QHBoxLayout() + self.camera_widget = CameraWidget() + self.lidar_widget = LidarWidget() + + layout.addWidget(self.camera_widget) + layout.addWidget(self.lidar_widget) + + self.setLayout(layout) diff --git a/widget/embed_detail.py b/widget/embed_detail.py new file mode 100644 index 0000000..6cf3685 --- /dev/null +++ b/widget/embed_detail.py @@ -0,0 +1,54 @@ +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QLabel, QPushButton, QGridLayout, QDialog + + +class EmbedDetail(QDialog): + def __init__(self, parent=None): + super(EmbedDetail, self).__init__(parent) + + self.setWindowFlags(Qt.WindowStaysOnTopHint) + self.setWindowFlags(Qt.FramelessWindowHint) + self.setWindowModality(Qt.ApplicationModal) + self.setAttribute(Qt.WA_DeleteOnClose) + self.setWindowTitle("预埋件详情") + + self.item_label_x = QLabel(self) + self.item_label_x.setText("X:") + self.item_label_x_value = QLabel(self) + + self.item_label_y = QLabel(self) + self.item_label_y.setText("Y:") + self.item_label_y_value = QLabel(self) + + self.item_label_w = QLabel(self) + self.item_label_w.setText("W:") + self.item_label_w_value = QLabel(self) + + self.item_label_h = QLabel(self) + self.item_label_h.setText("H:") + self.item_label_h_value = QLabel(self) + + item_button = QPushButton(self) + item_button.setText("关闭") + item_button.clicked.connect(self.reject) + + layout = QGridLayout() + layout.setContentsMargins(20,10,20,15) + layout.setHorizontalSpacing(5) + layout.addWidget(self.item_label_x, 0, 0) + layout.addWidget(self.item_label_x_value, 0, 1) + layout.addWidget(self.item_label_y, 1, 0) + layout.addWidget(self.item_label_y_value, 1, 1) + layout.addWidget(self.item_label_w, 2, 0) + layout.addWidget(self.item_label_w_value, 2, 1) + layout.addWidget(self.item_label_h, 3, 0) + layout.addWidget(self.item_label_h_value, 3, 1) + layout.addWidget(item_button, 4, 0, 1, 2) + + self.setLayout(layout) + + def setInfo(self, param): + self.item_label_x_value.setText(str(param["x"])) + self.item_label_y_value.setText(str(param["y"])) + self.item_label_w_value.setText(str(param["w"])) + self.item_label_h_value.setText(str(param["h"])) \ No newline at end of file diff --git a/widget/embed_item.py b/widget/embed_item.py new file mode 100644 index 0000000..18e4b84 --- /dev/null +++ b/widget/embed_item.py @@ -0,0 +1,56 @@ +from PyQt5.QtCore import QEvent, QRect, QSize +from PyQt5.QtWidgets import QLabel, QPushButton, QFrame +from widget.embed_detail import EmbedDetail + +class EmbedItem(QFrame): + + def __init__(self, parent=None): + super(EmbedItem, self).__init__(parent) + + self.rect = None + self.param = None + + self.item_button = QPushButton(self) + self.item_button.clicked.connect(self.click) + self.item_label = QLabel(self) + + def resizeEvent(self, event: QEvent): + super().resizeEvent(event) + button_size = self.item_button.size() + label_size = self.item_label.sizeHint() + self.setFixedSize(max(button_size.width(), label_size.width()), button_size.height() + label_size.height() +2) + widget_size = self.size() + if label_size.width() > button_size.width(): + self.item_button.move((widget_size.width() - button_size.width()) / 2, 0) + self.item_label.move(0, button_size.height() + 2) + self.move(self.rect.x() - (widget_size.width() - button_size.width()) / 2, self.rect.y()) + else: + self.item_label.move((widget_size.width() - label_size.width()) / 2, button_size.height() + 2) + self.item_button.move(0, 0) + self.move(self.rect.x(), self.rect.y()) + + def setItemParam(self, param): + self.param = param + self.rect = QRect(param["x"], param["y"], param["w"], param["h"]) + self.item_button.setFixedSize(QSize(param["w"], param["h"])) + self.item_button.setObjectName(param["code"]) + self.item_label.setText(param["code"]) + self.item_label.move(0, self.rect.height() + 5) + + def setIndex(self, index): + self.item_button.setText(index) + + def setChecked(self): + self.item_button.setStyleSheet("border: 1px solid #6cff6c;") + + def setError(self): + self.item_button.setStyleSheet("border: 1px solid #FF4444;") + + def click(self): + if self.item_button.text() is None or self.item_button.text() == "": return + detail = EmbedDetail() + detail.setInfo(self.param) + relative_pos = self.item_button.pos() + global_pos = self.mapToGlobal(relative_pos) + detail.move(global_pos.x() + self.item_button.width() + 2, global_pos.y() + 2) + detail.exec_() diff --git a/widget/task_edit.py b/widget/task_edit.py new file mode 100644 index 0000000..a3cb358 --- /dev/null +++ b/widget/task_edit.py @@ -0,0 +1,98 @@ +import json +from datetime import datetime + +import pandas as pd +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLineEdit, QLabel, QHBoxLayout, QPushButton, QFileDialog, \ + QPlainTextEdit, QGridLayout + +from core.context import AppContext +from widget.task_run import TaskRunDialog + + +class TaskEditWidget(QWidget): + def __init__(self, parent=None): + super(TaskEditWidget, self).__init__(parent) + self.ratio = AppContext.get_ratio() + self.run_dialog = None + + # 导入数据按钮 + file_button = QPushButton('导入任务数据') + file_button.setObjectName('fileButton') + file_button.setFixedHeight(self.ratio * 42) + file_button.setStyleSheet("background-color: #3A36DB;") + file_button.clicked.connect(self.import_excel) + + # 任务名称 + item_layout = QHBoxLayout() + item_layout.setContentsMargins(0, 0, 0, 0) + name_label = QLabel('任务名称') + self.name_text = QLineEdit() + self.name_text.setObjectName('name_text') + self.name_text.setReadOnly(True) + self.name_text.textChanged.connect(self.text_changed) + item_layout.addWidget(name_label) + item_layout.addSpacing(10) + item_layout.addWidget(self.name_text) + name_item = QWidget() + name_item.setLayout(item_layout) + + # 数据内容 + json_widget = QWidget() + json_widget.setObjectName('jsonWidget') + json_layout = QGridLayout() + json_layout.setContentsMargins(10, 10, 1, 10) + self.file_content = QPlainTextEdit() + self.file_content.setReadOnly(True) + self.file_content.textChanged.connect(self.text_changed) + json_layout.addWidget(self.file_content) + json_widget.setLayout(json_layout) + + # 任务开始按钮 + self.start_task_button = QPushButton('开始任务') + self.start_task_button.setObjectName('startTaskButton') + self.start_task_button.setEnabled(False) + self.start_task_button.setFixedWidth(self.ratio * 200) + self.start_task_button.setFixedHeight(self.ratio * 42) + self.start_task_button.clicked.connect(self.start_task) + + layout = QVBoxLayout() + layout.setSpacing(20) + layout.setContentsMargins(100,30,100,30) + layout.addWidget(file_button) + layout.addWidget(name_item) + layout.addWidget(json_widget) + layout.addWidget(self.start_task_button, alignment=Qt.AlignRight) + + self.setLayout(layout) + + def import_excel(self): + options = QFileDialog.Options() + file_path, _ = QFileDialog.getOpenFileName(self, "选择 Excel 文件", "", "Excel Files (*.xlsx *.xls)", options=options) + + if file_path: + try: + # Read Excel file into a pandas DataFrame + df = pd.read_excel(file_path) + # Convert DataFrame to JSON + json_data = json.loads(df.to_json(orient='records')) + for data in json_data: + if data["name"] is not None and data["name"] != '' : + self.name_text.setText(data["name"]) + del data["name"] + json_data_str = json.dumps(json_data, indent=4, ensure_ascii=False) + self.file_content.setPlainText(json_data_str) + except Exception as e: + self.file_content.setPlainText(f'Error: {e}') + + def start_task(self): + task_table = AppContext.get_edge_context().get_component("dat_task") + task_table.insert_task({ "name": self.name_text.text(), "param_json": self.file_content.toPlainText(), "start_time": datetime.now() }) + self.run_dialog = TaskRunDialog(json.loads(self.file_content.toPlainText())) + dialog_result = self.run_dialog.exec_() + if dialog_result == 0: + self.name_text.setText(None) + self.file_content.setPlainText(None) + + def text_changed(self): + self.start_task_button.setEnabled(self.file_content.toPlainText() !='' and self.name_text.text() != '') diff --git a/widget/task_list.py b/widget/task_list.py new file mode 100644 index 0000000..e8c578f --- /dev/null +++ b/widget/task_list.py @@ -0,0 +1,138 @@ +import json +from datetime import datetime + +import pandas as pd +from PyQt5.QtCore import Qt, QRect +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QGridLayout, QLineEdit, QLabel, QHBoxLayout, QPushButton, QFileDialog, \ + QScrollArea, QPlainTextEdit + +from core.context import AppContext +from widget.task_run import TaskRunDialog + + +class TaskListWidget(QWidget): + def __init__(self, parent=None): + super(TaskListWidget, self).__init__(parent) + self.ratio = AppContext.get_ratio() + self.run_dialog = None + + widget = QWidget() + + scroll_area = QScrollArea() + scroll_area.setStyleSheet("border: none;") + scroll_area.setWidgetResizable(True) + scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + + self.content_widget = QWidget() + + scroll_area.setWidget(self.content_widget) + + widget_layout = QVBoxLayout(widget) + widget_layout.setContentsMargins(10, 10, 10, 10) + widget_layout.setSpacing(0) + widget_layout.addWidget(scroll_area) + + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + layout.addWidget(widget) + + self.initUi() + + def initUi(self): + task_table = AppContext.get_edge_context().get_component("dat_task") + tasks = task_table.list_task(1) + + # 添加 TITLE + x = 0 + y = 0 + offset = 10 + x = x + offset + y = y + offset + label_name = QLabel(self.content_widget) + label_name.setObjectName("THeader") + label_name.setText("任务名称") + label_name.setGeometry(QRect(x, y, 200, 30)) + x = x + 200 + offset + label_sn = QLabel(self.content_widget) + label_sn.setObjectName("THeader") + label_sn.setText("设备序列号") + label_sn.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + label_param = QLabel(self.content_widget) + label_param.setObjectName("THeader") + label_param.setText("参数") + label_param.setGeometry(QRect(x, y, 400, 30)) + x = x + 400 + offset + label_start = QLabel(self.content_widget) + label_start.setObjectName("THeader") + label_start.setText("开始时间") + label_start.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + label_end = QLabel(self.content_widget) + label_end.setObjectName("THeader") + label_end.setText("结束时间") + label_end.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + label_create = QLabel(self.content_widget) + label_create.setObjectName("THeader") + label_create.setText("创建时间") + label_create.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + label_action = QLabel(self.content_widget) + label_action.setObjectName("THeader") + label_action.setText("操作") + label_action.setGeometry(QRect(x, y, 200, 30)) + + y = y + 30 + offset + for i in range(10): + x = offset + task = tasks[0] + label_name = QLabel(self.content_widget) + label_name.setText(task[1]) + label_name.setGeometry(QRect(x, y, 200, 30)) + x = x + 200 + offset + label_sn = QLabel(self.content_widget) + label_sn.setText(task[2]) + label_sn.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + param = QPlainTextEdit(self.content_widget) + param.setPlainText(task[3]) + param.setReadOnly(True) + param.setGeometry(QRect(x, y, 400, 120)) + x = x + 400 + offset + label_start = QLabel(self.content_widget) + label_start.setText(task[6]) + label_start.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + label_end = QLabel(self.content_widget) + label_end.setText(task[7]) + label_end.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + label_create = QLabel(self.content_widget) + label_create.setText(task[8]) + label_create.setGeometry(QRect(x, y, 150, 30)) + x = x + 150 + offset + run_task = QPushButton(self.content_widget) + run_task.setStyleSheet("background-color: #3A36DB;") + run_task.setObjectName("run_task") + run_task.setText("开始任务") + run_task.setGeometry(QRect(x, y, 120, 30)) + run_task.clicked.connect(lambda: self.start_task(task)) + + # run_task.clicked.connect(self.start_task) + + y = y + 120 + offset + + self.content_widget.setFixedHeight(y + offset) + + def start_task(self, task): + task_table = AppContext.get_edge_context().get_component("dat_task") + task_table.update_task({ "id": task[0], "state": 1, "start_time": datetime.now() }) + self.run_dialog = TaskRunDialog(json.loads(task[3])) + dialog_result = self.run_dialog.exec_() + if dialog_result == 0: + c = 0 + # self.name_text.setText(None) + # self.file_content.setPlainText(None) \ No newline at end of file diff --git a/widget/task_run.py b/widget/task_run.py new file mode 100644 index 0000000..6048a77 --- /dev/null +++ b/widget/task_run.py @@ -0,0 +1,252 @@ +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import QDialog, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QScrollArea, QPushButton, \ + QSpacerItem, QSizePolicy, QLabel + +from core.context import AppContext +from widget.embed_item import EmbedItem + +class TaskRunDialog(QDialog): + def __init__(self, param, parent=None): + super(TaskRunDialog, self).__init__(parent) + self.ratio = AppContext.get_ratio() + self.checkIndex = 1 + self.param = param + self.setWindowState(Qt.WindowMaximized) + self.setWindowFlags(Qt.WindowStaysOnTopHint) + self.setWindowFlags(Qt.FramelessWindowHint) + self.setWindowModality(Qt.ApplicationModal) + self.setAttribute(Qt.WA_DeleteOnClose) + self.setWindowTitle("检测窗口") + + self.bim_widget = None + self.check_widget = None + self.embed_widgets = [] + + self.start_adjust_button = None + self.start_check_button = None + self.stop_check_button = None + self.stop_task_button = None + + self.init_ui() + + def init_ui(self): + + # bim + self.bim_widget = QWidget() + self.bim_widget.setObjectName("bimWidget") + self.bim_widget.setFixedWidth(3000) + + # 选择区 + self.check_widget = QWidget(self.bim_widget) + self.check_widget.setObjectName("checkWidget") + self.check_widget.setFixedSize(160, 160) + self.check_widget.move(90, 230) + + # 预埋件 + self.embed_widgets = [] + for item in self.param: + embed_item = EmbedItem(self.bim_widget) + embed_item.setObjectName(item["code"]) + embed_item.setItemParam(item) + self.embed_widgets.append(embed_item) + + map_widget = QWidget() + legend_widget = QWidget() + + legend_layout = QHBoxLayout() + legend_layout.setContentsMargins(0, 0, 0, 0) + legend_layout.setSpacing(0) + legend_widget.setLayout(legend_layout) + + legend_check_widget = QWidget() + legend_check_widget.setObjectName("legendCheckWidget") + legend_std_widget = QWidget() + legend_std_widget.setObjectName("legendStdWidget") + legend_ok_widget = QWidget() + legend_ok_widget.setObjectName("legendOkWidget") + legend_ng_widget = QWidget() + legend_ng_widget.setObjectName("legendNgWidget") + legend_normal_widget = QWidget() + legend_normal_widget.setObjectName("legendNormalWidget") + + legend_check_label = QLabel() + legend_check_label.setObjectName("legendCheckLabel") + legend_check_label.setText("检测区域") + legend_std_label = QLabel() + legend_std_label.setObjectName("legendStdLabel") + legend_std_label.setText("基准件") + legend_ok_label = QLabel() + legend_ok_label.setObjectName("legendOkLabel") + legend_ok_label.setText("合格件") + legend_ng_label = QLabel() + legend_ng_label.setObjectName("legendNgLabel") + legend_ng_label.setText("不合格件") + legend_normal_label = QLabel() + legend_normal_label.setObjectName("legendNormalLabel") + legend_normal_label.setText("待检测件") + + legend_layout.addStretch(1) + legend_layout.addWidget(legend_check_widget) + legend_layout.addSpacing(5) + legend_layout.addWidget(legend_check_label) + legend_layout.addSpacing(40) + legend_layout.addWidget(legend_std_widget) + legend_layout.addSpacing(5) + legend_layout.addWidget(legend_std_label) + legend_layout.addSpacing(40) + legend_layout.addWidget(legend_ok_widget) + legend_layout.addSpacing(5) + legend_layout.addWidget(legend_ok_label) + legend_layout.addSpacing(40) + legend_layout.addWidget(legend_ng_widget) + legend_layout.addSpacing(5) + legend_layout.addWidget(legend_ng_label) + legend_layout.addSpacing(40) + legend_layout.addWidget(legend_normal_widget) + legend_layout.addSpacing(5) + legend_layout.addWidget(legend_normal_label) + legend_layout.addStretch(1) + + content_widget = QWidget() + content_widget.setObjectName("contentWidget") + + picture_label = QLabel() + picture_label.setObjectName("pictureWidget") + picture_label.setFixedWidth(450) + picture_label.setFixedHeight(450) + picture_label.setScaledContents(True) + picture_pixmap = QPixmap("assets/img2.png") + picture_label.setPixmap(picture_pixmap) + + view_widget = QWidget() + view_widget.setObjectName("viewWidget") + view_widget.setFixedHeight(450) + + view_label = QLabel(view_widget) + view_label.setObjectName("viewLabel") + view_label.setFixedHeight(450) + view_label.setScaledContents(True) + view_pixmap = QPixmap("assets/img1.jpg") + sc = view_pixmap.height() / 450 + view_label.setFixedWidth(view_pixmap.width() / sc) + view_label.setPixmap(view_pixmap) + view_label.setGeometry((view_widget.width() - view_label.width() / 2), 0, view_label.width(), view_label.height()) + + view_check_widget = QWidget(view_widget) + view_check_widget.setObjectName("viewCheckWidget") + view_check_widget.setGeometry(view_label.geometry().x() + 145, 170, 240, 240) + + # view_widget = QWidget() + # view_widget.setObjectName("viewWidget") + # view_widget.setFixedWidth(1000) + # view_widget.setFixedHeight(450) + # picture_widget = QWidget() + # picture_widget.setObjectName("pictureWidget") + # picture_widget.setFixedHeight(450) + + tool_widget = QWidget() + tool_widget.setObjectName("toolWidget") + tool_widget.setFixedWidth(150) + tool_widget.setFixedHeight(450) + + tool_layout = QVBoxLayout() + tool_layout.setContentsMargins(0, 0, 0, 0) + tool_layout.setSpacing(0) + + self.start_adjust_button = QPushButton() + self.start_adjust_button.setObjectName("startAdjustButton") + self.start_adjust_button.setText("开始校准") + self.start_adjust_button.setFixedHeight(self.ratio * 42) + self.start_adjust_button.clicked.connect(self.start_adjust) + + self.start_check_button = QPushButton() + self.start_check_button.setObjectName("startCheckButton") + self.start_check_button.setEnabled(False) + self.start_check_button.setText("开始检测") + self.start_check_button.setFixedHeight(self.ratio * 42) + self.start_check_button.clicked.connect(self.start_check) + + self.stop_check_button = QPushButton() + self.stop_check_button.setObjectName("stopCheckButton") + self.stop_check_button.setEnabled(False) + self.stop_check_button.setText("停止检测") + self.stop_check_button.setFixedHeight(self.ratio * 42) + self.stop_check_button.clicked.connect(self.stop_check) + + self.stop_task_button = QPushButton() + self.stop_task_button.setObjectName("stopTaskButton") + self.stop_task_button.setText("结束任务") + self.stop_task_button.setFixedHeight(self.ratio * 42) + self.stop_task_button.clicked.connect(self.reject) + + tool_widget.setLayout(tool_layout) + + spacer = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding) + tool_layout.addItem(spacer) + tool_layout.addWidget(self.start_adjust_button) + tool_layout.addSpacing(20) + tool_layout.addWidget(self.start_check_button) + tool_layout.addSpacing(20) + tool_layout.addWidget(self.stop_check_button) + tool_layout.addSpacing(20) + tool_layout.addWidget(self.stop_task_button) + tool_layout.addSpacing(20) + + content_layout = QHBoxLayout() + content_layout.setContentsMargins(0, 0, 0, 20) + content_layout.setSpacing(0) + content_layout.addWidget(view_widget) + content_layout.addSpacing(20) + content_layout.addWidget(picture_label) + content_layout.addSpacing(20) + content_layout.addWidget(tool_widget) + content_widget.setLayout(content_layout) + + layout = QVBoxLayout() + layout.setContentsMargins(20, 20, 20, 20) + layout.setSpacing(0) + layout.addWidget(map_widget) + layout.addSpacing(5) + layout.addWidget(legend_widget) + layout.addSpacing(20) + layout.addWidget(content_widget) + self.setLayout(layout) + + map_area = QScrollArea() + map_area.setObjectName("mapArea") + map_area.setWidgetResizable(True) + map_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + map_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + map_widget.setLayout(QGridLayout()) + map_widget.layout().setContentsMargins(0, 0, 0, 0) + map_widget.layout().setSpacing(0) + map_widget.layout().addWidget(map_area) + map_area.setWidget(self.bim_widget) + + def start_adjust(self): + self.start_adjust_button.setEnabled(False) + self.start_check_button.setEnabled(True) + self.stop_check_button.setEnabled(False) + + def start_check(self): + self.start_adjust_button.setEnabled(False) + self.start_check_button.setEnabled(False) + self.stop_check_button.setEnabled(True) + + index = 0 + for item in self.embed_widgets: + if item.objectName() == 'STD': continue + if self.check_widget.geometry().contains(item.rect): + item.setIndex(str(self.checkIndex)) + if index % 2 == 0: + item.setChecked() + else: + item.setError() + index += 1 + + def stop_check(self): + self.start_adjust_button.setEnabled(True) + self.start_check_button.setEnabled(False) + self.stop_check_button.setEnabled(False) + self.checkIndex += 1