Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a2764a9ade | ||
943fb6fb4b | |||
|
ce673bf330 | ||
|
28f2f60532 | ||
|
c846ae81a0 | ||
|
fd72d00925 | ||
|
8055f4e70f | ||
5ae5c5adc6 | |||
5b602e607c | |||
c3430caf2f | |||
0aa2721f67 | |||
|
6ee75bfae1 | ||
|
404889a19c | ||
|
881048a286 |
@ -6,22 +6,18 @@ on:
|
||||
- '**'
|
||||
tags-ignore:
|
||||
- 'v*'
|
||||
paths:
|
||||
- '**.cpp'
|
||||
- '**.h'
|
||||
- '**.conf'
|
||||
jobs:
|
||||
PullDocker:
|
||||
runs-on: [ubuntu-latest, ubuntu-24.04]
|
||||
steps:
|
||||
- name: Login to ACR
|
||||
uses: aliyun/acr-login@v1
|
||||
with:
|
||||
login-server: https://registry.cn-shenzhen.aliyuncs.com
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Pull Docker image
|
||||
run: docker pull registry.cn-shenzhen.aliyuncs.com/amass_toolset/ubuntu_dev:24.04
|
||||
Build:
|
||||
runs-on: [ubuntu-latest, ubuntu-24.04]
|
||||
container:
|
||||
image: registry.cn-shenzhen.aliyuncs.com/amass_toolset/ubuntu_dev:24.04
|
||||
credentials:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
steps:
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
|
@ -5,6 +5,10 @@ on:
|
||||
- '**'
|
||||
tags-ignore:
|
||||
- 'v*'
|
||||
paths:
|
||||
- '**.cpp'
|
||||
- '**.h'
|
||||
- '**.conf'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -32,4 +36,4 @@ jobs:
|
||||
with:
|
||||
body_path: build/CHANGELOG.txt
|
||||
files: |-
|
||||
build/SmartLockerTools_v*.zip
|
||||
build/掌静脉工具v*.zip
|
@ -1,5 +1,5 @@
|
||||
name: Deploy Release
|
||||
run-name: ${{ github.actor }} is building Bilby...
|
||||
run-name: ${{ github.actor }} is building SmartLockerTools...
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
@ -9,6 +9,9 @@ jobs:
|
||||
runs-on: [ubuntu-latest, ubuntu-24.04]
|
||||
container:
|
||||
image: registry.cn-shenzhen.aliyuncs.com/amass_toolset/ubuntu_dev:24.04
|
||||
credentials:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
steps:
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
|
@ -30,4 +30,4 @@ jobs:
|
||||
with:
|
||||
body_path: build/CHANGELOG.txt
|
||||
files: |-
|
||||
build/SmartLockerTools_v*.zip
|
||||
build/掌静脉工具v*.zip
|
@ -119,12 +119,15 @@ QStringList Application::availableSerialPorts() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
QStringList Application::availableUsbVideoCameras() const {
|
||||
QStringList ret;
|
||||
QVariantList Application::availableUsbVideoCameras() const {
|
||||
QVariantList ret;
|
||||
DeviceDiscovery d;
|
||||
auto devices = d.devices();
|
||||
for (auto &device : devices) {
|
||||
ret << QString::fromStdString(device);
|
||||
QVariantMap item;
|
||||
item.insert("name", QString::fromStdString(device.friendlyName));
|
||||
item.insert("path", QString::fromStdString(device.alternativeName));
|
||||
ret << item;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -478,6 +481,7 @@ bool Application::startOta(const QString &path) {
|
||||
LOG(info) << "device already in ota mode.";
|
||||
} else {
|
||||
if (m_communication) {
|
||||
resetModule();
|
||||
m_communication->startOta();
|
||||
} else {
|
||||
emit otaMessage("请先打开设备");
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
int exec();
|
||||
static Application *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
|
||||
Q_INVOKABLE QStringList availableSerialPorts() const;
|
||||
Q_INVOKABLE QStringList availableUsbVideoCameras() const;
|
||||
Q_INVOKABLE QVariantList availableUsbVideoCameras() const;
|
||||
Q_INVOKABLE bool open(const QString &portName, int baudRate);
|
||||
Q_INVOKABLE bool openUVC(const QString &deviceName);
|
||||
Q_INVOKABLE void close();
|
||||
|
@ -89,6 +89,8 @@ public:
|
||||
struct VerifyRequest {
|
||||
uint8_t save_image;
|
||||
uint8_t timeout; // timeout, unit second, default 10s
|
||||
uint16_t interval = 0;
|
||||
uint8_t reserved[4];
|
||||
};
|
||||
|
||||
struct PalmStateNote {
|
||||
|
@ -32,7 +32,7 @@ bool VideoPlayer::open(const std::string &deviceName) {
|
||||
#ifdef WIN32
|
||||
constexpr auto format = "dshow";
|
||||
std::ostringstream oss;
|
||||
oss << "video=" << deviceName;
|
||||
oss << "video=@device_pnp_" << deviceName;
|
||||
auto device = oss.str();
|
||||
#else
|
||||
constexpr auto format = "v4l2";
|
||||
@ -41,7 +41,10 @@ bool VideoPlayer::open(const std::string &deviceName) {
|
||||
auto inputFormat = av_find_input_format(format);
|
||||
|
||||
AVDictionary *dictionary = nullptr;
|
||||
// clang-format off
|
||||
// ffplay -f dshow -i video="@device_pnp_\\?\usb#vid_3346&pid_0001&mi_00#6&6ff22b9&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
|
||||
// ffmpeg -f dshow -list_options true -i video="UVC Camera"
|
||||
// clang-format on
|
||||
av_dict_set(&dictionary, "video_size", "800*600", 0);
|
||||
|
||||
int status = avformat_open_input(&m_formatContext, device.c_str(), inputFormat, &dictionary);
|
||||
|
@ -57,6 +57,7 @@ RowLayout {
|
||||
}
|
||||
ComboBox {
|
||||
id: uvcs
|
||||
textRole: "name"
|
||||
enabled: !App.uvcOpened
|
||||
implicitWidth: 150
|
||||
}
|
||||
@ -67,8 +68,7 @@ RowLayout {
|
||||
}
|
||||
Button {
|
||||
text: App.uvcOpened ? "关闭" : "连接"
|
||||
onClicked: App.uvcOpened ? App.closeUVC() : App.openUVC(
|
||||
uvcs.currentText)
|
||||
onClicked: App.uvcOpened ? App.closeUVC() : App.openUVC(uvcs.currentValue.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,11 @@ ListView {
|
||||
message: ""
|
||||
}
|
||||
}
|
||||
delegate: Text {
|
||||
delegate: TextEdit {
|
||||
width: ListView.view.width
|
||||
readOnly: true
|
||||
text: message
|
||||
selectByMouse: true
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
|
@ -54,18 +54,9 @@ Window {
|
||||
}
|
||||
}
|
||||
Item {
|
||||
// LogView {
|
||||
// id: logBrowser
|
||||
// anchors.fill: parent
|
||||
// }
|
||||
ScrollView {
|
||||
id: view
|
||||
anchors.fill: parent
|
||||
TextArea {
|
||||
LogView {
|
||||
id: logBrowser
|
||||
readOnly: true
|
||||
wrapMode: TextArea.WordWrap
|
||||
}
|
||||
anchors.fill: parent
|
||||
}
|
||||
Button {
|
||||
text: "清空"
|
||||
|
@ -17,7 +17,6 @@ if(WIN32)
|
||||
else()
|
||||
set(MBEDTLS_ROOT ${Libraries_ROOT}/mbedtls-3.6.2_msvc2022_64bit_debug)
|
||||
endif()
|
||||
add_compile_options(/showIncludes-)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND sh -c "echo $HOME"
|
||||
|
@ -1,9 +1,13 @@
|
||||
set(APPLICATION_NAME "掌静脉升级工具")
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets SerialPort)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets SerialPort)
|
||||
|
||||
configure_file(Configuration.h.in Configuration.h)
|
||||
|
||||
set(PROJECT_SOURCES OtaUpdate.rc
|
||||
main.cpp
|
||||
Widget.cpp
|
||||
@ -15,6 +19,10 @@ qt_add_executable(SmartLockerUpdater
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(SmartLockerUpdater
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(SmartLockerUpdater
|
||||
PRIVATE Peripheral
|
||||
PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
|
||||
|
3
OtaUpdate/Configuration.h.in
Normal file
3
OtaUpdate/Configuration.h.in
Normal file
@ -0,0 +1,3 @@
|
||||
#define APPLICATION_NAME "@APPLICATION_NAME@"
|
||||
#define GIT_COMMIT_ID "@GIT_COMMIT_ID@"
|
||||
#define APP_VERSION "@PROJECT_VERSION@"
|
@ -1,4 +1,5 @@
|
||||
#include "BoostLog.h"
|
||||
#include "Configuration.h"
|
||||
#include "Widget.h"
|
||||
#include <QApplication>
|
||||
#include <QFont>
|
||||
@ -7,11 +8,14 @@ int main(int argc, char *argv[]) {
|
||||
boost::log::initialize("logs/app");
|
||||
|
||||
QApplication a(argc, argv);
|
||||
a.setApplicationName(APPLICATION_NAME);
|
||||
a.setApplicationVersion(QString("v%1_%2 build: %3 %4").arg(APP_VERSION, GIT_COMMIT_ID, __DATE__, __TIME__));
|
||||
|
||||
QFont font;
|
||||
font.setPointSize(16);
|
||||
a.setFont(font);
|
||||
Widget w;
|
||||
w.setWindowTitle("掌静脉模组升级工具");
|
||||
w.setWindowTitle(QString("%1 %2").arg(a.applicationName()).arg(a.applicationVersion()));
|
||||
w.setMinimumWidth(520);
|
||||
w.setMinimumHeight(100);
|
||||
w.show();
|
||||
|
@ -15,6 +15,6 @@ target_include_directories(Peripheral
|
||||
target_link_libraries(Peripheral
|
||||
PUBLIC Universal
|
||||
PRIVATE Encrypt
|
||||
$<$<PLATFORM_ID:Windows>:Mfreadwrite Mf mfplat mfuuid>
|
||||
$<$<PLATFORM_ID:Windows>:Mfreadwrite Mf mfplat mfuuid strmiids comsuppw>
|
||||
PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort
|
||||
)
|
||||
|
@ -1,13 +1,15 @@
|
||||
#include "DeviceDiscovery.h"
|
||||
#include "BoostLog.h"
|
||||
#include "StringUtility.h"
|
||||
#include <boost/scope/scope_exit.hpp>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#ifdef WIN32
|
||||
#include <comutil.h>
|
||||
#include <dshow.h>
|
||||
#include <mfapi.h>
|
||||
#include <mfcaptureengine.h>
|
||||
#include <strmif.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <linux/videodev2.h>
|
||||
@ -27,6 +29,23 @@ DeviceDiscovery::DeviceDiscovery() {
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
void DeleteMediaType(AM_MEDIA_TYPE *pmt) {
|
||||
if (pmt != nullptr) {
|
||||
if (pmt->cbFormat != 0) {
|
||||
CoTaskMemFree((PVOID)pmt->pbFormat);
|
||||
pmt->cbFormat = 0;
|
||||
pmt->pbFormat = nullptr;
|
||||
}
|
||||
if (pmt->pUnk != nullptr) {
|
||||
// Unecessary because pUnk should not be used, but safest.
|
||||
pmt->pUnk->Release();
|
||||
pmt->pUnk = nullptr;
|
||||
}
|
||||
CoTaskMemFree((PVOID)pmt);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string deviceName(IMFActivate *device) {
|
||||
std::string ret;
|
||||
WCHAR *friendlyName = nullptr;
|
||||
@ -34,7 +53,7 @@ static std::string deviceName(IMFActivate *device) {
|
||||
auto result = device->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &friendlyName, &nameLength);
|
||||
|
||||
if (SUCCEEDED(result)) {
|
||||
ret = Amass::StringUtility::wstringToString(std::wstring(friendlyName, nameLength));
|
||||
ret = _com_util::ConvertBSTRToString(friendlyName);
|
||||
}
|
||||
if (friendlyName != nullptr) {
|
||||
CoTaskMemFree(friendlyName);
|
||||
@ -128,36 +147,108 @@ void DeviceDiscovery::enterOtaMode(const std::shared_ptr<Device> &device, std::e
|
||||
&llTimeStamp, &pSample);
|
||||
}
|
||||
|
||||
std::vector<std::string> DeviceDiscovery::devices() {
|
||||
std::vector<std::string> ret;
|
||||
IMFAttributes *attributes = nullptr;
|
||||
boost::scope::scope_exit guard([&attributes] { SafeRelease(&attributes); });
|
||||
auto result = MFCreateAttributes(&attributes, 1);
|
||||
if (FAILED(result)) {
|
||||
return ret;
|
||||
std::vector<DeviceDiscovery::Device> DeviceDiscovery::devices() {
|
||||
std::vector<Device> devices;
|
||||
ICreateDevEnum *pDevEnum = nullptr;
|
||||
IEnumMoniker *pEnum = nullptr;
|
||||
|
||||
HRESULT hr =
|
||||
CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return devices;
|
||||
}
|
||||
|
||||
result = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
|
||||
if (FAILED(result)) {
|
||||
return ret;
|
||||
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
|
||||
if (hr != S_OK) {
|
||||
pDevEnum->Release();
|
||||
return devices;
|
||||
}
|
||||
|
||||
UINT32 count;
|
||||
IMFActivate **devices = nullptr;
|
||||
result = MFEnumDeviceSources(attributes, &devices, &count);
|
||||
if (FAILED(result)) {
|
||||
return ret;
|
||||
IMoniker *pMoniker = nullptr;
|
||||
while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
|
||||
Device info;
|
||||
GetDeviceNames(pMoniker, info);
|
||||
|
||||
IBaseFilter *pFilter;
|
||||
hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void **)&pFilter);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IEnumPins *pEnumPins;
|
||||
hr = pFilter->EnumPins(&pEnumPins);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IPin *pPin;
|
||||
while (pEnumPins->Next(1, &pPin, nullptr) == S_OK) {
|
||||
PIN_DIRECTION pinDir;
|
||||
pPin->QueryDirection(&pinDir);
|
||||
if (pinDir == PINDIR_OUTPUT) {
|
||||
GetSupportedResolutions(pPin, info.resolutions);
|
||||
}
|
||||
pPin->Release();
|
||||
}
|
||||
pEnumPins->Release();
|
||||
}
|
||||
pFilter->Release();
|
||||
}
|
||||
devices.push_back(info);
|
||||
pMoniker->Release();
|
||||
}
|
||||
pEnum->Release();
|
||||
pDevEnum->Release();
|
||||
return devices;
|
||||
}
|
||||
|
||||
bool DeviceDiscovery::SetResolution(const std::string &deviceName, int width, int height) {
|
||||
ICreateDevEnum *pDevEnum = nullptr;
|
||||
IEnumMoniker *pEnum = nullptr;
|
||||
|
||||
HRESULT hr =
|
||||
CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return ret;
|
||||
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
|
||||
if (hr != S_OK) {
|
||||
pDevEnum->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
auto name = ::deviceName(devices[i]);
|
||||
ret.push_back(name);
|
||||
IMoniker *pMoniker = nullptr;
|
||||
while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
|
||||
IPropertyBag *pPropBag;
|
||||
hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag);
|
||||
if (SUCCEEDED(hr)) {
|
||||
VARIANT varName;
|
||||
VariantInit(&varName);
|
||||
hr = pPropBag->Read(L"FriendlyName", &varName, nullptr);
|
||||
if (SUCCEEDED(hr)) {
|
||||
std::string currentName = _com_util::ConvertBSTRToString(varName.bstrVal);
|
||||
if (currentName == deviceName) {
|
||||
IBaseFilter *pFilter;
|
||||
hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void **)&pFilter);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (SetPinResolution(pFilter, width, height)) {
|
||||
pFilter->Release();
|
||||
VariantClear(&varName);
|
||||
pPropBag->Release();
|
||||
pMoniker->Release();
|
||||
pEnum->Release();
|
||||
pDevEnum->Release();
|
||||
return true;
|
||||
}
|
||||
return ret;
|
||||
pFilter->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
VariantClear(&varName);
|
||||
pPropBag->Release();
|
||||
}
|
||||
pMoniker->Release();
|
||||
}
|
||||
pEnum->Release();
|
||||
pDevEnum->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr<Device> &source) {
|
||||
@ -182,6 +273,89 @@ DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::share
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeviceDiscovery::GetDeviceNames(IMoniker *pMoniker, Device &info) {
|
||||
IPropertyBag *pPropBag;
|
||||
HRESULT hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag);
|
||||
if (SUCCEEDED(hr)) {
|
||||
VARIANT varName;
|
||||
VariantInit(&varName);
|
||||
hr = pPropBag->Read(L"FriendlyName", &varName, nullptr);
|
||||
if (SUCCEEDED(hr)) {
|
||||
|
||||
info.friendlyName = _com_util::ConvertBSTRToString(varName.bstrVal);
|
||||
}
|
||||
VariantClear(&varName);
|
||||
|
||||
hr = pPropBag->Read(L"DevicePath", &varName, nullptr);
|
||||
if (SUCCEEDED(hr)) {
|
||||
info.alternativeName = _com_util::ConvertBSTRToString(varName.bstrVal);
|
||||
}
|
||||
VariantClear(&varName);
|
||||
|
||||
pPropBag->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceDiscovery::GetSupportedResolutions(IPin *pPin, std::vector<std::pair<int, int>> &resolutions) {
|
||||
IEnumMediaTypes *pEnumMediaTypes = nullptr;
|
||||
HRESULT hr = pPin->EnumMediaTypes(&pEnumMediaTypes);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AM_MEDIA_TYPE *pMediaType = nullptr;
|
||||
while (pEnumMediaTypes->Next(1, &pMediaType, nullptr) == S_OK) {
|
||||
if (pMediaType->formattype == FORMAT_VideoInfo) {
|
||||
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER *>(pMediaType->pbFormat);
|
||||
int width = pVih->bmiHeader.biWidth;
|
||||
int height = pVih->bmiHeader.biHeight;
|
||||
resolutions.emplace_back(width, height);
|
||||
}
|
||||
DeleteMediaType(pMediaType);
|
||||
}
|
||||
pEnumMediaTypes->Release();
|
||||
}
|
||||
|
||||
bool DeviceDiscovery::SetPinResolution(IBaseFilter *pFilter, int width, int height) {
|
||||
IEnumPins *pEnumPins;
|
||||
HRESULT hr = pFilter->EnumPins(&pEnumPins);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IPin *pPin;
|
||||
bool success = false;
|
||||
while (pEnumPins->Next(1, &pPin, nullptr) == S_OK) {
|
||||
PIN_DIRECTION pinDir;
|
||||
pPin->QueryDirection(&pinDir);
|
||||
if (pinDir == PINDIR_OUTPUT) {
|
||||
IEnumMediaTypes *pEnumMediaTypes = nullptr;
|
||||
hr = pPin->EnumMediaTypes(&pEnumMediaTypes);
|
||||
if (SUCCEEDED(hr)) {
|
||||
AM_MEDIA_TYPE *pMediaType = nullptr;
|
||||
while (pEnumMediaTypes->Next(1, &pMediaType, nullptr) == S_OK) {
|
||||
if (pMediaType->formattype == FORMAT_VideoInfo) {
|
||||
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER *>(pMediaType->pbFormat);
|
||||
if (pVih->bmiHeader.biWidth == width && pVih->bmiHeader.biHeight == height) {
|
||||
hr = pPin->QueryAccept(pMediaType);
|
||||
if (hr == S_OK) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
DeleteMediaType(pMediaType);
|
||||
if (success) break;
|
||||
}
|
||||
pEnumMediaTypes->Release();
|
||||
}
|
||||
}
|
||||
pPin->Release();
|
||||
if (success) break;
|
||||
}
|
||||
pEnumPins->Release();
|
||||
return success;
|
||||
}
|
||||
|
||||
DeviceDiscovery::Device::Device(IMFMediaSource *source) : source(source) {
|
||||
source->AddRef();
|
||||
auto result = MFCreateSourceReaderFromMediaSource(source, nullptr, &reader);
|
||||
@ -190,8 +364,8 @@ DeviceDiscovery::Device::Device(IMFMediaSource *source) : source(source) {
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::vector<std::string> DeviceDiscovery::devices() {
|
||||
std::vector<std::string> ret;
|
||||
std::vector<DeviceDiscovery::Device> DeviceDiscovery::devices() {
|
||||
std::vector<Device> ret;
|
||||
for (const auto &entry : std::filesystem::directory_iterator("/dev")) {
|
||||
if (entry.is_character_file() && entry.path().string().find("video") != std::string::npos) {
|
||||
int fd = open(entry.path().c_str(), O_RDWR);
|
||||
@ -203,7 +377,10 @@ std::vector<std::string> DeviceDiscovery::devices() {
|
||||
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
|
||||
if ((strstr(reinterpret_cast<const char *>(cap.card), DeviceName) != nullptr) &&
|
||||
(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
|
||||
ret.push_back(entry.path().string());
|
||||
Device device;
|
||||
device.friendlyName = entry.path().string();
|
||||
device.alternativeName = entry.path().string();
|
||||
ret.push_back(device);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
@ -240,7 +417,7 @@ static std::string find_video_device_by_name(const std::string &targetName) {
|
||||
|
||||
std::shared_ptr<DeviceDiscovery::Device> DeviceDiscovery::find(const std::string &deviceName, std::error_code &error) {
|
||||
auto ret = std::make_shared<Device>();
|
||||
ret->name = find_video_device_by_name(deviceName);
|
||||
ret->friendlyName = find_video_device_by_name(deviceName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -263,9 +440,9 @@ void DeviceDiscovery::enterOtaMode(const std::shared_ptr<Device> &device, std::e
|
||||
} else {
|
||||
LOG(info) << "found ota specific resolution: " << OtaSpecificWidth << "x" << otaSpecificHeight;
|
||||
}
|
||||
int fd = open(device->name.c_str(), O_RDWR);
|
||||
int fd = open(device->friendlyName.c_str(), O_RDWR);
|
||||
if (fd <= 0) {
|
||||
LOG(error) << "Failed to open device " << device->name;
|
||||
LOG(error) << "Failed to open device " << device->friendlyName;
|
||||
} else {
|
||||
struct v4l2_format format;
|
||||
memset(&format, 0, sizeof(format));
|
||||
@ -343,9 +520,9 @@ void DeviceDiscovery::enterOtaMode(const std::shared_ptr<Device> &device, std::e
|
||||
|
||||
DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr<Device> &source) {
|
||||
Resolutions ret;
|
||||
int fd = open(source->name.c_str(), O_RDWR);
|
||||
int fd = open(source->friendlyName.c_str(), O_RDWR);
|
||||
if (fd <= 0) {
|
||||
LOG(error) << "Failed to open device " << source->name;
|
||||
LOG(error) << "Failed to open device " << source->friendlyName;
|
||||
} else {
|
||||
struct v4l2_fmtdesc fmt;
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
@ -8,6 +8,9 @@
|
||||
#ifdef WIN32
|
||||
#include <mfidl.h>
|
||||
#include <mfreadwrite.h>
|
||||
|
||||
class IPin;
|
||||
class IBaseFilter;
|
||||
#endif
|
||||
|
||||
class DeviceDiscovery {
|
||||
@ -15,25 +18,33 @@ class DeviceDiscovery {
|
||||
|
||||
public:
|
||||
constexpr static auto DeviceName = "UVC Camera";
|
||||
using Resolution = std::pair<int32_t, int32_t>;
|
||||
using Resolutions = std::vector<Resolution>;
|
||||
struct Device {
|
||||
Device() = default;
|
||||
std::string friendlyName;
|
||||
std::string alternativeName;
|
||||
Resolutions resolutions;
|
||||
~Device();
|
||||
#ifdef WIN32
|
||||
Device(IMFMediaSource *source);
|
||||
IMFMediaSource *source = nullptr;
|
||||
IMFSourceReader *reader = nullptr;
|
||||
#else
|
||||
std::string name;
|
||||
#endif
|
||||
};
|
||||
|
||||
using Resolution = std::pair<int32_t, int32_t>;
|
||||
using Resolutions = std::vector<Resolution>;
|
||||
DeviceDiscovery();
|
||||
std::shared_ptr<Device> find(const std::string &deviceName, std::error_code &error);
|
||||
void enterOtaMode(const std::shared_ptr<Device> &device, std::error_code &error);
|
||||
std::vector<std::string> devices();
|
||||
std::vector<Device> devices();
|
||||
bool SetResolution(const std::string &deviceName, int width, int height);
|
||||
|
||||
protected:
|
||||
Resolutions deviceResolutions(const std::shared_ptr<Device> &source);
|
||||
#ifdef WIN32
|
||||
void GetDeviceNames(IMoniker *pMoniker, Device &info);
|
||||
void GetSupportedResolutions(IPin *pPin, std::vector<std::pair<int, int>> &resolutions);
|
||||
bool SetPinResolution(IBaseFilter *pFilter, int width, int height);
|
||||
#endif
|
||||
};
|
||||
#endif // __DEVICEDISCOVERY_H__
|
||||
|
@ -73,7 +73,7 @@ HOST_TOOLS := /opt/Xuantie-900-gcc-elf-newlib-x86_64-V2.6.1/bin
|
||||
|
||||
docker run -it --rm --user 1000:1000 -v /opt:/opt -v $(pwd)/..:$(pwd)/.. -w $(pwd) registry.cn-shenzhen.aliyuncs.com/amass_toolset/yoctools:22.04 ./rebuild-app.sh y L015 V200 R002
|
||||
|
||||
docker run -it --rm --user 1000:1000 -v /opt:/opt -v $(pwd)/..:$(pwd)/.. -w $(pwd) registry.cn-shenzhen.aliyuncs.com/amass_toolset/yoctools:22.04 ./rebuild-app-ota.sh y L015 V200 R002 08
|
||||
docker run -it --rm --user 1000:1000 -v /opt:/opt -v $(pwd)/..:$(pwd)/.. -w $(pwd) registry.cn-shenzhen.aliyuncs.com/amass_toolset/yoctools:22.04 ./rebuild-app-ota.sh y L015 V200 R002 09
|
||||
```
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ BOOST_AUTO_TEST_CASE(EnumDevice) {
|
||||
auto device = discovery.find("UVC Camera", error);
|
||||
auto devices = discovery.devices();
|
||||
for (int i = 0; i < devices.size(); i++) {
|
||||
LOG(info) << "device[" << i << "] " << devices.at(i);
|
||||
LOG(info) << "device[" << i << "] " << devices.at(i).friendlyName;
|
||||
}
|
||||
|
||||
discovery.enterOtaMode(device, error);
|
||||
|
BIN
resources/7za.exe
Normal file
BIN
resources/7za.exe
Normal file
Binary file not shown.
26
resources/L015掌静脉模组集成拓扑图.md
Normal file
26
resources/L015掌静脉模组集成拓扑图.md
Normal file
@ -0,0 +1,26 @@
|
||||
掌静脉模组支持掌静脉注册,识别功能,同时也提供通过图片注册掌静脉功能。
|
||||
|
||||
常见有两种应用场景:单模组使用,多设备集群分发注册识别。
|
||||
|
||||
## 单模组使用
|
||||
|
||||
![](./local_use.svg)
|
||||
|
||||
在此应用场景下,【门禁面板机】需要完成和【掌静脉模组】通信协议的对接。
|
||||
|
||||
当注册用户时,门禁面板机向掌静脉模组发送注册命令使模块进入【注册模式】,并提示【用户】将【手掌】置于掌静脉模组上方进行注册,注册成功后,掌静脉模组将会上报一个【唯一标识ID】至门禁面板机,面板机需要将该ID与用户进行绑定。
|
||||
|
||||
注册成功后,门禁面板机可发送指令使模组切换至【识别模式】,当用户将手置于掌静脉模组上方,模组会和之前注册的掌静脉底库进行比对,比对结束之后,将会向门禁面板机上报ID以标识此次识别结果,此时面板机可依靠此ID和之前记录的注册ID进行比对,如果ID相同则表示匹配上该用户。
|
||||
|
||||
## 多模组集群使用
|
||||
|
||||
![](muti_use.svg)
|
||||
|
||||
在此应用场景下,【门禁面板机】需要完成和【掌静脉模组】通行协议的对接,且需要服务器进行掌静脉注册图片管理及分发。
|
||||
|
||||
考虑到常见的门禁通行场景,掌静脉模组支持注册时获取掌静脉注册图片,然后再使用该图片进行掌静脉注册。
|
||||
|
||||
通过这种方式,用户在注册时,可以通掌静脉注册机获取掌静脉注册图片,然后在将掌静脉注册图片发送至服务器,服务器接收到图片之后,再将图片下发至各个门禁面板机以图片方式进行注册。这样就可以达到一端注册,多端使用的效果。
|
||||
|
||||
|
||||
|
@ -74,15 +74,17 @@ USB(CDC):
|
||||
|
||||
```c++
|
||||
struct msg_verify_data {
|
||||
uint8_t reserved;
|
||||
uint8_t reserved1;
|
||||
uint8_t timeout;
|
||||
uint8_t reserved2[6];
|
||||
};
|
||||
```
|
||||
|
||||
参数说明:
|
||||
|
||||
- reserved:保留字段,暂未使用。需设置为 0x00。
|
||||
- reserved1:保留字段,暂未使用。需设置为 0x00。
|
||||
- timeout:识别超时时间,默认为10s,用户可以自行设置(最大不超过255s)。**主控等待模组录入应答的超时时间应大于此参数设置值。**
|
||||
- reserved2:保留字段,暂未使用。需设置为 0x00。
|
||||
|
||||
主控下发消息格式如下:
|
||||
|
||||
@ -93,22 +95,23 @@ struct msg_verify_data {
|
||||
<th>SyncWord</th>
|
||||
<th>MsgID</th>
|
||||
<th>DataSize</th>
|
||||
<th colspan="2">Data</th>
|
||||
<th colspan="3">Data</th>
|
||||
<th>ParityCheck</th>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>2 bytes</td>
|
||||
<td>1 byte</td>
|
||||
<td>2 bytes</td>
|
||||
<td colspan="2">2 bytes</td>
|
||||
<td colspan="3">8 bytes</td>
|
||||
<td>1 byte</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>0xEF 0xAA</td>
|
||||
<td>MID_VERIFY<br/>(0x12)</td>
|
||||
<td>0x02</td>
|
||||
<td>reserved<br/>(1 byte)</td>
|
||||
<td>0x08</td>
|
||||
<td>reserved1<br/>(1 byte)</td>
|
||||
<td>timeout<br/>(1 byte)</td>
|
||||
<td>reserved2<br/>(6 bytes)</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -392,6 +395,8 @@ MID_NOTE 消息主要作用是主动向主控上报一些信息,报文格式
|
||||
|
||||
2024/10/14:V0.1版本,描述模组识别、录入、删除几个基本协议指令。
|
||||
|
||||
2024/11/18:V0.2版本,`MID_VERIFY` 消息新增字段预留空间,用于功能扩展。
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
param($type)
|
||||
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
# [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("GBK")
|
||||
|
||||
$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' }
|
||||
@ -24,8 +25,7 @@ if ($fileContent -match 'project\([^\)]+VERSION\s+([0-9]+\.[0-9]+)') {
|
||||
Write-Output "未找到版本号"
|
||||
}
|
||||
|
||||
$deployPath = Join-Path -Path $buildPath -ChildPath "SmartLockerTools_v$version"
|
||||
$zipFilePath = Join-Path -Path $buildPath -ChildPath "SmartLockerTools_v$version.zip"
|
||||
$deployPath = Join-Path -Path $buildPath -ChildPath "掌静脉工具v$version"
|
||||
$changelogPath = Join-Path -Path $buildPath -ChildPath "CHANGELOG.txt"
|
||||
|
||||
function Build() {
|
||||
@ -61,6 +61,9 @@ function Deploy() {
|
||||
& $qtHome\bin\windeployqt.exe $deployPath\$oldName --qmldir=$qtHome\qml
|
||||
Rename-Item -Path $deployPath\$oldName -NewName $deployPath\$newName
|
||||
}
|
||||
Remove-Item -Path $deployPath\Qt6Quick3D*
|
||||
Remove-Item -Path $deployPath\translations -Recurse -Force # 暂时不需要翻译文件
|
||||
Remove-Item -Path $deployPath\qmltooling -Recurse -Force
|
||||
|
||||
$modules = "QmlCore"
|
||||
foreach ($module in $modules) {
|
||||
@ -83,12 +86,14 @@ function Deploy() {
|
||||
Copy-Item -Path $boostRoot\lib\boost_$boost-vc143-mt-x64-1_86.dll -Destination $deployPath
|
||||
}
|
||||
|
||||
$ffmpegs = "avcodec-61", "avdevice-61", "avfilter-10", "avformat-61", "avutil-59", "postproc-58", "swresample-5", "swscale-8"
|
||||
$ffmpegs = "avcodec-61", "avdevice-61", "avformat-61", "avutil-59", "postproc-58", "swresample-5", "swscale-8" # avfilter-10
|
||||
foreach ($ffmpeg in $ffmpegs) {
|
||||
Copy-Item -Path $ffmpegRoot\bin\$ffmpeg.dll -Destination $deployPath
|
||||
}
|
||||
|
||||
Compress-Archive -Path $deployPath -DestinationPath $zipFilePath -Force
|
||||
$zipFilePath = Join-Path -Path $buildPath -ChildPath "掌静脉工具v$version.7z"
|
||||
# Compress-Archive -Path $deployPath -DestinationPath $zipFilePath -Force
|
||||
& resources\7za.exe a -t7z -mx=9 $zipFilePath $deployPath
|
||||
}
|
||||
|
||||
function Clean() {
|
||||
|
288
resources/local_use.svg
Normal file
288
resources/local_use.svg
Normal file
@ -0,0 +1,288 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- 由 Microsoft Visio, SVG Export 生成 绘图1.svg 页-1 -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
|
||||
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="1.39583in" height="3.17224in"
|
||||
viewBox="0 0 100.5 228.401" xml:space="preserve" color-interpolation-filters="sRGB" class="st20">
|
||||
<v:documentProperties v:langID="2052" v:metric="true" v:viewMarkup="false"/>
|
||||
|
||||
<style type="text/css">
|
||||
<![CDATA[
|
||||
.st1 {fill:#ff00ff;fill-opacity:0;stroke:#c7c8c8;stroke-opacity:0;stroke-width:0.72}
|
||||
.st2 {fill:url(#grad0-7);stroke:#ffffff;stroke-width:0.72}
|
||||
.st3 {fill:url(#grad11-11)}
|
||||
.st4 {stroke:#000000;stroke-width:0.72}
|
||||
.st5 {stroke:#c8c8c8;stroke-width:1.5}
|
||||
.st6 {fill:#ff00ff;fill-opacity:0}
|
||||
.st7 {stroke:#c7c8c8;stroke-opacity:0;stroke-width:0.72}
|
||||
.st8 {fill:url(#grad7-25);stroke:none;stroke-width:0.72}
|
||||
.st9 {fill:url(#grad0-29);stroke:none;stroke-width:0.72}
|
||||
.st10 {stroke:#ffffff;stroke-width:0.72}
|
||||
.st11 {fill:url(#grad3-40);stroke:#c7c8c8;stroke-width:0.12}
|
||||
.st12 {fill:url(#grad7-44);stroke:#c7c8c8;stroke-width:0.24}
|
||||
.st13 {fill:url(#grad10-48);stroke:#000000;stroke-width:0.72}
|
||||
.st14 {fill:#ffffff;stroke:#000000;stroke-width:0.24}
|
||||
.st15 {fill:url(#grad0-58);stroke:#000000;stroke-width:0.72}
|
||||
.st16 {fill:none;stroke:none;stroke-width:0.25}
|
||||
.st17 {fill:#4672c4;font-family:黑体;font-size:1.16666em}
|
||||
.st18 {marker-end:url(#mrkr13-73);marker-start:url(#mrkr13-71);stroke:#4672c4;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
|
||||
.st19 {fill:#4672c4;fill-opacity:1;stroke:#4672c4;stroke-opacity:1;stroke-width:0.28409090909091}
|
||||
.st20 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
|
||||
]]>
|
||||
</style>
|
||||
|
||||
<defs id="Patterns_And_Gradients">
|
||||
<linearGradient id="grad0-7" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(270 0.5 0.5)">
|
||||
<stop offset="0" stop-color="#668ace" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#626262" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<pattern id="grad11-11" x="0" y="0" width="1" height="1" patternContentUnits="objectBoundingBox">
|
||||
<path d="M 0 1 L 0 0 L 1 0 z" style="fill:url(#grad0-12)"/>
|
||||
<path d="M 0 1 L 1 1 L 1 0 z" style="fill:url(#grad0-13)"/>
|
||||
</pattern>
|
||||
<linearGradient id="grad0-12" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(180 0.5 0.5)">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#c0c0c0" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad0-13" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(90 0.5 0.5)">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#c0c0c0" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="grad7-25" cx="0" cy="0" r="1.4">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#c0c0c0" stop-opacity="1"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="grad0-29" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(180 0.5 0.5)">
|
||||
<stop offset="0.01" stop-color="#3e3e3e" stop-opacity="1"/>
|
||||
<stop offset="0.5" stop-color="#365fa9" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#3e3e3e" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="grad3-40" cx="0.5" cy="0.5" r="0.73">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#008000" stop-opacity="1"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="grad7-44" cx="0" cy="0" r="1.4">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#000000" stop-opacity="1"/>
|
||||
</radialGradient>
|
||||
<pattern id="grad10-48" x="0" y="0" width="1" height="1" patternContentUnits="objectBoundingBox">
|
||||
<path d="M 0.5 0.5 L 0 0 L 0 1 z" style="fill:url(#grad0-49)"/>
|
||||
<path d="M 0.5 0.5 L 1 0 L 1 1 z" style="fill:url(#grad0-50)"/>
|
||||
<path d="M 0.5 0.5 L 0 0 L 1 0 z" style="fill:url(#grad0-51)"/>
|
||||
<path d="M 0.5 0.5 L 0 1 L 1 1 z" style="fill:url(#grad0-52)"/>
|
||||
</pattern>
|
||||
<linearGradient id="grad0-49" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(180 0.5 0.5)">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#c6d1e3" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad0-50" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(360 0.5 0.5)">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#c6d1e3" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad0-51" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(270 0.5 0.5)">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#c6d1e3" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad0-52" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(90 0.5 0.5)">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#c6d1e3" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad0-58" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(90 0.5 0.5)">
|
||||
<stop offset="0.01" stop-color="#000000" stop-opacity="1"/>
|
||||
<stop offset="0.5" stop-color="#ffffff" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="#000000" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<defs id="Markers">
|
||||
<g id="lend13">
|
||||
<path d="M 3 1 L 0 0 L 3 -1 L 3 1 " style="stroke:none"/>
|
||||
</g>
|
||||
<marker id="mrkr13-71" class="st19" v:arrowType="13" v:arrowSize="2" v:setback="10.2" refX="10.2" orient="auto"
|
||||
markerUnits="strokeWidth" overflow="visible">
|
||||
<use xlink:href="#lend13" transform="scale(3.52) "/>
|
||||
</marker>
|
||||
<marker id="mrkr13-73" class="st19" v:arrowType="13" v:arrowSize="2" v:setback="10.56" refX="-10.56" orient="auto"
|
||||
markerUnits="strokeWidth" overflow="visible">
|
||||
<use xlink:href="#lend13" transform="scale(-3.52,-3.52) "/>
|
||||
</marker>
|
||||
</defs>
|
||||
<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
|
||||
<title>页-1</title>
|
||||
<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394"
|
||||
v:shadowOffsetY="-8.50394"/>
|
||||
<v:layer v:name="连接线" v:index="0"/>
|
||||
<g id="group66-1" transform="translate(33.9508,-22.4051)" v:mID="66" v:groupContext="group">
|
||||
<v:custProps>
|
||||
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:type="0" v:invis="true" v:ask="false" v:langID="2052"
|
||||
v:val="VT4(连接性)"/>
|
||||
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:type="0" v:invis="true" v:ask="false" v:langID="2052" v:val="VT4(概念)"/>
|
||||
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:type="0" v:invis="true" v:ask="false" v:langID="2052"
|
||||
v:val="VT4(数据)"/>
|
||||
</v:custProps>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="visLegendShape" v:val="VT0(2):26"/>
|
||||
<v:ud v:nameU="HasText" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="SolSH" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
|
||||
<v:ud v:nameU="ShapeClass" v:val="VT0(4):26"/>
|
||||
<v:ud v:nameU="ShapeType" v:val="VT0(7):26"/>
|
||||
<v:ud v:nameU="SubShapeType" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
</v:userDefs>
|
||||
<title>数据</title>
|
||||
<g id="shape67-2" v:mID="67" v:groupContext="shape" transform="translate(0,0.000575512)">
|
||||
<title>工作表.67</title>
|
||||
<path d="M32.54 205.26 A16.2701 9.45359 0.18 0 1 32.15 207.35 A16.2701 9.45359 -179.82 0 0 32.54 205.26 ZM32.54 205.26
|
||||
A16.2701 9.45359 -179.82 0 0 0 205.26 L0 218.95 A16.2702 9.45363 -179.82 0 0 32.54 218.95 L32.6 218.95
|
||||
L32.6 205.26 L32.54 205.26 Z" class="st1"/>
|
||||
</g>
|
||||
<g id="shape68-4" v:mID="68" v:groupContext="shape" transform="translate(-1.65867E-13,-13.6911)">
|
||||
<title>工作表.68</title>
|
||||
<path d="M0 218.9 A16.2701 9.45359 -179.82 1 1 32.54 219 A16.2701 9.45359 -179.82 1 1 0 218.9 Z" class="st2"/>
|
||||
</g>
|
||||
<g id="shape69-8" v:mID="69" v:groupContext="shape">
|
||||
<title>工作表.69</title>
|
||||
<path d="M0 205.26 L0 218.9 L0 218.95 A16.2702 9.45363 -179.82 0 0 32.54 218.95 L32.6 218.95 L32.6 205.26 L32.54
|
||||
205.26 A16.2701 9.45359 0.18 0 1 0 205.26 L0 205.26 Z" class="st3"/>
|
||||
<path d="M0 205.26 L0 218.9 L0 218.95 A16.2702 9.45363 -179.82 0 0 32.54 218.95 L32.6 218.95 L32.6 205.26 L32.54
|
||||
205.26 A16.2701 9.45359 0.18 0 1 0 205.26" class="st4"/>
|
||||
</g>
|
||||
<g id="shape70-15" v:mID="70" v:groupContext="shape" transform="translate(0,0.000264735)">
|
||||
<title>工作表.70</title>
|
||||
<path d="M32.54 205.26 A16.2701 9.45359 -179.82 1 0 0 205.26 L0 218.95 A16.2702 9.45363 -179.82 0 0 32.54 218.95
|
||||
L32.6 218.95 L32.6 205.26 L32.54 205.26" class="st5"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="group71-18" transform="translate(16.2343,-106.027)" v:mID="71" v:groupContext="group">
|
||||
<v:custProps>
|
||||
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:type="0" v:invis="true" v:ask="false" v:langID="2052"
|
||||
v:val="VT4(设备)"/>
|
||||
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:type="0" v:invis="true" v:ask="false" v:langID="2052"
|
||||
v:val="VT4(计算机)"/>
|
||||
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:type="0" v:invis="true" v:ask="false" v:langID="2052"
|
||||
v:val="VT4(平板)"/>
|
||||
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:type="0" v:sortKey="Equipment" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:type="0" v:sortKey="Equipment" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:type="0" v:sortKey="Equipment" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:type="0" v:sortKey="Equipment" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:type="0" v:sortKey="Asset" v:invis="false" v:ask="false" v:langID="2052"/>
|
||||
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:type="0" v:sortKey="Asset" v:invis="false" v:ask="false" v:langID="2052"/>
|
||||
<v:cp v:nameU="Location" v:lbl="位置" v:type="0" v:sortKey="Asset" v:invis="false" v:ask="false" v:langID="2052"/>
|
||||
<v:cp v:nameU="Building" v:lbl="建筑物" v:type="0" v:sortKey="Asset" v:invis="false" v:ask="false" v:langID="2052"/>
|
||||
<v:cp v:nameU="Room" v:lbl="房间" v:type="0" v:sortKey="Asset" v:invis="false" v:ask="false" v:langID="2052"/>
|
||||
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:type="0" v:sortKey="Network" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="HardDriveSize" v:lbl="硬盘容量" v:type="0" v:sortKey="Workstation" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
<v:cp v:nameU="CPU" v:lbl="CPU" v:type="0" v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="2052"/>
|
||||
<v:cp v:nameU="Memory" v:lbl="内存" v:type="0" v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="2052"/>
|
||||
<v:cp v:nameU="OperatingSystem" v:lbl="操作系统" v:type="0" v:sortKey="Workstation" v:invis="false" v:ask="false"
|
||||
v:langID="2052"/>
|
||||
</v:custProps>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="HasText" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="ShapeClass" v:val="VT0(5):26"/>
|
||||
<v:ud v:nameU="ShapeType" v:val="VT0(6):26"/>
|
||||
<v:ud v:nameU="SubShapeType" v:val="VT0(69):26"/>
|
||||
<v:ud v:nameU="SolSH" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
|
||||
<v:ud v:nameU="visLegendShape" v:val="VT0(2):26"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
</v:userDefs>
|
||||
<title>平板电脑</title>
|
||||
<g id="shape72-19" v:mID="72" v:groupContext="shape" transform="translate(0,8.6288E-06)">
|
||||
<title>工作表.72</title>
|
||||
<path d="M22.75 134.89 A158.111 145.912 89.84 0 1 41.19 144.31 A158.315 146.1 -89.8 0 0 22.75 134.89 ZM2.33 154.56
|
||||
A2.43186 1.43688 -80 1 0 1.93 158.87 A3.97821 3.67126 90 0 1 1.53 159.34 L22.75 163.3 L22.75 205.59
|
||||
A105.542 97.3984 -90.11 0 0 59.76 228.4 A11.7512 10.8448 -89.38 0 0 66.73 223.18 L66.84 153.37 A5.68818
|
||||
5.2493 89.94 0 1 59.76 157.7 A6.18103 5.7045 -89 0 0 66.84 153.37 A126.613 116.845 -90.37 0 0 29.5 130.79
|
||||
A6.74865 6.22798 -89.71 0 0 22.75 134.89 L22.75 159.32 L2.27 154.55 L2.33 154.56 Z" class="st6"/>
|
||||
<path d="M22.75 134.89 A158.111 145.912 89.84 0 1 41.19 144.31 A158.315 146.1 -89.8 0 0 22.75 134.89M2.33 154.56
|
||||
A2.43186 1.43688 -80 1 0 1.93 158.87 A3.97821 3.67126 90 0 1 1.53 159.34 L22.75 163.3 L22.75 205.59
|
||||
A105.542 97.3984 -90.11 0 0 59.76 228.4 A11.7512 10.8448 -89.38 0 0 66.73 223.18 L66.84 153.37 A5.68818
|
||||
5.2493 89.94 0 1 59.76 157.7 A6.18103 5.7045 -89 0 0 66.84 153.37 A126.613 116.845 -90.37 0 0 29.5 130.79
|
||||
A6.74865 6.22798 -89.71 0 0 22.75 134.89 L22.75 159.32 L2.27 154.55" class="st7"/>
|
||||
</g>
|
||||
<g id="shape73-22" v:mID="73" v:groupContext="shape" transform="translate(23.9471,0)">
|
||||
<title>工作表.73</title>
|
||||
<path d="M0 205.59 A105.542 97.3984 -90.11 0 0 37.01 228.4 L37.01 157.7 A158.315 146.1 -89.8 0 0 0 134.89 L0 205.59
|
||||
Z" class="st8"/>
|
||||
</g>
|
||||
<g id="shape74-26" v:mID="74" v:groupContext="shape" transform="translate(60.9562,0)">
|
||||
<title>工作表.74</title>
|
||||
<path d="M-0 228.4 A11.7512 10.8448 -89.38 0 0 6.97 223.18 L7.08 153.37 A5.68818 5.2493 89.94 0 1 0 157.7 L0 228.4
|
||||
Z" class="st9"/>
|
||||
</g>
|
||||
<g id="shape75-30" v:mID="75" v:groupContext="shape" transform="translate(23.9471,-70.4662)">
|
||||
<title>工作表.75</title>
|
||||
<path d="M-0 205.36 A158.111 145.912 89.84 0 1 37.01 228.17 A6.18103 5.7045 -89 0 0 44.08 223.83 A126.613 116.845
|
||||
-90.37 0 0 6.75 201.25 A6.74865 6.22798 -89.71 0 0 0 205.36 Z" class="st2"/>
|
||||
</g>
|
||||
<g id="shape76-33" v:mID="76" v:groupContext="shape" transform="translate(27.648,-10.263)">
|
||||
<title>工作表.76</title>
|
||||
<path d="M30.59 228.4 L0 209.93 L0 151.2" class="st10"/>
|
||||
</g>
|
||||
<g id="shape77-37" v:mID="77" v:groupContext="shape" transform="translate(-62.8892,22.9027) rotate(-30)">
|
||||
<title>工作表.77</title>
|
||||
<ellipse cx="1.27835" cy="226.724" rx="1.27835" ry="1.67692" class="st11"/>
|
||||
</g>
|
||||
<g id="shape78-41" v:mID="78" v:groupContext="shape" transform="translate(40.4972,-66.0307) rotate(10)">
|
||||
<title>工作表.78</title>
|
||||
<ellipse cx="1.43688" cy="225.969" rx="1.43688" ry="2.43186" class="st12"/>
|
||||
</g>
|
||||
<g id="shape79-45" v:mID="79" v:groupContext="shape" transform="translate(27.648,-10.263)">
|
||||
<title>工作表.79</title>
|
||||
<path d="M1.2 209.24 L30.59 227.26 L30.59 228.4 L30.59 169.67 L0 151.2 L1.2 152 L1.2 209.24 Z" class="st13"/>
|
||||
</g>
|
||||
<g id="shape80-53" v:mID="80" v:groupContext="shape" transform="translate(36.0639,-63.1276)">
|
||||
<title>工作表.80</title>
|
||||
<path d="M0 228.4 L3.08 227.75 L0.23 225.57 L0 228.4 Z" class="st14"/>
|
||||
</g>
|
||||
<g id="shape81-55" v:mID="81" v:groupContext="shape" transform="translate(2.72921,-62.8833)">
|
||||
<title>工作表.81</title>
|
||||
<path d="M0 222.22 L33.1 228.4 L33.6 225.08 L0.74 217.43 A3.67126 3.97821 0 0 1 0 222.22 Z" class="st15"/>
|
||||
</g>
|
||||
<g id="shape82-59" v:mID="82" v:groupContext="shape" transform="translate(0,1.38061E-05)">
|
||||
<title>工作表.82</title>
|
||||
<path d="M23.95 205.59 A108.227 98.8731 -90.39 0 0 60.96 228.4 A8.47998 7.74722 -90.69 0 0 67.93 223.63 L68.03 153.37
|
||||
A137.519 125.635 -90.57 0 0 30.7 130.79 A6.89496 6.29907 -89.53 0 0 23.95 134.89 L23.96 157.82 L3.63
|
||||
153.09 A3.13064 3.42684 -180 0 0 0.02 156.81 A3.85578 4.22058 -180 0 0 2.7 160.59 L23.95 164.28 L23.95
|
||||
205.59" class="st5"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="shape83-62" v:mID="83" v:groupContext="shape" transform="translate(0.25,-3.31851)">
|
||||
<title>工作表.83</title>
|
||||
<desc>掌静脉模组</desc>
|
||||
<v:textBlock v:margins="rect(2,2,2,2)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="50" cy="222.401" width="100.01" height="12"/>
|
||||
<rect x="0" y="216.401" width="100" height="12" class="st16"/>
|
||||
<text x="15" y="227.06" class="st17" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>掌静脉模组</text> </g>
|
||||
<g id="shape84-65" v:mID="84" v:groupContext="shape" v:layerMember="0" transform="translate(43.1634,-55.0034)">
|
||||
<title>动态连接线</title>
|
||||
<path d="M7.09 218.2 L7.09 217.84 L7.09 174.68" class="st18"/>
|
||||
</g>
|
||||
<g id="shape85-74" v:mID="85" v:groupContext="shape" transform="translate(0.25,-213.082)">
|
||||
<title>工作表.85</title>
|
||||
<desc>门禁面板机</desc>
|
||||
<v:textBlock v:margins="rect(2,2,2,2)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="50" cy="222.401" width="100.01" height="12"/>
|
||||
<rect x="0" y="216.401" width="100" height="12" class="st16"/>
|
||||
<text x="15" y="227.06" class="st17" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>门禁面板机</text> </g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 18 KiB |
1558
resources/muti_use.svg
Normal file
1558
resources/muti_use.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 98 KiB |
Loading…
Reference in New Issue
Block a user