From e68348fbffeff6f3c30429e0be36a0bae632d050 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Wed, 15 Oct 2025 11:09:48 +0100 Subject: [PATCH] emblemscanner: subpackage support --- .gitignore | 1 + Makefile | 8 +- alg/pre.wx.js | 18 ++- scanner/pages/emblemscanner/README.md | 104 ++++++++++++++++++ scanner/pages/emblemscanner/emblemscanner.js | 72 ++++++++---- .../pages/emblemscanner/emblemscanner.wxss | 10 ++ .../pages/emblemscanner/libemblemscanner.js | 82 +++++++++++++- scanner/pages/emblemscanner/qrtool.wx.js | 16 ++- .../pages/emblemscanner/worker/qrtool.wx.js | 16 ++- 9 files changed, 301 insertions(+), 26 deletions(-) create mode 100644 scanner/pages/emblemscanner/README.md diff --git a/.gitignore b/.gitignore index f9b8ef8..92c60a4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build /dataset/local /detection/model /api/db.sqlite3 +/emblemscanner-release diff --git a/Makefile b/Makefile index 66e7a11..6cd31bf 100644 --- a/Makefile +++ b/Makefile @@ -135,8 +135,8 @@ RELEASE_VERSION := $(shell git describe --tags --abbrev=0 | sed 's/^emblemscanne emblemscanner-release: build/emblemscanner-$(RELEASE_VERSION).tar.gz -build/emblemscanner-$(RELEASE_VERSION).tar.gz: - # if tree dirty or git head not tagged, error out +build/emblemscanner-$(RELEASE_VERSION).tar.gz: FORCE + - rm -rf $@ git diff --exit-code if test -z "$(RELEASE_VERSION)"; then \ echo "RELEASE_VERSION is empty"; \ @@ -146,4 +146,6 @@ build/emblemscanner-$(RELEASE_VERSION).tar.gz: echo "git head not tagged as $(RELEASE_VERSION)"; \ exit 1; \ fi - tar -czvf build/emblemscanner-$(RELEASE_VERSION).tar.gz -C scanner pages/emblemscanner \ No newline at end of file + tar -cf build/emblemscanner-$(RELEASE_VERSION).tar -C scanner pages/emblemscanner + cd scanner/pages/emblemscanner && tar -uf ../../../build/emblemscanner-$(RELEASE_VERSION).tar README.md + gzip build/emblemscanner-$(RELEASE_VERSION).tar \ No newline at end of file diff --git a/alg/pre.wx.js b/alg/pre.wx.js index 68eaeb0..05df429 100644 --- a/alg/pre.wx.js +++ b/alg/pre.wx.js @@ -7,7 +7,23 @@ var performance = { }; Module['instantiateWasm'] = (info, receiveInstance) => { console.log("loading wasm...", info); - WebAssembly.instantiate("pages/emblemscanner/qrtool.wx.wasm.br", info).then((result) => { + + // Use dynamic path from global data if available, otherwise fallback to hardcoded path + var wasmPath = "pages/emblemscanner/qrtool.wx.wasm.br"; + try { + // Check if we're in a WeChat miniprogram environment and have access to getApp + if (typeof getApp !== 'undefined') { + var app = getApp(); + if (app && app.globalData && app.globalData.wasmFilePath) { + wasmPath = app.globalData.wasmFilePath; + console.log("Using dynamic WASM path:", wasmPath); + } + } + } catch (e) { + console.warn("Failed to get dynamic WASM path, using fallback:", e); + } + + WebAssembly.instantiate(wasmPath, info).then((result) => { console.log("result:", result); var inst = result['instance']; receiveInstance(inst); diff --git a/scanner/pages/emblemscanner/README.md b/scanner/pages/emblemscanner/README.md new file mode 100644 index 0000000..c0dfdd7 --- /dev/null +++ b/scanner/pages/emblemscanner/README.md @@ -0,0 +1,104 @@ + +# EmblemScanner 微信小程序扫码验证模块 + +## Release notes +- Version 1.0.1: initial release +- Version 1.1.0: sub package support + + +## 概述 + +EmblemScanner 是一个专为微信小程序设计的二维码扫描验证模块,集成了先进的 AI 图像识别技术和二维码检测算法,能够快速准确地识别和验证二维码。 + +## 集成方式和接口 + +### 调用参数 + +| 参数 | 说明 | 示例值 | +|------|------|--------| +| return_page | 扫描完成后跳转的页面路径 | `/pages/scan_result/scan_result` | + +### 扫描结果 + +扫描成功后,模块会跳转到指定的 return_page,携带以下参数: +- `ok=1` - 扫描成功 +- `qr_code` - 二维码内容 +- `serial_code` - 验证序列号(如有) + +扫描失败时: +- `ok=0` - 扫描失败 + +### 依赖要求 + +- 相机权限 +- 网络访问权限(连接 themblem.com API等) + +## 使用步骤 + +### 1. 项目准备 +创建 JS 版小程序项目。 + +### 2. 文件部署 +解压 emblemscanner 代码到小程序项目目录。 + +``` +pages/emblemscanner/ +├── assets/ # 静态资源 +├── emblemscanner.json # 页面配置 +├── emblemscanner.wxml # 页面结构 +├── emblemscanner.wxss # 样式文件 +├── emblemscanner.js # 主页面逻辑 +├── libemblemscanner.js # 核心库 +├── qrprocessor.js # 二维码处理器 +├── qrtool.wx.js # 二维码工具 +├── qrtool.wx.wasm.br # WebAssembly 二进制 +├── upload.js # 上传模块 +└── worker/ # Worker 线程 +``` + +### 3. 项目配置 + +在 `app.json` 中添加页面和 worker 配置: + +```json +{ + "pages": [ + "pages/index/index", + "pages/emblemscanner/emblemscanner", + "pages/logs/logs" + ], + "workers": "pages/emblemscanner/worker" +} +``` + +### 4. 域名配置 + +参考[微信小程序网络配置指南](https://developers.weixin.qq.com/miniprogram/dev/framework/ability/network.html),在小程序后台配置合法域名: + +- `themblem.com` + +### 5. 扫码调用 + +#### 跳转到扫码页面: +```javascript +navigateToScanner() { + wx.navigateTo({ + url: '/pages/emblemscanner/emblemscanner?return_page=/pages/scan_result/scan_result' + }) +} +``` + +#### 处理扫码结果: +```javascript +onLoad(options) { + var verify_ok = options['ok']; // ok == 1表示验证通过 + var qr_code = options['qr_code']; // 二维码内容 + var serial_code = options['serial_code']; // 序列码 + + this.setData({ + verify_ok, + qr_code, + serial_code, + }) +} +``` \ No newline at end of file diff --git a/scanner/pages/emblemscanner/emblemscanner.js b/scanner/pages/emblemscanner/emblemscanner.js index 3052a93..9073fd8 100644 --- a/scanner/pages/emblemscanner/emblemscanner.js +++ b/scanner/pages/emblemscanner/emblemscanner.js @@ -1,6 +1,12 @@ // QR Scanner Module - Self-contained QR scanning page // Adapted from existing camera implementation // +// DYNAMIC PATH RESOLUTION: +// - The system now dynamically computes the correct WASM file and worker paths based on the current page location +// - This allows the emblemscanner to work in both main pages directory and subpackages +// - Paths are computed using getCurrentPages() API and stored in global data during page load +// - WASM files and workers use these dynamic paths instead of hardcoded absolute paths +// // SCANNING MODES: // 1. Web View Mode - Uses external camera-5.1 web interface in web-view component // - Fallback for devices that need special handling @@ -31,7 +37,9 @@ const { make_query, fetch_real_ip, get_tenant_id, - is_emblem_qr_pattern + is_emblem_qr_pattern, + get_current_package_path, + get_wasm_file_path } = require('./libemblemscanner.js'); // Import upload functionality for verification (self-contained) @@ -115,6 +123,13 @@ Page({ no_web_view: no_web_view }); + // Store current package path in global data for dynamic WASM loading + const currentPackagePath = get_current_package_path(); + getApp().globalData.currentPackagePath = currentPackagePath; + getApp().globalData.wasmFilePath = get_wasm_file_path('qrtool.wx.wasm.br'); + console.log('Current package path:', currentPackagePath); + console.log('WASM file path:', getApp().globalData.wasmFilePath); + // Log page load for backend reporting (like camera.js) this.log("emblemscanner page load"); this.log("options", JSON.stringify(options)); @@ -189,8 +204,12 @@ Page({ * Set up worker for iPhone processing like camera.js */ setupWorker() { - // Create worker with local worker file - var worker = wx.createWorker('/pages/emblemscanner/worker/index.js', { + // Create worker with dynamic path based on current package location + var packagePath = getApp().globalData.currentPackagePath || 'pages/emblemscanner'; + var workerPath = `${packagePath}/worker/index.js`; + console.log('Creating worker with path:', workerPath); + + var worker = wx.createWorker(workerPath, { useExperimentalWorker: true, }); @@ -240,7 +259,10 @@ Page({ if (this.data.app_state === 'final_scanning' && result.ok) { // Request worker to submit image data - this.get_worker().postMessage({ type: "ready_to_submit" }); + const worker = this.get_worker(); + if (worker) { + worker.postMessage({ type: "ready_to_submit" }); + } } } @@ -361,7 +383,6 @@ Page({ show_scanguide() { - console.log('show_scanguide'); this.setData({ show_modal: 'scanguide', show_tip: false @@ -420,12 +441,12 @@ Page({ }); this.log(`Camera set initial zoom to ${initial_zoom}x, will zoom in to ${zoom}x when QR is found`); - this.camera_context.setZoom({ zoom: 6 }); + this.camera_context.setZoom({ zoom: initial_zoom }); // Set up zoom-in behavior when QR is found this.on_first_qr_found = () => { this.log(`First QR found, zoom to ${zoom}x`); - this.camera_context.setZoom({ zoom: 6 }); + this.camera_context.setZoom({ zoom: zoom }); this.setData({ zoom: zoom, qrmarkers_class: '', @@ -479,7 +500,7 @@ Page({ }); // Start the listener with worker if using worker mode - var worker = this.get_worker(); + var worker = this.data.use_worker ? this.get_worker() : null; this.listener.start({ worker, }); @@ -531,12 +552,15 @@ Page({ // Set processing flag before sending message this.setData({ worker_processing: true }); - this.get_worker().postMessage({ - type: 'frame', - width: frame.width, - height: frame.height, - camera_sensitivity: this.data.camera_sensitivity - }); + const worker = this.get_worker(); + if (worker) { + worker.postMessage({ + type: 'frame', + width: frame.width, + height: frame.height, + camera_sensitivity: this.data.camera_sensitivity + }); + } } else { // Direct processing (other devices) this.processFrameDirect(frame); @@ -698,7 +722,6 @@ Page({ submitImageForVerification(dataUrls, qrCode) { this.log('Submitting images for verification'); const begin = Date.now(); - wx.vibrateShort({ type: "heavy" }); const success = (res) => { this.log(`Upload success, code: ${res.statusCode}`); @@ -1029,6 +1052,7 @@ Page({ */ onUnload() { this.cleanupListener(); + this.terminateWorker(); }, /** @@ -1053,11 +1077,10 @@ Page({ }, get_worker() { - var gd = getApp().globalData; - if (!gd.emblemscanner_worker) { - gd.emblemscanner_worker = this.setupWorker(); + if (!this.worker && this.data.use_worker) { + this.worker = this.setupWorker(); } - return gd.emblemscanner_worker; + return this.worker; }, /** @@ -1075,6 +1098,17 @@ Page({ this.log('Worker state cleaned up'); } + }, + + /** + * Terminate and clean up worker on page unload + */ + terminateWorker() { + if (this.worker) { + this.worker.terminate(); + this.worker = null; + this.log('Worker terminated'); + } } }); \ No newline at end of file diff --git a/scanner/pages/emblemscanner/emblemscanner.wxss b/scanner/pages/emblemscanner/emblemscanner.wxss index ccc3ccc..048674b 100644 --- a/scanner/pages/emblemscanner/emblemscanner.wxss +++ b/scanner/pages/emblemscanner/emblemscanner.wxss @@ -1,3 +1,13 @@ + +page { + width: 100%; + height: 100%; +} + +.hidden { + display:none; +} + /* Main container */ view.wrapper { width: 100%; diff --git a/scanner/pages/emblemscanner/libemblemscanner.js b/scanner/pages/emblemscanner/libemblemscanner.js index f378ee5..f155cc5 100644 --- a/scanner/pages/emblemscanner/libemblemscanner.js +++ b/scanner/pages/emblemscanner/libemblemscanner.js @@ -131,6 +131,83 @@ function is_emblem_qr_pattern(p) { return false; } +/** + * Get the current package path for dynamic WASM file loading + * This function determines the correct path to WASM files based on the current page location + */ +function get_current_package_path() { + try { + // Get current pages stack to determine current page location + const pages = getCurrentPages(); + if (pages && pages.length > 0) { + const currentPage = pages[pages.length - 1]; + const route = currentPage.route; + + // Extract package path from route + // For example: "bofen/packages/emblemscanner/emblemscanner" -> "bofen/packages/emblemscanner" + const pathParts = route.split('/'); + if (pathParts.length >= 2) { + // Remove the page file name and return the package path + return pathParts.slice(0, -1).join('/'); + } + } + } catch (e) { + console.warn('Failed to get current package path:', e); + } + + // Fallback: assume we're in the main pages directory + return 'pages/emblemscanner'; +} + +/** + * Get the full WASM file path based on current package location + */ +function get_wasm_file_path(filename) { + const packagePath = get_current_package_path(); + return `${packagePath}/${filename}`; +} + +/** + * Test function to verify path resolution works correctly + * This can be called in development to test the path resolution logic + */ +function test_path_resolution() { + console.log('Testing path resolution...'); + + // Mock getCurrentPages for testing (would normally be available in WeChat miniprogram) + const originalGetCurrentPages = global.getCurrentPages; + global.getCurrentPages = function() { + return [{ + route: 'bofen/packages/emblemscanner/emblemscanner' + }]; + }; + + try { + const packagePath = get_current_package_path(); + const wasmPath = get_wasm_file_path('qrtool.wx.wasm.br'); + + console.log('Expected package path: bofen/packages/emblemscanner'); + console.log('Actual package path:', packagePath); + console.log('Expected WASM path: bofen/packages/emblemscanner/qrtool.wx.wasm.br'); + console.log('Actual WASM path:', wasmPath); + + if (packagePath === 'bofen/packages/emblemscanner' && wasmPath === 'bofen/packages/emblemscanner/qrtool.wx.wasm.br') { + console.log('✅ Path resolution test PASSED'); + return true; + } else { + console.log('❌ Path resolution test FAILED'); + return false; + } + } finally { + // Restore original function + if (originalGetCurrentPages) { + global.getCurrentPages = originalGetCurrentPages; + } else { + delete global.getCurrentPages; + } + } +} + module.exports = { get_system_info, get_phone_model, @@ -138,5 +215,8 @@ module.exports = { make_query, fetch_real_ip, get_tenant_id, - is_emblem_qr_pattern + is_emblem_qr_pattern, + get_current_package_path, + get_wasm_file_path, + test_path_resolution }; diff --git a/scanner/pages/emblemscanner/qrtool.wx.js b/scanner/pages/emblemscanner/qrtool.wx.js index c3ed19d..49f3f82 100644 --- a/scanner/pages/emblemscanner/qrtool.wx.js +++ b/scanner/pages/emblemscanner/qrtool.wx.js @@ -34,7 +34,21 @@ var performance = { }; Module["instantiateWasm"] = (info, receiveInstance) => { console.log("loading wasm...", info); - WebAssembly.instantiate("pages/emblemscanner/qrtool.wx.wasm.br", info).then(result => { + // Use dynamic path from global data if available, otherwise fallback to hardcoded path + var wasmPath = "pages/emblemscanner/qrtool.wx.wasm.br"; + try { + // Check if we're in a WeChat miniprogram environment and have access to getApp + if (typeof getApp !== "undefined") { + var app = getApp(); + if (app && app.globalData && app.globalData.wasmFilePath) { + wasmPath = app.globalData.wasmFilePath; + console.log("Using dynamic WASM path:", wasmPath); + } + } + } catch (e) { + console.warn("Failed to get dynamic WASM path, using fallback:", e); + } + WebAssembly.instantiate(wasmPath, info).then(result => { console.log("result:", result); var inst = result["instance"]; receiveInstance(inst); diff --git a/scanner/pages/emblemscanner/worker/qrtool.wx.js b/scanner/pages/emblemscanner/worker/qrtool.wx.js index c3ed19d..49f3f82 100644 --- a/scanner/pages/emblemscanner/worker/qrtool.wx.js +++ b/scanner/pages/emblemscanner/worker/qrtool.wx.js @@ -34,7 +34,21 @@ var performance = { }; Module["instantiateWasm"] = (info, receiveInstance) => { console.log("loading wasm...", info); - WebAssembly.instantiate("pages/emblemscanner/qrtool.wx.wasm.br", info).then(result => { + // Use dynamic path from global data if available, otherwise fallback to hardcoded path + var wasmPath = "pages/emblemscanner/qrtool.wx.wasm.br"; + try { + // Check if we're in a WeChat miniprogram environment and have access to getApp + if (typeof getApp !== "undefined") { + var app = getApp(); + if (app && app.globalData && app.globalData.wasmFilePath) { + wasmPath = app.globalData.wasmFilePath; + console.log("Using dynamic WASM path:", wasmPath); + } + } + } catch (e) { + console.warn("Failed to get dynamic WASM path, using fallback:", e); + } + WebAssembly.instantiate(wasmPath, info).then(result => { console.log("result:", result); var inst = result["instance"]; receiveInstance(inst);