Compare commits

...

27 Commits
v1.3 ... master

Author SHA1 Message Date
luocai
21df3a8ee3 修改参数保存方式。
All checks were successful
Release tag / build (push) Successful in 2m19s
2024-12-31 11:29:56 +08:00
luocai
d86a45680b 添加异常判断处理。 2024-12-27 18:10:34 +08:00
d3ccaba32f fix linux ota file error. 2024-12-16 19:01:28 +08:00
7c2a29740b linux not use gbk. 2024-12-16 16:59:23 +08:00
8f72fbded7 add log for create udp. 2024-12-16 15:59:29 +08:00
luocai
798962e8bb 适配Linux。 2024-12-16 15:34:40 +08:00
luocai
fd494f2a03 实现检测阈值设置。 2024-12-13 16:57:00 +08:00
luocai
dbe7c1a64e 实现安全帽阈值下发控制。 2024-12-12 18:49:42 +08:00
luocai
559867bd31 添加安全帽检测阈值设置。 2024-12-11 19:05:37 +08:00
luocai
7284f18c43 更新版本。
All checks were successful
Release tag / build (push) Successful in 3m9s
2024-12-04 15:24:37 +08:00
luocai
fb27b0fa57 实现向设备发起防夹灵敏度设置,恢复出厂设置请求。 2024-12-04 15:23:42 +08:00
luocai
77dd027ff7 将任务定时器和心跳计时器分开。
All checks were successful
Release tag / build (push) Successful in 2m46s
2024-12-03 19:11:30 +08:00
luocai
8e0018480b 精简打包。 2024-12-03 18:22:55 +08:00
luocai
16757c5f78 修复Windows7打包行为。 2024-12-03 18:13:16 +08:00
luocai
ef7aa73c8d 修复早期T009版本不回复不支持的命令,导致上位机不发送后续命令的问题。
All checks were successful
Release tag / build (push) Successful in 3m5s
2024-12-03 17:56:24 +08:00
95b68bc67d config mbedtls. 2024-12-02 23:23:49 +08:00
d0966cfcc7 修正ci拉取代码步骤。
All checks were successful
Release tag / build (push) Successful in 2m53s
2024-11-14 12:15:48 +08:00
d6a36c0450 完善CI构建。
Some checks failed
Release tag / build (push) Failing after 49s
2024-11-14 12:11:59 +08:00
27e5db45d6 减小打包体积。 2024-11-14 11:20:32 +08:00
luocai
51f9a2af52 打包时显示版本号。 2024-11-13 14:54:14 +08:00
luocai
0b3198c90e 修正编译条件。 2024-11-13 14:40:10 +08:00
luocai
f55d3aa9df 实现视频旋转及翻转指令控制。 2024-11-13 14:25:22 +08:00
luocai
bf3ad2be2f 修改升级文件校验。 2024-10-21 19:35:49 +08:00
luocai
f230e4cc8b update ffmpeg ver. 2024-10-21 17:00:48 +08:00
baa991dba0 fix build env.
All checks were successful
Release tag / build (push) Successful in 3m2s
2024-10-15 18:58:02 +08:00
luocai
b46cd406a7 支持CI构建。
Some checks failed
Release tag / build (push) Failing after 17s
2024-10-15 18:14:21 +08:00
luocai
6810dbd801 增加设备发现过滤配置,以灵活支持T013以及后续型号。 2024-10-15 17:48:51 +08:00
17 changed files with 1114 additions and 150 deletions

View File

@ -0,0 +1,33 @@
name: Release tag
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: [windows11]
steps:
- name: Clone repository
run: |
$ref = "${{ github.ref }}"
if ($ref -like 'refs/heads/*') {
$branch = $ref -replace '^refs/heads/', ''
} elseif ($ref -like 'refs/tags/*') {
$branch = $ref -replace '^refs/tags/', ''
} else {
$branch = $ref
}
git clone --depth 1 --branch=$branch https://${{ secrets.GIT_USERNAME }}:${{ secrets.GIT_PASSWORD }}@amass.fun/gitea/${{ gitea.repository }}.git .
git checkout ${{ github.sha }}
- name: Build and deploy
run: |
resources/build.ps1 build
resources/build.ps1 deploy
resources/build.ps1 changelog
- name: Upload Gitea Release
uses: akkuman/gitea-release-action@v1
with:
body_path: build/CHANGELOG.txt
files: |-
build/视觉防夹设备上位机工具*.zip

View File

@ -12,5 +12,7 @@
<file>resources/prompt_delete.svg</file>
<file>resources/successfull.svg</file>
<file>resources/warning.svg</file>
<file>qml/MessageBar.qml</file>
<file>qml/Object.qml</file>
</qresource>
</RCC>

View File

@ -40,7 +40,7 @@ Application::Application(int &argc, char **argv)
font.setFamily("微软雅黑");
m_app->setFont(font);
m_app->setApplicationName(APPLICATION_NAME);
m_app->setApplicationVersion(QString("V%1").arg(APP_VERSION));
m_app->setApplicationVersion(QString("v%1").arg(APP_VERSION));
m_player->open();
}
@ -127,7 +127,24 @@ void Application::setCurrentAntiClipAreaEnabled(bool enabled) {
emit currentAntiClipAreaEnabledChanged();
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateAntiClipAreaPoints(m_currentAntiClipAreaEnabled, m_currentAntiClipAreaPoints);
device->updateAntiClipAreaPoints(m_currentAntiClipAreaEnabled, m_currentAntiClipAreaPoints,
m_currentAntiClipSensitivity);
}
}
}
int Application::currentAntiClipSensitivity() const {
return m_currentAntiClipSensitivity;
}
void Application::setCurrentAntiClipSensitivity(int sensitivity) {
if (m_currentAntiClipSensitivity != sensitivity) {
m_currentAntiClipSensitivity = sensitivity;
emit currentAntiClipSensitivityChanged();
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateAntiClipAreaPoints(m_currentAntiClipAreaEnabled, m_currentAntiClipAreaPoints,
m_currentAntiClipSensitivity);
}
}
}
@ -153,7 +170,7 @@ void Application::updateOpenDoorAreaPoints(const QList<QPointF> &points) {
void Application::updateAntiClipAreaPoints(const QList<QPointF> &points) {
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateAntiClipAreaPoints(m_currentAntiClipAreaEnabled, points);
device->updateAntiClipAreaPoints(m_currentAntiClipAreaEnabled, points, m_currentAntiClipSensitivity);
}
}
@ -164,6 +181,80 @@ void Application::updateShieldedAreaPoints(const QList<QPointF> &points) {
}
}
bool Application::currentDeviceFlip() const {
return m_currentDeviceFlip;
}
void Application::setCurrentDeviceFlip(bool flip) {
if (m_currentDeviceFlip != flip) {
m_currentDeviceFlip = flip;
emit currentDeviceFlipChanged();
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateFlip(flip);
}
}
}
int Application::currentDeviceRotation() const {
return m_currentDeviceRotation;
}
void Application::setCurrentDeviceRotation(int rotation) {
if (m_currentDeviceRotation != rotation) {
m_currentDeviceRotation = rotation;
emit currentDeviceRotationChanged();
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateRotation(rotation);
}
}
}
int Application::currentHelmetThreshold() const {
return m_currentHelmetThreshold;
}
void Application::setCurrentHelmetThreshold(int threshold) {
if (m_currentHelmetThreshold != threshold) {
m_currentHelmetThreshold = threshold;
emit currentHelmetThresholdChanged();
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateDetectThreshold(m_currentHelmetThreshold, m_currentHeadThreshold, m_currentDetectFrameSize);
}
}
}
int Application::currentHeadThreshold() const {
return m_currentHeadThreshold;
}
void Application::setCurrentHeadThreshold(int threshold) {
if (m_currentHeadThreshold != threshold) {
m_currentHeadThreshold = threshold;
emit currentHeadThresholdChanged();
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateDetectThreshold(m_currentHelmetThreshold, m_currentHeadThreshold, m_currentDetectFrameSize);
}
}
}
int Application::currentDetectFrameSize() const {
return m_currentDetectFrameSize;
}
void Application::setCurrentDetectFrameSize(int size) {
if (m_currentDetectFrameSize != size) {
m_currentDetectFrameSize = size;
emit currentDetectFrameSizeChanged();
if (!m_device.expired()) {
auto device = m_device.lock();
device->updateDetectThreshold(m_currentHelmetThreshold, m_currentHeadThreshold, m_currentDetectFrameSize);
}
}
}
void Application::updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, const QString &gateway,
const QString &dns) {
if (!m_device.expired()) {
@ -180,9 +271,13 @@ void Application::updateNetworkInfomation(bool dhcp, const QString &ip, const QS
void Application::connectToDevice(int index) {
if (!m_device.expired()) {
auto device = m_device.lock();
disconnect(device.get(), &DeviceConnection::rotationChanged, this, &Application::onDeviceRotationChanged);
disconnect(device.get(), &DeviceConnection::flipChanged, this, &Application::onDeviceFlipChanged);
disconnect(device.get(), &DeviceConnection::openDoorAreaChanged, this, &Application::onDeviceOpenDoorArea);
disconnect(device.get(), &DeviceConnection::shieldedAreaChanged, this, &Application::onDeviceShieldedArea);
disconnect(device.get(), &DeviceConnection::antiClipAreaChanged, this, &Application::onDeviceAntiClipArea);
disconnect(device.get(), &DeviceConnection::detectThresholdChanged, this,
&Application::onDeviceDetectThresholdChanged);
disconnect(device.get(), &DeviceConnection::networkInfomationChanged, this,
&Application::onDeviceNetworkInfomation);
disconnect(device.get(), &DeviceConnection::firmwareChanged, this, &Application::onDeviceFirmware);
@ -202,9 +297,13 @@ void Application::connectToDevice(int index) {
auto device = m_devices->device(index);
m_device = device;
connect(device.get(), &DeviceConnection::rotationChanged, this, &Application::onDeviceRotationChanged);
connect(device.get(), &DeviceConnection::flipChanged, this, &Application::onDeviceFlipChanged);
connect(device.get(), &DeviceConnection::openDoorAreaChanged, this, &Application::onDeviceOpenDoorArea);
connect(device.get(), &DeviceConnection::shieldedAreaChanged, this, &Application::onDeviceShieldedArea);
connect(device.get(), &DeviceConnection::antiClipAreaChanged, this, &Application::onDeviceAntiClipArea);
connect(device.get(), &DeviceConnection::detectThresholdChanged, this,
&Application::onDeviceDetectThresholdChanged);
connect(device.get(), &DeviceConnection::networkInfomationChanged, this,
&Application::onDeviceNetworkInfomation);
connect(device.get(), &DeviceConnection::firmwareChanged, this, &Application::onDeviceFirmware);
@ -220,22 +319,34 @@ void Application::connectToDevice(int index) {
}
});
device->setLiveStreamEnabled(true);
auto area = device->area();
m_currentOpenDoorAreaWay = area.openDoorAreaWay;
m_currentOpenDoorAreaPoints = area.openDoorArea;
m_currentShieldedAreaEnabled = area.shieldedAreaEnabled;
m_currentShieldedAreaPoints = area.shieldedArea;
m_currentAntiClipAreaEnabled = area.antiClipAreaEnabled;
m_currentAntiClipAreaPoints = area.antiClipArea;
auto info = device->infomation();
m_currentOpenDoorAreaWay = info.openDoorAreaWay;
m_currentOpenDoorAreaPoints = info.openDoorArea;
m_currentShieldedAreaEnabled = info.shieldedAreaEnabled;
m_currentShieldedAreaPoints = info.shieldedArea;
m_currentAntiClipAreaEnabled = info.antiClipAreaEnabled;
m_currentAntiClipAreaPoints = info.antiClipArea;
m_currentAntiClipSensitivity = info.antiClipSensitivity;
m_currentNetworkInfomation = device->networkInfomation();
m_currentFirmware = device->infomation().firmwareVersion;
m_currentDeviceConnected = device->isConnected();
m_currentDeviceFlip = info.flip;
m_currentDeviceRotation = info.rotation;
m_currentHelmetThreshold = info.helmetThreshold;
m_currentHeadThreshold = info.headThreshold;
m_currentDetectFrameSize = info.detectFrameSize;
emit currentDeviceRotationChanged();
emit currentDeviceFlipChanged();
emit currentOpenDoorAreaPointsChanged();
emit currentShieldedAreaPointsChanged();
emit currentAntiClipAreaPointsChanged();
emit currentOpenDoorAreaWayChanged();
emit currentShieldedAreaEnabledChanged();
emit currentAntiClipAreaEnabledChanged();
emit currentAntiClipSensitivityChanged();
emit currentHelmetThresholdChanged();
emit currentHeadThresholdChanged();
emit currentDetectFrameSizeChanged();
emit currentNetworkInfomationChanged();
}
emit currentFirmwareChanged();
@ -248,23 +359,34 @@ void Application::startSearchDevice() {
}
void Application::upgradeDevice(const QString &file) {
constexpr auto versionPrefix = "RD_T009";
if (m_device.expired()) return;
auto device = m_device.lock();
auto infomation = device->infomation();
auto versionPrefix = infomation.softwareVersion.left(7);
constexpr auto version = "RD_T009_V21R003B013";
QFileInfo fileInfo(file);
QString baseName = fileInfo.baseName();
int position = baseName.indexOf(versionPrefix);
if (position < 0 || ((baseName.length() - position) < std::strlen(version))) {
emit newMessage(2, "OTA升级", "文件名格式不合法!");
LOG(error) << "baseName: " << baseName.toStdString() << ", position: " << position;
return;
}
QString firmware = baseName.mid(position, std::strlen(version));
if (!m_device.expired()) {
auto device = m_device.lock();
if (device->isConnected()) {
device->requestOta(firmware, file);
} else {
emit newMessage(2, "OTA升级", "设备已离线,请重新连接设备!");
}
QString firmware = baseName.mid(position);
if (device->isConnected()) {
device->requestOta(firmware, file);
} else {
emit newMessage(2, "OTA升级", "设备已离线,请重新连接设备!");
}
}
void Application::resetDevice() {
if (m_device.expired()) return;
auto device = m_device.lock();
if (device->isConnected()) {
device->requestReset();
} else {
emit newMessage(2, "恢复出厂设置", "设备已离线,请重新连接设备!");
}
}
@ -273,14 +395,28 @@ void Application::onDeviceOpenDoorArea(DeviceConnection::AreaWay way, const QLis
setCurrentOpenDoorAreaPoints(points);
}
void Application::onDeviceRotationChanged(int rotation) {
setCurrentDeviceRotation(rotation);
}
void Application::onDeviceFlipChanged(bool flip) {
setCurrentDeviceFlip(flip);
}
void Application::onDeviceShieldedArea(bool enabled, const QList<QPointF> &points) {
setCurrentShieldedAreaEnabled(enabled);
setCurrentShieldedAreaPoints(points);
}
void Application::onDeviceAntiClipArea(bool enabled, const QList<QPointF> &points) {
void Application::onDeviceAntiClipArea(bool enabled, const QList<QPointF> &points, int sensitivity) {
setCurrentAntiClipAreaEnabled(enabled);
setCurrentAntiClipAreaPoints(points);
setCurrentAntiClipSensitivity(sensitivity);
}
void Application::onDeviceDetectThresholdChanged(int helmetThreshold, int headThreshold, int detectFrameSize) {
setCurrentHelmetThreshold(helmetThreshold);
setCurrentHeadThreshold(headThreshold);
setCurrentDetectFrameSize(detectFrameSize);
}
void Application::onDeviceNetworkInfomation(const NetworkInfomation &info) {

View File

@ -34,12 +34,23 @@ class Application : public QObject {
setCurrentShieldedAreaPoints NOTIFY currentShieldedAreaPointsChanged)
Q_PROPERTY(bool currentAntiClipAreaEnabled READ currentAntiClipAreaEnabled WRITE setCurrentAntiClipAreaEnabled
NOTIFY currentAntiClipAreaEnabledChanged)
Q_PROPERTY(int currentAntiClipSensitivity READ currentAntiClipSensitivity WRITE setCurrentAntiClipSensitivity NOTIFY currentAntiClipSensitivityChanged)
Q_PROPERTY(int currentHelmetThreshold READ currentHelmetThreshold WRITE setCurrentHelmetThreshold NOTIFY
currentHelmetThresholdChanged)
Q_PROPERTY(int currentHeadThreshold READ currentHeadThreshold WRITE setCurrentHeadThreshold NOTIFY
currentHeadThresholdChanged)
Q_PROPERTY(int currentDetectFrameSize READ currentDetectFrameSize WRITE setCurrentDetectFrameSize NOTIFY
currentDetectFrameSizeChanged)
Q_PROPERTY(QList<QPointF> currentAntiClipAreaPoints READ currentAntiClipAreaPoints WRITE
setCurrentAntiClipAreaPoints NOTIFY currentAntiClipAreaPointsChanged)
Q_PROPERTY(
NetworkInfomation currentNetworkInfomation READ currentNetworkInfomation NOTIFY currentNetworkInfomationChanged)
Q_PROPERTY(QString currentFirmware MEMBER m_currentFirmware NOTIFY currentFirmwareChanged)
Q_PROPERTY(bool currentDeviceConnected MEMBER m_currentDeviceConnected NOTIFY currentDeviceConnectedChanged)
Q_PROPERTY(bool currentDeviceFlip READ currentDeviceFlip WRITE setCurrentDeviceFlip NOTIFY currentDeviceFlipChanged)
Q_PROPERTY(int currentDeviceRotation READ currentDeviceRotation WRITE setCurrentDeviceRotation NOTIFY
currentDeviceRotationChanged)
friend class Amass::Singleton<Application>;
public:
@ -61,9 +72,26 @@ public:
bool currentAntiClipAreaEnabled() const;
void setCurrentAntiClipAreaEnabled(bool enabled);
int currentAntiClipSensitivity() const;
void setCurrentAntiClipSensitivity(int sensitivity);
QList<QPointF> currentAntiClipAreaPoints() const;
void setCurrentAntiClipAreaPoints(const QList<QPointF> &points);
bool currentDeviceFlip() const;
void setCurrentDeviceFlip(bool flip);
int currentDeviceRotation() const;
void setCurrentDeviceRotation(int rotation);
int currentHelmetThreshold() const;
void setCurrentHelmetThreshold(int threshold);
int currentHeadThreshold() const;
void setCurrentHeadThreshold(int threshold);
int currentDetectFrameSize() const;
void setCurrentDetectFrameSize(int size);
Q_INVOKABLE void updateOpenDoorAreaPoints(const QList<QPointF> &points);
Q_INVOKABLE void updateAntiClipAreaPoints(const QList<QPointF> &points);
Q_INVOKABLE void updateShieldedAreaPoints(const QList<QPointF> &points);
@ -71,6 +99,7 @@ public:
const QString &gateway, const QString &dns);
Q_INVOKABLE void connectToDevice(int index);
Q_INVOKABLE void upgradeDevice(const QString &file);
Q_INVOKABLE void resetDevice();
Q_INVOKABLE void startSearchDevice();
int exec();
@ -84,17 +113,26 @@ signals:
void currentOpenDoorAreaWayChanged();
void currentShieldedAreaEnabledChanged();
void currentAntiClipAreaEnabledChanged();
void currentAntiClipSensitivityChanged();
void currentHelmetThresholdChanged();
void currentDetectFrameSizeChanged();
void currentHeadThresholdChanged();
void currentNetworkInfomationChanged();
void currentFirmwareChanged();
void currentDeviceConnectedChanged();
void currentDeviceOtaProgressChanged(bool status, int progress, const QString &message);
void currentDeviceFlipChanged();
void currentDeviceRotationChanged();
void newMessage(int type, const QString &title, const QString &message);
protected:
Application(int &argc, char **argv);
void onDeviceRotationChanged(int rotation);
void onDeviceFlipChanged(bool flip);
void onDeviceOpenDoorArea(DeviceConnection::AreaWay way, const QList<QPointF> &points);
void onDeviceShieldedArea(bool enabled, const QList<QPointF> &points);
void onDeviceAntiClipArea(bool enabled, const QList<QPointF> &points);
void onDeviceAntiClipArea(bool enabled, const QList<QPointF> &points, int sensitivity);
void onDeviceDetectThresholdChanged(int helmetThreshold, int headThreshold, int detectFrameSize);
void onDeviceNetworkInfomation(const NetworkInfomation &info);
void onDeviceFirmware(const QString &firmware);
void onDeviceConnected();
@ -114,10 +152,16 @@ private:
bool m_currentShieldedAreaEnabled = false;
QList<QPointF> m_currentShieldedAreaPoints;
bool m_currentAntiClipAreaEnabled = false;
int m_currentAntiClipSensitivity = 1;
QList<QPointF> m_currentAntiClipAreaPoints;
NetworkInfomation m_currentNetworkInfomation;
QString m_currentFirmware;
bool m_currentDeviceConnected = false;
bool m_currentDeviceFlip = false;
int m_currentDeviceRotation = 0;
int m_currentHelmetThreshold = 1;
int m_currentHeadThreshold = 2;
int m_currentDetectFrameSize = 3;
};
#endif // APPLICATION_H

View File

@ -1,13 +1,17 @@
cmake_minimum_required(VERSION 3.16)
project(AntiClipSettings VERSION 1.3 LANGUAGES C CXX)
project(AntiClipSettings VERSION 1.8 LANGUAGES C CXX)
set(APPLICATION_NAME "视觉防夹设备上位机工具")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(Projects_ROOT E:/Projects)
set(Libraries_ROOT ${Projects_ROOT}/Libraries)
if(LINUX)
set(Libraries_ROOT /opt/Libraries CACHE STRING "Libraries directory.")
else()
set(Libraries_ROOT E:/Projects/Libraries CACHE STRING "Libraries directory.")
endif()
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Qml Quick Network QuickControls2)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Qml Quick Network QuickControls2)
@ -19,25 +23,45 @@ if (Qt6_FOUND)
add_compile_definitions(
BOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN10
)
set(FFmpeg_ROOT ${Libraries_ROOT}/ffmpeg-7.0.2-full_build-shared)
else()
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(BOOST_ROOT ${Libraries_ROOT}/boost_1_83_0_msvc2022_64bit)
if(LINUX)
set(BOOST_ROOT ${Libraries_ROOT}/boost_1_83_0)
else()
set(BOOST_ROOT ${Libraries_ROOT}/boost_1_83_0_msvc2022_64bit)
endif()
set(Boost_INCLUDE_DIR ${BOOST_ROOT}/include/boost-1_83)
set(FFmpeg_ROOT ${Libraries_ROOT}/ffmpeg-6.1.1-full_build-shared)
endif()
option(Boost_USE_STATIC_LIBS OFF)
set(FFmpeg_ROOT ${Libraries_ROOT}/ffmpeg-6.1.1-full_build-shared)
set(OpenSSL_ROOT D:/Qt/Tools/OpenSSLv3/Win_x64)
set(OPENSSL_INCLUDE_DIR ${OpenSSL_ROOT}/include)
set(OpenSSL_LIBRARY_DIRS ${OpenSSL_ROOT}/lib)
set(OpenSSL_LIBRARIES libssl libcrypto)
set(FFmpeg_INCLUDE_DIR ${FFmpeg_ROOT}/include)
set(FFmpeg_LIB_DIR ${FFmpeg_ROOT}/lib)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(JPEGTURBO_ROOT ${Libraries_ROOT}/libjpeg-turbo-3.0.3_msvc2022_64bit_debug)
if(LINUX)
set(MBEDTLS_ROOT ${Libraries_ROOT}/mbedtls-3.6.2)
set(JPEGTURBO_ROOT ${Libraries_ROOT}/libjpeg-turbo-3.1.0)
else()
set(MBEDTLS_ROOT ${Libraries_ROOT}/mbedtls-3.6.2_msvc2022_64bit_debug)
set(JPEGTURBO_ROOT ${Libraries_ROOT}/libjpeg-turbo-3.0.3_msvc2022_64bit_debug)
endif()
else()
set(JPEGTURBO_ROOT ${Libraries_ROOT}/libjpeg-turbo-3.0.3_msvc2022_64bit_release)
set(MBEDTLS_ROOT ${Libraries_ROOT}/mbedtls-3.6.2_msvc2022_64bit_release)
endif()
set(MbedTLS_DIR ${MBEDTLS_ROOT}/lib/cmake/MbedTLS)
set(JPEGTURBO_INCLUDE_DIR ${JPEGTURBO_ROOT}/include)
set(JPEGTURBO_LIB_DIR ${JPEGTURBO_ROOT}/lib)
@ -95,8 +119,13 @@ set_target_properties(AntiClipSettings PROPERTIES
WIN32_EXECUTABLE TRUE
)
add_subdirectory(${Projects_ROOT}/Kylin/Universal Universal)
add_subdirectory(${Projects_ROOT}/Kylin/Encrypt Encrypt)
include(CPack)
include(FetchContent)
FetchContent_Declare(Kylin
GIT_REPOSITORY https://amass.fun/gitea/amass/Kylin.git
)
set(KYLIN_WITH_FLUENT OFF)
FetchContent_MakeAvailable(Kylin)
target_include_directories(AntiClipSettings
PRIVATE ${FFmpeg_INCLUDE_DIR}
@ -122,8 +151,9 @@ target_link_libraries(AntiClipSettings
PRIVATE avformat
PRIVATE Universal
PRIVATE Encrypt
PRIVATE turbojpeg-static
PRIVATE Ws2_32
$<$<PLATFORM_ID:Linux>:turbojpeg>
$<$<PLATFORM_ID:Windows>:turbojpeg-static>
$<$<PLATFORM_ID:Windows>:Ws2_32>
)
include(GNUInstallDirs)

View File

@ -4,13 +4,18 @@
#include <QFileInfo>
#include <QPointF>
#include <QTimer>
#include <WinSock2.h>
#include <QTimerEvent>
#include <boost/json/object.hpp>
#include <boost/json/parse.hpp>
#include <boost/json/serialize.hpp>
#include <filesystem>
#include <fstream>
#include <mbedtls/md5.h>
#ifdef Q_OS_LINUX
#include <arpa/inet.h>
#else
#include <WinSock2.h>
#endif
DeviceConnection::DeviceConnection(QObject *parent) : QObject{parent} {
}
@ -24,9 +29,9 @@ void DeviceConnection::close() {
m_otaTimer->deleteLater();
m_otaTimer = nullptr;
}
if (m_timerId > 0) {
killTimer(m_timerId);
m_timerId = -1;
if (m_heartbeatTimerId > 0) {
killTimer(m_heartbeatTimerId);
m_heartbeatTimerId = -1;
}
if (m_commandSocket != nullptr) {
m_commandSocket->deleteLater();
@ -73,10 +78,6 @@ void DeviceConnection::connect(const Infomation &infomation) {
m_h264Socket->connectToHost(infomation.ip, 8000);
}
DeviceConnection::Area DeviceConnection::area() const {
return m_area;
}
NetworkInfomation DeviceConnection::networkInfomation() const {
return m_networkInfomation;
}
@ -213,15 +214,16 @@ void DeviceConnection::requestAntiClipArea() {
m_requests.push(task);
}
void DeviceConnection::updateAntiClipAreaPoints(bool enabled, const QList<QPointF> &points) {
void DeviceConnection::updateAntiClipAreaPoints(bool enabled, const QList<QPointF> &points, int sensitivity) {
Task task;
task.command = "a03opendoor5_setdata";
task.task = [this, enabled, points]() {
task.task = [this, enabled, points, sensitivity]() {
boost::json::object request;
request["func"] = "a03opendoor5_setdata";
request["deviceid"] = "0";
boost::json::object data;
data["value"] = enabled ? "1" : "0";
data["sensitivity"] = std::to_string(sensitivity);
boost::json::array pointArray;
for (auto &p : points) {
boost::json::object point;
@ -242,6 +244,56 @@ void DeviceConnection::updateAntiClipAreaPoints(bool enabled, const QList<QPoint
m_requests.push(task);
}
QFuture<bool> DeviceConnection::updateDetectThreshold(int helmetThreshold, int headThreshold, int detectFrameSize) {
constexpr auto command = "headdetectparam_setdata";
Task task;
task.command = command;
task.task = [this, helmetThreshold, headThreshold, detectFrameSize, command]() {
boost::json::object request;
request["func"] = command;
request["deviceid"] = "0";
boost::json::object data;
if (headThreshold >= 0) {
data["headthreshold"] = headThreshold;
}
if (helmetThreshold >= 0) {
data["hatthreshold"] = helmetThreshold;
}
if (detectFrameSize >= 0) {
data["detectframenum"] = detectFrameSize;
}
request["data"] = std::move(data);
auto text = boost::json::serialize(request);
m_commandSocket->write(text.data(), text.size());
};
task.future = std::make_shared<QFutureInterface<bool>>();
if (m_requests.empty()) {
task.task();
}
auto ret = task.future->future();
m_requests.push(task);
return ret;
}
void DeviceConnection::requestHeadDetectThreshold() {
constexpr auto command = "headdetectparam_getdata";
Task task;
task.command = command;
task.task = [this, command]() {
boost::json::object request;
request["func"] = command;
request["deviceid"] = "0";
boost::json::object data;
request["data"] = std::move(data);
auto text = boost::json::serialize(request);
m_commandSocket->write(text.data(), text.size());
};
if (m_requests.empty()) {
task.task();
}
m_requests.push(task);
}
void DeviceConnection::requestResolution(Resolution resolution) {
Task task;
task.command = "quality_setdata";
@ -262,6 +314,44 @@ void DeviceConnection::requestResolution(Resolution resolution) {
m_requests.push(task);
}
void DeviceConnection::updateRotation(int rotation) {
Task task;
task.command = "a23imagerotate_setdata";
task.task = [this, rotation]() {
boost::json::object request;
request["func"] = "a23imagerotate_setdata";
request["deviceid"] = "0";
boost::json::object data;
data["value"] = std::to_string(rotation);
request["data"] = std::move(data);
auto text = boost::json::serialize(request);
m_commandSocket->write(text.data(), text.size());
LOG(info) << "updateRotation: " << text;
};
if (m_requests.empty()) {
task.task();
}
m_requests.push(task);
}
void DeviceConnection::updateFlip(bool flip) {
Task task;
task.command = "a04imageflipping_setdata";
task.task = [this, flip]() {
boost::json::object request;
request["func"] = "a04imageflipping_setdata";
request["deviceid"] = "0";
boost::json::object data;
data["value"] = flip ? "2" : "1";
request["data"] = std::move(data);
auto text = boost::json::serialize(request);
m_commandSocket->write(text.data(), text.size());
};
if (m_requests.empty()) {
task.task();
}
m_requests.push(task);
}
void DeviceConnection::requestVersion() {
Task task;
task.command = "a15devicedetail_getdata";
@ -281,6 +371,27 @@ void DeviceConnection::requestVersion() {
m_requests.push(task);
}
void DeviceConnection::requestVideoInformation() {
constexpr const char *commands[] = {"a23imagerotate_getdata", "a04imageflipping_getdata"};
for (auto command : commands) {
Task task;
task.command = command;
task.task = [this, command]() {
boost::json::object request;
request["func"] = command;
request["deviceid"] = "0";
boost::json::object data;
request["data"] = std::move(data);
auto text = boost::json::serialize(request);
m_commandSocket->write(text.data(), text.size());
};
if (m_requests.empty()) {
task.task();
}
m_requests.push(task);
}
}
void DeviceConnection::requestNetworkInfomation() {
Task task;
task.command = "netconfig_getdata";
@ -332,17 +443,21 @@ QFuture<bool> DeviceConnection::updateNetworkInfomation(bool dhcp, const QString
void DeviceConnection::requestOta(const QString &firmware, const QString &file) {
m_otaProgress = 0;
emit otaProgressChanged(true, m_otaProgress, "正在向设备发起OTA请求......");
if (m_timerId > 0) {
killTimer(m_timerId);
m_timerId = -1;
if (m_heartbeatTimerId > 0) {
killTimer(m_heartbeatTimerId);
m_heartbeatTimerId = -1;
}
if (!m_requests.empty()) {
while (!m_requests.empty()) { // 清除之前的命令
m_requests.pop();
}
Task task;
task.command = "a22devicefirmware_setdata";
task.task = [this, file, firmware]() {
#ifdef Q_OS_LINUX
std::ifstream ifs(file.toStdString(), std::ifstream::binary);
#else
std::ifstream ifs(Amass::StringUtility::UTF8ToGBK(file.toStdString()), std::ifstream::binary);
#endif
m_uploadBuffer = std::vector<uint8_t>((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
m_sendedSize = 0;
@ -377,10 +492,36 @@ void DeviceConnection::requestOta(const QString &firmware, const QString &file)
m_requests.push(task);
}
void DeviceConnection::requestReset() {
Task task;
task.command = "a12factory_setdata";
task.task = [this]() {
boost::json::object request;
request["func"] = "a12factory_setdata";
request["deviceid"] = "0";
boost::json::object data;
data["value"] = "1";
request["data"] = std::move(data);
auto text = boost::json::serialize(request);
m_commandSocket->write(text.data(), text.size());
};
task.future = std::make_shared<QFutureInterface<bool>>();
if (m_requests.empty()) {
task.task();
}
auto ret = task.future->future();
m_requests.push(task);
}
void DeviceConnection::transferBinContent() {
constexpr int SliceSize = 1024;
constexpr int WaitMd5CheckTime = 3000; // ms
if (m_sendedSize >= m_uploadBuffer.size()) return;
if (m_sendedSize >= m_uploadBuffer.size()) {
LOG(warning) << "file size is wrong, sended size: " << m_sendedSize
<< "ota file size: " << m_uploadBuffer.size();
return;
}
char buffer[1 + sizeof(int32_t) + 1024];
int sendSize = SliceSize;
@ -407,18 +548,6 @@ void DeviceConnection::transferBinContent() {
} else if (m_sendedSize >= m_uploadBuffer.size()) {
LOG(info) << "transfer ota file finished, wait " << WaitMd5CheckTime
<< " ms for send check, total sended size: " << m_sendedSize;
// QTimer::singleShot(WaitMd5CheckTime, this, [this]() {
// boost::json::object request;
// request["func"] = "a22devicefirmware_setdata";
// request["deviceid"] = "0";
// boost::json::object data;
// data["target_linux04_firmware"] = "RD_T009_V21R003B001";
// request["data"] = std::move(data);
// auto text = boost::json::serialize(request);
// m_commandSocket->write(text.data(), text.size());
// LOG(info) << "request md5 check result: " << text;
// });
if (m_otaTimer == nullptr) {
m_otaTimer = new QTimer(this);
m_otaTimer->setSingleShot(true);
@ -437,7 +566,7 @@ void DeviceConnection::transferBinContent() {
m_h264Socket->close();
},
type);
m_otaTimer->start(60 * 1000);
m_otaTimer->start(5 * 60 * 1000); // 固件升级五分钟正常升级2.5分钟左右(包含算法模型)
}
}
@ -469,8 +598,8 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
} else if (value == "2") {
way = Quadrangle;
}
m_area.openDoorAreaWay = way;
m_area.openDoorArea = points;
m_infomation.openDoorAreaWay = way;
m_infomation.openDoorArea = points;
emit openDoorAreaChanged(way, points);
} else if (function == "a03opendoor4_getdata") {
auto &data = reply.at("data").as_object();
@ -484,8 +613,8 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
point.setY(obj.at("y").as_double());
points.push_back(point);
}
m_area.shieldedAreaEnabled = value == "1";
m_area.shieldedArea = points;
m_infomation.shieldedAreaEnabled = value == "1";
m_infomation.shieldedArea = points;
emit shieldedAreaChanged(value == "1", points);
} else if (function == "a03opendoor5_getdata") {
auto &data = reply.at("data").as_object();
@ -499,9 +628,12 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
point.setY(obj.at("y").as_double());
points.push_back(point);
}
m_area.antiClipAreaEnabled = value == "1";
m_area.antiClipArea = points;
emit antiClipAreaChanged(value == "1", points);
m_infomation.antiClipAreaEnabled = value == "1";
m_infomation.antiClipArea = points;
if (data.contains("sensitivity")) {
m_infomation.antiClipSensitivity = std::stoi(static_cast<std::string>(data.at("sensitivity").as_string()));
}
emit antiClipAreaChanged(value == "1", points, m_infomation.antiClipSensitivity);
} else if (function == "netconfig_getdata") {
auto &data = reply.at("data").as_object();
m_networkInfomation.dhcp = data.at("type").as_string() == "dhcp";
@ -570,7 +702,7 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
emit otaProgressChanged(true, m_otaProgress, "设备正在升级中,请稍后......");
} else {
m_otaTimer->stop(); // 这里不需要再超时了
emit otaProgressChanged(true, 100, "设备正在升级中,请于分钟后重新连接wifi搜索设备");
emit otaProgressChanged(true, 100, "设备正在升级中,请于分钟后重新连接wifi搜索设备");
}
} else {
const char *message = nullptr;
@ -591,10 +723,69 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
}
emit otaProgressChanged(false, m_otaProgress, tip);
}
} else if (function == "a23imagerotate_getdata") {
// {"data":{"value":"2"},"deviceid":"0","flag":"ok","func":"a23imagerotate_getdata"}
auto &data = reply.at("data").as_object();
auto &value = data.at("value").as_string();
m_infomation.rotation = std::stoi(static_cast<std::string>(value));
emit rotationChanged(m_infomation.rotation);
} else if (function == "a04imageflipping_getdata") {
// {"data":{"value":"1"},"deviceid":"0","flag":"ok","func":"a04imageflipping_getdata"}
auto &data = reply.at("data").as_object();
auto &value = data.at("value").as_string();
m_infomation.flip = value == "2";
emit flipChanged(m_infomation.flip);
} else if (function == "a04imageflipping_setdata") {
if ((task != nullptr) && (task->command.toStdString() == function)) {
if (task->timeoutTimer) {
task->timeoutTimer->stop();
}
bool status = true;
if (task->future) {
task->future->reportFinished(&status);
}
}
requestVideoInformation();
} else if (function == "a23imagerotate_setdata") {
if ((task != nullptr) && (task->command.toStdString() == function)) {
if (task->timeoutTimer) {
task->timeoutTimer->stop();
}
bool status = true;
if (task->future) {
task->future->reportFinished(&status);
}
}
requestVideoInformation();
} else if (function == "a12factory_setdata") {
LOG(info) << "device factory reset";
} else if (function == "headdetectparam_getdata") {
auto &data = reply.at("data").as_object();
if (data.contains("hatthreshold")) {
m_infomation.helmetThreshold = data.at("hatthreshold").as_int64();
}
if (data.contains("headthreshold")) {
m_infomation.headThreshold = data.at("headthreshold").as_int64();
}
if (data.contains("detectframenum")) {
m_infomation.detectFrameSize = data.at("detectframenum").as_int64();
}
emit detectThresholdChanged(m_infomation.helmetThreshold, m_infomation.headThreshold,
m_infomation.detectFrameSize);
} else if (function == "thresholdwithhat_setdata") {
if ((task != nullptr) && (task->command.toStdString() == function)) {
if (task->timeoutTimer) {
task->timeoutTimer->stop();
}
bool status = true;
if (task->future) {
task->future->reportFinished(&status);
}
}
} else {
LOG(warning) << "unknown reply: " << replyText;
}
return QString::fromStdString(std::string(std::move(function)));
return QString::fromStdString(std::string(function));
}
void DeviceConnection::onConnected() {
@ -605,8 +796,10 @@ void DeviceConnection::onConnected() {
requestShieldedArea();
requestAntiClipArea();
requestNetworkInfomation();
requestVideoInformation();
requestHeadDetectThreshold();
emit connected();
m_timerId = startTimer(2500);
m_heartbeatTimerId = startTimer(2500);
if (m_otaProgress == 99) {
m_otaProgress = -1;
emit otaProgressChanged(true, 100, "设备升级成功!");
@ -614,6 +807,9 @@ void DeviceConnection::onConnected() {
if (m_otaTimer != nullptr) {
m_otaTimer->stop();
}
if (m_requestTimerId < 0) {
m_requestTimerId = startTimer(HeartbeatInterval);
}
} else if (socket == m_h264Socket) {
if (m_videoEnabled) {
setLiveStreamEnabled(true);
@ -624,9 +820,13 @@ void DeviceConnection::onConnected() {
void DeviceConnection::onDisconnected() {
auto socket = dynamic_cast<QTcpSocket *>(sender());
if (socket == m_commandSocket) {
if (m_timerId > 0) {
killTimer(m_timerId);
m_timerId = -1;
if (m_requestTimerId > 0) {
killTimer(m_requestTimerId);
m_requestTimerId = -1;
}
if (m_heartbeatTimerId > 0) {
killTimer(m_heartbeatTimerId);
m_heartbeatTimerId = -1;
}
emit disconnected();
if ((m_otaProgress >= 0) && (m_otaProgress <= 98)) {
@ -665,7 +865,6 @@ void DeviceConnection::onH264ReadyRead() {
void DeviceConnection::onCommandReadyRead() {
auto data = m_commandSocket->readAll();
m_commandBuffer.push_back(data);
while (!m_commandBuffer.isEmpty()) {
auto packageSize = ntohl(*reinterpret_cast<uint32_t *>(m_commandBuffer.data()));
if (m_commandBuffer.size() < (packageSize + sizeof(uint32_t))) break;
@ -677,9 +876,13 @@ void DeviceConnection::onCommandReadyRead() {
auto &task = m_requests.front();
if (task.command == command) {
m_requests.pop();
} else {
LOG(warning) << "current command[" << command.toStdString() << "] is no the task queue's head["
<< task.command.toStdString() << "]";
}
if (!m_requests.empty()) {
m_requests.front().task();
auto &command = m_requests.front();
command.task();
}
}
}
@ -691,15 +894,35 @@ void DeviceConnection::onErrorOccurred(QAbstractSocket::SocketError socketError)
}
void DeviceConnection::timerEvent(QTimerEvent *event) {
if (isConnected()) {
int index = heartbeats % 3;
if (index == 0) {
requestOpenDoorArea();
} else if (index == 1) {
requestShieldedArea();
} else if (index == 2) {
requestAntiClipArea();
using namespace std::chrono;
if (event->timerId() == m_heartbeatTimerId) {
if (isConnected()) {
int index = heartbeats % 3;
if (index == 0) {
requestOpenDoorArea();
} else if (index == 1) {
requestShieldedArea();
} else if (index == 2) {
requestAntiClipArea();
}
heartbeats++;
}
} else if (event->timerId() == m_requestTimerId) {
if (!m_requests.empty()) {
auto &command = m_requests.front();
auto elapsed = duration_cast<milliseconds>(system_clock::now() - command.time);
if (elapsed > (HeartbeatInterval * 2)) {
LOG(info) << "not received command[" << command.command.toStdString() << "] more than "
<< (HeartbeatInterval * 2).count() << " ms, consider it failed, send next command.";
m_requests.pop();
if (!m_requests.empty()) {
m_requests.front().task();
}
} else if (elapsed > HeartbeatInterval) {
LOG(info) << "not received command[" << command.command.toStdString() << "] more than "
<< HeartbeatInterval.count() << " ms, resend it.";
command.task();
}
}
heartbeats++;
}
}

View File

@ -9,8 +9,10 @@
#include <QTcpSocket>
#include <queue>
#include <string_view>
#include <QPointF>
class NetworkInfomation;
class QTimer;
class DeviceConnection : public QObject {
Q_OBJECT
@ -19,6 +21,7 @@ class DeviceConnection : public QObject {
public:
constexpr static auto WirelessAddress = "192.168.10.2";
constexpr static auto HeartbeatInterval = std::chrono::milliseconds(2500);
enum Resolution {
Video_360P = 0,
Video_720P,
@ -34,11 +37,10 @@ public:
QString softwareVersion;
QString firmwareVersion;
QString ip;
};
Q_ENUM(AreaWay)
class Area {
public:
int rotation = 0;
bool flip = false;
QList<QPointF> openDoorArea;
AreaWay openDoorAreaWay;
@ -47,7 +49,12 @@ public:
QList<QPointF> antiClipArea;
bool antiClipAreaEnabled;
int antiClipSensitivity = 1;
int helmetThreshold = 1;
int headThreshold = 1;
int detectFrameSize = 1;
};
Q_ENUM(AreaWay)
using H264FrameCallback = std::function<void(const char *data, uint32_t size)>;
DeviceConnection(QObject *parent = nullptr);
@ -60,19 +67,23 @@ public:
void setLiveStreamEnabled(bool enabled);
NetworkInfomation networkInfomation() const;
Area area() const;
void requestOpenDoorArea();
QFuture<bool> updateOpenDoorAreaPoints(AreaWay way, const QList<QPointF> &points);
void requestShieldedArea();
void updateShieldedAreaPoints(bool enabled, const QList<QPointF> &points);
void requestAntiClipArea();
void updateAntiClipAreaPoints(bool enabled, const QList<QPointF> &points);
void updateAntiClipAreaPoints(bool enabled, const QList<QPointF> &points, int sensitivity);
void requestResolution(Resolution resolution);
void requestNetworkInfomation();
QFuture<bool> updateDetectThreshold(int helmetThreshold, int headThreshold, int detectFrameSize);
void requestHeadDetectThreshold();
QFuture<bool> updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, const QString &gateway,
const QString &dns);
void requestVersion();
void requestVideoInformation();
void updateRotation(int rotation);
void updateFlip(bool flip);
/**
* @brief OTA,
@ -84,13 +95,17 @@ public:
* @param file
*/
void requestOta(const QString &firmware, const QString &file);
void requestReset();
signals:
void connected();
void disconnected();
void openDoorAreaChanged(AreaWay way, const QList<QPointF> &points);
void shieldedAreaChanged(bool enabled, const QList<QPointF> &points);
void antiClipAreaChanged(bool enabled, const QList<QPointF> &points);
void antiClipAreaChanged(bool enabled, const QList<QPointF> &points, int sensitivity);
void rotationChanged(int rotation);
void flipChanged(bool flip);
void detectThresholdChanged(int helmetThreshold, int headThreshold, int detectFrameSize);
void networkInfomationChanged(const NetworkInfomation &info);
void firmwareChanged(const QString &firmware);
void otaProgressChanged(bool status, int progress, const QString &message);
@ -99,6 +114,7 @@ protected:
class Task {
public:
QString command;
std::chrono::system_clock::time_point time = std::chrono::system_clock::now();
std::function<void()> task;
std::shared_ptr<QTimer> timeoutTimer = nullptr;
std::shared_ptr<QFutureInterface<bool>> future;
@ -131,9 +147,9 @@ private:
H264FrameCallback m_frameCallback;
std::queue<Task> m_requests;
int m_timerId = -1;
int m_requestTimerId = -1;
int m_heartbeatTimerId = -1;
int heartbeats = 0;
Area m_area;
NetworkInfomation m_networkInfomation;
QString m_firmware;
};

View File

@ -1,6 +1,7 @@
#include "DeviceListModel.h"
#include "BoostLog.h"
#include "DeviceConnection.h"
#include "Settings.h"
#include <QNetworkDatagram>
#include <QNetworkInterface>
#include <QTimer>
@ -138,6 +139,8 @@ void DeviceListModel::startSearchDevice() {
emit searchProgressChanged();
m_timerId = startTimer(1000);
emit isSearchingChanged();
} else {
LOG(warning) << "cannot creat udp sockets.";
}
}
@ -186,6 +189,7 @@ void DeviceListModel::onDeviceDisconnected() {
}
void DeviceListModel::onDeviceReplyReadyRead() {
using namespace Amass;
auto udp = dynamic_cast<QUdpSocket *>(sender());
while (udp->hasPendingDatagrams()) {
QNetworkDatagram datagram = udp->receiveDatagram();
@ -202,7 +206,12 @@ void DeviceListModel::onDeviceReplyReadyRead() {
if (reply.contains("sw_ver")) {
device.softwareVersion = QString::fromStdString(std::string(reply.at("sw_ver").as_string()));
}
if (!device.softwareVersion.startsWith("RD_T009")) continue; // 其它设备不予显示
auto settings = Singleton<Settings>::instance();
auto supportedDevices = settings->supportedDevices();
auto item = std::find_if(supportedDevices.cbegin(), supportedDevices.cend(), [&device](const std::string &model) {
return device.softwareVersion.startsWith(QString::fromStdString(model));
});
if (item == supportedDevices.cend()) continue; // 其它设备不予显示
device.ip = datagram.senderAddress().toString();
auto iterator = std::find_if(m_devices.cbegin(), m_devices.cend(),

View File

@ -25,20 +25,36 @@ void Settings::save() {
ptree.put("Application.DataCollection.ImageQuality", m_imageQuality);
ptree.put("Application.DataCollection.ImageQuality.<xmlcomment>", "0-100,仅对jpg有效");
for (auto &device : m_supportedDevices) {
ptree.add("Application.SupportedDevices.Model", device);
}
boost::property_tree::xml_writer_settings<std::string> settings('\t', 1);
boost::property_tree::write_xml(SettingsFilePath, ptree, std::locale(), settings);
}
void Settings::load() {
boost::property_tree::ptree ptree;
m_supportedDevices.clear();
try {
boost::property_tree::read_xml(SettingsFilePath, ptree);
m_imageFormat = static_cast<ImageFormat>(ptree.get<int>("Application.DataCollection.ImageFormat"));
m_imageQuality = ptree.get<int>("Application.DataCollection.ImageQuality");
for (auto &child : ptree.get_child("Application.SupportedDevices")) {
if (child.first != "Model") continue;
m_supportedDevices.push_back(child.second.get<std::string>(""));
}
} catch (...) {
LOG(error) << "parse " << SettingsFilePath << " failed.";
}
LOG(info) << "image format: " << m_imageFormat << ", quality: " << m_imageQuality;
std::ostringstream oss;
oss << "[";
for (auto &device : m_supportedDevices) {
oss << device << ", ";
}
oss << "]";
LOG(info) << "image format: " << m_imageFormat << ", quality: " << m_imageQuality
<< ", supported devices: " << oss.str();
}
ImageFormat Settings::imageFormat() const {
@ -48,3 +64,7 @@ ImageFormat Settings::imageFormat() const {
int Settings::imageQuality() const {
return m_imageQuality;
}
std::list<std::string> Settings::supportedDevices() const {
return m_supportedDevices;
}

View File

@ -12,6 +12,7 @@ public:
void load();
ImageFormat imageFormat() const;
int imageQuality() const;
std::list<std::string> supportedDevices() const;
protected:
Settings();
@ -19,6 +20,7 @@ protected:
private:
ImageFormat m_imageFormat = ImageFormat::Jpeg; // 0: jpg 1: yuv
int m_imageQuality = 100;
std::list<std::string> m_supportedDevices{"RD_T009", "RD_T013"};
};
#endif // SETTINGS_H

View File

@ -16,6 +16,13 @@ Item {
property color antiClipAreaColor: "blue"
property var antiClipAreaPoints: []
property bool antiClipAreaEnabled: false
property int antiClipSensitivity: 1
property alias helmetThreshold: helmetInput.text
property alias headThreshold: headInput.text
property alias detectFrameSize: detectFrameInput.text
property alias flip: flipSwitch.checked
property alias videoRotation: rotateComboBox.currentIndex
Item {
anchors.left: parent.left
anchors.right: parent.right
@ -238,17 +245,45 @@ Item {
}
}
}
Grid {
GridLayout {
id: controlBar
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
columns: 2
spacing: 10
verticalItemAlignment: Qt.AlignVCenter
Label { text: qsTr("图像: ") }
Row {
enabled: root.enabled
Label {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("旋转")
}
Item {width: 10; height:10}
ComboBox {
id: rotateComboBox
model: ["0°","90°","180°","270°"]
onCurrentIndexChanged: {
App.currentDeviceRotation = rotateComboBox.currentIndex
}
}
Item {width: 70; height:10}
Label {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("翻转")
}
Item {width: 10; height:10}
Switch {
id: flipSwitch
onToggled: App.currentDeviceFlip=flipSwitch.checked
}
}
Label {
text: qsTr("开门区域: ")
}
Row {
enabled: root.enabled
RadioButton {
@ -274,28 +309,141 @@ Item {
}
}
Label {text: qsTr("防夹区域: ")}
Row {
enabled: root.enabled
RadioButton {
text: "关闭"
checked: !App.currentAntiClipAreaEnabled
onToggled: {
App.currentAntiClipAreaEnabled=false
}
}
RadioButton {
text: "四边形"
checked: App.currentAntiClipAreaEnabled
onToggled: {
App.currentAntiClipAreaEnabled=true
}
}
Label {
Layout.alignment: Qt.AlignTop
Layout.topMargin: 14
text: qsTr("防夹区域: ")
}
Flow{
Layout.fillHeight: true
Layout.fillWidth: true
RowLayout {
enabled: root.enabled
RadioButton {
text: "关闭"
checked: !App.currentAntiClipAreaEnabled
onToggled: {
App.currentAntiClipAreaEnabled=false
}
}
RadioButton {
text: "四边形"
checked: App.currentAntiClipAreaEnabled
onToggled: {
App.currentAntiClipAreaEnabled=true
}
Layout.rightMargin: 20
}
}
RowLayout {
Label { text: qsTr("灵敏度: ")
Layout.alignment: Qt.AlignRight
}
ComboBox {
id: antiClipSensitivityComboBox
enabled: root.enabled
implicitWidth: 60
Layout.alignment: Qt.AlignLeft
model: [1,2,3,4,5]
currentIndex: root.antiClipSensitivity-1
onCurrentIndexChanged: {
App.currentAntiClipSensitivity = antiClipSensitivityComboBox.currentIndex+1
}
Layout.rightMargin: 20
}
}
}
Label {
Layout.alignment: Qt.AlignTop
Layout.topMargin: 14
text: qsTr("阈值设置: ")
}
Flow {
Layout.fillHeight: true
Layout.fillWidth: true
RowLayout {
Label { text: qsTr("安全帽阈值: ")
Layout.alignment: Qt.AlignRight
}
TextField {
id: helmetInput
enabled: root.enabled
implicitWidth: 60
Layout.alignment: Qt.AlignLeft
selectByMouse: true
ToolTip.visible: helmetInput.hovered
ToolTip.text: "阈值范围 0-300"
validator: IntValidator {
bottom: 0
top: 300
}
Layout.rightMargin: 20
}
}
RowLayout {
Label { text: qsTr("头肩阈值: ")
Layout.alignment: Qt.AlignRight
}
TextField {
id: headInput
enabled: root.enabled
implicitWidth: 60
Layout.alignment: Qt.AlignLeft
selectByMouse: true
ToolTip.visible: headInput.hovered
ToolTip.text: "阈值范围 0-300"
validator: IntValidator {
bottom: 0
top: 300
}
Layout.rightMargin: 20
}
}
RowLayout {
Label { text: qsTr("识别帧数: ")
Layout.alignment: Qt.AlignRight
}
TextField {
id: detectFrameInput
enabled: root.enabled
implicitWidth: 60
Layout.alignment: Qt.AlignLeft
selectByMouse: true
ToolTip.visible: detectFrameInput.hovered
ToolTip.text: "帧数范围 0-30"
validator: IntValidator {
bottom: 0
top: 30
}
}
}
Button {
enabled: root.enabled
text: "保存"
onClicked: {
if(App.currentHelmetThreshold !== parseInt(helmetInput.text)){
App.currentHelmetThreshold= parseInt(helmetInput.text)
window.showSuccess("安全帽阈值设置成功",2500)
}
if(App.currentHeadThreshold !== parseInt(headInput.text)){
App.currentHeadThreshold= parseInt(headInput.text)
window.showSuccess("头肩阈值设置成功",2500)
}
if(App.currentDetectFrameSize !== parseInt(detectFrameInput.text)){
App.currentDetectFrameSize= parseInt(detectFrameInput.text)
window.showSuccess("识别帧数设置成功",2500)
}
}
}
}
Label {text: qsTr("屏蔽区域: ")}
Row {
id: shieldedRow

View File

@ -48,7 +48,8 @@ ApplicationWindow {
id: emptyHint
visible: false
anchors.centerIn: parent
text: qsTr("未搜索到设备")
horizontalAlignment: Text.AlignHCenter
text: qsTr("未搜索到设备\n请尝试关闭Windows防火墙后再重试")
}
delegate: Rectangle {
width: deviceList.width
@ -80,7 +81,7 @@ ApplicationWindow {
deviceList.currentIndex = index
}
onDoubleClicked: {
if (!softwareVersion.includes("R003")) {
if (softwareVersion.includes("T009") && !softwareVersion.includes("R003")) {
showMessageDialog(2, "网络设置", "当前设备不支持有线网络设置!")
} else if (onlineStatus) {
networkPopup.open()
@ -118,7 +119,13 @@ ApplicationWindow {
shieldedAreaEnabled: App.currentShieldedAreaEnabled
shieldedAreaPoints: App.currentShieldedAreaPoints
antiClipAreaEnabled: App.currentAntiClipAreaEnabled
antiClipSensitivity: App.currentAntiClipSensitivity
antiClipAreaPoints: App.currentAntiClipAreaPoints
helmetThreshold: App.currentHelmetThreshold
headThreshold: App.currentHeadThreshold
detectFrameSize: App.currentDetectFrameSize
flip: App.currentDeviceFlip
videoRotation: App.currentDeviceRotation
}
NetworkSettingPopup {
@ -149,9 +156,9 @@ ApplicationWindow {
}
if (App.collector.path.length <= 0) {
showFolderDialog(folder => {
App.collector.path = folder
App.collector.start(App.devices.get(deviceList.currentIndex).ip)
})
App.collector.path = folder
App.collector.start(App.devices.get(deviceList.currentIndex).ip)
})
} else {
App.collector.start(App.devices.get(
deviceList.currentIndex).ip)
@ -161,6 +168,7 @@ ApplicationWindow {
}
Item {}
Button {
Layout.alignment: Qt.AlignRight
text: "升级"
onClicked: {
if (deviceList.currentIndex < 0) {
@ -173,18 +181,41 @@ ApplicationWindow {
otaPopup.open()
}
}
Item {}
Button {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: 5
text: "重置"
onClicked: {
if (deviceList.currentIndex < 0) {
showMessageDialog(2, "恢复出厂设置", "请先选择设备")
return
} else {
showMessageDialog(2, "恢复出厂设置", "设备将会重启,重启后配置恢复默认", () => {
App.resetDevice()
})
}
}
}
spacing: (parent.width - (2 * 100)) / 3
}
function showMessageDialog(type, title, message) {
MessageBar {
id: messageBar
root: window.contentItem
}
function showSuccess(text, duration) {
return messageBar.showSuccess(text, duration)
}
function showMessageDialog(type, title, message, callback) {
let component = Qt.createComponent("MessageDialog.qml")
if (component.status === Component.Ready) {
let dialog = component.createObject(window, {
"type": type,
"titleText": title,
"text": message
})
"type": type,
"titleText": title,
"text": message,
"callback": callback
})
dialog.open()
}
}
@ -197,11 +228,14 @@ ApplicationWindow {
dialog = Qt.createQmlObject("import QtQuick.Dialogs; FileDialog {}", window, "myDynamicSnippet")
}
if (dialog) {
dialog.nameFilters = nameFilters;
dialog.nameFilters = nameFilters
dialog.visible = true
dialog.accepted.connect(function () {
let fileUrl = isQt5 ? dialog.fileUrl.toString() : dialog.selectedFile.toString()
let localFilePath = fileUrl.startsWith("file:///") ? fileUrl.substring(8) : fileUrl
let localFilePath = fileUrl
if (fileUrl.startsWith("file:///")) {
localFilePath = fileUrl.substring(Qt.platform.os === "windows" ? 8 : 7)
}
onSelected(localFilePath)
dialog.destroy()
})

101
qml/MessageBar.qml Normal file
View File

@ -0,0 +1,101 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
Object {
id: control
property var root
property var screenLayout: null
property int layoutY: 75
function showSuccess(text,duration=1000){
return create("success",text,duration)
}
function create(type,text,duration){
if(screenLayout){
let last = screenLayout.getLastloader()
if(last.type === type && last.text === text){
last.duration = duration
if (duration > 0) last.restart()
return last
}
} else {
initScreenLayout()
}
return contentComponent.createObject(screenLayout,{type:type,text:text,duration:duration,})
}
function initScreenLayout() {
if(control.screenLayout == null) {
control.screenLayout = screenLayoutComponent.createObject(root)
screenLayout.y = control.layoutY
screenLayout.z = 100000
}
}
Component {
id:contentComponent
Rectangle {
id: content
property int duration: 1500
property string type
property alias text: message.text
color: "#EBF8ED"
radius: 3.2
border.width: 1
border.color: Qt.darker(content.color)
layer.enabled: true
x:(parent.width - width) / 2
width: 200
height: 32
Row {
anchors.fill: parent
Image {
width: 32
height: 32
fillMode: Image.Pad
source: "qrc:/qt/qml/AntiClipSettings/resources/successfull.svg"
anchors.verticalCenter: parent.verticalCenter
}
Label {
id: message
anchors.verticalCenter: parent.verticalCenter
}
}
Timer {
id:delayTimer
interval: duration
running: duration > 0
repeat: duration > 0
onTriggered: content.close()
}
function close(){
content.destroy()
}
}
}
Component {
id:screenLayoutComponent
Column{
parent: Overlay.overlay
z:999
spacing: 5
width: root.width
move: Transition {
NumberAnimation {
properties: "y"
easing.type: Easing.InOutQuad
duration: 167
}
}
onChildrenChanged: if(children.length === 0) destroy()
function getLastloader(){
if(children.length > 0){
return children[children.length - 1]
}
return null
}
}
}
}

View File

@ -13,6 +13,7 @@ Dialog {
property alias text: textLabel.text
property alias textFontSize: textLabel.font.pixelSize
property int type: MessageDialog.Type.Successful
property var callback
closePolicy: Popup.CloseOnEscap | Popup.NoAutoClose
background: Rectangle {
radius: 8
@ -62,30 +63,60 @@ Dialog {
wrapMode: Text.WordWrap
}
Button {
id: cancelButton
Row {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: 6
Layout.preferredWidth: 72
Layout.preferredHeight: 40
font.family: control.font.family
text: "关闭"
font.pixelSize: 14
contentItem: Text {
text: parent.text
font: parent.font
color: parent.down ? "#FFFFFF" : "#53627C"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
spacing: 10
Button {
id: okButton
width: 72
height: 40
font.family: control.font.family
text: "好的"
font.pixelSize: 14
contentItem: Text {
text: parent.text
font: parent.font
color: parent.down ? "#FFFFFF" : "#53627C"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
border.color: "#E1E4E8"
border.width: 1
color: parent.down ? "#F25959" : "#FFFFFF"
radius: 2
}
onClicked: {
if(callback !== undefined){
callback()
}
control.accept()
}
}
background: Rectangle {
border.color: "#E1E4E8"
border.width: 1
color: parent.down ? "#F25959" : "#FFFFFF"
radius: 2
Button {
id: cancelButton
visible: callback !== undefined
width: 72
height: 40
font.family: control.font.family
text: "取消"
font.pixelSize: 14
contentItem: Text {
text: parent.text
font: parent.font
color: parent.down ? "#FFFFFF" : "#53627C"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
border.color: "#E1E4E8"
border.width: 1
color: parent.down ? "#F25959" : "#FFFFFF"
radius: 2
}
onClicked: control.reject()
}
onClicked: control.reject()
}
}
onTypeChanged: {

5
qml/Object.qml Normal file
View File

@ -0,0 +1,5 @@
import QtQml 2.15
QtObject {
default property list<QtObject> children
}

View File

@ -41,6 +41,7 @@ Popup {
Layout.fillWidth: true
readOnly: true
placeholderText: "请选择升级bin文件"
selectByMouse: true
}
Button {
enabled: otaFinished

129
resources/build.ps1 Normal file
View File

@ -0,0 +1,129 @@
param($type)
# 该脚本设置为 UTF8-bom 编码即可支持中文
$MsvcScript = 'D:\Program Files\Microsoft Visual Studio\2022\\Community\Common7\Tools\Launch-VsDevShell.ps1'
if (!(Test-Path $MsvcScript)) { $MsvcScript = 'D:\Program Files\Microsoft Visual Studio\2022\\Professional\Common7\Tools\Launch-VsDevShell.ps1' }
. $MsvcScript -SkipAutomaticLocation -Arch amd64
$qtHome = "D:\Qt\5.15.2\msvc2019_64"
$openSSLRoot = "D:\Qt\Tools\OpenSSLv3\Win_x64"
$librariesPath = "E:\Projects\Libraries"
if (!(Test-Path $librariesPath)) { $librariesPath = "D:\Projects\Libraries" }
$boostRoot = "$librariesPath\boost_1_83_0_msvc2022_64bit"
# 6.1.1 版本支持 windows7
$ffmpegRoot = "$librariesPath\ffmpeg-6.1.1-full_build-shared"
$projectPath = Get-Location
$buildPath = Join-Path -Path $projectPath -ChildPath "build"
$fileContent = (Get-Content -Path "CMakeLists.txt") -join " "
if ($fileContent -match 'project\([^\)]+VERSION\s+([0-9]+\.[0-9]+)') {
$version = $Matches[1]
} else {
Write-Output "未找到版本号"
}
$deployPath = Join-Path -Path $buildPath -ChildPath "视觉防夹设备上位机工具v$version"
$zipFilePath = Join-Path -Path $buildPath -ChildPath "视觉防夹设备上位机工具v$version.zip"
$changelogPath = Join-Path -Path $buildPath -ChildPath "CHANGELOG.txt"
function Build() {
if (!(Test-Path $buildPath\CMakeCache.txt)) {
cmake.exe -G Ninja -S . -B build `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_PREFIX_PATH=$qtHome `
-DQT_DIR="$qtHome\lib\cmake\Qt5" `
-DQt5_DIR="$qtHome\lib\cmake\Qt5" `
-DLibraries_ROOT="$librariesPath"
}
cmake.exe --build $buildPath --target all
}
function Deploy() {
if (Test-Path $deployPath) {
Remove-Item $deployPath -Recurse -Force
}
New-Item $deployPath -ItemType Directory
Copy-Item $buildPath\AntiClipSettings.exe $deployPath\视觉防夹设备上位机工具v$version.exe
& $qtHome\bin\windeployqt.exe $deployPath\视觉防夹设备上位机工具v$version.exe --qmldir=$qtHome\qml
# Remove-Item -Path $deployPath\d3dcompiler_47.dll -Force # 删了这个会导致Windows 7 无法正常显示
Remove-Item -Path $deployPath\Qt5Multimedia.dll -Force
Remove-Item -Path $deployPath\Qt5MultimediaQuick.dll -Force
Remove-Item -Path $deployPath\Qt5RemoteObjects.dll -Force
Remove-Item -Path $deployPath\Qt5Test.dll -Force
Remove-Item -Path $deployPath\Qt5QuickTest.dll -Force
Remove-Item -Path $deployPath\Qt5WinExtras.dll -Force
Remove-Item -Path $deployPath\translations -Recurse -Force # 暂时不需要翻译文件
Remove-Item -Path $deployPath\qmltooling -Recurse -Force
Remove-Item -Path $deployPath\QtTest -Recurse -Force
Remove-Item -Path $deployPath\QtGraphicalEffects -Recurse -Force
# $modules = "QmlCore"
# foreach ($module in $modules) {
# Copy-Item -Path $qtHome\bin\Qt6$module.dll -Destination $deployPath
# }
# if (-Not (Test-Path -Path $deployPath\qml\QtCore)) {
# New-Item $deployPath\qml\QtCore -ItemType Directory
# $plugins = "qtqmlcoreplugin.dll", "qmldir", "plugins.qmltypes"
# foreach ($plugin in $plugins) {
# Copy-Item -Path $QtHome\qml\QtCore\$plugin -Destination $deployPath\qml\QtCore
# }
# }
Copy-Item $openSSLRoot\bin\libssl-3-x64.dll $deployPath
Copy-Item $openSSLRoot\bin\libcrypto-3-x64.dll $deployPath
$boosts = "atomic", "thread", "filesystem", "log", "json"
foreach ($boost in $boosts) {
Copy-Item -Path $boostRoot\lib\boost_$boost-vc143-mt-x64-1_83.dll -Destination $deployPath
}
# 暂时不需要 avfilter-9、avformat-60、postproc-57、avdevice-60
$ffmpegs = "avcodec-60", "avutil-58", "swresample-4", "swscale-7"
foreach ($ffmpeg in $ffmpegs) {
Copy-Item -Path $ffmpegRoot\bin\$ffmpeg.dll -Destination $deployPath
}
Compress-Archive -Path $deployPath -DestinationPath $zipFilePath -Force
}
function Clean() {
if (Test-Path $buildPath) {
Remove-Item $buildPath -Recurse -Force
}
}
function Changelog() {
$commit_message = git log -1 --pretty=format:"%B"
Write-Output "Latest commit message:"
Write-Output $commit_message
$commit_message | Out-File -FilePath $changelogPath -Encoding utf8
Write-Output "Commit message has been written to $changelogPath"
}
switch ($type) {
"build" {
Build
}
"deploy" {
Deploy
}
"clean" {
Clean
}
"installer" {
Installer
}
"changelog" {
Changelog
}
"update" {
UpdateServer
}
}