emblemscanner: native wasm works
This commit is contained in:
parent
431f81faad
commit
7c7c94fa7b
@ -27,7 +27,6 @@
|
|||||||
// Import utility functions from library
|
// Import utility functions from library
|
||||||
const {
|
const {
|
||||||
get_system_info,
|
get_system_info,
|
||||||
get_phone_model,
|
|
||||||
get_camera_rule,
|
get_camera_rule,
|
||||||
make_query,
|
make_query,
|
||||||
fetch_real_ip,
|
fetch_real_ip,
|
||||||
@ -40,7 +39,6 @@ const {
|
|||||||
load_qrtool,
|
load_qrtool,
|
||||||
is_qrtool_ready,
|
is_qrtool_ready,
|
||||||
process_frame,
|
process_frame,
|
||||||
process_frame_with_debug,
|
|
||||||
make_hint_text
|
make_hint_text
|
||||||
} = require('./qrprocessor.js');
|
} = require('./qrprocessor.js');
|
||||||
|
|
||||||
@ -72,30 +70,28 @@ Page({
|
|||||||
tenant_id: '', // Tenant identifier
|
tenant_id: '', // Tenant identifier
|
||||||
debug_msgs: [],
|
debug_msgs: [],
|
||||||
debug_image_data_url: '',
|
debug_image_data_url: '',
|
||||||
|
debug_last_result: null,
|
||||||
qrtool_ready: false,
|
qrtool_ready: false,
|
||||||
|
frame_processing_started: false,
|
||||||
// Frame processing statistics
|
// Frame processing statistics
|
||||||
frames_processed: 0,
|
frames_processed: 0,
|
||||||
frames_skipped: 0,
|
frames_skipped: 0,
|
||||||
total_processing_time: 0,
|
total_processing_time: 0,
|
||||||
avg_processing_time: 0,
|
avg_processing_time_ms: 0,
|
||||||
last_frame_time: 0,
|
last_frame_time_ms: 0,
|
||||||
rule_zoom: -1,
|
rule_zoom: -1,
|
||||||
camera_rule: null,
|
camera_rule: null,
|
||||||
use_web_view: false,
|
use_web_view: false,
|
||||||
emblem_camera_url: null,
|
emblem_camera_url: null,
|
||||||
// State machine: loading -> scanning -> verifying -> result
|
// State machine: initializing -> loading -> scanning -> verifying -> result
|
||||||
app_state: 'loading', // 'loading', 'scanning_camera', 'scanning_webview', 'verifying', 'result'
|
app_state: 'initializing', // 'initializing', 'loading', 'scanning_camera', 'scanning_webview', 'verifying', 'result'
|
||||||
scan_mode: 'unknown', // 'camera', 'webview'
|
scan_mode: 'unknown', // 'camera', 'webview'
|
||||||
no_web_view: false // Override web-view rule, force native camera
|
no_web_view: false // Override web-view rule, force native camera
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Lifecycle function--Called when page load
|
|
||||||
*/
|
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
console.log('QR Scanner module loaded', options);
|
console.log('QR Scanner module loaded', options);
|
||||||
|
|
||||||
// Store query parameters
|
|
||||||
const no_web_view = options.no_web_view === '1' || options.no_web_view === 'true';
|
const no_web_view = options.no_web_view === '1' || options.no_web_view === 'true';
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
@ -103,54 +99,38 @@ Page({
|
|||||||
no_web_view: no_web_view
|
no_web_view: no_web_view
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize image data storage
|
|
||||||
this.image_data_urls = [];
|
this.image_data_urls = [];
|
||||||
|
|
||||||
// Handle debug mode locally
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
const enable_debug = options.debug || options.scene == 'debug' || false;
|
const enable_debug = options.debug || options.scene == 'debug' || false;
|
||||||
|
|
||||||
// Get system information
|
|
||||||
this.initializeSystem(enable_debug);
|
this.initializeSystem(enable_debug);
|
||||||
|
|
||||||
// Initialize QRTool WASM
|
|
||||||
this.initializeQRTool();
|
this.initializeQRTool();
|
||||||
|
|
||||||
// Fetch IP address and tenant info, then load camera rules
|
|
||||||
this.fetchRealIP();
|
this.fetchRealIP();
|
||||||
this.fetchTenantID();
|
this.fetchTenantID();
|
||||||
this.loadCameraRules();
|
this.loadCameraRules();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch real IP address
|
|
||||||
*/
|
|
||||||
fetchRealIP() {
|
fetchRealIP() {
|
||||||
fetch_real_ip((err, ip) => {
|
fetch_real_ip((err, ip) => {
|
||||||
this.setData({ real_ip: ip });
|
this.setData({ real_ip: ip });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch tenant ID (can be customized based on app logic)
|
|
||||||
*/
|
|
||||||
fetchTenantID() {
|
fetchTenantID() {
|
||||||
const tenant_id = get_tenant_id();
|
const tenant_id = get_tenant_id();
|
||||||
this.setData({ tenant_id });
|
this.setData({ tenant_id });
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize QRTool WASM module
|
|
||||||
*/
|
|
||||||
initializeQRTool() {
|
initializeQRTool() {
|
||||||
load_qrtool();
|
load_qrtool();
|
||||||
|
|
||||||
// Check readiness periodically
|
|
||||||
const checkReady = () => {
|
const checkReady = () => {
|
||||||
if (is_qrtool_ready()) {
|
if (is_qrtool_ready()) {
|
||||||
this.setData({ qrtool_ready: true });
|
this.setData({ qrtool_ready: true });
|
||||||
this.addDebugMessage('QRTool WASM loaded and ready');
|
this.addDebugMessage('QRTool WASM loaded and ready');
|
||||||
this.startFrameProcessing();
|
this.transitionToState('loading');
|
||||||
|
this.startFrameProcessingMaybe();
|
||||||
} else {
|
} else {
|
||||||
setTimeout(checkReady, 100);
|
setTimeout(checkReady, 100);
|
||||||
}
|
}
|
||||||
@ -158,9 +138,6 @@ Page({
|
|||||||
checkReady();
|
checkReady();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize system information and device detection
|
|
||||||
*/
|
|
||||||
initializeSystem(enable_debug) {
|
initializeSystem(enable_debug) {
|
||||||
const systemInfo = get_system_info();
|
const systemInfo = get_system_info();
|
||||||
const phone_model = systemInfo.model;
|
const phone_model = systemInfo.model;
|
||||||
@ -175,18 +152,13 @@ Page({
|
|||||||
console.log(`Phone model: ${phone_model}, Using native camera mode`);
|
console.log(`Phone model: ${phone_model}, Using native camera mode`);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Load camera rules from API
|
|
||||||
*/
|
|
||||||
loadCameraRules() {
|
loadCameraRules() {
|
||||||
get_camera_rule(null, (rule) => {
|
get_camera_rule(null, (rule) => {
|
||||||
console.log('Camera rule loaded:', rule);
|
console.log('Camera rule loaded:', rule);
|
||||||
|
|
||||||
// Check for no_web_view override
|
|
||||||
const should_use_webview = rule.web_view && !this.data.no_web_view;
|
const should_use_webview = rule.web_view && !this.data.no_web_view;
|
||||||
let emblem_camera_url = null;
|
let emblem_camera_url = null;
|
||||||
|
|
||||||
// Set up web-view URL if needed
|
|
||||||
if (should_use_webview) {
|
if (should_use_webview) {
|
||||||
emblem_camera_url = "https://themblem.com/camera-5.1/?" + make_query(rule.zoom, this.data.return_page, this.data.real_ip, this.data.tenant_id);
|
emblem_camera_url = "https://themblem.com/camera-5.1/?" + make_query(rule.zoom, this.data.return_page, this.data.real_ip, this.data.tenant_id);
|
||||||
this.addDebugMessage(`Using web-view camera: ${emblem_camera_url}`);
|
this.addDebugMessage(`Using web-view camera: ${emblem_camera_url}`);
|
||||||
@ -203,10 +175,8 @@ Page({
|
|||||||
emblem_camera_url: emblem_camera_url
|
emblem_camera_url: emblem_camera_url
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add rule info to debug messages
|
|
||||||
this.addDebugMessage(`Camera rule: zoom=${rule.zoom}, web_view=${rule.web_view}${this.data.no_web_view ? ' (NO_WEB_VIEW)' : ''}`);
|
this.addDebugMessage(`Camera rule: zoom=${rule.zoom}, web_view=${rule.web_view}${this.data.no_web_view ? ' (NO_WEB_VIEW)' : ''}`);
|
||||||
|
|
||||||
// Transition to appropriate scanning state
|
|
||||||
if (should_use_webview) {
|
if (should_use_webview) {
|
||||||
this.startWebviewScanning();
|
this.startWebviewScanning();
|
||||||
} else {
|
} else {
|
||||||
@ -215,19 +185,13 @@ Page({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Camera ready callback
|
|
||||||
*/
|
|
||||||
onCameraReady(e) {
|
onCameraReady(e) {
|
||||||
console.log('Camera ready', e);
|
console.log('Camera ready', e);
|
||||||
this.camera_context = wx.createCameraContext();
|
this.camera_context = wx.createCameraContext();
|
||||||
this.addDebugMessage('Camera initialized for WASM processing');
|
this.addDebugMessage('Camera initialized for WASM processing');
|
||||||
// State transition is handled in loadCameraRules
|
this.startFrameProcessingMaybe();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Camera error callback
|
|
||||||
*/
|
|
||||||
onCameraError(e) {
|
onCameraError(e) {
|
||||||
console.error('Camera error', e);
|
console.error('Camera error', e);
|
||||||
this.addDebugMessage(`Camera error: ${JSON.stringify(e.detail)}`);
|
this.addDebugMessage(`Camera error: ${JSON.stringify(e.detail)}`);
|
||||||
@ -237,11 +201,6 @@ Page({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle torch/flash
|
|
||||||
*/
|
|
||||||
toggle_torch() {
|
toggle_torch() {
|
||||||
const newFlash = this.data.camera_flash === 'torch' ? 'off' : 'torch';
|
const newFlash = this.data.camera_flash === 'torch' ? 'off' : 'torch';
|
||||||
this.setData({
|
this.setData({
|
||||||
@ -250,9 +209,6 @@ Page({
|
|||||||
console.log('Torch toggled to:', newFlash);
|
console.log('Torch toggled to:', newFlash);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Show scan guide
|
|
||||||
*/
|
|
||||||
show_scanguide() {
|
show_scanguide() {
|
||||||
this.setData({
|
this.setData({
|
||||||
show_modal: 'scanguide',
|
show_modal: 'scanguide',
|
||||||
@ -260,34 +216,22 @@ Page({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Show service modal
|
|
||||||
*/
|
|
||||||
show_service() {
|
show_service() {
|
||||||
this.setData({
|
this.setData({
|
||||||
show_modal: 'service'
|
show_modal: 'service'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Close modal and restart camera (legacy method name for WXML compatibility)
|
|
||||||
*/
|
|
||||||
restart_camera() {
|
restart_camera() {
|
||||||
this.restartScanning();
|
this.restartScanning();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Close any modal
|
|
||||||
*/
|
|
||||||
close_modal() {
|
close_modal() {
|
||||||
this.setData({
|
this.setData({
|
||||||
show_modal: ''
|
show_modal: ''
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug tap handler
|
|
||||||
*/
|
|
||||||
debug_tap() {
|
debug_tap() {
|
||||||
const count = (this.debug_tap_count || 0) + 1;
|
const count = (this.debug_tap_count || 0) + 1;
|
||||||
this.debug_tap_count = count;
|
this.debug_tap_count = count;
|
||||||
@ -299,7 +243,6 @@ Page({
|
|||||||
this.debug_tap_count = 0;
|
this.debug_tap_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear count after 3 seconds
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.debug_tap_count = 0;
|
this.debug_tap_count = 0;
|
||||||
}, 3000);
|
}, 3000);
|
||||||
@ -331,11 +274,19 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start frame processing - sets up camera frame listener
|
* Start frame processing if both QRTool and camera are ready
|
||||||
*/
|
*/
|
||||||
startFrameProcessing() {
|
startFrameProcessingMaybe() {
|
||||||
|
// Already started, nothing to do
|
||||||
|
if (this.data.frame_processing_started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if both are ready
|
||||||
if (!this.data.qrtool_ready || !this.camera_context) {
|
if (!this.data.qrtool_ready || !this.camera_context) {
|
||||||
this.addDebugMessage('QRTool or camera not ready, skipping frame processing');
|
const qrStatus = this.data.qrtool_ready ? 'ready' : 'not ready';
|
||||||
|
const cameraStatus = this.camera_context ? 'ready' : 'not ready';
|
||||||
|
this.addDebugMessage(`Cannot start frame processing - QRTool: ${qrStatus}, Camera: ${cameraStatus}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,6 +300,9 @@ Page({
|
|||||||
|
|
||||||
// Start the listener
|
// Start the listener
|
||||||
this.listener.start();
|
this.listener.start();
|
||||||
|
|
||||||
|
// Mark as started
|
||||||
|
this.setData({ frame_processing_started: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -371,28 +325,27 @@ Page({
|
|||||||
this.lastFrameTime = now;
|
this.lastFrameTime = now;
|
||||||
|
|
||||||
// Start timing frame processing
|
// Start timing frame processing
|
||||||
const processStart = performance.now();
|
const processStart = Date.now();
|
||||||
|
|
||||||
// Process frame data directly
|
// Process frame data directly
|
||||||
try {
|
try {
|
||||||
const result = this.data.enable_debug ?
|
const result = process_frame(frame.width, frame.height, frame.data, this.data.camera_sensitivity, this.data.enable_debug);
|
||||||
process_frame_with_debug(frame.width, frame.height, frame.data, this.data.camera_sensitivity) :
|
|
||||||
process_frame(frame.width, frame.height, frame.data, this.data.camera_sensitivity);
|
|
||||||
|
|
||||||
// Calculate processing time
|
// Calculate processing time
|
||||||
const processEnd = performance.now();
|
const processEnd = Date.now();
|
||||||
const processingTime = processEnd - processStart;
|
const processingTime = processEnd - processStart;
|
||||||
|
|
||||||
// Update statistics
|
// Update statistics
|
||||||
const newFramesProcessed = this.data.frames_processed + 1;
|
const newFramesProcessed = this.data.frames_processed + 1;
|
||||||
const newTotalTime = this.data.total_processing_time + processingTime;
|
const newTotalTime = this.data.total_processing_time + processingTime;
|
||||||
const newAvgTime = newTotalTime / newFramesProcessed;
|
const newAvgTime = Math.round(newTotalTime / newFramesProcessed);
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
frames_processed: newFramesProcessed,
|
frames_processed: newFramesProcessed,
|
||||||
total_processing_time: newTotalTime,
|
total_processing_time: newTotalTime,
|
||||||
avg_processing_time: newAvgTime,
|
avg_processing_time_ms: newAvgTime,
|
||||||
last_frame_time: processingTime
|
last_frame_time_ms: Math.round(processingTime),
|
||||||
|
debug_last_result: result
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
@ -401,18 +354,19 @@ Page({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.addDebugMessage(`Frame processing error: ${error.message}`);
|
this.addDebugMessage(`Frame processing error: ${error.message}`);
|
||||||
// Still count failed processing attempts
|
// Still count failed processing attempts
|
||||||
const processEnd = performance.now();
|
const processEnd = Date.now();
|
||||||
const processingTime = processEnd - processStart;
|
const processingTime = processEnd - processStart;
|
||||||
|
|
||||||
const newFramesProcessed = this.data.frames_processed + 1;
|
const newFramesProcessed = this.data.frames_processed + 1;
|
||||||
const newTotalTime = this.data.total_processing_time + processingTime;
|
const newTotalTime = this.data.total_processing_time + processingTime;
|
||||||
const newAvgTime = newTotalTime / newFramesProcessed;
|
const newAvgTime = Math.round(newTotalTime / newFramesProcessed);
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
frames_processed: newFramesProcessed,
|
frames_processed: newFramesProcessed,
|
||||||
total_processing_time: newTotalTime,
|
total_processing_time: newTotalTime,
|
||||||
avg_processing_time: newAvgTime,
|
avg_processing_time_ms: newAvgTime,
|
||||||
last_frame_time: processingTime
|
last_frame_time_ms: Math.round(processingTime),
|
||||||
|
debug_last_result: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -507,24 +461,31 @@ Page({
|
|||||||
* State: Any -> Loading (restart)
|
* State: Any -> Loading (restart)
|
||||||
*/
|
*/
|
||||||
restartScanning() {
|
restartScanning() {
|
||||||
this.transitionToState('loading');
|
// Go to initializing if QRTool isn't ready, otherwise loading
|
||||||
|
const newState = this.data.qrtool_ready ? 'loading' : 'initializing';
|
||||||
|
this.transitionToState(newState);
|
||||||
|
|
||||||
|
const hintText = this.data.qrtool_ready ? '初始化相机...' : '初始化QR工具...';
|
||||||
this.setData({
|
this.setData({
|
||||||
show_modal: '',
|
show_modal: '',
|
||||||
hint_text: '初始化相机...',
|
hint_text: hintText,
|
||||||
busy: true,
|
busy: true,
|
||||||
// Reset frame processing statistics
|
// Reset frame processing statistics
|
||||||
frames_processed: 0,
|
frames_processed: 0,
|
||||||
frames_skipped: 0,
|
frames_skipped: 0,
|
||||||
total_processing_time: 0,
|
total_processing_time: 0,
|
||||||
avg_processing_time: 0,
|
avg_processing_time_ms: 0,
|
||||||
last_frame_time: 0
|
last_frame_time_ms: 0,
|
||||||
|
frame_processing_started: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset frame timing
|
// Reset frame timing
|
||||||
this.lastFrameTime = 0;
|
this.lastFrameTime = 0;
|
||||||
|
|
||||||
// Reload camera rules to restart the flow
|
// Reload camera rules to restart the flow (only if QRTool is ready)
|
||||||
this.loadCameraRules();
|
if (this.data.qrtool_ready) {
|
||||||
|
this.loadCameraRules();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -537,7 +498,7 @@ Page({
|
|||||||
const debugMsg = `${timestamp}: ${message}`;
|
const debugMsg = `${timestamp}: ${message}`;
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
debug_msgs: [...this.data.debug_msgs, debugMsg].slice(-10) // Keep last 10 messages
|
debug_msgs: [debugMsg, ...this.data.debug_msgs].slice(0, 5) // Keep first 5 messages (newest on top)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
<view class="wrapper">
|
<view class="wrapper">
|
||||||
|
<!-- STATE: INITIALIZING -->
|
||||||
|
<view wx:if="{{ app_state == 'initializing' }}" class="loading-spinner">
|
||||||
|
<view class="spinner"></view>
|
||||||
|
<text>初始化QR工具...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- STATE: LOADING -->
|
<!-- STATE: LOADING -->
|
||||||
<view wx:if="{{ app_state == 'loading' }}" class="loading-spinner">
|
<view wx:if="{{ app_state == 'loading' }}" class="loading-spinner">
|
||||||
<view class="spinner"></view>
|
<view class="spinner"></view>
|
||||||
@ -31,7 +37,7 @@
|
|||||||
<camera class="camera"
|
<camera class="camera"
|
||||||
flash="{{ camera_flash }}"
|
flash="{{ camera_flash }}"
|
||||||
frame-size="medium"
|
frame-size="medium"
|
||||||
bindready="onCameraReady"
|
bindinitdone="onCameraReady"
|
||||||
binderror="onCameraError">
|
binderror="onCameraError">
|
||||||
</camera>
|
</camera>
|
||||||
</block>
|
</block>
|
||||||
@ -46,58 +52,95 @@
|
|||||||
|
|
||||||
<!-- Debug overlay (available in all states) -->
|
<!-- Debug overlay (available in all states) -->
|
||||||
<view class="debug" wx:if="{{ enable_debug }}">
|
<view class="debug" wx:if="{{ enable_debug }}">
|
||||||
<view><image src="{{ debug_image_data_url }}"></image></view>
|
<view class="debug-top-row">
|
||||||
<view class="debug-messages">
|
<view class="debug-info-panel">
|
||||||
<text wx:for="{{ debug_msgs }}" class="debug-msg">{{ item }}</text>
|
<view class="debug-items">
|
||||||
</view>
|
<!-- Application State -->
|
||||||
<view class="debug-items">
|
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">state:</text>
|
<text class="debug-label">state:</text>
|
||||||
<text class="debug-value">{{ app_state }}</text>
|
<text class="debug-value">{{ app_state }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- System Info -->
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">model:</text>
|
<text class="debug-label">model:</text>
|
||||||
<text class="debug-value">{{ phone_model }}</text>
|
<text class="debug-value">{{ phone_model }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="debug-item">
|
|
||||||
<text class="debug-label">zoom:</text>
|
<!-- Camera Configuration -->
|
||||||
<text class="debug-value">{{ zoom }}/{{ max_zoom }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="debug-item debug-flag-box" wx:if="{{ no_web_view }}">
|
|
||||||
<text class="debug-flag">no_web_view</text>
|
|
||||||
</view>
|
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">rule:</text>
|
<text class="debug-label">rule:</text>
|
||||||
<text class="debug-value">{{ camera_rule.model || 'default' }}</text>
|
<text class="debug-value">{{ camera_rule.model || 'default' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">web_view:</text>
|
<text class="debug-label">zoom:</text>
|
||||||
<text class="debug-value">{{ use_web_view }}</text>
|
<text class="debug-value-number">{{ zoom }}/{{ max_zoom }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">sensitivity:</text>
|
<text class="debug-label">sensitivity:</text>
|
||||||
<text class="debug-value">{{ camera_sensitivity }}</text>
|
<text class="debug-value-number">{{ camera_sensitivity }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="debug-item">
|
||||||
|
<text class="debug-label">web_view:</text>
|
||||||
|
<text class="debug-value">{{ use_web_view }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="debug-item debug-flag-box" wx:if="{{ no_web_view }}">
|
||||||
|
<text class="debug-flag">no_web_view</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Processing Status -->
|
||||||
|
<view class="debug-item">
|
||||||
|
<text class="debug-label">qrtool:</text>
|
||||||
|
<text class="debug-value">{{ qrtool_ready ? 'ready' : 'loading' }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Frame Statistics -->
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">frames:</text>
|
<text class="debug-label">frames:</text>
|
||||||
<text class="debug-value">{{ frames_processed }}/{{ frames_processed + frames_skipped }}</text>
|
<text class="debug-value-number">{{ frames_processed }}</text>
|
||||||
|
<text class="debug-value">/</text>
|
||||||
|
<text class="debug-value-number">{{ frames_processed + frames_skipped }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">skipped:</text>
|
<text class="debug-label">skipped:</text>
|
||||||
<text class="debug-value">{{ frames_skipped }}</text>
|
<text class="debug-value-number">{{ frames_skipped }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">avg:</text>
|
<text class="debug-label">avg:</text>
|
||||||
<text class="debug-value">{{ avg_processing_time.toFixed(2) }}ms</text>
|
<text class="debug-value-number">{{ avg_processing_time_ms }}ms</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="debug-item">
|
<view class="debug-item">
|
||||||
<text class="debug-label">last:</text>
|
<text class="debug-label">last:</text>
|
||||||
<text class="debug-value">{{ last_frame_time.toFixed(2) }}ms</text>
|
<text class="debug-value-number">{{ last_frame_time_ms }}ms</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- QR Detection Results -->
|
||||||
|
<view class="debug-item" wx:if="{{ debug_last_result }}">
|
||||||
|
<text class="debug-label">qr:</text>
|
||||||
|
<text class="debug-value">{{ debug_last_result.qrcode || 'none' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="debug-item" wx:if="{{ debug_last_result }}">
|
||||||
|
<text class="debug-label">ok:</text>
|
||||||
|
<text class="debug-value">{{ debug_last_result.ok ? 'yes' : 'no' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="debug-item" wx:if="{{ debug_last_result && debug_last_result.err }}">
|
||||||
|
<text class="debug-label">err:</text>
|
||||||
|
<text class="debug-value">{{ debug_last_result.err }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Legacy Result -->
|
||||||
<view class="debug-item" wx:if="{{ result }}">
|
<view class="debug-item" wx:if="{{ result }}">
|
||||||
<text class="debug-label">result:</text>
|
<text class="debug-label">result:</text>
|
||||||
<text class="debug-value">{{ result }}</text>
|
<text class="debug-value">{{ result }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="debug-image-box">
|
||||||
|
<image src="{{ debug_image_data_url }}"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="debug-messages">
|
||||||
|
<text wx:for="{{ debug_msgs }}" class="debug-msg">{{ item }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|||||||
@ -198,23 +198,37 @@ view.debug {
|
|||||||
background-color: rgba(100, 100, 100, 0.5);
|
background-color: rgba(100, 100, 100, 0.5);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
overflow-y: auto;
|
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.debug image {
|
.debug-top-row {
|
||||||
right: 10px;
|
display: flex;
|
||||||
top: 10px;
|
gap: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-image-box {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-image-box image {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
border: 1px solid rgba(239, 72, 35, 0.8);
|
border: 1px solid rgba(239, 72, 35, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.debug-info-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
/* Debug messages section */
|
/* Debug messages section */
|
||||||
.debug-messages {
|
.debug-messages {
|
||||||
margin-bottom: 4px;
|
margin-top: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.debug-msg {
|
.debug-msg {
|
||||||
@ -222,6 +236,8 @@ view.debug image {
|
|||||||
margin: 1px 0;
|
margin: 1px 0;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug items container */
|
/* Debug items container */
|
||||||
@ -257,6 +273,13 @@ view.debug image {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.debug-value-number {
|
||||||
|
color: #99ff99;
|
||||||
|
font-family: monospace;
|
||||||
|
min-width: 32px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
/* Special styling for no_web_view flag box */
|
/* Special styling for no_web_view flag box */
|
||||||
.debug-item.debug-flag-box {
|
.debug-item.debug-flag-box {
|
||||||
border: 1px solid rgba(255, 0, 0, 0.6);
|
border: 1px solid rgba(255, 0, 0, 0.6);
|
||||||
|
|||||||
@ -26,91 +26,90 @@ function is_qrtool_ready() {
|
|||||||
/**
|
/**
|
||||||
* Process camera frame for QR code detection
|
* Process camera frame for QR code detection
|
||||||
*/
|
*/
|
||||||
function process_frame(width, height, image_data, camera_sensitivity) {
|
function process_frame(width, height, image_data, camera_sensitivity, enable_debug = false) {
|
||||||
if (!qrtool_ready) {
|
if (!qrtool_ready) {
|
||||||
console.log("qrtool not ready");
|
console.log("qrtool not ready");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('process_frame called:', {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data_type: typeof image_data,
|
||||||
|
data_constructor: image_data ? image_data.constructor.name : 'undefined',
|
||||||
|
data_length: image_data ? image_data.length : 'undefined',
|
||||||
|
data_byteLength: image_data ? image_data.byteLength : 'undefined',
|
||||||
|
bytes_per_element: image_data ? image_data.BYTES_PER_ELEMENT : 'undefined',
|
||||||
|
camera_sensitivity,
|
||||||
|
enable_debug
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Allocate buffer for image data
|
// Copy frame data to avoid TOCTOU
|
||||||
var buf = qrtool._malloc(image_data.length * image_data.BYTES_PER_ELEMENT);
|
var uca1 = new Uint8ClampedArray(image_data);
|
||||||
qrtool.HEAPU8.set(image_data, buf);
|
var uca = new Uint8ClampedArray(uca1);
|
||||||
|
|
||||||
|
console.log('Frame data copied:', {
|
||||||
|
uca_length: uca.length,
|
||||||
|
uca_bytes_per_element: uca.BYTES_PER_ELEMENT
|
||||||
|
});
|
||||||
|
|
||||||
|
var buf = qrtool._malloc(uca.length * uca.BYTES_PER_ELEMENT);
|
||||||
|
qrtool.HEAPU8.set(uca, buf);
|
||||||
|
|
||||||
|
console.log('Buffer allocated:', {
|
||||||
|
buffer_size: uca.length * uca.BYTES_PER_ELEMENT
|
||||||
|
});
|
||||||
|
|
||||||
|
var dot_area_buf = 0;
|
||||||
|
var debug_data_url = null;
|
||||||
|
|
||||||
|
if (enable_debug) {
|
||||||
|
const dot_area_size = 32;
|
||||||
|
const da_len = dot_area_size * dot_area_size * uca.BYTES_PER_ELEMENT * 4;
|
||||||
|
dot_area_buf = qrtool._malloc(da_len);
|
||||||
|
}
|
||||||
|
|
||||||
// Process QR code detection with angle
|
|
||||||
var result_str = qrtool.ccall('qrtool_angle', 'string',
|
var result_str = qrtool.ccall('qrtool_angle', 'string',
|
||||||
['number', 'number', 'number', 'number', 'number'],
|
['number', 'number', 'number', 'number', 'number'],
|
||||||
[buf, width, height, 0, camera_sensitivity || 1.0]
|
[buf, width, height, dot_area_buf, camera_sensitivity || 1]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Clean up buffer
|
console.log('qrtool_angle result:', result_str);
|
||||||
|
|
||||||
|
if (enable_debug && dot_area_buf) {
|
||||||
|
const dot_area_size = 32;
|
||||||
|
const da_len = dot_area_size * dot_area_size * uca.BYTES_PER_ELEMENT * 4;
|
||||||
|
var debug_view = qrtool.HEAPU8.subarray(dot_area_buf, dot_area_buf + da_len);
|
||||||
|
debug_data_url = data_url_from_frame(dot_area_size, dot_area_size, debug_view);
|
||||||
|
}
|
||||||
qrtool._free(buf);
|
qrtool._free(buf);
|
||||||
|
if (dot_area_buf) {
|
||||||
|
qrtool._free(dot_area_buf);
|
||||||
|
}
|
||||||
|
|
||||||
// Parse result
|
// Parse result
|
||||||
var result = JSON.parse(result_str);
|
var result = JSON.parse(result_str);
|
||||||
|
|
||||||
return {
|
var returnValue = {
|
||||||
qrcode: result.qrcode || '',
|
qrcode: result.qrcode || '',
|
||||||
angle: result.angle || 0,
|
angle: result.angle || 0,
|
||||||
ok: result.ok || false,
|
ok: result.ok || false,
|
||||||
err: result.err || '',
|
err: result.err || '',
|
||||||
valid_pattern: is_emblem_qr_pattern(result.qrcode || '')
|
valid_pattern: is_emblem_qr_pattern(result.qrcode || '')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (enable_debug && debug_data_url) {
|
||||||
|
returnValue.debug_data_url = debug_data_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('QR processing error:', error);
|
console.error('QR processing error:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Process frame with dot area extraction (for debug visualization)
|
|
||||||
*/
|
|
||||||
function process_frame_with_debug(width, height, image_data, camera_sensitivity) {
|
|
||||||
if (!qrtool_ready) {
|
|
||||||
console.log("qrtool not ready");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Allocate buffer for image data
|
|
||||||
var buf = qrtool._malloc(image_data.length * image_data.BYTES_PER_ELEMENT);
|
|
||||||
qrtool.HEAPU8.set(image_data, buf);
|
|
||||||
|
|
||||||
// Allocate buffer for debug dot area
|
|
||||||
const dot_area_size = 32;
|
|
||||||
const da_len = dot_area_size * dot_area_size * image_data.BYTES_PER_ELEMENT * 4;
|
|
||||||
var dot_area_buf = qrtool._malloc(da_len);
|
|
||||||
|
|
||||||
// Process QR code detection with debug output
|
|
||||||
var result_str = qrtool.ccall('qrtool_angle', 'string',
|
|
||||||
['number', 'number', 'number', 'number', 'number'],
|
|
||||||
[buf, width, height, dot_area_buf, camera_sensitivity || 1.0]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extract debug image
|
|
||||||
var debug_view = qrtool.HEAPU8.subarray(dot_area_buf, dot_area_buf + da_len);
|
|
||||||
var debug_data_url = data_url_from_frame(dot_area_size, dot_area_size, debug_view);
|
|
||||||
|
|
||||||
// Clean up buffers
|
|
||||||
qrtool._free(buf);
|
|
||||||
qrtool._free(dot_area_buf);
|
|
||||||
|
|
||||||
// Parse result
|
|
||||||
var result = JSON.parse(result_str);
|
|
||||||
|
|
||||||
return {
|
|
||||||
qrcode: result.qrcode || '',
|
|
||||||
angle: result.angle || 0,
|
|
||||||
ok: result.ok || false,
|
|
||||||
err: result.err || '',
|
|
||||||
valid_pattern: is_emblem_qr_pattern(result.qrcode || ''),
|
|
||||||
debug_data_url: debug_data_url
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('QR processing error:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create offscreen canvas for debug image generation
|
* Create offscreen canvas for debug image generation
|
||||||
@ -179,7 +178,6 @@ module.exports = {
|
|||||||
load_qrtool,
|
load_qrtool,
|
||||||
is_qrtool_ready,
|
is_qrtool_ready,
|
||||||
process_frame,
|
process_frame,
|
||||||
process_frame_with_debug,
|
|
||||||
data_url_from_frame,
|
data_url_from_frame,
|
||||||
is_emblem_qr_pattern,
|
is_emblem_qr_pattern,
|
||||||
make_hint_text
|
make_hint_text
|
||||||
|
|||||||
@ -34,7 +34,7 @@ var performance = {
|
|||||||
};
|
};
|
||||||
Module["instantiateWasm"] = (info, receiveInstance) => {
|
Module["instantiateWasm"] = (info, receiveInstance) => {
|
||||||
console.log("loading wasm...", info);
|
console.log("loading wasm...", info);
|
||||||
WebAssembly.instantiate("assets/qrtool.wx.wasm.br", info).then(result => {
|
WebAssembly.instantiate("pages/emblemscanner/assets/qrtool.wx.wasm.br", info).then(result => {
|
||||||
console.log("result:", result);
|
console.log("result:", result);
|
||||||
var inst = result["instance"];
|
var inst = result["instance"];
|
||||||
receiveInstance(inst);
|
receiveInstance(inst);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user