7166be1203732de369fb298f9b619b10c06dd00f..e491cdb48129752324c4e3764f99bd9203c56dec
2026-03-31 lgq
1.新增VF205门禁机代码
e491cd 对比 | 目录
已添加397个文件
26360 ■■■■■ 文件已修改
vf205_access/.temp/dxide_debug.log 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/md5s.json 330 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/md5snew.json 330 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxAlsa/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxAudio/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxAudio/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxBase64/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxBase64/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCameraCalibration/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCameraCalibration/test.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.2.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.3.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCommon/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCommon/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCommonUtils/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxConfig/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxCryptoES/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxDisplay/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxDisplay/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxDriver/1.1.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxDriver/1.1.4.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxDriver/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxEid/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxEventBus/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxEventBus/2.0.2.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxEventBus/2.0.3.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxFace/1.1.8.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxFacial/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxGpio/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxGpioKey/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxGpioKey/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxHttp/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxHttpClient/2.0.3.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxHttpClient/2.0.4.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxHttpServer/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxHttpServer/1.0.5.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxLogger/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxLogger/2.0.3.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxMap/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxMap/2.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxMqtt/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxMqttClient/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNet/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNet/1.0.2.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNetwork/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNfc/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNfcCard/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNfcCard/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNtp/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNtp/2.0.2.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxNtp/2.0.3.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxOs/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxOta/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxOta/2.0.2.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxPwm/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxPwm/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxPwm/2.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxQrRule/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxSqlite/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxSqlite/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxSqliteDB/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxStd/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxStd/2.0.3.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxUart/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxUi/1.0.2.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxWatchdog/1.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxWatchdog/1.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxWorkerPool/2.0.0.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/zipFolder/VF105_V12/dxWorkerPool/2.0.1.zip 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/README.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/README_CN.md 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/app.dxproj 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/cameraCalibrationWorker.js 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/capturerWorker.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxAlsa.js 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxBase64.js 308 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxCameraCalibration.js 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxCapturer.js 336 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxCommon.js 733 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxConfig.js 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxDriver.js 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxEid.js 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxEventBus.js 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxFace.js 287 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxGpio.js 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxGpioKey.js 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxHttp.js 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxLogger.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxMap.js 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxMqtt.js 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxNet.js 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxNfc.js 409 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxNtp.js 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxOta.js 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxPwm.js 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxQrRule.js 215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxSqlite.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxStd.js 399 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxUart.js 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxUi.js 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxWatchdog.js 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/dxWorkerPool.js 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/faceWorker.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/gpioKeyWorker.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libJLReader.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libalc.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libid_jpg.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libid_jpg_codec.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libie_jpg.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libie_jpg_codec.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/liblombo_jpeg.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-b-dxface.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-b-dxgpio.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-b-dxpwm.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-b-dxwatchdog.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-audio_gain.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-capturer.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-capturer_calibration.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-display.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-face.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-gpio.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-memory.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-pwm.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-soc.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-tts.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-drv-watchdog.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-alsa.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-capturer.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-channel.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-common.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxalsa.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxcapturer.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxcapturer_calibration.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxchannel.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxcommon.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxeid.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxhttp.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxkey.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxmap.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxmqtt.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxnet.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxsqlite.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-dxui.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-eid.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-key.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-net.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-m-vgmqtt.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-p-dxnfc.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvbar-p-nfc.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libvccore.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/libyuv.so 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/mqttWorker.js 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/netWorker.js 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/nfcWorker.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiBase.js 582 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiButton.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiButtons.js 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiCheckbox.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiDropdown.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiFont.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiImage.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiKeyboard.js 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiLabel.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiLine.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiList.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiSlider.js 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiStyle.js 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiSwitch.js 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiTextarea.js 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiUtils.js 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/uiView.js 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/dxmodules/vgUartWorker.js 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/access_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/access_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn11.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn12.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn13.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn21.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn22.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn23.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn31.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn32.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/btn33.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/calibration_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/control_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/door_close.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/door_open.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/emergency.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/emergency_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/emergency_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/failed.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/light_close.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/light_open.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/network.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/read.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/recg_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/recg_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/recognition.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/recognition_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/register.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/stranger.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/user2.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/user2_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_10x_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_10x_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_200_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_200_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_300_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_300_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_400_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/CN/wav/verify_400_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/calibration_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/network.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/read.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/recg_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/recg_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/recognition.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/recognition_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/register.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/stranger.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_10x_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_10x_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_200_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_200_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_300_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_300_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_400_f.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/EN/wav/verify_400_s.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/font/AlibabaPuHuiTi-2-65-Medium.ttf 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/4g.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/4g_dark.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/accessCtrl.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/add.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/advance.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/app.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/app_btn.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/app_qrcode.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/arrow_right.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/back.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/back_2.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/background.jpg 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/backspace.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/basic.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/black_btn.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/card.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/close.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/close_small.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/cloudCert.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/co2_f.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/co2_s.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/commMgmt.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/config.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/config_btn.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/delete.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/delete_fill.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/devInfo.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/deviceInfo.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/doorControl.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/down.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/emergencyOpen.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/empty.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/enter.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/enter_b.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/eth_disable.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/eth_enable.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/ethernet.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/ethernet_dark.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/eye-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/eye-off.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/eye_fill.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/eye_fill_show.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/face.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/faceAdd.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/faceEmpty.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/faceError.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/faceRec.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/faceRec2.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/factoryTest.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/failBg.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/grey_btn.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/help.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/idleImage.jpg 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/input_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/light_close.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/light_open.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/localUser.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/lock.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/menu_btn.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/mini_app.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/mini_background.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/mini_config.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/mini_password.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/mqtt.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/mqtt_dark.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/mqtt_enable.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/network.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/networkSetting.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/network_dark.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/o2_f.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/o2_s.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/ph3_f.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/ph3_s.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/pwd_btn.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/qrcode_small.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/recQuery.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/recordQuery.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/rectangle.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/register.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/right.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/select_arrow.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/setting.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/setting32.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/space.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/successBg.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/success_fill.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/sysSettings.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/sys_info.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/systemSetting.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/title_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/trackFace.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/unlock.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/user.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/userGuide.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/userMgmt.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/user_1.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/user_f.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/user_s.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/user_w.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/view_f.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/view_s.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/vip.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/voiceBroadcast.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/wifi.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/image/wifi_dark.png 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/langPack.js 664 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/resource/wav/alarm.wav 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/common/consts/configConst.js 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/common/utils/utils.js 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/config.json 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/controller.js 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/driver.js 1226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/main.js 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/screen.js 1470 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/accessService.js 501 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/codeService.js 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/configService.js 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/faceService.js 447 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/gpiokeyService.js 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/grainService.js 676 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/mqttService.js 1832 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/nfcService.js 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/platService.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/sqliteService.js 616 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/service/uart485Service.js 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/services.js 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/ui.js 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/appView.js 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/configView.js 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/identityVerificationView.js 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/cloudCertView.js 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/deviceInfo/dataCapacityInfoView.js 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/deviceInfo/systemInfoView.js 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/deviceInfoView.js 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/dockingSetting.js 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/doorControlView.js 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/factoryTestView.js 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/helpView.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/localUser/faceEnterView.js 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/localUser/localUserAddView.js 682 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/localUserView.js 315 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/networkSettingView.js 441 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/recordQuery/recordQueryDetailView.js 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/recordQueryView.js 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSetting/displaySettingView.js 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSetting/faceRecognitionSettingView.js 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSetting/passLogSettingView.js 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSetting/passwordManagementView.js 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSetting/passwordOpenDoorSettingView.js 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSetting/swipeCardRecognitionSettingView.js 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSetting/timeSettingView.js 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/systemSettingView.js 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/menu/voiceBroadcastView.js 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/config/newPwdView.js 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/emergencyPwdView.js 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/i18n.js 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/idleView.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/mainView.js 1646 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/pinyin/dict.js 410 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/pinyin/pinyin.js 1049 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/pwdView.js 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/topView.js 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/src/view/viewUtils.js 475 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf205_access/.temp/dxide_debug.log
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
[2026/3/31 08:59:19] --- Start runUsb ---
[2026/3/31 08:59:19] Platform: win32
[2026/3/31 08:59:19] Kill command defined: wmic process where "name='node.exe' and commandline like '%device_manager.js%'" delete || powershell -NoProfile -ExecutionPolicy Bypass -Command "Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*device_manager.js*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"
[2026/3/31 08:59:20] Node.js check passed
[2026/3/31 08:59:20] Process exited with code: 0, signal: null
[2026/3/31 08:59:20] Kill stdout: É¾ï¿½ï¿½Êµï¿½ï¿½ \\ACER-LGQ\ROOT\CIMV2:Win32_Process.Handle="42064"
ʵ��ɾ���ɹ���
[2026/3/31 08:59:21] Manager script path: c:\Users\lgq10\.vscode\extensions\dxide.dxide-1.0.40\src\device\device_manager.js
[2026/3/31 08:59:21] Spawning child process...
[2026/3/31 08:59:21] Sending connect command: {"cmd":"connect","lang":"zh","model":"VF105_V12"}
[2026/3/31 09:25:48] Process exited with code: 0, signal: null
vf205_access/.temp/md5s.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,330 @@
{
  "app.dxproj": "cb10fd21e8e17ada45c25e1e1ba3226e",
  "dxmodules\\cameraCalibrationWorker.js": "5812507304ced88da3677a3c68f79a78",
  "dxmodules\\capturerWorker.js": "a3104a1d73ab1bd4f596495d817ecf39",
  "dxmodules\\dxAlsa.js": "d82fe4f74383edf6198269968a82b5e7",
  "dxmodules\\dxBase64.js": "7cb517ecd99830dde0ec13a501e831ea",
  "dxmodules\\dxCameraCalibration.js": "8e42bf3752616896b88f9e9efbe20783",
  "dxmodules\\dxCapturer.js": "7420d8e269fb8ebfc48c306e0a8fa7de",
  "dxmodules\\dxCommon.js": "19f77916eb02e6fd01106597aca412bd",
  "dxmodules\\dxConfig.js": "24072559b3cb1ec5c206cb358eaaaaae",
  "dxmodules\\dxDriver.js": "c5969e91097030b90e9bcb62a827e344",
  "dxmodules\\dxEid.js": "07d966e53b69629f152baf54cd163372",
  "dxmodules\\dxEventBus.js": "81adf285545c12d49d7784327c41970a",
  "dxmodules\\dxFace.js": "81790e30c6da75ae3bcb96afcafa5b02",
  "dxmodules\\dxGpio.js": "30c34469e2dc8d80026fc5eac5bfd1f7",
  "dxmodules\\dxGpioKey.js": "97ef2763bad50e0c82cfa5fb9b79f1eb",
  "dxmodules\\dxHttp.js": "fa75a0a2951399fb5af7618d7ab4c6f1",
  "dxmodules\\dxLogger.js": "f6d3f73086944fcdeeffa579ef6d1508",
  "dxmodules\\dxMap.js": "2255d584c240c35a567f2c7b92e0a9da",
  "dxmodules\\dxMqtt.js": "277fafb81971c8f3508a8a38f8f2703a",
  "dxmodules\\dxNet.js": "58b13f928daeb1bae680c2350873baca",
  "dxmodules\\dxNfc.js": "feb5bc21d4b13ab9736de1941290312d",
  "dxmodules\\dxNtp.js": "0c90048eeb11e7e3b458e4d6e48932f2",
  "dxmodules\\dxOta.js": "760dc97eb0724280f16f8616a2be171f",
  "dxmodules\\dxPwm.js": "dc0734c231d7d4cd5f26a34c315cc4a2",
  "dxmodules\\dxQrRule.js": "bfa9e2b4725c4fa358a2988f63c67210",
  "dxmodules\\dxSqlite.js": "fb6a2f60b49d67a8ef36148355e5e30e",
  "dxmodules\\dxStd.js": "6f239f2fd7d23e7ed053b762b235bd3c",
  "dxmodules\\dxUart.js": "71edddfd98de79ccbd91e4e013af269b",
  "dxmodules\\dxUi.js": "97bd858defaec480d755c162105a3f76",
  "dxmodules\\dxWatchdog.js": "c4a7d5c8f0c203e6dafbc289a0f4fcbd",
  "dxmodules\\dxWorkerPool.js": "5aad9b8f838d1339fc64083188d3ba69",
  "dxmodules\\faceWorker.js": "d1d0d4f5e5503ca87bd69d1d2ee3ad00",
  "dxmodules\\gpioKeyWorker.js": "78a7489cc3cf098fb4d218b17294175d",
  "dxmodules\\libalc.so": "3128cc7cd0cc978e02db995433962c87",
  "dxmodules\\libid_jpg.so": "18887cb0d0df6256604ed039d47dfc7b",
  "dxmodules\\libid_jpg_codec.so": "c36bd0c28d2b43e9296736856da05129",
  "dxmodules\\libie_jpg.so": "1343b7bc765f6464cf8e483c03edd542",
  "dxmodules\\libie_jpg_codec.so": "508defffc1c72b0ae90afab117d6db80",
  "dxmodules\\libJLReader.so": "ce7f448dea6a1c8988469d03ecfa6575",
  "dxmodules\\liblombo_jpeg.so": "49508ccd603ed795efee3d0f8e7e3e2a",
  "dxmodules\\libvbar-b-dxface.so": "689ee554f67057262860f7726042327b",
  "dxmodules\\libvbar-b-dxgpio.so": "ca009351ab7d9719a71cd6105e9acba6",
  "dxmodules\\libvbar-b-dxpwm.so": "b3e0e7cc950811385ceb204876fd8b19",
  "dxmodules\\libvbar-b-dxwatchdog.so": "878352b309e9a9e17ce3ac83774d0fa9",
  "dxmodules\\libvbar-drv-audio_gain.so": "14d23fe5134ff95982f1655058084709",
  "dxmodules\\libvbar-drv-capturer.so": "295aaa64df97ee08053c26789ff57437",
  "dxmodules\\libvbar-drv-capturer_calibration.so": "cdb2ca2205e36d82ebcce910e457bd9b",
  "dxmodules\\libvbar-drv-display.so": "31c2a7a2ed4166d27359d208e4d34568",
  "dxmodules\\libvbar-drv-face.so": "2c54abd0de9a287be3bbea490d0c45c4",
  "dxmodules\\libvbar-drv-gpio.so": "e663856deb2d403558426455097e0bab",
  "dxmodules\\libvbar-drv-memory.so": "0ed48486e154f24d6681a0195a7525ad",
  "dxmodules\\libvbar-drv-pwm.so": "b9c2154b7020d41b01d63f6caac1510f",
  "dxmodules\\libvbar-drv-soc.so": "73e4b6657af4bcff0affaacf55702096",
  "dxmodules\\libvbar-drv-tts.so": "97076b55a3338196f512c61101ac0e9a",
  "dxmodules\\libvbar-drv-watchdog.so": "9367e995f85cb191da0567d0acd4ed67",
  "dxmodules\\libvbar-m-alsa.so": "1154ff7cb2f84ee378f29e0d03551f96",
  "dxmodules\\libvbar-m-capturer.so": "8f0b6383db39494904294c3f60c5f013",
  "dxmodules\\libvbar-m-channel.so": "6b6f7d8edb178b11b9a93123e0a7abdb",
  "dxmodules\\libvbar-m-common.so": "074eb14e701c5ed910b979339453f484",
  "dxmodules\\libvbar-m-dxalsa.so": "bb4fcf0be6ccb0cc2d7b295002925148",
  "dxmodules\\libvbar-m-dxcapturer.so": "506307dec2552200eaa22412987b643e",
  "dxmodules\\libvbar-m-dxcapturer_calibration.so": "c41e691d69621ab5a96322f47d3eb286",
  "dxmodules\\libvbar-m-dxchannel.so": "ff80aaf50113c160c663a80015471079",
  "dxmodules\\libvbar-m-dxcommon.so": "d4ca3ce51543d07c81cc75974adcbccb",
  "dxmodules\\libvbar-m-dxeid.so": "3458f8221dd572220003e4344837d846",
  "dxmodules\\libvbar-m-dxhttp.so": "1ec2f9ccc26aebc390944340afde6044",
  "dxmodules\\libvbar-m-dxkey.so": "e49a86e25944050bda8565677713431a",
  "dxmodules\\libvbar-m-dxmap.so": "55ebd66234d59490ab56f88398419dde",
  "dxmodules\\libvbar-m-dxmqtt.so": "0cbeb2b46441ee89678516a1a941cd58",
  "dxmodules\\libvbar-m-dxnet.so": "eed60270cabaf872983c6f905b0ceb03",
  "dxmodules\\libvbar-m-dxsqlite.so": "15a0eb0d18b58cad3868f86e9edfb409",
  "dxmodules\\libvbar-m-dxui.so": "ca22e1a0c52977749a32b2cc70e942a9",
  "dxmodules\\libvbar-m-eid.so": "e7d1db4d1bfab4c462c9d22807099268",
  "dxmodules\\libvbar-m-key.so": "f7f1f2ae6c9c1f298376375a8b16b6ee",
  "dxmodules\\libvbar-m-net.so": "afa1adebb44fc9734b26b99e87e0728d",
  "dxmodules\\libvbar-m-vgmqtt.so": "56e94165ba896867fbe3ead167022d3f",
  "dxmodules\\libvbar-p-dxnfc.so": "14d17fe22a0dbfc00b13c4d9cef8512f",
  "dxmodules\\libvbar-p-nfc.so": "f1a1f96558b2509c5b629790cec16386",
  "dxmodules\\libvccore.so": "9c383ca78ee117ea3295baefb1f1fee2",
  "dxmodules\\libyuv.so": "2c4faa3ab51260f5285cd91dda44b3a9",
  "dxmodules\\mqttWorker.js": "4eedadbd41d6bd183cde2830a3347ad1",
  "dxmodules\\netWorker.js": "c5d4c9022f8912b47249b14477aed70a",
  "dxmodules\\nfcWorker.js": "88fadfc469cc397b66fd5e52db073bde",
  "dxmodules\\uiBase.js": "9ea5cd2b5ed52d183fde2d87b8f70ca1",
  "dxmodules\\uiButton.js": "8ffe0ffce9ac6b341c9993e260b52b05",
  "dxmodules\\uiButtons.js": "52cb9727c2225f75ac6144e756a6d511",
  "dxmodules\\uiCheckbox.js": "5aa8c851e8b2333cbe46cdd69452ab8c",
  "dxmodules\\uiDropdown.js": "2b73bfacf3d4b115ab3997c83284c478",
  "dxmodules\\uiFont.js": "257aa2f90ce2f0ace4095a03d3447b0b",
  "dxmodules\\uiImage.js": "b2a0a963993474205bc7dc91c8696ec1",
  "dxmodules\\uiKeyboard.js": "b225c62ff2f4b8aa0bdb675776b236a1",
  "dxmodules\\uiLabel.js": "e0ae808d468459a6fedc7ba9a551359e",
  "dxmodules\\uiLine.js": "cc18bc32c0a6f0f8db7992d6e0b4fd81",
  "dxmodules\\uiList.js": "8b779a85cd54b25bc50ec616ed9f08d1",
  "dxmodules\\uiSlider.js": "91b743902824b5decf5535dad49fc97e",
  "dxmodules\\uiStyle.js": "60350cf77db84793bf093bda341f2cb9",
  "dxmodules\\uiSwitch.js": "9a836405abe0b09f5a2940cf22698968",
  "dxmodules\\uiTextarea.js": "0d5008a5a95b78ea8efde3408ba6fb4a",
  "dxmodules\\uiUtils.js": "a04cc3baa5ba1dc6d1ab65b526464fa6",
  "dxmodules\\uiView.js": "26fe7b992ac82bf5d45447f2921bff2f",
  "dxmodules\\vgUartWorker.js": "fc3e9e0051836d5063c0eecf555d9521",
  "README.md": "c472daef5243f3790f2faee1cf8e2255",
  "README_CN.md": "a46b1c92c09f375881a49d8c42adf6b3",
  "resource\\CN\\wav\\access_f.wav": "f951f3cc1f03aaa794231413fd4efcc6",
  "resource\\CN\\wav\\access_s.wav": "6d1b31bdcaaa13ba719e3223b55d5235",
  "resource\\CN\\wav\\btn11.wav": "a623797305d7b0e6b8cb6b2ab3d43591",
  "resource\\CN\\wav\\btn12.wav": "26bbfc3eed36211713dca9afb423e168",
  "resource\\CN\\wav\\btn13.wav": "67d6fe32693cb2e7653c2ff2141ce873",
  "resource\\CN\\wav\\btn21.wav": "53071472fce6128fdd2ed304e4d78406",
  "resource\\CN\\wav\\btn22.wav": "e35c87d370695099d3b6d7f512496395",
  "resource\\CN\\wav\\btn23.wav": "f25db5592331f1e6deb67beecb3c1131",
  "resource\\CN\\wav\\btn31.wav": "054a4f36ebda08fadca3162d955560bf",
  "resource\\CN\\wav\\btn32.wav": "64eb5fef123eafd7b52108ebcd6bace9",
  "resource\\CN\\wav\\btn33.wav": "67d6fe32693cb2e7653c2ff2141ce873",
  "resource\\CN\\wav\\calibration_s.wav": "d2ff1f1f4a40bf4166130dc079939079",
  "resource\\CN\\wav\\control_f.wav": "4dbaca7e4d227a720620b846544b469a",
  "resource\\CN\\wav\\door_close.wav": "325ca8082f8e9fceb2c7eeeb78d8645b",
  "resource\\CN\\wav\\door_open.wav": "f3b5291fc8babaffd32107198cdd5afa",
  "resource\\CN\\wav\\emergency.wav": "a1855c22f3ef2f69d5c84d3a2852ae1f",
  "resource\\CN\\wav\\emergency_f.wav": "946ac86e74b849ad8f1bec3615cf9b39",
  "resource\\CN\\wav\\emergency_s.wav": "a717e3417891ff306225ac075ba28082",
  "resource\\CN\\wav\\failed.wav": "891194e741b0bc8f6332f78eb607cc85",
  "resource\\CN\\wav\\light_close.wav": "b1ac09e91ac4ce3825614ad1654a3f58",
  "resource\\CN\\wav\\light_open.wav": "16b7b6b8c6eb469645a15e4a54a3e014",
  "resource\\CN\\wav\\network.wav": "7bbc6d740918a20acfb5ef75df685bdb",
  "resource\\CN\\wav\\read.wav": "c83edd035dc15f7a716644319e849215",
  "resource\\CN\\wav\\recg_f.wav": "5cd4d88db4e3f8332b9e73fe05222c83",
  "resource\\CN\\wav\\recg_s.wav": "4afadeb018b08518d5d3dd1cfc5e0d64",
  "resource\\CN\\wav\\recognition.wav": "da00dd97d1e8ca0ed20dc43eb4daafc8",
  "resource\\CN\\wav\\recognition_s.wav": "df9916d6fe3d285894668e63d1aeeb1d",
  "resource\\CN\\wav\\register.wav": "a91d6ebb846dc834816a954168da1352",
  "resource\\CN\\wav\\stranger.wav": "2e6690d7ddcd3609ca3e6d6717b61a9c",
  "resource\\CN\\wav\\user2.wav": "868a9b15aac20b62457f98c955f4e5e0",
  "resource\\CN\\wav\\user2_s.wav": "98904054df059be8cd69e08c302317e7",
  "resource\\CN\\wav\\verify.wav": "2bc2b96e0ca05052985fda98fb552ef9",
  "resource\\CN\\wav\\verify_10x_f.wav": "bf26ce11bbd801f5efeff8c9250f219f",
  "resource\\CN\\wav\\verify_10x_s.wav": "2d04b22277844d9c35e08e4e2c67c11c",
  "resource\\CN\\wav\\verify_200_f.wav": "393036b5fc28a3c04580593d59e26e7e",
  "resource\\CN\\wav\\verify_200_s.wav": "585267371d37f3f45e3826ec7dcafbd3",
  "resource\\CN\\wav\\verify_300_f.wav": "e44977e8b6dcac8adbf304894afd24e0",
  "resource\\CN\\wav\\verify_300_s.wav": "d8e23a91e4999b2d77454aab824bd611",
  "resource\\CN\\wav\\verify_400_f.wav": "05752c8c9c2bf4334943999842529d8b",
  "resource\\CN\\wav\\verify_400_s.wav": "740a63ffe907dda0f2d0e14cb9a83f74",
  "resource\\EN\\wav\\calibration_s.wav": "b5497547d9e7e6fb5b28ee307b27752b",
  "resource\\EN\\wav\\network.wav": "1e8ccfd03ca83976fefdba1edcf1b194",
  "resource\\EN\\wav\\read.wav": "8c1f6ee62c7bf74db5ecab28d3988eeb",
  "resource\\EN\\wav\\recg_f.wav": "e303563b867dd6eaebac18679ca760df",
  "resource\\EN\\wav\\recg_s.wav": "183a843b668aa919311a6d352af80f35",
  "resource\\EN\\wav\\recognition.wav": "1c9e06bc338c49c120aa101b1fac8de0",
  "resource\\EN\\wav\\recognition_s.wav": "f887f2b1615121bbe815fef7f64d3b92",
  "resource\\EN\\wav\\register.wav": "de5d0048f840243fdd1392c09d9ac164",
  "resource\\EN\\wav\\stranger.wav": "31775350903916827fdec25b9c65dd94",
  "resource\\EN\\wav\\verify.wav": "2e77ec2c754e1dea329988d4e462aa2f",
  "resource\\EN\\wav\\verify_10x_f.wav": "7ccc23490436b9f1de32200230953a62",
  "resource\\EN\\wav\\verify_10x_s.wav": "0630e0410fca3c7fd7e701c69e8ea4bc",
  "resource\\EN\\wav\\verify_200_f.wav": "64ce473b0f560cc1613469ab94197ce6",
  "resource\\EN\\wav\\verify_200_s.wav": "e3d28c408cc3bef5461620c75b15abdf",
  "resource\\EN\\wav\\verify_300_f.wav": "7fa0d7bf85d040b72a89cfb9e2f06bcf",
  "resource\\EN\\wav\\verify_300_s.wav": "3e69fd763477d6cc30543d838f33d718",
  "resource\\EN\\wav\\verify_400_f.wav": "d074b8f6e4e968b6d1984b123ed4d387",
  "resource\\EN\\wav\\verify_400_s.wav": "9141136d3310a5fac5917d3f6056249f",
  "resource\\font\\AlibabaPuHuiTi-2-65-Medium.ttf": "092a99ee52bbaef7481cc96c5b85b992",
  "resource\\image\\4g.png": "e5b27ed5a596cb16c7ab695d82fe3014",
  "resource\\image\\4g_dark.png": "414b3a9fcefd8ea6909158b51038d4d8",
  "resource\\image\\accessCtrl.png": "6be30c8f648ec7153ae2c39a15884181",
  "resource\\image\\add.png": "498480ce68e4d6047eb74d3aa5229f56",
  "resource\\image\\advance.png": "d98aeb99a04163bce23b6c2638cd705a",
  "resource\\image\\app.png": "7640c7358a3f5dba1f887b8413b93a9d",
  "resource\\image\\app_btn.png": "aa325cea46fd3918c86d76bf009b1663",
  "resource\\image\\app_qrcode.png": "0a20655d02ff0e473106bf41f7c9687b",
  "resource\\image\\arrow_right.png": "edc6876d6fa1e2d0be2e606c73e0f2ec",
  "resource\\image\\back.png": "aa5869ff78051dbdc5f688f1805064da",
  "resource\\image\\background.jpg": "90d464f4221f62132ebf74e69446b6d7",
  "resource\\image\\backspace.png": "26302e37dd8618e92c3a47d68039d0ec",
  "resource\\image\\back_2.png": "b3f16ab01606d85c2c70124d50b3af1b",
  "resource\\image\\basic.png": "84eedd84efdc5fdb54138dd29cf6fc41",
  "resource\\image\\black_btn.png": "13ee1720aff247ba3f8e22e00f89a316",
  "resource\\image\\card.png": "c05047d2ad6549db001d08790cb5d9ff",
  "resource\\image\\close.png": "a5353c231df804fcc4577672ab3a4302",
  "resource\\image\\close_small.png": "7d6cddddc38ce8d4950789169213add4",
  "resource\\image\\cloudCert.png": "4481cfb9c2d1f44f0a0dd0489cb6fc2b",
  "resource\\image\\co2_f.png": "7e6f00c03b71a4dbe439491083ca731c",
  "resource\\image\\co2_s.png": "4fb691286b8856f1ed25ef8bacdb6099",
  "resource\\image\\commMgmt.png": "5cd157e1b8c82fdf9ddba1f6d5047b9b",
  "resource\\image\\config.png": "50d2091b9f7fba5915dbed0aa0dcf918",
  "resource\\image\\config_btn.png": "e54cbc27d30e7c6480b83107a631e7ef",
  "resource\\image\\delete.png": "014bad6d9a94a133c58ef350e198101e",
  "resource\\image\\delete_fill.png": "947fc08278354a1151d5599382c2c5f3",
  "resource\\image\\deviceInfo.png": "3e40246e01c1f7eede4d76fcbba33825",
  "resource\\image\\devInfo.png": "1a80aba6780a45f8775bdfdcff4df23a",
  "resource\\image\\doorControl.png": "68993ef92bb8c6b2d0dab2c75cc7533d",
  "resource\\image\\down.png": "c4d5c1883db4694ccedb7c1140d89da9",
  "resource\\image\\emergencyOpen.png": "890877dee840dabe60487f8ce57b41f6",
  "resource\\image\\empty.png": "8283ac78099d9c13ef4b552ce86f5c38",
  "resource\\image\\enter.png": "787e076256c8a47e07f2091a57585fb5",
  "resource\\image\\enter_b.png": "02cb6c84fec128a639e3cf0828a6ee5f",
  "resource\\image\\ethernet.png": "f3abb111d96a11a56f7ed77b21abab4c",
  "resource\\image\\ethernet_dark.png": "10656303a6d22e204014f55ed0fb3efb",
  "resource\\image\\eth_disable.png": "7017f5cc2c9b4f802f082f6c0f5bb581",
  "resource\\image\\eth_enable.png": "84d1334e524ac669c3b79e8a69b41eb1",
  "resource\\image\\eye-fill.png": "9ae71914bd47423be04d0a22eb4f3995",
  "resource\\image\\eye-off.png": "295c3e8255ced50cf2667ecda524e11e",
  "resource\\image\\eye_fill.png": "d0ac0d07f13e02e5fae7a12a0858ec49",
  "resource\\image\\eye_fill_show.png": "265cf669797b94a138982c01c29d0bd9",
  "resource\\image\\face.png": "d695a5f29dbf051fc0c6e0d4e177f5c5",
  "resource\\image\\faceAdd.png": "5e0e3d4eb3f034a179a8eef5c08d4c63",
  "resource\\image\\faceEmpty.png": "9ef3bd1d776183e203e69d5c91e4b129",
  "resource\\image\\faceError.png": "19841af9136d4483642a254ab1a6f57c",
  "resource\\image\\faceRec.png": "f1bfcb61f4642c6c1bbc04856fb57905",
  "resource\\image\\faceRec2.png": "580c72783b4cfc64ec0a9593e77c456a",
  "resource\\image\\factoryTest.png": "4bdb36420046870efcabd4a040e31913",
  "resource\\image\\failBg.png": "5d6dca3cc98032a10ef4bc0658f7e546",
  "resource\\image\\grey_btn.png": "b0ca6e44c0e01a17d0bdda5f7e057cc2",
  "resource\\image\\help.png": "7d75da0510ca74870858a639882dc2ef",
  "resource\\image\\idleImage.jpg": "90d464f4221f62132ebf74e69446b6d7",
  "resource\\image\\input_bg.png": "8a8f5c43f1118869a7679ccbaaf47de2",
  "resource\\image\\light_close.png": "607b38280bb5321327cacce5bd6c3fe9",
  "resource\\image\\light_open.png": "1a8df3753b6f0df9e7abb5b53dabd72e",
  "resource\\image\\localUser.png": "6429f43f7fee002d66d50a3d92a087da",
  "resource\\image\\lock.png": "c1419aa2a9c9da4b7dbf3529a1af37f3",
  "resource\\image\\logo.png": "5768b9344bedc53096d2227bee5d52d4",
  "resource\\image\\menu_btn.png": "df98739cd4804d08b23eaf33bb92e1bc",
  "resource\\image\\mini_app.png": "82ee9005b5d162a6fbb0d32764088da4",
  "resource\\image\\mini_background.png": "620bf06dbfffe37f717b91fb3ed8ef40",
  "resource\\image\\mini_config.png": "6bf78553fb53673a89e80613cfbee4c1",
  "resource\\image\\mini_password.png": "6892385a457e084953bcf9acb2ae9957",
  "resource\\image\\mqtt.png": "542ebc6bdbb66ea0f25cf29aeb9d264c",
  "resource\\image\\mqtt_dark.png": "d4aab5adc128846c47a3b8bb015c757b",
  "resource\\image\\mqtt_enable.png": "fc766f5fcbb95eca648ee10d421cc8fe",
  "resource\\image\\network.png": "0a0119e3d5fce101b28575a4e44aaf7e",
  "resource\\image\\networkSetting.png": "3e83d354e8293ed112fe7cc405b94187",
  "resource\\image\\network_dark.png": "3c5f8ce5732e95ad613cdd41dc5adbc0",
  "resource\\image\\o2_f.png": "b787951eba880b3ae82b42594b4e30df",
  "resource\\image\\o2_s.png": "50564fb865bb7f5ed0352e43ea17d853",
  "resource\\image\\ph3_f.png": "e95a348c887298dc4ed0555c577ea5bb",
  "resource\\image\\ph3_s.png": "96e68d741b75fcd6938a6909014cccb0",
  "resource\\image\\pwd_btn.png": "8764458bee98bff728445837e126231e",
  "resource\\image\\qrcode_small.png": "285bbe7e8f968c8bf4506a563dffcac2",
  "resource\\image\\recordQuery.png": "d340255a0c2342382bff6f8c57f6376b",
  "resource\\image\\recQuery.png": "1b8c58663a6e61f4f885e784ee4e87d4",
  "resource\\image\\rectangle.png": "394e2e483120908674f6de7fb879bfcf",
  "resource\\image\\register.png": "7fa975d92007703532ba8011f2a0109f",
  "resource\\image\\right.png": "a6f6bc770ad7a8220effcc96e750aaaa",
  "resource\\image\\select_arrow.png": "9d82daa1092375abc413d581f36aaf2d",
  "resource\\image\\setting.png": "92365fd93f2cfa63c7901ce3d8900a42",
  "resource\\image\\setting32.png": "e531bf8a9ce7f6cf93a8ea9baf95377c",
  "resource\\image\\space.png": "de9816e31308bd7ce187fe03ab634a37",
  "resource\\image\\successBg.png": "1ef294bd7e1688bba47c2337906b1d1d",
  "resource\\image\\success_fill.png": "09166b0cd4da44b76b4f6cdc2c550103",
  "resource\\image\\sysSettings.png": "0a84bbe887481a1cce1ca562f83d5b89",
  "resource\\image\\systemSetting.png": "9aee20d073df321250a5bbc57d16d5f1",
  "resource\\image\\sys_info.png": "20dc23c019d07cd2c3ce32a160b7b63f",
  "resource\\image\\title_bg.png": "82fbdcc4133899d03072dcd57a92f203",
  "resource\\image\\trackFace.png": "e90f92eb629563ede01aadca2b719de9",
  "resource\\image\\unlock.png": "7b97b659c36d3ba4f435f44792b90a80",
  "resource\\image\\user.png": "6429f43f7fee002d66d50a3d92a087da",
  "resource\\image\\userGuide.png": "3227210f670f66cd6c29641e1212b0b8",
  "resource\\image\\userMgmt.png": "5dad96f344513f970c2b582d7feb537d",
  "resource\\image\\user_1.png": "36ab26e22dc39ea28c03f05f4ab891ce",
  "resource\\image\\user_f.png": "d82d235224deccd2235ab15dbf1fda26",
  "resource\\image\\user_s.png": "709953f3695cae9ac1bb50533f1a96ba",
  "resource\\image\\user_w.png": "bc2ca556f37181815c0093d802f2f24f",
  "resource\\image\\view_f.png": "4fbdda957ef2966bc4938990725d0246",
  "resource\\image\\view_s.png": "93ddad11d282c5a8f56233682290b38c",
  "resource\\image\\vip.png": "0e816b4860ee87ed1169154e3f0fc524",
  "resource\\image\\voiceBroadcast.png": "e6f1a31ba7159962d18b77deef71c106",
  "resource\\image\\wifi.png": "fd668b648ac984ed92fad8e40e151283",
  "resource\\image\\wifi_dark.png": "37505f892ac6a43cb8dc5ea685de9740",
  "resource\\langPack.js": "b537e7ad7b7bd36ebef146692604007d",
  "resource\\wav\\alarm.wav": "fe9d43cfb930f873973cc31fd6e8c132",
  "src\\common\\consts\\configConst.js": "8cdbeff06611f7c94fc349afb2d04364",
  "src\\common\\utils\\utils.js": "f45a9074748680b00ba663ad6490acf0",
  "src\\config.json": "c6c52fdf036881dd11081f96b491fee5",
  "src\\controller.js": "f09517062ac2a8641ea17909f9e635fd",
  "src\\driver.js": "02c38e2dcc749c50fb348b94629278d0",
  "src\\main.js": "a893be0e60fcd2ca32c94baf8534f996",
  "src\\screen.js": "a2f377ed5832bb9139dfcaadffe091c7",
  "src\\service\\accessService.js": "ca2abf5ea0cae58a1fc9ccffb9f03454",
  "src\\service\\codeService.js": "63934baff614eedf7f8c7ee4855f9ebe",
  "src\\service\\configService.js": "d991f7261781465e306cd9d4548eab4d",
  "src\\service\\faceService.js": "4e6ef420f61bb617e3b57491f9305cb2",
  "src\\service\\gpiokeyService.js": "71c8f8a2589492153671f1d7ed96e6c8",
  "src\\service\\grainService.js": "99f05184d3fcdf5c4e54fce08500267b",
  "src\\service\\mqttService.js": "78d18810234846a26b49161dc6f193af",
  "src\\service\\nfcService.js": "45705bc6a95206927085f11e7208cac2",
  "src\\service\\platService.js": "a38387366ed212029bb46c6d3a85f2e9",
  "src\\service\\sqliteService.js": "f2f0864ae07d893789bfc1666b584410",
  "src\\service\\uart485Service.js": "c62ad21c538095f950dffe12d6770aa0",
  "src\\services.js": "b87698f1e9e03bcfac820956034b06fe",
  "src\\ui.js": "e9adc3aac685e711427667fa7705be6f",
  "src\\view\\appView.js": "3ada10f0136eb464c035aec809a6e600",
  "src\\view\\config\\configView.js": "6e30bb6aa2a9f6e6187dc6d86d832901",
  "src\\view\\config\\identityVerificationView.js": "aa8cdd417184b2bdcef1a6cbd49df487",
  "src\\view\\config\\menu\\cloudCertView.js": "7c9181ccfa5b5e96a234f1f55307ccb9",
  "src\\view\\config\\menu\\deviceInfo\\dataCapacityInfoView.js": "9b6d8db4b83758d778f84ac2a1a266c8",
  "src\\view\\config\\menu\\deviceInfo\\systemInfoView.js": "9396136cd353b10bb9a56f9bda2315d5",
  "src\\view\\config\\menu\\deviceInfoView.js": "b33452d2a4d7188c5d14c6f76b726e90",
  "src\\view\\config\\menu\\dockingSetting.js": "13a156aa4889a7d032a6fa26605e85b8",
  "src\\view\\config\\menu\\doorControlView.js": "ffd1d5bc1bb7c652bc678c52e9969fda",
  "src\\view\\config\\menu\\factoryTestView.js": "9588321c8282257e9707ebedb5920cda",
  "src\\view\\config\\menu\\helpView.js": "00e750b2b637dc318f75e840d901fb5c",
  "src\\view\\config\\menu\\localUser\\faceEnterView.js": "d993d07711e15cf5f0fee01a22753bfa",
  "src\\view\\config\\menu\\localUser\\localUserAddView.js": "bf4646521acd0e14832eb732bc424e77",
  "src\\view\\config\\menu\\localUserView.js": "c870b92aaaec5aff368e07e9f254e8aa",
  "src\\view\\config\\menu\\networkSettingView.js": "325d70ed8ab1e170ffc1fa03fff637f5",
  "src\\view\\config\\menu\\recordQuery\\recordQueryDetailView.js": "7077207b15bf1d7eae3a9a01dc68dc77",
  "src\\view\\config\\menu\\recordQueryView.js": "67b573825af0639a4103bdc97b0f2c4a",
  "src\\view\\config\\menu\\systemSetting\\displaySettingView.js": "c57f3b9faf85e048864c08d592481a72",
  "src\\view\\config\\menu\\systemSetting\\faceRecognitionSettingView.js": "4d6b4e8b6c0f6e0ccbaaaa169e65b780",
  "src\\view\\config\\menu\\systemSetting\\passLogSettingView.js": "d51958ac39b104846586b1ed6590414c",
  "src\\view\\config\\menu\\systemSetting\\passwordManagementView.js": "98a6ca138cc1e2bc33ff6a3248e11263",
  "src\\view\\config\\menu\\systemSetting\\passwordOpenDoorSettingView.js": "6e2ab3793e6c569097b702c339ef1602",
  "src\\view\\config\\menu\\systemSetting\\swipeCardRecognitionSettingView.js": "871b483fbc3d778df95b803c130101e1",
  "src\\view\\config\\menu\\systemSetting\\timeSettingView.js": "6950faa3a3296a573d048eb782c0e6ca",
  "src\\view\\config\\menu\\systemSettingView.js": "ca3ac7b0e0390c7d542b62d569f57b24",
  "src\\view\\config\\menu\\voiceBroadcastView.js": "a809af8acdae03f15c6e677bef6f20a1",
  "src\\view\\config\\newPwdView.js": "32e3cfb0cc7f88361f1c69797cdb14b8",
  "src\\view\\emergencyPwdView.js": "c6413bc04d38d54e57f590ce44204bcb",
  "src\\view\\i18n.js": "94f43798d35026189125bf2534d1bffd",
  "src\\view\\idleView.js": "b95c100ae4b2757ea2b0844a3df35c9e",
  "src\\view\\mainView.js": "ec96a69994b99ed36778338198f74765",
  "src\\view\\pinyin\\dict.js": "e40764bfc6bab80119c584315c0c48b7",
  "src\\view\\pinyin\\pinyin.js": "c0809d9d1ebe5937cb6831b2b70db439",
  "src\\view\\pwdView.js": "226b8b83a13b82cc925519ae051f8b3f",
  "src\\view\\topView.js": "c9d8a4e3bc362edb1a5007135323beea",
  "src\\view\\viewUtils.js": "ceaaad4fb40024b448a66a03911a7279",
  "src\\worker\\accessWorker.js": "1b1253b5a7fbb2e733be2a9b0714ecca",
  "src\\worker\\httpWorker.js": "47ddc63bdd95ecf36f7eaaa5f409424c"
}
vf205_access/.temp/md5snew.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,330 @@
{
  "app.dxproj": "cb10fd21e8e17ada45c25e1e1ba3226e",
  "dxmodules\\cameraCalibrationWorker.js": "5812507304ced88da3677a3c68f79a78",
  "dxmodules\\capturerWorker.js": "a3104a1d73ab1bd4f596495d817ecf39",
  "dxmodules\\dxAlsa.js": "d82fe4f74383edf6198269968a82b5e7",
  "dxmodules\\dxBase64.js": "7cb517ecd99830dde0ec13a501e831ea",
  "dxmodules\\dxCameraCalibration.js": "8e42bf3752616896b88f9e9efbe20783",
  "dxmodules\\dxCapturer.js": "7420d8e269fb8ebfc48c306e0a8fa7de",
  "dxmodules\\dxCommon.js": "19f77916eb02e6fd01106597aca412bd",
  "dxmodules\\dxConfig.js": "24072559b3cb1ec5c206cb358eaaaaae",
  "dxmodules\\dxDriver.js": "c5969e91097030b90e9bcb62a827e344",
  "dxmodules\\dxEid.js": "07d966e53b69629f152baf54cd163372",
  "dxmodules\\dxEventBus.js": "81adf285545c12d49d7784327c41970a",
  "dxmodules\\dxFace.js": "81790e30c6da75ae3bcb96afcafa5b02",
  "dxmodules\\dxGpio.js": "30c34469e2dc8d80026fc5eac5bfd1f7",
  "dxmodules\\dxGpioKey.js": "97ef2763bad50e0c82cfa5fb9b79f1eb",
  "dxmodules\\dxHttp.js": "fa75a0a2951399fb5af7618d7ab4c6f1",
  "dxmodules\\dxLogger.js": "f6d3f73086944fcdeeffa579ef6d1508",
  "dxmodules\\dxMap.js": "2255d584c240c35a567f2c7b92e0a9da",
  "dxmodules\\dxMqtt.js": "277fafb81971c8f3508a8a38f8f2703a",
  "dxmodules\\dxNet.js": "58b13f928daeb1bae680c2350873baca",
  "dxmodules\\dxNfc.js": "feb5bc21d4b13ab9736de1941290312d",
  "dxmodules\\dxNtp.js": "0c90048eeb11e7e3b458e4d6e48932f2",
  "dxmodules\\dxOta.js": "760dc97eb0724280f16f8616a2be171f",
  "dxmodules\\dxPwm.js": "dc0734c231d7d4cd5f26a34c315cc4a2",
  "dxmodules\\dxQrRule.js": "bfa9e2b4725c4fa358a2988f63c67210",
  "dxmodules\\dxSqlite.js": "fb6a2f60b49d67a8ef36148355e5e30e",
  "dxmodules\\dxStd.js": "6f239f2fd7d23e7ed053b762b235bd3c",
  "dxmodules\\dxUart.js": "71edddfd98de79ccbd91e4e013af269b",
  "dxmodules\\dxUi.js": "97bd858defaec480d755c162105a3f76",
  "dxmodules\\dxWatchdog.js": "c4a7d5c8f0c203e6dafbc289a0f4fcbd",
  "dxmodules\\dxWorkerPool.js": "5aad9b8f838d1339fc64083188d3ba69",
  "dxmodules\\faceWorker.js": "d1d0d4f5e5503ca87bd69d1d2ee3ad00",
  "dxmodules\\gpioKeyWorker.js": "78a7489cc3cf098fb4d218b17294175d",
  "dxmodules\\libalc.so": "3128cc7cd0cc978e02db995433962c87",
  "dxmodules\\libid_jpg.so": "18887cb0d0df6256604ed039d47dfc7b",
  "dxmodules\\libid_jpg_codec.so": "c36bd0c28d2b43e9296736856da05129",
  "dxmodules\\libie_jpg.so": "1343b7bc765f6464cf8e483c03edd542",
  "dxmodules\\libie_jpg_codec.so": "508defffc1c72b0ae90afab117d6db80",
  "dxmodules\\libJLReader.so": "ce7f448dea6a1c8988469d03ecfa6575",
  "dxmodules\\liblombo_jpeg.so": "49508ccd603ed795efee3d0f8e7e3e2a",
  "dxmodules\\libvbar-b-dxface.so": "689ee554f67057262860f7726042327b",
  "dxmodules\\libvbar-b-dxgpio.so": "ca009351ab7d9719a71cd6105e9acba6",
  "dxmodules\\libvbar-b-dxpwm.so": "b3e0e7cc950811385ceb204876fd8b19",
  "dxmodules\\libvbar-b-dxwatchdog.so": "878352b309e9a9e17ce3ac83774d0fa9",
  "dxmodules\\libvbar-drv-audio_gain.so": "14d23fe5134ff95982f1655058084709",
  "dxmodules\\libvbar-drv-capturer.so": "295aaa64df97ee08053c26789ff57437",
  "dxmodules\\libvbar-drv-capturer_calibration.so": "cdb2ca2205e36d82ebcce910e457bd9b",
  "dxmodules\\libvbar-drv-display.so": "31c2a7a2ed4166d27359d208e4d34568",
  "dxmodules\\libvbar-drv-face.so": "2c54abd0de9a287be3bbea490d0c45c4",
  "dxmodules\\libvbar-drv-gpio.so": "e663856deb2d403558426455097e0bab",
  "dxmodules\\libvbar-drv-memory.so": "0ed48486e154f24d6681a0195a7525ad",
  "dxmodules\\libvbar-drv-pwm.so": "b9c2154b7020d41b01d63f6caac1510f",
  "dxmodules\\libvbar-drv-soc.so": "73e4b6657af4bcff0affaacf55702096",
  "dxmodules\\libvbar-drv-tts.so": "97076b55a3338196f512c61101ac0e9a",
  "dxmodules\\libvbar-drv-watchdog.so": "9367e995f85cb191da0567d0acd4ed67",
  "dxmodules\\libvbar-m-alsa.so": "1154ff7cb2f84ee378f29e0d03551f96",
  "dxmodules\\libvbar-m-capturer.so": "8f0b6383db39494904294c3f60c5f013",
  "dxmodules\\libvbar-m-channel.so": "6b6f7d8edb178b11b9a93123e0a7abdb",
  "dxmodules\\libvbar-m-common.so": "074eb14e701c5ed910b979339453f484",
  "dxmodules\\libvbar-m-dxalsa.so": "bb4fcf0be6ccb0cc2d7b295002925148",
  "dxmodules\\libvbar-m-dxcapturer.so": "506307dec2552200eaa22412987b643e",
  "dxmodules\\libvbar-m-dxcapturer_calibration.so": "c41e691d69621ab5a96322f47d3eb286",
  "dxmodules\\libvbar-m-dxchannel.so": "ff80aaf50113c160c663a80015471079",
  "dxmodules\\libvbar-m-dxcommon.so": "d4ca3ce51543d07c81cc75974adcbccb",
  "dxmodules\\libvbar-m-dxeid.so": "3458f8221dd572220003e4344837d846",
  "dxmodules\\libvbar-m-dxhttp.so": "1ec2f9ccc26aebc390944340afde6044",
  "dxmodules\\libvbar-m-dxkey.so": "e49a86e25944050bda8565677713431a",
  "dxmodules\\libvbar-m-dxmap.so": "55ebd66234d59490ab56f88398419dde",
  "dxmodules\\libvbar-m-dxmqtt.so": "0cbeb2b46441ee89678516a1a941cd58",
  "dxmodules\\libvbar-m-dxnet.so": "eed60270cabaf872983c6f905b0ceb03",
  "dxmodules\\libvbar-m-dxsqlite.so": "15a0eb0d18b58cad3868f86e9edfb409",
  "dxmodules\\libvbar-m-dxui.so": "ca22e1a0c52977749a32b2cc70e942a9",
  "dxmodules\\libvbar-m-eid.so": "e7d1db4d1bfab4c462c9d22807099268",
  "dxmodules\\libvbar-m-key.so": "f7f1f2ae6c9c1f298376375a8b16b6ee",
  "dxmodules\\libvbar-m-net.so": "afa1adebb44fc9734b26b99e87e0728d",
  "dxmodules\\libvbar-m-vgmqtt.so": "56e94165ba896867fbe3ead167022d3f",
  "dxmodules\\libvbar-p-dxnfc.so": "14d17fe22a0dbfc00b13c4d9cef8512f",
  "dxmodules\\libvbar-p-nfc.so": "f1a1f96558b2509c5b629790cec16386",
  "dxmodules\\libvccore.so": "9c383ca78ee117ea3295baefb1f1fee2",
  "dxmodules\\libyuv.so": "2c4faa3ab51260f5285cd91dda44b3a9",
  "dxmodules\\mqttWorker.js": "4eedadbd41d6bd183cde2830a3347ad1",
  "dxmodules\\netWorker.js": "c5d4c9022f8912b47249b14477aed70a",
  "dxmodules\\nfcWorker.js": "88fadfc469cc397b66fd5e52db073bde",
  "dxmodules\\uiBase.js": "9ea5cd2b5ed52d183fde2d87b8f70ca1",
  "dxmodules\\uiButton.js": "8ffe0ffce9ac6b341c9993e260b52b05",
  "dxmodules\\uiButtons.js": "52cb9727c2225f75ac6144e756a6d511",
  "dxmodules\\uiCheckbox.js": "5aa8c851e8b2333cbe46cdd69452ab8c",
  "dxmodules\\uiDropdown.js": "2b73bfacf3d4b115ab3997c83284c478",
  "dxmodules\\uiFont.js": "257aa2f90ce2f0ace4095a03d3447b0b",
  "dxmodules\\uiImage.js": "b2a0a963993474205bc7dc91c8696ec1",
  "dxmodules\\uiKeyboard.js": "b225c62ff2f4b8aa0bdb675776b236a1",
  "dxmodules\\uiLabel.js": "e0ae808d468459a6fedc7ba9a551359e",
  "dxmodules\\uiLine.js": "cc18bc32c0a6f0f8db7992d6e0b4fd81",
  "dxmodules\\uiList.js": "8b779a85cd54b25bc50ec616ed9f08d1",
  "dxmodules\\uiSlider.js": "91b743902824b5decf5535dad49fc97e",
  "dxmodules\\uiStyle.js": "60350cf77db84793bf093bda341f2cb9",
  "dxmodules\\uiSwitch.js": "9a836405abe0b09f5a2940cf22698968",
  "dxmodules\\uiTextarea.js": "0d5008a5a95b78ea8efde3408ba6fb4a",
  "dxmodules\\uiUtils.js": "a04cc3baa5ba1dc6d1ab65b526464fa6",
  "dxmodules\\uiView.js": "26fe7b992ac82bf5d45447f2921bff2f",
  "dxmodules\\vgUartWorker.js": "fc3e9e0051836d5063c0eecf555d9521",
  "README.md": "c472daef5243f3790f2faee1cf8e2255",
  "README_CN.md": "a46b1c92c09f375881a49d8c42adf6b3",
  "resource\\CN\\wav\\access_f.wav": "f951f3cc1f03aaa794231413fd4efcc6",
  "resource\\CN\\wav\\access_s.wav": "6d1b31bdcaaa13ba719e3223b55d5235",
  "resource\\CN\\wav\\btn11.wav": "a623797305d7b0e6b8cb6b2ab3d43591",
  "resource\\CN\\wav\\btn12.wav": "26bbfc3eed36211713dca9afb423e168",
  "resource\\CN\\wav\\btn13.wav": "67d6fe32693cb2e7653c2ff2141ce873",
  "resource\\CN\\wav\\btn21.wav": "53071472fce6128fdd2ed304e4d78406",
  "resource\\CN\\wav\\btn22.wav": "e35c87d370695099d3b6d7f512496395",
  "resource\\CN\\wav\\btn23.wav": "f25db5592331f1e6deb67beecb3c1131",
  "resource\\CN\\wav\\btn31.wav": "054a4f36ebda08fadca3162d955560bf",
  "resource\\CN\\wav\\btn32.wav": "64eb5fef123eafd7b52108ebcd6bace9",
  "resource\\CN\\wav\\btn33.wav": "67d6fe32693cb2e7653c2ff2141ce873",
  "resource\\CN\\wav\\calibration_s.wav": "d2ff1f1f4a40bf4166130dc079939079",
  "resource\\CN\\wav\\control_f.wav": "4dbaca7e4d227a720620b846544b469a",
  "resource\\CN\\wav\\door_close.wav": "325ca8082f8e9fceb2c7eeeb78d8645b",
  "resource\\CN\\wav\\door_open.wav": "f3b5291fc8babaffd32107198cdd5afa",
  "resource\\CN\\wav\\emergency.wav": "a1855c22f3ef2f69d5c84d3a2852ae1f",
  "resource\\CN\\wav\\emergency_f.wav": "946ac86e74b849ad8f1bec3615cf9b39",
  "resource\\CN\\wav\\emergency_s.wav": "a717e3417891ff306225ac075ba28082",
  "resource\\CN\\wav\\failed.wav": "891194e741b0bc8f6332f78eb607cc85",
  "resource\\CN\\wav\\light_close.wav": "b1ac09e91ac4ce3825614ad1654a3f58",
  "resource\\CN\\wav\\light_open.wav": "16b7b6b8c6eb469645a15e4a54a3e014",
  "resource\\CN\\wav\\network.wav": "7bbc6d740918a20acfb5ef75df685bdb",
  "resource\\CN\\wav\\read.wav": "c83edd035dc15f7a716644319e849215",
  "resource\\CN\\wav\\recg_f.wav": "5cd4d88db4e3f8332b9e73fe05222c83",
  "resource\\CN\\wav\\recg_s.wav": "4afadeb018b08518d5d3dd1cfc5e0d64",
  "resource\\CN\\wav\\recognition.wav": "da00dd97d1e8ca0ed20dc43eb4daafc8",
  "resource\\CN\\wav\\recognition_s.wav": "df9916d6fe3d285894668e63d1aeeb1d",
  "resource\\CN\\wav\\register.wav": "a91d6ebb846dc834816a954168da1352",
  "resource\\CN\\wav\\stranger.wav": "2e6690d7ddcd3609ca3e6d6717b61a9c",
  "resource\\CN\\wav\\user2.wav": "868a9b15aac20b62457f98c955f4e5e0",
  "resource\\CN\\wav\\user2_s.wav": "98904054df059be8cd69e08c302317e7",
  "resource\\CN\\wav\\verify.wav": "2bc2b96e0ca05052985fda98fb552ef9",
  "resource\\CN\\wav\\verify_10x_f.wav": "bf26ce11bbd801f5efeff8c9250f219f",
  "resource\\CN\\wav\\verify_10x_s.wav": "2d04b22277844d9c35e08e4e2c67c11c",
  "resource\\CN\\wav\\verify_200_f.wav": "393036b5fc28a3c04580593d59e26e7e",
  "resource\\CN\\wav\\verify_200_s.wav": "585267371d37f3f45e3826ec7dcafbd3",
  "resource\\CN\\wav\\verify_300_f.wav": "e44977e8b6dcac8adbf304894afd24e0",
  "resource\\CN\\wav\\verify_300_s.wav": "d8e23a91e4999b2d77454aab824bd611",
  "resource\\CN\\wav\\verify_400_f.wav": "05752c8c9c2bf4334943999842529d8b",
  "resource\\CN\\wav\\verify_400_s.wav": "740a63ffe907dda0f2d0e14cb9a83f74",
  "resource\\EN\\wav\\calibration_s.wav": "b5497547d9e7e6fb5b28ee307b27752b",
  "resource\\EN\\wav\\network.wav": "1e8ccfd03ca83976fefdba1edcf1b194",
  "resource\\EN\\wav\\read.wav": "8c1f6ee62c7bf74db5ecab28d3988eeb",
  "resource\\EN\\wav\\recg_f.wav": "e303563b867dd6eaebac18679ca760df",
  "resource\\EN\\wav\\recg_s.wav": "183a843b668aa919311a6d352af80f35",
  "resource\\EN\\wav\\recognition.wav": "1c9e06bc338c49c120aa101b1fac8de0",
  "resource\\EN\\wav\\recognition_s.wav": "f887f2b1615121bbe815fef7f64d3b92",
  "resource\\EN\\wav\\register.wav": "de5d0048f840243fdd1392c09d9ac164",
  "resource\\EN\\wav\\stranger.wav": "31775350903916827fdec25b9c65dd94",
  "resource\\EN\\wav\\verify.wav": "2e77ec2c754e1dea329988d4e462aa2f",
  "resource\\EN\\wav\\verify_10x_f.wav": "7ccc23490436b9f1de32200230953a62",
  "resource\\EN\\wav\\verify_10x_s.wav": "0630e0410fca3c7fd7e701c69e8ea4bc",
  "resource\\EN\\wav\\verify_200_f.wav": "64ce473b0f560cc1613469ab94197ce6",
  "resource\\EN\\wav\\verify_200_s.wav": "e3d28c408cc3bef5461620c75b15abdf",
  "resource\\EN\\wav\\verify_300_f.wav": "7fa0d7bf85d040b72a89cfb9e2f06bcf",
  "resource\\EN\\wav\\verify_300_s.wav": "3e69fd763477d6cc30543d838f33d718",
  "resource\\EN\\wav\\verify_400_f.wav": "d074b8f6e4e968b6d1984b123ed4d387",
  "resource\\EN\\wav\\verify_400_s.wav": "9141136d3310a5fac5917d3f6056249f",
  "resource\\font\\AlibabaPuHuiTi-2-65-Medium.ttf": "092a99ee52bbaef7481cc96c5b85b992",
  "resource\\image\\4g.png": "e5b27ed5a596cb16c7ab695d82fe3014",
  "resource\\image\\4g_dark.png": "414b3a9fcefd8ea6909158b51038d4d8",
  "resource\\image\\accessCtrl.png": "6be30c8f648ec7153ae2c39a15884181",
  "resource\\image\\add.png": "498480ce68e4d6047eb74d3aa5229f56",
  "resource\\image\\advance.png": "d98aeb99a04163bce23b6c2638cd705a",
  "resource\\image\\app.png": "7640c7358a3f5dba1f887b8413b93a9d",
  "resource\\image\\app_btn.png": "aa325cea46fd3918c86d76bf009b1663",
  "resource\\image\\app_qrcode.png": "0a20655d02ff0e473106bf41f7c9687b",
  "resource\\image\\arrow_right.png": "edc6876d6fa1e2d0be2e606c73e0f2ec",
  "resource\\image\\back.png": "aa5869ff78051dbdc5f688f1805064da",
  "resource\\image\\background.jpg": "90d464f4221f62132ebf74e69446b6d7",
  "resource\\image\\backspace.png": "26302e37dd8618e92c3a47d68039d0ec",
  "resource\\image\\back_2.png": "b3f16ab01606d85c2c70124d50b3af1b",
  "resource\\image\\basic.png": "84eedd84efdc5fdb54138dd29cf6fc41",
  "resource\\image\\black_btn.png": "13ee1720aff247ba3f8e22e00f89a316",
  "resource\\image\\card.png": "c05047d2ad6549db001d08790cb5d9ff",
  "resource\\image\\close.png": "a5353c231df804fcc4577672ab3a4302",
  "resource\\image\\close_small.png": "7d6cddddc38ce8d4950789169213add4",
  "resource\\image\\cloudCert.png": "4481cfb9c2d1f44f0a0dd0489cb6fc2b",
  "resource\\image\\co2_f.png": "7e6f00c03b71a4dbe439491083ca731c",
  "resource\\image\\co2_s.png": "4fb691286b8856f1ed25ef8bacdb6099",
  "resource\\image\\commMgmt.png": "5cd157e1b8c82fdf9ddba1f6d5047b9b",
  "resource\\image\\config.png": "50d2091b9f7fba5915dbed0aa0dcf918",
  "resource\\image\\config_btn.png": "e54cbc27d30e7c6480b83107a631e7ef",
  "resource\\image\\delete.png": "014bad6d9a94a133c58ef350e198101e",
  "resource\\image\\delete_fill.png": "947fc08278354a1151d5599382c2c5f3",
  "resource\\image\\deviceInfo.png": "3e40246e01c1f7eede4d76fcbba33825",
  "resource\\image\\devInfo.png": "1a80aba6780a45f8775bdfdcff4df23a",
  "resource\\image\\doorControl.png": "68993ef92bb8c6b2d0dab2c75cc7533d",
  "resource\\image\\down.png": "c4d5c1883db4694ccedb7c1140d89da9",
  "resource\\image\\emergencyOpen.png": "890877dee840dabe60487f8ce57b41f6",
  "resource\\image\\empty.png": "8283ac78099d9c13ef4b552ce86f5c38",
  "resource\\image\\enter.png": "787e076256c8a47e07f2091a57585fb5",
  "resource\\image\\enter_b.png": "02cb6c84fec128a639e3cf0828a6ee5f",
  "resource\\image\\ethernet.png": "f3abb111d96a11a56f7ed77b21abab4c",
  "resource\\image\\ethernet_dark.png": "10656303a6d22e204014f55ed0fb3efb",
  "resource\\image\\eth_disable.png": "7017f5cc2c9b4f802f082f6c0f5bb581",
  "resource\\image\\eth_enable.png": "84d1334e524ac669c3b79e8a69b41eb1",
  "resource\\image\\eye-fill.png": "9ae71914bd47423be04d0a22eb4f3995",
  "resource\\image\\eye-off.png": "295c3e8255ced50cf2667ecda524e11e",
  "resource\\image\\eye_fill.png": "d0ac0d07f13e02e5fae7a12a0858ec49",
  "resource\\image\\eye_fill_show.png": "265cf669797b94a138982c01c29d0bd9",
  "resource\\image\\face.png": "d695a5f29dbf051fc0c6e0d4e177f5c5",
  "resource\\image\\faceAdd.png": "5e0e3d4eb3f034a179a8eef5c08d4c63",
  "resource\\image\\faceEmpty.png": "9ef3bd1d776183e203e69d5c91e4b129",
  "resource\\image\\faceError.png": "19841af9136d4483642a254ab1a6f57c",
  "resource\\image\\faceRec.png": "f1bfcb61f4642c6c1bbc04856fb57905",
  "resource\\image\\faceRec2.png": "580c72783b4cfc64ec0a9593e77c456a",
  "resource\\image\\factoryTest.png": "4bdb36420046870efcabd4a040e31913",
  "resource\\image\\failBg.png": "5d6dca3cc98032a10ef4bc0658f7e546",
  "resource\\image\\grey_btn.png": "b0ca6e44c0e01a17d0bdda5f7e057cc2",
  "resource\\image\\help.png": "7d75da0510ca74870858a639882dc2ef",
  "resource\\image\\idleImage.jpg": "90d464f4221f62132ebf74e69446b6d7",
  "resource\\image\\input_bg.png": "8a8f5c43f1118869a7679ccbaaf47de2",
  "resource\\image\\light_close.png": "607b38280bb5321327cacce5bd6c3fe9",
  "resource\\image\\light_open.png": "1a8df3753b6f0df9e7abb5b53dabd72e",
  "resource\\image\\localUser.png": "6429f43f7fee002d66d50a3d92a087da",
  "resource\\image\\lock.png": "c1419aa2a9c9da4b7dbf3529a1af37f3",
  "resource\\image\\logo.png": "5768b9344bedc53096d2227bee5d52d4",
  "resource\\image\\menu_btn.png": "df98739cd4804d08b23eaf33bb92e1bc",
  "resource\\image\\mini_app.png": "82ee9005b5d162a6fbb0d32764088da4",
  "resource\\image\\mini_background.png": "620bf06dbfffe37f717b91fb3ed8ef40",
  "resource\\image\\mini_config.png": "6bf78553fb53673a89e80613cfbee4c1",
  "resource\\image\\mini_password.png": "6892385a457e084953bcf9acb2ae9957",
  "resource\\image\\mqtt.png": "542ebc6bdbb66ea0f25cf29aeb9d264c",
  "resource\\image\\mqtt_dark.png": "d4aab5adc128846c47a3b8bb015c757b",
  "resource\\image\\mqtt_enable.png": "fc766f5fcbb95eca648ee10d421cc8fe",
  "resource\\image\\network.png": "0a0119e3d5fce101b28575a4e44aaf7e",
  "resource\\image\\networkSetting.png": "3e83d354e8293ed112fe7cc405b94187",
  "resource\\image\\network_dark.png": "3c5f8ce5732e95ad613cdd41dc5adbc0",
  "resource\\image\\o2_f.png": "b787951eba880b3ae82b42594b4e30df",
  "resource\\image\\o2_s.png": "50564fb865bb7f5ed0352e43ea17d853",
  "resource\\image\\ph3_f.png": "e95a348c887298dc4ed0555c577ea5bb",
  "resource\\image\\ph3_s.png": "96e68d741b75fcd6938a6909014cccb0",
  "resource\\image\\pwd_btn.png": "8764458bee98bff728445837e126231e",
  "resource\\image\\qrcode_small.png": "285bbe7e8f968c8bf4506a563dffcac2",
  "resource\\image\\recordQuery.png": "d340255a0c2342382bff6f8c57f6376b",
  "resource\\image\\recQuery.png": "1b8c58663a6e61f4f885e784ee4e87d4",
  "resource\\image\\rectangle.png": "394e2e483120908674f6de7fb879bfcf",
  "resource\\image\\register.png": "7fa975d92007703532ba8011f2a0109f",
  "resource\\image\\right.png": "a6f6bc770ad7a8220effcc96e750aaaa",
  "resource\\image\\select_arrow.png": "9d82daa1092375abc413d581f36aaf2d",
  "resource\\image\\setting.png": "92365fd93f2cfa63c7901ce3d8900a42",
  "resource\\image\\setting32.png": "e531bf8a9ce7f6cf93a8ea9baf95377c",
  "resource\\image\\space.png": "de9816e31308bd7ce187fe03ab634a37",
  "resource\\image\\successBg.png": "1ef294bd7e1688bba47c2337906b1d1d",
  "resource\\image\\success_fill.png": "09166b0cd4da44b76b4f6cdc2c550103",
  "resource\\image\\sysSettings.png": "0a84bbe887481a1cce1ca562f83d5b89",
  "resource\\image\\systemSetting.png": "9aee20d073df321250a5bbc57d16d5f1",
  "resource\\image\\sys_info.png": "20dc23c019d07cd2c3ce32a160b7b63f",
  "resource\\image\\title_bg.png": "82fbdcc4133899d03072dcd57a92f203",
  "resource\\image\\trackFace.png": "e90f92eb629563ede01aadca2b719de9",
  "resource\\image\\unlock.png": "7b97b659c36d3ba4f435f44792b90a80",
  "resource\\image\\user.png": "6429f43f7fee002d66d50a3d92a087da",
  "resource\\image\\userGuide.png": "3227210f670f66cd6c29641e1212b0b8",
  "resource\\image\\userMgmt.png": "5dad96f344513f970c2b582d7feb537d",
  "resource\\image\\user_1.png": "36ab26e22dc39ea28c03f05f4ab891ce",
  "resource\\image\\user_f.png": "d82d235224deccd2235ab15dbf1fda26",
  "resource\\image\\user_s.png": "709953f3695cae9ac1bb50533f1a96ba",
  "resource\\image\\user_w.png": "bc2ca556f37181815c0093d802f2f24f",
  "resource\\image\\view_f.png": "4fbdda957ef2966bc4938990725d0246",
  "resource\\image\\view_s.png": "93ddad11d282c5a8f56233682290b38c",
  "resource\\image\\vip.png": "0e816b4860ee87ed1169154e3f0fc524",
  "resource\\image\\voiceBroadcast.png": "e6f1a31ba7159962d18b77deef71c106",
  "resource\\image\\wifi.png": "fd668b648ac984ed92fad8e40e151283",
  "resource\\image\\wifi_dark.png": "37505f892ac6a43cb8dc5ea685de9740",
  "resource\\langPack.js": "b537e7ad7b7bd36ebef146692604007d",
  "resource\\wav\\alarm.wav": "fe9d43cfb930f873973cc31fd6e8c132",
  "src\\common\\consts\\configConst.js": "8cdbeff06611f7c94fc349afb2d04364",
  "src\\common\\utils\\utils.js": "f45a9074748680b00ba663ad6490acf0",
  "src\\config.json": "c6c52fdf036881dd11081f96b491fee5",
  "src\\controller.js": "f09517062ac2a8641ea17909f9e635fd",
  "src\\driver.js": "02c38e2dcc749c50fb348b94629278d0",
  "src\\main.js": "a893be0e60fcd2ca32c94baf8534f996",
  "src\\screen.js": "a2f377ed5832bb9139dfcaadffe091c7",
  "src\\service\\accessService.js": "ca2abf5ea0cae58a1fc9ccffb9f03454",
  "src\\service\\codeService.js": "63934baff614eedf7f8c7ee4855f9ebe",
  "src\\service\\configService.js": "d991f7261781465e306cd9d4548eab4d",
  "src\\service\\faceService.js": "4e6ef420f61bb617e3b57491f9305cb2",
  "src\\service\\gpiokeyService.js": "71c8f8a2589492153671f1d7ed96e6c8",
  "src\\service\\grainService.js": "99f05184d3fcdf5c4e54fce08500267b",
  "src\\service\\mqttService.js": "78d18810234846a26b49161dc6f193af",
  "src\\service\\nfcService.js": "45705bc6a95206927085f11e7208cac2",
  "src\\service\\platService.js": "a38387366ed212029bb46c6d3a85f2e9",
  "src\\service\\sqliteService.js": "f2f0864ae07d893789bfc1666b584410",
  "src\\service\\uart485Service.js": "c62ad21c538095f950dffe12d6770aa0",
  "src\\services.js": "b87698f1e9e03bcfac820956034b06fe",
  "src\\ui.js": "e9adc3aac685e711427667fa7705be6f",
  "src\\view\\appView.js": "3ada10f0136eb464c035aec809a6e600",
  "src\\view\\config\\configView.js": "6e30bb6aa2a9f6e6187dc6d86d832901",
  "src\\view\\config\\identityVerificationView.js": "aa8cdd417184b2bdcef1a6cbd49df487",
  "src\\view\\config\\menu\\cloudCertView.js": "7c9181ccfa5b5e96a234f1f55307ccb9",
  "src\\view\\config\\menu\\deviceInfo\\dataCapacityInfoView.js": "9b6d8db4b83758d778f84ac2a1a266c8",
  "src\\view\\config\\menu\\deviceInfo\\systemInfoView.js": "9396136cd353b10bb9a56f9bda2315d5",
  "src\\view\\config\\menu\\deviceInfoView.js": "b33452d2a4d7188c5d14c6f76b726e90",
  "src\\view\\config\\menu\\dockingSetting.js": "13a156aa4889a7d032a6fa26605e85b8",
  "src\\view\\config\\menu\\doorControlView.js": "ffd1d5bc1bb7c652bc678c52e9969fda",
  "src\\view\\config\\menu\\factoryTestView.js": "9588321c8282257e9707ebedb5920cda",
  "src\\view\\config\\menu\\helpView.js": "00e750b2b637dc318f75e840d901fb5c",
  "src\\view\\config\\menu\\localUser\\faceEnterView.js": "d993d07711e15cf5f0fee01a22753bfa",
  "src\\view\\config\\menu\\localUser\\localUserAddView.js": "bf4646521acd0e14832eb732bc424e77",
  "src\\view\\config\\menu\\localUserView.js": "c870b92aaaec5aff368e07e9f254e8aa",
  "src\\view\\config\\menu\\networkSettingView.js": "325d70ed8ab1e170ffc1fa03fff637f5",
  "src\\view\\config\\menu\\recordQuery\\recordQueryDetailView.js": "7077207b15bf1d7eae3a9a01dc68dc77",
  "src\\view\\config\\menu\\recordQueryView.js": "67b573825af0639a4103bdc97b0f2c4a",
  "src\\view\\config\\menu\\systemSetting\\displaySettingView.js": "c57f3b9faf85e048864c08d592481a72",
  "src\\view\\config\\menu\\systemSetting\\faceRecognitionSettingView.js": "4d6b4e8b6c0f6e0ccbaaaa169e65b780",
  "src\\view\\config\\menu\\systemSetting\\passLogSettingView.js": "d51958ac39b104846586b1ed6590414c",
  "src\\view\\config\\menu\\systemSetting\\passwordManagementView.js": "98a6ca138cc1e2bc33ff6a3248e11263",
  "src\\view\\config\\menu\\systemSetting\\passwordOpenDoorSettingView.js": "6e2ab3793e6c569097b702c339ef1602",
  "src\\view\\config\\menu\\systemSetting\\swipeCardRecognitionSettingView.js": "871b483fbc3d778df95b803c130101e1",
  "src\\view\\config\\menu\\systemSetting\\timeSettingView.js": "6950faa3a3296a573d048eb782c0e6ca",
  "src\\view\\config\\menu\\systemSettingView.js": "ca3ac7b0e0390c7d542b62d569f57b24",
  "src\\view\\config\\menu\\voiceBroadcastView.js": "a809af8acdae03f15c6e677bef6f20a1",
  "src\\view\\config\\newPwdView.js": "32e3cfb0cc7f88361f1c69797cdb14b8",
  "src\\view\\emergencyPwdView.js": "c6413bc04d38d54e57f590ce44204bcb",
  "src\\view\\i18n.js": "94f43798d35026189125bf2534d1bffd",
  "src\\view\\idleView.js": "b95c100ae4b2757ea2b0844a3df35c9e",
  "src\\view\\mainView.js": "ec96a69994b99ed36778338198f74765",
  "src\\view\\pinyin\\dict.js": "e40764bfc6bab80119c584315c0c48b7",
  "src\\view\\pinyin\\pinyin.js": "c0809d9d1ebe5937cb6831b2b70db439",
  "src\\view\\pwdView.js": "226b8b83a13b82cc925519ae051f8b3f",
  "src\\view\\topView.js": "c9d8a4e3bc362edb1a5007135323beea",
  "src\\view\\viewUtils.js": "ceaaad4fb40024b448a66a03911a7279",
  "src\\worker\\accessWorker.js": "1b1253b5a7fbb2e733be2a9b0714ecca",
  "src\\worker\\httpWorker.js": "47ddc63bdd95ecf36f7eaaa5f409424c"
}
vf205_access/.temp/zipFolder/VF105_V12/dxAlsa/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxAudio/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxAudio/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxBase64/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxBase64/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCameraCalibration/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCameraCalibration/test.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.2.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCapturer/1.0.3.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCommon/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCommon/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCommonUtils/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxConfig/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxCryptoES/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxDisplay/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxDisplay/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxDriver/1.1.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxDriver/1.1.4.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxDriver/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxEid/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxEventBus/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxEventBus/2.0.2.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxEventBus/2.0.3.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxFace/1.1.8.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxFacial/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxGpio/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxGpioKey/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxGpioKey/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxHttp/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxHttpClient/2.0.3.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxHttpClient/2.0.4.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxHttpServer/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxHttpServer/1.0.5.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxLogger/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxLogger/2.0.3.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxMap/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxMap/2.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxMqtt/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxMqttClient/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNet/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNet/1.0.2.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNetwork/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNfc/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNfcCard/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNfcCard/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNtp/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNtp/2.0.2.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxNtp/2.0.3.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxOs/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxOta/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxOta/2.0.2.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxPwm/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxPwm/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxPwm/2.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxQrRule/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxSqlite/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxSqlite/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxSqliteDB/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxStd/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxStd/2.0.3.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxUart/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxUi/1.0.2.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxWatchdog/1.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxWatchdog/1.0.1.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxWorkerPool/2.0.0.zip
Binary files differ
vf205_access/.temp/zipFolder/VF105_V12/dxWorkerPool/2.0.1.zip
Binary files differ
vf205_access/README.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
# vf105_v12_access_new Access Control Application Source Code
This directory contains the JavaScript source code for the `vf105_v12_access_new` access control application.
**Important Note:**
The JavaScript source code in this project is identical to the one used in the `vf203_v12_access_new` project (located at `../../vf203_v12/vf203_v12_access_new`).
The only difference between the two projects lies in the underlying native shared library (`.so`) files. The specific version of the `.so` files to be used is determined and configured by the `app.dxproj` project file.
vf205_access/README_CN.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
# vf105_v12_access_new é—¨ç¦åº”用源码
此目录包含 `vf105_v12_access_new` é—¨ç¦åº”用的 JavaScript æºç ã€‚
**重要说明:**
本项目的 JavaScript æºç ä¸Ž `vf203_v12_access_new` é¡¹ç›®ï¼ˆè·¯å¾„:`../../vf203_v12/vf203_v12_access_new`)使用的是完全相同的一套源码。
两个项目唯一的差别在于底层的原生共享库(`.so`)文件。具体使用哪个版本的 `.so` æ–‡ä»¶ï¼Œæ˜¯é€šè¿‡ `app.dxproj` é¡¹ç›®æ–‡ä»¶æ¥åŒºåˆ†å’Œé…ç½®çš„。
---
当前项目已修改模组文件-dxmodules/dxEventBus.js,使事件总线支持多个事件处理函数:
1. ä¿®æ”¹ bus.on å‡½æ•° ï¼šå°†å•个事件处理函数改为存储为数组,支持多个处理函数
2. ä¿®æ”¹ bus.fire å‡½æ•° ï¼šæ‰§è¡Œæ‰€æœ‰æ³¨å†Œçš„事件处理函数,而不仅仅是最后一个
3. æ·»åŠ é”™è¯¯å¤„ç† ï¼šç¡®ä¿ä¸€ä¸ªäº‹ä»¶å¤„理函数出错时不会影响其他处理函数的执行
---
vf205_access/app.dxproj
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
{
  "name": "vf105_v12_access",
  "model": "VF105_V12",
  "components": [
    {
      "component": "dxAlsa",
      "version": "1.0.0"
    },
    {
      "component": "dxBase64",
      "version": "1.0.0"
    },
    {
      "component": "dxCapturer",
      "version": "1.0.2"
    },
    {
      "component": "dxCommon",
      "version": "1.0.0"
    },
    {
      "component": "dxConfig",
      "version": "2.0.0"
    },
    {
      "component": "dxCameraCalibration",
      "version": "test"
    },
    {
      "component": "dxDriver",
      "version": "1.1.0"
    },
    {
      "component": "dxEventBus",
      "version": "2.0.0"
    },
    {
      "component": "dxEid",
      "version": "1.0.0"
    },
    {
      "component": "dxFace",
      "version": "1.1.8"
    },
    {
      "component": "dxGpio",
      "version": "1.0.0"
    },
    {
      "component": "dxGpioKey",
      "version": "1.0.0"
    },
    {
      "component": "dxHttp",
      "version": "1.0.0"
    },
    {
      "component": "dxLogger",
      "version": "2.0.0"
    },
    {
      "component": "dxMap",
      "version": "1.0.0"
    },
    {
      "component": "dxMqtt",
      "version": "1.0.1"
    },
    {
      "component": "dxNet",
      "version": "1.0.0"
    },
    {
      "component": "dxNtp",
      "version": "2.0.0"
    },
    {
      "component": "dxNfc",
      "version": "1.0.0"
    },
    {
      "component": "dxOta",
      "version": "1.0.0"
    },
    {
      "component": "dxPwm",
      "version": "1.0.0"
    },
    {
      "component": "dxQrRule",
      "version": "1.0.0"
    },
    {
      "component": "dxStd",
      "version": "1.0.0"
    },
    {
      "component": "dxSqlite",
      "version": "1.0.0"
    },
    {
      "component": "dxUi",
      "version": "1.0.2"
    },
    {
      "component": "dxUart",
      "version": "1.0.0"
    },
    {
      "component": "dxWorkerPool",
      "version": "2.0.0"
    },
    {
      "component": "dxWatchdog",
      "version": "1.0.0"
    }
  ],
  "ignore": {
    "folder": ".temp,.git",
    "file": "md5s.json,md5snew.json"
  },
  "version": "2.0.1.0"
}
vf205_access/dxmodules/cameraCalibrationWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
//build:20240524
//用于简化cameraCalibration组件的使用,把cameraCalibration封装在这个worker里,使用者只需要订阅eventcenter的事件就可以监听cameraCalibration
import log from './dxLogger.js'
import cameraCalibration from './dxCameraCalibration.js'
import capturer from './dxCapturer.js'
import std from './dxStd.js'
import bus from './dxEventBus.js'
import dxMap from './dxMap.js'
import * as os from "os";
const map = dxMap.get('default')
const options = map.get("__cameraCalibration__run_init")
function run() {
    cameraCalibration.init()
    log.info('cameraCalibration start......')
    let startTime = new Date().getTime()
    let cnt = 0
    let timerId = std.setInterval(() => {
        try {
            let imageRgb = capturer.readImage(options.capturerRgbId)
            let imageNir = capturer.readImage(options.capturerNirId)
            let res = cameraCalibration.calibrationFromImage(imageRgb, imageNir, cnt)
            if (res) {
                if (cnt >= 1) {
                    log.info("两次标定成功,结束标定")
                    cameraCalibration.getMap(imageRgb, imageNir, cnt, "/app/path.txt")
                    bus.fire(cameraCalibration.RECEIVE_MSG, "success1")
                    capturer.destroyImage(imageRgb)
                    capturer.destroyImage(imageNir)
                    std.clearInterval(timerId)
                }
                log.info("第" + (cnt + 1) + "次标定成功")
                bus.fire(cameraCalibration.RECEIVE_MSG, "success0")
                cnt += 1
                log.info("开始进行第" + (cnt + 1) + "次标定")
            } else {
                log.error("第" + (cnt + 1) + "次标定失败,重试中")
            }
            capturer.destroyImage(imageRgb)
            capturer.destroyImage(imageNir)
            let endTime = new Date().getTime()
            if (endTime - startTime > options.timeout * 1000) {
                log.error('标定超时,请重新执行标定')
                bus.fire(cameraCalibration.RECEIVE_MSG, "timeout")
                std.clearInterval(timerId)
            }
        } catch (error) {
            log.error(error, error.stack)
        }
    }, 10)
}
try {
    run()
} catch (error) {
    log.error(error, error.stack)
}
vf205_access/dxmodules/capturerWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
//build:20240524
//用于简化capturer组件的使用,把capturer封装在这个worker里,使用者只需要订阅eventbus的事件就可以监听capturer
import log from './dxLogger.js'
import capturer from './dxCapturer.js'
import dxMap from './dxMap.js'
import std from './dxStd.js'
const map = dxMap.get('default')
const id = "{{id}}"
const options = map.get("__capturer__run_init" + id)
function run() {
    capturer.worker.beforeLoop(options)
    log.info('capturer start......,id =', options.id)
    std.setInterval (function() {
        try {
            capturer.worker.loop(options)
        } catch (error) {
            log.error(error)
        }
    },10)
}
try {
    run()
} catch (error) {
    log.error(error)
}
vf205_access/dxmodules/dxAlsa.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
//build: 20240525
//依赖组件:dxDriver,dxCommon
import { alsaClass } from './libvbar-m-dxalsa.so'
import dxCommon from './dxCommon.js'
const alsaObj = new alsaClass();
const alsa = {}
/**
 * alsa åˆå§‹åŒ–
 * @param {string} id å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 * @param {number} volume éŸ³é‡ï¼Œéžå¿…å¡«
 * @param {number} periodSize å‘¨æœŸå¤§å°ï¼Œéžå¿…å¡«
 * @param {number} bufferSize ç¼“存大小,非必填
 * @returns å¥æŸ„id
 */
alsa.init = function (id, volume, periodSize, bufferSize) {
    if (volume === undefined || volume === null) {
        volume = 35
    }
    if (periodSize === undefined || periodSize === null) {
        periodSize = 512
    }
    if (bufferSize === undefined || bufferSize === null) {
        bufferSize = 2048
    }
    let pointer = alsaObj.alsaInit(volume, periodSize, bufferSize)
    if (!pointer) {
        throw new Error("alsa.init: init failed")
    }
    return dxCommon.handleId("alsa", id, pointer)
}
/**
 * alsa å–消初始化
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
alsa.deinit = function (id) {
    let pointer = dxCommon.handleId("alsa", id)
    return alsaObj.alsaDeinit(pointer)
}
/**
 * æ’­æ”¾éŸ³ä¹æ–‡ä»¶
 * @param {string} path wav文件绝对路径,路径是以'/app/code/' å¼€å¤´ï¼Œé€šå¸¸æ”¾åœ¨é¡¹ç›®çš„resource目录下(和src同级),必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
alsa.play = function (path, id) {
    if (!path) {
        throw new Error("alsa.play: 'path' parameter should not be null")
    }
    let pointer = dxCommon.handleId("alsa", id)
    return alsaObj.alsaWav(pointer, path)
}
/**
 * TTS文字转语音
 * @param {string} è¦æ’­æ”¾çš„音频文字,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
alsa.ttsPlay = function (str, id) {
    if (!str) {
        throw new Error("alsa.ttsPlay: 'str' parameter should not be null")
    }
    let pointer = dxCommon.handleId("alsa", id)
    return alsaObj.alsaAudioPlayString(pointer, str)
}
/**
 * èŽ·å–éŸ³é‡
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns è¿”回数字类型的音量,不会超出音量范围
 */
alsa.getVolume = function (id) {
    let pointer = dxCommon.handleId("alsa", id)
    return alsaObj.alsaGetVolume(pointer)
}
/**
 * è®¾ç½®éŸ³é‡ è®¾ç½®è¿‡å¤§æˆ–过小会缺省等于音量范围的最大或最小值
 * @param {number} volume éŸ³é‡ï¼Œå¿…å¡«
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
alsa.setVolume = function (volume, id) {
    let pointer = dxCommon.handleId("alsa", id)
    return alsaObj.alsaSetVolume(pointer, volume)
}
/**
 * æ’­æ”¾æµå¼éŸ³é¢‘
 * @param {ArrayBuffer} éŸ³é¢‘流 ï¼Œå¿…å¡«
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
alsa.alsaWavData = function (buffer, id) {
    if (!buffer) {
        throw new Error("alsa.alsaWavData: 'buffer' parameter should not be null")
    }
    let pointer = dxCommon.handleId("alsa", id)
    return alsaObj.alsaWavData(pointer, buffer)
}
export default alsa;
vf205_access/dxmodules/dxBase64.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,308 @@
//build:20240308
//Base64编解码工具
//依赖组件:无
//基本包括以下函数:
/**
 * 1. encode(str): å­—符串转base64字符串,比如Base64.encode("aa的健康aab")得到YWHnmoTlgaXlurdhYWI=
 * 2. decode(b64): base64字符串转原字符串
 * 3. fromUint8Array(arr): byte数组转base64字符串
 * 4. toUnit8Array(b64):base64字符串转byte数组
 * 5. fromHexString(hex):16进制字符串(小写,无空格)转base64字符串
 * 6. toHexString(b64):base64字符串转16进制字符串(小写,无空格
 */
/**
 *  base64.ts
 *
 *  Licensed under the BSD 3-Clause License.
 *    http://opensource.org/licenses/BSD-3-Clause
 *
 *  References:
 *    http://en.wikipedia.org/wiki/Base64
 *
 * @author Dan Kogai (https://github.com/dankogai)
 */
let version = '3.7.7';
/**
 * @deprecated use lowercase `version`.
 */
let VERSION = version;
let _hasBuffer = typeof Buffer === 'function';
let _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
let _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
let b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let b64chs = Array.prototype.slice.call(b64ch);
let b64tab = (function (a) {
    let tab = {};
    a.forEach(function (c, i) { return tab[c] = i; });
    return tab;
})(b64chs);
let b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
let _fromCC = String.fromCharCode.bind(String);
let _U8Afrom = typeof Uint8Array.from === 'function'
    ? Uint8Array.from.bind(Uint8Array)
    : function (it) { return new Uint8Array(Array.prototype.slice.call(it, 0)); };
let _mkUriSafe = function (src) {
    return src
        .replace(/=/g, '').replace(/[+\/]/g, function (m0) { return m0 == '+' ? '-' : '_'; });
};
let _tidyB64 = function (s) { return s.replace(/[^A-Za-z0-9\+\/]/g, ''); };
/**
 * polyfill version of `btoa`
 */
let btoaPolyfill = function (bin) {
    // console.log('polyfilled');
    let u32, c0, c1, c2, asc = '';
    let pad = bin.length % 3;
    for (let i = 0; i < bin.length;) {
        if ((c0 = bin.charCodeAt(i++)) > 255 ||
            (c1 = bin.charCodeAt(i++)) > 255 ||
            (c2 = bin.charCodeAt(i++)) > 255)
            throw new TypeError('invalid character found');
        u32 = (c0 << 16) | (c1 << 8) | c2;
        asc += b64chs[u32 >> 18 & 63]
            + b64chs[u32 >> 12 & 63]
            + b64chs[u32 >> 6 & 63]
            + b64chs[u32 & 63];
    }
    return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
};
/**
 * does what `window.btoa` of web browsers do.
 * @param {String} bin binary string
 * @returns {string} Base64-encoded string
 */
let _btoa = typeof btoa === 'function' ? function (bin) { return btoa(bin); }
    : _hasBuffer ? function (bin) { return Buffer.from(bin, 'binary').toString('base64'); }
        : btoaPolyfill;
let _fromUint8Array = _hasBuffer
    ? function (u8a) { return Buffer.from(u8a).toString('base64'); }
    : function (u8a) {
        // cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326
        let maxargs = 0x1000;
        let strs = [];
        for (let i = 0, l = u8a.length; i < l; i += maxargs) {
            strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
        }
        return _btoa(strs.join(''));
    };
/**
 * converts a Uint8Array to a Base64 string.
 * @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 Â§5
 * @returns {string} Base64 string
 */
let fromUint8Array = function (u8a, urlsafe) {
    if (urlsafe === void 0) { urlsafe = false; }
    return urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
};
let fromHexString = function (hexString) {
    let byteString = hexString.match(/.{1,2}/g);
    let byteArray = byteString.map(function (byte) {
        return parseInt(byte, 16);
    });
    let buffer = new Uint8Array(byteArray);
    return fromUint8Array(buffer)
}
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
// const utob = (src: string) => unescape(encodeURIComponent(src));
// reverting good old fationed regexp
let cb_utob = function (c) {
    if (c.length < 2) {
        let cc = c.charCodeAt(0);
        return cc < 0x80 ? c
            : cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6))
                + _fromCC(0x80 | (cc & 0x3f)))
                : (_fromCC(0xe0 | ((cc >>> 12) & 0x0f))
                    + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
                    + _fromCC(0x80 | (cc & 0x3f)));
    }
    else {
        let cc = 0x10000
            + (c.charCodeAt(0) - 0xD800) * 0x400
            + (c.charCodeAt(1) - 0xDC00);
        return (_fromCC(0xf0 | ((cc >>> 18) & 0x07))
            + _fromCC(0x80 | ((cc >>> 12) & 0x3f))
            + _fromCC(0x80 | ((cc >>> 6) & 0x3f))
            + _fromCC(0x80 | (cc & 0x3f)));
    }
};
let re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
/**
 * @deprecated should have been internal use only.
 * @param {string} src UTF-8 string
 * @returns {string} UTF-16 string
 */
let utob = function (u) { return u.replace(re_utob, cb_utob); };
//
let _encode = _hasBuffer
    ? function (s) { return Buffer.from(s, 'utf8').toString('base64'); }
    : _TE
        ? function (s) { return _fromUint8Array(_TE.encode(s)); }
        : function (s) { return _btoa(utob(s)); };
/**
 * converts a UTF-8-encoded string to a Base64 string.
 * @param {boolean} [urlsafe] if `true` make the result URL-safe
 * @returns {string} Base64 string
 */
let encode = function (src, urlsafe) {
    if (urlsafe === void 0) { urlsafe = false; }
    return urlsafe
        ? _mkUriSafe(_encode(src))
        : _encode(src);
};
/**
 * converts a UTF-8-encoded string to URL-safe Base64 RFC4648 Â§5.
 * @returns {string} Base64 string
 */
let encodeURI = function (src) { return encode(src, true); };
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
// const btou = (src: string) => decodeURIComponent(escape(src));
// reverting good old fationed regexp
let re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
let cb_btou = function (cccc) {
    switch (cccc.length) {
        case 4:
            let cp = ((0x07 & cccc.charCodeAt(0)) << 18)
                | ((0x3f & cccc.charCodeAt(1)) << 12)
                | ((0x3f & cccc.charCodeAt(2)) << 6)
                | (0x3f & cccc.charCodeAt(3)), offset = cp - 0x10000;
            return (_fromCC((offset >>> 10) + 0xD800)
                + _fromCC((offset & 0x3FF) + 0xDC00));
        case 3:
            return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12)
                | ((0x3f & cccc.charCodeAt(1)) << 6)
                | (0x3f & cccc.charCodeAt(2)));
        default:
            return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6)
                | (0x3f & cccc.charCodeAt(1)));
    }
};
/**
 * @deprecated should have been internal use only.
 * @param {string} src UTF-16 string
 * @returns {string} UTF-8 string
 */
let btou = function (b) { return b.replace(re_btou, cb_btou); };
/**
 * polyfill version of `atob`
 */
let atobPolyfill = function (asc) {
    // console.log('polyfilled');
    asc = asc.replace(/\s+/g, '');
    if (!b64re.test(asc))
        throw new TypeError('malformed base64.');
    asc += '=='.slice(2 - (asc.length & 3));
    let u24, bin = '', r1, r2;
    for (let i = 0; i < asc.length;) {
        u24 = b64tab[asc.charAt(i++)] << 18
            | b64tab[asc.charAt(i++)] << 12
            | (r1 = b64tab[asc.charAt(i++)]) << 6
            | (r2 = b64tab[asc.charAt(i++)]);
        bin += r1 === 64 ? _fromCC(u24 >> 16 & 255)
            : r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255)
                : _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
    }
    return bin;
};
/**
 * does what `window.atob` of web browsers do.
 * @param {String} asc Base64-encoded string
 * @returns {string} binary string
 */
let _atob = typeof atob === 'function' ? function (asc) { return atob(_tidyB64(asc)); }
    : _hasBuffer ? function (asc) { return Buffer.from(asc, 'base64').toString('binary'); }
        : atobPolyfill;
//
let _toUint8Array = _hasBuffer
    ? function (a) { return _U8Afrom(Buffer.from(a, 'base64')); }
    : function (a) { return _U8Afrom(_atob(a).split('').map(function (c) { return c.charCodeAt(0); })); };
/**
 * converts a Base64 string to a Uint8Array.
 */
let toUint8Array = function (a) { return _toUint8Array(_unURI(a)); };
//
let toHexString = function (a) {
    let uint8 = toUint8Array(a)
    return Array.from(uint8)
        .map((i) => i.toString(16).padStart(2, '0'))
        .join('');;
}
let _decode = _hasBuffer
    ? function (a) { return Buffer.from(a, 'base64').toString('utf8'); }
    : _TD
        ? function (a) { return _TD.decode(_toUint8Array(a)); }
        : function (a) { return btou(_atob(a)); };
let _unURI = function (a) { return _tidyB64(a.replace(/[-_]/g, function (m0) { return m0 == '-' ? '+' : '/'; })); };
/**
 * converts a Base64 string to a UTF-8 string.
 * @param {String} src Base64 string.  Both normal and URL-safe are supported
 * @returns {string} UTF-8 string
 */
let decode = function (src) { return _decode(_unURI(src)); };
/**
 * check if a value is a valid Base64 string
 * @param {String} src a value to check
  */
let isValid = function (src) {
    if (typeof src !== 'string')
        return false;
    let s = src.replace(/\s+/g, '').replace(/={0,2}$/, '');
    return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
};
//
let _noEnum = function (v) {
    return {
        value: v, enumerable: false, writable: true, configurable: true
    };
};
/**
 * extend String.prototype with relevant methods
 */
let extendString = function () {
    let _add = function (name, body) { return Object.defineProperty(String.prototype, name, _noEnum(body)); };
    _add('fromBase64', function () { return decode(this); });
    _add('toBase64', function (urlsafe) { return encode(this, urlsafe); });
    _add('toBase64URI', function () { return encode(this, true); });
    _add('toBase64URL', function () { return encode(this, true); });
    _add('toUint8Array', function () { return toUint8Array(this); });
};
/**
 * extend Uint8Array.prototype with relevant methods
 */
let extendUint8Array = function () {
    let _add = function (name, body) { return Object.defineProperty(Uint8Array.prototype, name, _noEnum(body)); };
    _add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe); });
    _add('toBase64URI', function () { return fromUint8Array(this, true); });
    _add('toBase64URL', function () { return fromUint8Array(this, true); });
};
/**
 * extend Builtin prototypes with relevant methods
 */
let extendBuiltins = function () {
    extendString();
    extendUint8Array();
};
let gBase64 = {
    version: version,
    VERSION: VERSION,
    atob: _atob,
    atobPolyfill: atobPolyfill,
    btoa: _btoa,
    btoaPolyfill: btoaPolyfill,
    fromBase64: decode,
    toBase64: encode,
    encode: encode,
    encodeURI: encodeURI,
    encodeURL: encodeURI,
    utob: utob,
    btou: btou,
    decode: decode,
    isValid: isValid,
    fromUint8Array: fromUint8Array,
    toUint8Array: toUint8Array,
    fromHexString: fromHexString,
    toHexString: toHexString,
    extendString: extendString,
    extendUint8Array: extendUint8Array,
    extendBuiltins: extendBuiltins
};
export default gBase64
vf205_access/dxmodules/dxCameraCalibration.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
//build: 20240528
//依赖组件:dxDriver,dxLogger,dxMap,dxCapturer
import { calibrationClass } from './libvbar-m-dxcapturer_calibration.so'
import capturer from '../dxmodules/dxCapturer.js'
import * as os from "os";
import dxMap from './dxMap.js'
import log from './dxLogger.js'
import bus from './dxEventBus.js'
const calibrationObj = new calibrationClass();
const map = dxMap.get('default')
const calibration = {}
let cnt = 0, startTime = 0;
/**
 * calibration åˆå§‹åŒ–
 * @returns true/false
 */
calibration.init = function () {
    return calibrationObj.init();
}
/**
 * calibration é”€æ¯
 * @returns true/false
 */
calibration.deinit = function () {
    return calibrationObj.deinit();
}
/**
 * è¯†åˆ«æ ‡å®šï¼ˆæ‹¿æ–¹æ ¼çº¸å¯¹å‡†å±å¹•上的方框)
 * @param {number} imageRgb image指针,必填
 * @param {number} imageNir image指针,必填
 * @param {number} cnt æ ‡å®šæ¬¡æ•°ï¼Œå¿…填(0:第一次标定,1:第二次标定)
 * @returns true/false
 */
calibration.calibrationFromImage = function (imageRgb, imageNir, cnt) {
    return calibrationObj.calibrationFromImage(imageRgb, imageNir, cnt);
}
/**
 * è®¡ç®—并存储标定结果
 * @param {number} imageNir image指针,必填
 * @param {string} path å­˜å‚¨è·¯å¾„,必填
 * @returns true/false
 */
calibration.getMap = function (imageRgb, imageNir,cnt, path) {
    return calibrationObj.getMap(imageRgb, imageNir,cnt, path);
}
/**
 * èŽ·å–ç»˜åˆ¶æ ‡å®šui框信息
 * @param {number} cnt æ ‡å®šæ¬¡æ•°ï¼Œå¿…填(0:第一次标定,1:第二次标定)
 * @param {number} type æ¨ªç«–屏,必填(1:横屏,0:竖屏)
 * @returns {x:横坐标,y:纵坐标,w:宽,h:高}
 */
calibration.getBox = function (cnt, type) {
    let box = calibrationObj.getBox(cnt);
    let coordinate = {
        x: type == 1 ? box.x : box.y,
        y: type == 1 ? box.y : box.x,
        w: type == 1 ? box.w : box.h,
        h: type == 1 ? box.h : box.w
    }
    return coordinate;
}
calibration.RECEIVE_MSG = '__calibration__MsgReceive'
/**
 * ç®€åŒ–cameraCalibration组件的使用,无需轮询去获取数据,数据会通过eventbus发送出去
 * ç”±äºŽè¯†åˆ«æ ‡å®šcalibrationFromImage是阻塞线程的方法,所以必须新开一个线程执行,否则会阻塞其他线程
 * run åªä¼šæ‰§è¡Œä¸€æ¬¡
 * @param {object} options é…ç½®å‚æ•°
 * @param {string} options.capturerRgbId      å¿…填,rgb取图句柄id
 * @param {string} options.capturerNirId      å¿…填,nir取图句柄id
 * @param {number} options.timeout          å•位秒,非必填(缺省20秒),标定的超时时间,在此期间内未完成两次标定,则标定失败结束线程,如需重新标定,必须再次执行run方法
 */
calibration.run = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxCameraCalibration.run:'options' parameter should not be null or empty")
    }
    if (options.capturerRgbId === undefined || options.capturerRgbId === null || options.capturerRgbId.length <= 0) {
        throw new Error("dxCameraCalibration.run:'capturerRgbId' should not be null or empty")
    }
    if (options.capturerNirId === undefined || options.capturerNirId === null || options.capturerNirId.length <= 0) {
        throw new Error("dxCameraCalibration.run:'capturerNirId' should not be null or empty")
    }
    options.timeout = options.timeout ? options.timeout : 20
    try {
        if(startTime == null || startTime == 0){
            startTime = new Date().getTime()
        }
        let imageRgb = capturer.readImage(options.capturerRgbId)
        let imageNir = capturer.readImage(options.capturerNirId)
        let res = this.calibrationFromImage(imageRgb, imageNir, cnt)
        if (res) {
            if (cnt >= 1) {
                log.info("两次标定成功,结束标定")
                let path = "/etc/.cameraCalibration"
                if(options.path && options.path.length > 0){
                    path = options.path
                }
                this.getMap(imageRgb, imageNir, cnt, path)
                bus.fire(this.RECEIVE_MSG, "success1")
                capturer.destroyImage(imageRgb)
                capturer.destroyImage(imageNir)
                cnt = 0;
                startTime = 0;
                return "success1"
            }
            log.info("第" + (cnt + 1) + "次标定成功")
            bus.fire(this.RECEIVE_MSG, "success0")
            cnt += 1
            log.info("开始进行第" + (cnt + 1) + "次标定")
            return "success0"
        } else {
            log.error("第" + (cnt + 1) + "次标定失败,重试中")
        }
        capturer.destroyImage(imageRgb)
        capturer.destroyImage(imageNir)
        let endTime = new Date().getTime()
        if (endTime - startTime > options.timeout * 1000) {
            log.error('标定超时,请重新执行标定')
            bus.fire(this.RECEIVE_MSG, "timeout")
            cnt = 0
            startTime = 0;
            return "timeout"
        }
        return "failed"
    } catch (error) {
        log.error(error)
    }
}
export default calibration;
vf205_access/dxmodules/dxCapturer.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,336 @@
//build: 20240524
//摄像头取图组件,主要用于获取二维码图像然后利用dxDecoder组件来解析二维码图像
//依赖组件:dxDriver,dxCommon,dxStd,dxMap
import { capturerClass } from './libvbar-m-dxcapturer.so'
import * as os from "os"
import std from './dxStd.js'
import dxMap from './dxMap.js'
import dxCommon from './dxCommon.js';
import bus from './dxEventBus.js'
const capturerObj = new capturerClass();
const map = dxMap.get('default')
const capturer = {}
/**
 * å–图模块初始化
 * @param {object} options é…ç½®å‚数,大部分可以用默认值
 * @param {string} options.path                 å¿…填,图像采集设备路径,每种设备有差异,比如DW200对应的值是'/dev/video11', M500对应的'/dev/video0'
 * @param {number} options.width                éžå¿…填,图像宽,缺省是0
 * @param {number} options.height               éžå¿…填,图像高,缺省是0
 * @param {number} options.widthbytes           éžå¿…填,每个像素所占字节数 GREY : 1, YUV : 2,DW200缺省是1 VF203缺省是2
 * @param {number} options.pixel_format          éžå¿…填,像素格式, ç¼ºçœæ˜¯1497715271表示V4L2_PIX_FMT_GREY
 * @param {number} options.max_channels          éžå¿…填,最大支持的同步输出channel数量,缺省是3
 * @param {number} options.rotation             éžå¿…填,旋转角度,缺省是90
 * @param {number} options.frame_num             éžå¿…填,帧编号,缺省是3
 * @param {number} options.preview_enable        éžå¿…填,预览是否启用,缺省是3
 * @param {number} options.preview_left          éžå¿…填,预览框左边框坐标,缺省是0
 * @param {number} options.preview_top           éžå¿…填,预览框上边框坐标,缺省是0
 * @param {number} options.preview_width         éžå¿…填,预览框宽度,VF203缺省是1024
 * @param {number} options.preview_height        éžå¿…填,预览框高度,VF203缺省是600
 * @param {number} options.preview_rotation      éžå¿…填,预览框旋转角度,缺省是0
 * @param {number} options.preview_mode          éžå¿…填,预览框模式,缺省是2
 * @param {number} options.preview_screen_index   éžå¿…填,预览框索引,缺省是0
 * @param {string} id å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 */
capturer.init = function (options, id) {
    if (options.path === undefined || options.path === null || options.path.length < 1) {
        throw new Error("dxCapturer.init: 'path' parameter should not be null or empty")
    }
    let pointer = capturerObj.init(options);
    if (!pointer) {
        throw new Error("dxCapturer.init: init failed")
    }
    dxCommon.handleId("capturer", id, pointer)
}
/**
 * å›žè°ƒæ³¨å†Œ
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @param {string} capturerDogId æ‘„像头看门狗句柄id,非必填
 * @returns true/false
 */
capturer.registerCallback = function (id, capturerDogId) {
    let pointer = dxCommon.handleId("capturer", id)
    let capturerDogPointer = null;
    print("capturerDogPointer:", capturerDogPointer)
    if(capturerDogId){
        capturerDogPointer = dxCommon.handleId("watchdog", capturerDogId)
        print("capturerDogPointer:", capturerDogPointer)
    }
    return capturerObj.registerCallback(pointer, "decoderCapturerImage", capturerDogPointer)
}
/**
 * èŽ·å–åŸºæœ¬ä¿¡æ¯
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns æ ¼å¼ç±»ä¼¼ï¼š {"width":800,"widthbytes":1,"height":600,"name":{},"type":6}
 */
capturer.getInfo = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.getInfo(pointer)
}
/**
 * å…³é—­å–图模块
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
capturer.close = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.close(pointer)
}
/**
 * èŽ·å–å›¾åƒæ•°æ®ï¼Œè½®è¯¢å¯è°ƒç”¨æ­¤æŽ¥å£ï¼Œç±»ä¼¼capturer.msgReceive方法的获取,若使用这个方法,必须手动销毁获取的image指针
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns image指针
 */
capturer.readImage = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.readImage(pointer)
}
/**
 * é”€æ¯èŽ·å–çš„image指针,与capturer.readImage方法共同使用
 * @param {number} image image指针,必填
 * @returns true/false
 */
capturer.destroyImage = function (image) {
    return capturerObj.destroyImage(image)
}
/**
 * ä½¿èƒ½/关闭capture预览
 * @param {number}  æ‘„像头启用/禁用,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
capturer.capturerEnable = function (enable, id) {
    if (enable == null) {
        throw new Error("nirEnable should not be null or empty")
    }
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.capturerPreviewEnable(pointer, enable)
}
/**
 * @brief å›¾ç‰‡æ–‡ä»¶è½¬image
 * @param {string} fileName æ–‡ä»¶è·¯å¾„
 * @param {number} type å›¾åƒç±»åž‹ IMAGE_YUV420P = 0, 1IMAGE_YUV420SP = 1,
 * @return imageId image句柄id
 */
capturer.pictureFileToImage = function (fileName, type) {
    if (fileName == null) {
        throw new Error("fileName should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    return capturerObj.pictureFileToImage(fileName, type)
}
/**
 * @brief å›¾ç‰‡è£å‰ª
 * @param {string} src_pic æºæ–‡ä»¶è·¯å¾„
 * @param {number} width å›¾åƒå®½åº¦
 * @param {number} height å›¾åƒé«˜åº¦
 * @param {string} out_pic è£å‰ªåŽçš„图片路径
 * @return ture/false
 */
capturer.pictureCropping = function (src_pic, width, height, out_pic) {
    if (src_pic == null) {
        throw new Error("src_pic should not be null or empty")
    }
    if (width == null) {
        throw new Error("width should not be null or empty")
    }
    if (height == null) {
        throw new Error("height should not be null or empty")
    }
    if (out_pic == null) {
        throw new Error("out_pic should not be null or empty")
    }
    return capturerObj.pictureCropping(src_pic, width, height, out_pic)
}
/**
 * å›¾ç‰‡æ•°æ®è½¬image
 * @param {string}  base64Data å›¾ç‰‡base64数据
 * @param {number}  dataLen æ•°æ®é•¿åº¦dataLen
 * @param {number}  type å›¾åƒç±»åž‹ IMAGE_YUV420P = 0, 1IMAGE_YUV420SP = 1,
 * @returns imageId image句柄id
 */
capturer.pictureDataToImage = function (base64Data, dataLen, type) {
    if (base64Data == null) {
        throw new Error("base64Data should not be null or empty")
    }
    if (dataLen == null) {
        throw new Error("dataLen should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    return capturerObj.pictureDataToImage(base64Data, dataLen, type)
}
// image, (enum image_type)type, (enum vbar_drv_picture_type)save_type, quality, pic_data, data_len
/**
 * image è½¬å›¾ç‰‡æ•°æ®
 * @param {number}  imageId image图片句柄id
 * @param {number}  type å›¾åƒç±»åž‹ IMAGE_YUV420P = 0, 1IMAGE_YUV420SP = 1,
 * @param {number}  saveType è½¬æ¢åŽçš„图片类型 TYPE_JPEG = 0, TYPE_BMP = 1, TYPE_PNG = 2, TYPE_UNKNOE = 3;
 * @param {number}  quality åŽ‹ç¼©æ¯”ï¼Œjpeg 0-100, png æ— æŸåŽ‹ç¼©æ— éœ€æ­¤å‚æ•°ï¼Œ bmp位图无需此参数
 * @returns å›¾ç‰‡base64数据
 */
capturer.imageToPictureData = function (imageId, type, saveType, quality) {
    if (imageId == null) {
        throw new Error("imageId should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    if (saveType == null) {
        throw new Error("saveType should not be null or empty")
    }
    if (quality == null) {
        throw new Error("quality should not be null or empty")
    }
    return capturerObj.imageToPictureData(imageId, type, saveType, quality)
}
/**
 * è½¬å›¾ç‰‡æ–‡ä»¶
 * @param {number}  imageId image图像句柄id
 * @param {string}  type å›¾åƒç±»åž‹ IMAGE_YUV420P = 0, 1IMAGE_YUV420SP = 1,
 * @param {number}  saveType è½¬æ¢åŽçš„图片类型 YPE_JPEG = 0, TYPE_BMP = 1, TYPE_PNG = 2, TYPE_UNKNOE = 3;
 * @param {number}  quality åŽ‹ç¼©æ¯”ï¼Œjpeg 0-100, png æ— æŸåŽ‹ç¼©æ— éœ€æ­¤å‚æ•°ï¼Œ bmp位图无需此参数
 * @param {number}  savePath å›¾ç‰‡ä¿å­˜è·¯å¾„
 * @returns true/false
 */
capturer.imageToPictureFile = function (imageId, type, saveType, quality, savePath) {
    if (imageId == null) {
        throw new Error("imageId should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    if (saveType == null) {
        throw new Error("saveType should not be null or empty")
    }
    if (quality == null) {
        throw new Error("quality should not be null or empty")
    }
    if (savePath == null) {
        throw new Error("savePath should not be null or empty")
    }
    return capturerObj.imageToPictureFile(imageId, type, saveType, quality, savePath)
}
/**
* å›¾ç‰‡ç¼©æ”¾
* @param {number}   imageId image图像句柄id
* @param {number}   width  ç›®æ ‡å›¾åƒå®½åº¦
* @param {number}   height ç›®æ ‡å›¾åƒé«˜åº¦
* @param {number}   mode       æ»¤æ³¢å™¨æ¨¡å¼
*                   FILTER_MODE_NONE     ä¸è¿›è¡Œæ»¤æ³¢ï¼Œç›´æŽ¥é‡‡æ ·ï¼›é€Ÿåº¦æœ€å¿«ã€‚
*                   FILTER_MODE_LINEAR   åªæ²¿æ°´å¹³æ–¹å‘滤波。
*                   FILTER_MODE_BILINEAR åŒçº¿æ€§æ»¤æ³¢ï¼›æ¯”盒滤波更快,但在缩小图像时质量较低。
*                   FILTER_MODE_BOX      ç›’滤波;提供最高的缩放质量
*/
capturer.imageResizeResolution = function (imageId, width, height, mode) {
    if (imageId == null) {
        throw new Error("imageId should not be null or empty")
    }
    if (width == null) {
        throw new Error("width should not be null or empty")
    }
    if (height == null) {
        throw new Error("height should not be null or empty")
    }
    if (mode == null) {
        throw new Error("mode should not be null or empty")
    }
    return capturerObj.imageResizeResolution(imageId, width, height, mode)
}
/**
 * åˆ¤æ–­capturer消息队列是否为空
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
capturer.msgIsEmpty = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.msgIsEmpty(pointer)
}
/**
 * ä»Žcapturer消息队列中读取数据
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns image指针
 */
capturer.msgReceive = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.msgReceive(pointer)
}
/**
 * æŸ¥è¯¢capturer消息队列大小
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns size
 */
capturer.msgQueueSize = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.msgQueueSize(pointer)
}
capturer.RECEIVE_MSG = '__capturer__MsgReceive'
/**
 * ç”¨äºŽç®€åŒ–capturer组件的使用,把capturer封装在这个worker里,使用者只需要订阅eventbus的事件就可以监听capturer
 * @param {object} options capturer组件参数,参考capturer.init,必填
 * @param {string} options.id  å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 */
capturer.run = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxcapturer.run:'options' parameter should not be null or empty")
    }
    if (options.id === undefined || options.id === null || typeof options.id !== 'string') {
        // å¥æŸ„id
        options.id = ""
    }
    if (options.path === undefined || options.path === null || options.path.length <= 0) {
        throw new Error("dxcapturer.run:'path' should not be null or empty")
    }
    let oldfilepre = '/app/code/dxmodules/capturerWorker'
    let content = std.loadFile(oldfilepre + '.js').replace("{{id}}", options.id)
    let newfile = oldfilepre + options.id + '.js'
    std.saveFile(newfile, content)
    let init = map.get("__capturer__run_init" + options.id)
    if (!init) {//确保只初始化一次
        map.put("__capturer__run_init" + options.id, options)
        bus.newWorker(options.id || '__capturer', newfile)
    }
}
/**
 * å¦‚æžœcapturer单独一个线程,可以直接使用run函数,会自动启动一个线程,
 * å¦‚果想加入到其他已有的线程,可以使用以下封装的函数
 */
capturer.worker = {
    //在while循环前
    beforeLoop: function (options) {
        capturer.init(options, options.id)
        capturer.registerCallback(options.id)
    },
    //在while循环里
    loop: function (options) {
        if (!capturer.msgIsEmpty(options.id)) {
            let res = capturer.msgReceive(options.id);
            if (options.id === undefined || options.id === null || typeof options.id !== 'string') {
                // å¥æŸ„id
                options.id = ""
            }
            bus.fire(capturer.RECEIVE_MSG + options.id, res)
        }
    }
}
export default capturer;
vf205_access/dxmodules/dxCommon.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,733 @@
//build: 20240617
// ç³»ç»Ÿçš„一些基本操作、还有一些常用的工具函数,基本上每个项目都需要依赖这个组件,另外dxLogger也依赖这个组件
// ä¾èµ–组件:dxDriver,dxMap
import { commonClass } from './libvbar-m-dxcommon.so'
import dxMap from './dxMap.js'
import * as std from 'std';
import * as os from "os"
const commonObj = new commonClass();
const common = {}
/**
 * èŽ·å–ç³»ç»Ÿå¯åŠ¨çš„è¿è¡Œæ—¶é—´(单位是秒)
 * @returns
 */
common.getUptime = function () {
    return commonObj.getUptime();
}
/**
 * èŽ·å–ç³»ç»Ÿçš„æ€»å†…å­˜(单位是字节)
 * @returns
 */
common.getTotalmem = function () {
    return commonObj.getTotalmem();
}
/**
 * èŽ·å–ç³»ç»Ÿå‰©ä½™å†…å­˜(单位是字节)
 * @returns
 */
common.getFreemem = function () {
    return commonObj.getFreemem();
}
/**
 * Deprcated, Please use `common.logMemory` instead.
 */
common.logMem = function (logger, interval = 10) {
    if (logger) {
        logger.debug("Deprcated, Please use `common.logMemory` instead.")
    }
}
/**
 * Logs the current memory usage at regular intervals for debugging and monitoring purposes.
 * @param {object} logger The dxLogger module object.
 * @param {number} interval The logging interval in seconds, defaulting to 10 seconds.
 */
common.logMemory = function (logger, interval = 10) {
    if (!logger) return;
    let first = new Date().getTime();
    let min = common.getFreemem() / 1024;
    let max = min;
    const _logmemory = () => {
        try {
            const now = new Date().getTime();
            const pass = (now - first) / 1000;
            const free = common.getFreemem() / 1024;
            min = Math.min(min, free);
            max = Math.max(max, free);
            // Format time
            let passStr;
            if (pass > 1700000000) {
                first = now;
                passStr = 'time synced, 0s';
            } else if (pass >= 3600) {
                const hours = Math.floor(pass / 3600);
                const minutes = Math.floor((pass % 3600) / 60);
                const seconds = Math.floor(pass % 60);
                passStr = `${hours}h ${minutes}m ${seconds}s`;
            } else if (pass >= 60) {
                const minutes = Math.floor(pass / 60);
                const seconds = Math.floor(pass % 60);
                passStr = `${minutes}m ${seconds}s`;
            } else {
                passStr = `${Math.floor(pass)}s`;
            }
            const log = `------ ${passStr} passed, free memory (k): ${free}, min free memory (k): ${min}, max free memory (k): ${max} ------`;
            logger.info(log);
        } catch (err) {
            logger.error('Error in logMemory:', err);
        } finally {
            os.setTimeout(_logmemory, interval * 1000);
        }
    };
    os.setTimeout(_logmemory, interval * 1000);
};
/**
 * The principle of converting asynchronous to synchronous is as follows:
 * the `request` function periodically checks a designated variable in memory for a value.
 * If the value is found within the timeout period, the result is returned; otherwise,
 * it is considered a timeout. The `response` function is responsible for storing the result
 *  in the designated variable once the asynchronous request is completed.
 */
common.sync = {
    /**
     * Block and wait for data
     * Usage:
        common.sync.request(topic, 200)
        .then((data) => {
            log.info("Received data:", data);
        })
        .catch((err) => {
            log.error("Request failed:", err.message);
        });
     * @param {string} topic The unique identifier for each request
     * @param {int} timeout waitting timeout(microsecond),default is 200 ms
     * @returns
     */
    request: function (topic, timeout = 200) {
        return new Promise((resolve, reject) => {
            let map = dxMap.get("SYNC");
            let startTime = Date.now();
            const checkData = () => {
                let data = map.get(topic);
                if (data) {
                    map.del(topic); //del data in map
                    resolve(data); //return data
                } else if (Date.now() - startTime >= timeout) {
                    map.del(topic); // del data in map with timeout
                    reject(new Error(`Timeout exceeded for topic: ${topic}`));
                } else {
                    os.setTimeout(checkData, 10); //every 10 ms to check
                }
            };
            os.setTimeout(checkData, 10); // first check
        });
    },
    /**
     * notify data to requester
     * @param {string} topic The unique identifier for each request
     * @param {*} data
     * @returns
     */
    response: function (topic, data) {
        let map = dxMap.get("SYNC");
        map.put(topic, data); // save data in map
    }
};
/**
 * èŽ·å–ç³»ç»Ÿå¯ç”¨ç£ç›˜æ€»é‡(单位是字节)
 * @param {string} path ä¸åŒçš„磁盘分区名称(不是目录名),非必填,缺省是'/'
 */
common.getTotaldisk = function (path) {
    return commonObj.getTotaldisk(!path ? "/" : path);
}
/**
 * èŽ·å–ç³»ç»Ÿç£ç›˜å‰©ä½™å¯ç”¨é‡(单位是字节)
 * @param {string} path ä¸åŒçš„磁盘分区名称(不是目录名),非必填,缺省是'/'
 * @returns
 */
common.getFreedisk = function (path) {
    return commonObj.getFreedisk(!path ? "/" : path);
}
/**
 * èŽ·å–CPU ID
 * @param {number} len éžå¿…填,缺省长度是33位长
 * @returns
 */
common.getCpuid = function () {
    return commonObj.getCpuid(33);
}
/**
 * èŽ·å–è®¾å¤‡uuid(字符串)
 * @returns
 */
common.getUuid = function () {
    return commonObj.getUuid(19);
}
/**
 * èŽ·å–è®¾å¤‡å”¯ä¸€æ ‡è¯†
 * @returns
 */
common.getSn = function () {
    let sn = std.loadFile('/etc/.sn')
    if (sn) {
        return sn
    } else {
        return commonObj.getUuid(19);
    }
}
/**
 * èŽ·å–é€šè¿‡uuid计算的mac地址,这个可以用来初始化网卡的时候用
 * @returns æ ¼å¼ç±»ä¼¼ï¼šb2:a1:63:3f:99:b6
 */
common.getUuid2mac = function () {
    return commonObj.getUuid2mac(19);
}
/**
 * èŽ·å–cpu占用率(不大于100的数字)
 * @returns
 */
common.getFreecpu = function () {
    return commonObj.getFreecpu();
}
/**
 * RSA è§£å¯† ï¼ˆç§é’¥åŠ å¯†å…¬é’¥è§£å¯†ï¼‰
 * æ¯”如公钥是
 * @param {ArrayBuffer} data è¦è§£å¯†çš„æ•°æ®ï¼Œå¿…å¡«
 * @param {string} publicKey å…¬é’¥ï¼Œå¿…å¡«
 * @returns
 */
common.arrayBufferRsaDecrypt = function (data, publicKey) {
    if (data === undefined || data === null) {
        throw new Error("dxCommon.arrayBufferRsaDecrypt:'data' parameter should not be null or empty")
    }
    if (publicKey === undefined || publicKey === null || publicKey.length < 1) {
        throw new Error("dxCommon.arrayBufferRsaDecrypt:'publicKey' parameter should not be null or empty")
    }
    return commonObj.arrayBufferRsaDecrypt(data, publicKey)
}
/**
 * @brief   Stirng aes åР坆
 */
common.aes128EcbEncrypt = function (input, key) {
    return commonObj.aes128EcbEncrypt(input, key)
}
/**
 * @brief   Stirng aes è§£å¯†
 */
common.aes128EcbDecrypt = function (input, key) {
    return commonObj.aes128EcbDecrypt(input, key)
}
/**
 * arraybuffer ecb 128bit Pkcs5Padding aes åР坆
 * @param {ArrayBuffer} input æ˜Žæ–‡
 * @param {ArrayBuffer} key å¯†é’¥
 * @returns ArrayBuffer å¯†æ–‡
 */
common.aes128EcbPkcs5PaddingEncode = function (input, key) {
    return commonObj.aes128Pkcs7PaddingEncode(input, key)
}
/**
 * arraybuffer ecb 128bit Pkcs5Padding aes è§£å¯†
 *
 * @param {ArrayBuffer} input å¯†æ–‡
 * @param {ArrayBuffer} key å¯†é’¥
 * @returns ArrayBuffer æ˜Žæ–‡
 */
common.aesEcb128Pkcs5PaddingDecode = function (input, key) {
    return commonObj.aes128Pkcs7PaddingDecode(input, key)
}
/**
 * aes ECB Pkcs5Padding 128 åР坆
 * ç¤ºä¾‹ï¼šcommon.aes128EcbPkcs5PaddingEncrypt("stamp=202008文&tic", "1234567890123456")
 * ç»“果:ef7c3cff9df57b3bcb0951938c574f969e13ffdcc1eadad298ddbd1fb1a4d2f7
 * å‚考 https://www.devglan.com/online-tools/aes-encryption-decryption
 * @param {string} input  æ˜Žæ–‡æ•°æ®
 * @param {string} key     å¯†é’¥ 16字节字符串
 * @return å¯†æ–‡ 16进制字符串
 */
common.aes128EcbPkcs5PaddingEncrypt = function (input, key) {
    let data = common.hexStringToArrayBuffer(common.strToUtf8Hex(input))
    key = common.hexStringToArrayBuffer(common.strToUtf8Hex(key))
    // åР坆
    let hex = common.arrayBufferToHexString(common.aes128EcbPkcs5PaddingEncode(data, key))
    return hex
}
/**
   * aes ECB Pkcs5Padding 128 è§£å¯†
   * @param {string} input å¯†æ–‡ 16进制字符串
   * @param {string} key   å¯†é’¥ 16字节字符串
   * @return æ˜Žæ–‡
   */
common.aes128EcbPkcs5PaddingDecrypt = function (input, key) {
    key = common.hexStringToArrayBuffer(common.strToUtf8Hex(key))
    let res = common.aesEcb128Pkcs5PaddingDecode(common.hexStringToArrayBuffer(input), key)
    return common.utf8HexToStr(common.arrayBufferToHexString(res))
}
/**
 * æ‰§è¡Œæ“ä½œç³»ç»Ÿçš„命令
 * @param {*} cmd å‘½ä»¤
 * @returns
 */
common.system = function (cmd) {
    return commonObj.system(cmd)
}
/**
 * æ‰§è¡Œæ“ä½œç³»ç»Ÿçš„命令
 * @param {*} cmd å‘½ä»¤ æ“ä½œç³»ç»Ÿå¸¸ç”¨æŒ‡ä»¤(linux绝大部分指令都支持),必填
 * @returns
 */
common.systemBrief = function (cmd) {
    return commonObj.systemBrief(cmd)
}
/**
 * æ‰§è¡Œæ“ä½œç³»ç»Ÿçš„命令并返回结果
 * @param {*} cmd å‘½ä»¤ æ“ä½œç³»ç»Ÿå¸¸ç”¨æŒ‡ä»¤(linux绝大部分指令都支持),必填
 * @param {*} resLen æŽ¥æ”¶æ•°æ®é•¿åº¦ æœ‰æ—¶å€™è¿”回的数据很大,可以通过这个值来返回固定长度的数据,必填
 * @returns
 */
common.systemWithRes = function (cmd, resLen) {
    return commonObj.systemWithRes(cmd, resLen)
}
/**
 * æ‰§è¡Œæ“ä½œç³»ç»Ÿçš„命令阻塞执行
 * @param {*} cmd å‘½ä»¤  æ“ä½œç³»ç»Ÿå¸¸ç”¨æŒ‡ä»¤(linux绝大部分指令都支持),必填
 * @returns
 */
common.systemBlocked = function (cmd) {
    return commonObj.systemBlocked(cmd)
}
/**
 * å¼‚步延迟重启
 * @param {*} delay_s å»¶è¿Ÿæ—¶é—´
 * @returns
 */
common.asyncReboot = function (delay_s) {
    return commonObj.asyncReboot(delay_s)
}
/**
 * bcc校验
 * @param {array} data eg:[49,50,51,52,53,54]对应的值是7
 * @returns æ ¡éªŒè®¡ç®—结果
 */
common.calculateBcc = function (data) {
    return commonObj.calculateBcc(data)
}
/**
 * crc校验 æ¯”如字符串'123456'校验计算的结果是数字 158520161
 * @param {string} content è¦æ ¡éªŒçš„字符串数据,
 * @returns
 */
common.crc32 = function (content) {
    if (content === undefined || content === null || typeof (content) != "string" || content.length < 1) {
        throw new Error("dxCommon.crc32:'content' paramter should not be empty")
    }
    return commonObj.crc32(content)
}
/**
 * è®¡ç®—MD5哈希,比如'123456'对应的数字数组是[49,50,51,52,53,54] å¯¹åº”çš„md5是'e10adc3949ba59abbe56e057f20f883e',
 * ä½†æ˜¯è¿”回的不是16进制字符串,是数字数组,可以使用arrToHex函数转换
 * @param {array} arr æ•°å­—数组
 * @returns æ•°å­—数组
 */
common.md5Hash = function (arr) {
    return commonObj.md5Hash(arr)
}
/**
 * æ–‡ä»¶è®¡ç®—MD5哈希,比如文件里的内容是'123456',对应的md5是'e10adc3949ba59abbe56e057f20f883e'
 * ä½†æ˜¯è¿”回的不是16进制字符串,是数字数组,可以使用arrToHex函数转换
 * @param {string} æ–‡ä»¶è·¯å¾„,绝对路径,必填,通常是以/app/code开头
 * @returns æ•°å­—数组
 */
common.md5HashFile = function (filePath) {
    if (filePath === undefined || filePath === null || typeof (filePath) != "string") {
        return null
    }
    return commonObj.md5HashFile(filePath)
}
/**
 * è®¡ç®—HMAC MD5加密,比如加密的数据是'123456',密钥是'654321',对应的结果是'357cbe6d81a8ec770799879dc8629a53'
 * ä½†æ˜¯å‚数和返回的值都是ArrayBuffer
 * @param {ArrayBuffer} data éœ€è¦åŠ å¯†çš„å†…å®¹,必填
 * @param {ArrayBuffer} key å¯†é’¥ ,必填
 * @returns ArrayBuffer
 */
common.hmacMd5Hash = function (data, key) {
    return commonObj.hmacMd5Hash(data, key)
}
/**
 * è®¡ç®—HMAC MD5加密,比如加密的数据是'123456',密钥是'654321',对应的结果是'357cbe6d81a8ec770799879dc8629a53'
 * @param {string} data éœ€è¦åŠ å¯†çš„å†…å®¹,必填
 * @param {string} key å¯†é’¥ ,必填
 * @returns ArrayBuffer
 */
common.hmac = function (data, key) {
    return commonObj.hmac(data, key)
}
/**
 * æ–‡ä»¶è®¡ç®—HMAC MD5加密,比如文件里的内容是'123456',密钥是'654321',对应的结果是'357cbe6d81a8ec770799879dc8629a53'
 * @param {string} filePath éœ€è¦åŠ å¯†çš„å†…å®¹å­˜å‚¨çš„æ–‡ä»¶è·¯å¾„ï¼Œç»å¯¹è·¯å¾„ï¼Œå¿…å¡«ï¼Œé€šå¸¸æ˜¯ä»¥/app/code开头
 * @param {array} key å¯†é’¥ ,数字数组,必填
 * @returns æ•°å­—数组
 */
common.hmacMd5HashFile = function (filePath, key) {
    return commonObj.hmacMd5HashFile(filePath, key)
}
/**
 * base64转bin文件
 * @param {string} file_path æ–‡ä»¶è·¯å¾„,必填
 * @param {string} base64Data base64数据,必填
 * @returns
 */
common.base64_2binfile = function (file_path, base64Data) {
    return commonObj.base64_2binfile(file_path, base64Data);
}
/**
 * bin文件转base64
 * @param {string} file_path æ–‡ä»¶è·¯å¾„,必填
 * @returns base64Data base64数据,必填
 */
common.binfile_2base64 = function (file_path) {
    return commonObj.binfile_2base64(file_path);
}
/**
 * åˆ‡æ¢è®¾å¤‡æ¨¡å¼
 * @description æ¨¡å¼åˆ‡æ¢åŽä¼šé‡å¯è®¾å¤‡ï¼Œè¿›å…¥æŒ‡å®šæ¨¡å¼ï¼Œä½¿ç”¨æ–¹æ³•时需完整维护相互切换的逻辑,切换为业务模式后不能使用IDE功能
 * @param {number} mode æ³¨æ„ï¼šæ—§ç‰ˆæœ¬æ¨¡å¼åˆ‡æ¢ä½¿ç”¨ï¼ˆ1、2、3),新版本模式切换使用(dev、test、prod、safe)
 * @returns true false
 */
common.setMode = function (mode) {
    // æ³¨æ„ï¼šæ—§ç‰ˆæœ¬æ¨¡å¼åˆ‡æ¢ä½¿ç”¨ï¼ˆ1、2、3)
    if (mode == 1) {
        //生产模式
        commonObj.systemWithRes(`echo 'app' > /etc/.mode`, 2)
        // 1.0版本切换为其他模式后删除工厂检测(后续版本可能会调整)
        commonObj.systemWithRes(`rm -rf /test`, 2)
    } else if (mode == 2) {
        //调试模式
        commonObj.systemWithRes(`echo 'debug' > /etc/.mode`, 2)
        // 1.0版本切换为其他模式后删除工厂检测(后续版本可能会调整)
        commonObj.systemWithRes(`rm -rf /test`, 2)
    } else if (mode == 3) {
        //试产模式
        commonObj.systemWithRes(`echo 'pp' > /etc/.mode`, 2)
    }
    // æ³¨æ„ï¼šæ–°ç‰ˆæœ¬æ¨¡å¼åˆ‡æ¢ä½¿ç”¨ï¼ˆdev、test、prod、safe)
    else if (mode == "dev") {
        //开发模式
        commonObj.systemWithRes(`echo 'dev' > /etc/.mode_v1`, 2)
    } else if (mode == "test") {
        //测试模式(试产模式)
        commonObj.systemWithRes(`echo 'test' > /etc/.mode_v1`, 2)
    } else if (mode == "prod") {
        //生产模式
        commonObj.systemWithRes(`echo 'prod' > /etc/.mode_v1`, 2)
    } else if (mode == "safe") {
        //安全模式
        commonObj.systemWithRes(`echo 'safe' > /etc/.mode_v1`, 2)
    } else {
        return false
    }
    commonObj.systemWithRes(`sync`, 2)
    commonObj.asyncReboot(2)
    return true
}
/**
 * æŸ¥è¯¢è®¾å¤‡æ¨¡å¼
 * @description èŽ·å–è®¾å¤‡å½“å‰æ¨¡å¼
 * @returns ä¸šåŠ¡æ¨¡å¼ï¼š1,开发模式:2,工厂模式:28, å¼‚常模式:-1
 */
common.getMode = function () {
    let ret = commonObj.systemWithRes(`test -e "/etc/.mode" && echo "OK" || echo "NO"`, 2)
    if (ret.includes('NO')) {
        return 28
    }
    let mode = commonObj.systemWithRes(`cat "/etc/.mode"`, 10)
    if (mode.includes('app')) {
        return 1
    } else if (mode.includes('debug')) {
        return 2
    } else {
        return -1
    }
}
/**
 * åå…­è¿›åˆ¶è½¬å­—节数组 eg:313233616263->[49,50,51,97,98,99]
 * @param {string} str 16进制字符串 å°å†™ä¸”中间无空隔的十六进制字符串
 * @returns æ•°å­—æ•°å­—
 */
common.hexToArr = function (str) {
    if (str === undefined || str === null || (typeof str) != 'string' || str.length < 1) {
        throw new Error("dxCommon.hexToArr:'str' parameter should not be empty")
    }
    let regex = /.{2}/g;
    let arr = str.match(regex);
    return arr.map(item => parseInt(item, 16));
}
/**
 * å­—节数组转十六进制 eg:[49,50,51,97,98,99]->313233616263
 * @param {array}numbers æ•°å­—数组
 * @returns str 16进制字符串 å°å†™ä¸”中间无空隔的十六进制字符串
 */
common.arrToHex = function (numbers) {
    const hexArray = numbers.map(num => num.toString(16).padStart(2, '0').toLowerCase());
    const hexString = hexArray.join('');
    return hexString;
}
/**
 * åå…­è¿›åˆ¶è½¬å­—符串 eg:313233616263->123abc
 * æ³¨æ„å¦‚æžœ16进制字符串是由中文转过去的,再转回中文字符串会有乱码,因为是一个一个字节的转换
 * @param {string} str è¦è½¬çš„16进制字符串
 * @returns
 */
common.hexToString = function (str) {
    let regex = /.{2}/g;
    let arr = str.match(regex);
    arr = arr.map(item => String.fromCharCode(parseInt(item, 16)));
    return arr.join("");
}
// å°†å­—符串转换为 UTF-8 ç¼–码的16进制字符串
common.strToUtf8Hex = function (str) {
    const bytes = [];
    for (let i = 0; i < str.length; i++) {
        let code = str.charCodeAt(i);
        if (code < 0x80) {
            bytes.push(code);
        } else if (code < 0x800) {
            bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
        } else if (code < 0xd800 || code >= 0xe000) {
            bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
        } else {
            // å¤„理 Unicode ç¼–码
            i++;
            code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
            bytes.push(
                0xf0 | (code >> 18),
                0x80 | ((code >> 12) & 0x3f),
                0x80 | ((code >> 6) & 0x3f),
                0x80 | (code & 0x3f)
            );
        }
    }
    return this.arrToHex(bytes);
}
/**
 * ä¼ é€’过来的utf-8的16进制字符串转换成字符串
 * @param {string} hex
 * @returns
 */
common.utf8HexToStr = function (hex) {
    let array = this.hexToArr(hex)
    var out, i, len, c;
    var char2, char3;
    out = "";
    len = array.length;
    i = 0;
    while (i < len) {
        c = array[i++];
        switch (c >> 4) {
            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                // 0xxxxxxx
                out += String.fromCharCode(c);
                break;
            case 12: case 13:
                // 110x xxxx   10xx xxxx
                char2 = array[i++];
                out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
                break;
            case 14:
                // 1110 xxxx  10xx xxxx  10xx xxxx
                char2 = array[i++];
                char3 = array[i++];
                out += String.fromCharCode(((c & 0x0F) << 12) |
                    ((char2 & 0x3F) << 6) |
                    ((char3 & 0x3F) << 0));
                break;
        }
    }
    return out;
}
/**
 * å­—符串转十六进制 eg:123abc->313233616263
 * @param {string} str è¦è½¬çš„字符串
 * @returns
 */
common.stringToHex = function (str) {
    if (str === undefined || str === null || typeof (str) != "string") {
        return null
    }
    let val = "";
    for (let i = 0; i < str.length; i++) {
        val += str.charCodeAt(i).toString(16)
    }
    return val
}
/**
 * å°ç«¯æ ¼å¼è½¬åè¿›åˆ¶æ•° eg:001001->69632
 * @param {string} hexString 16进制字符串 å°å†™ä¸”中间无空隔的十六进制字符串
 * @returns æ•°å­—
 */
common.littleEndianToDecimal = function (hexString) {
    // å°†å°ç«¯æ ¼å¼çš„十六进制字符串进行反转
    let reversedHexString = hexString
        .match(/.{2}/g)  // æ¯ä¸¤ä¸ªå­—符分隔
        .reverse()  // åè½¬æ•°ç»„
        .join("");  // åˆå¹¶ä¸ºå­—符串
    // å°†åè½¬åŽçš„十六进制字符串转换为十进制数
    let decimal = parseInt(reversedHexString, 16);
    return decimal;
}
/**
 * åè¿›åˆ¶æ•°è½¬æ¢ä¸º16进制小端格式字符串
 * eg:300->2c01
 * eg:230->e600
 * @param {number} decimalNumber åè¿›åˆ¶æ•°å­—,必填
 * @param {number} byteSize ç”Ÿæˆä½æ•° å­—节的个数,如果超出实际字节个数,会在右边补0,低于会截取,非必填,缺省是2
 * @returns
 */
common.decimalToLittleEndianHex = function (decimalNumber, byteSize) {
    if (decimalNumber === undefined || decimalNumber === null || (typeof decimalNumber) != 'number') {
        throw new Error("dxCommon.decimalToLittleEndianHex:'decimalNumber' parameter should be number")
    }
    if (byteSize === undefined || byteSize === null || (typeof byteSize) != 'number' || byteSize <= 0) {
        byteSize = 2
    }
    const littleEndianBytes = [];
    for (let i = 0; i < byteSize; i++) {
        littleEndianBytes.push(decimalNumber & 0xFF);
        decimalNumber >>= 8;//相当于除以256
    }
    const littleEndianHex = littleEndianBytes
        .map((byte) => byte.toString(16).padStart(2, '0'))
        .join('');
    return littleEndianHex;
}
/**
 * å°†16进制字符串转换为ArrayBuffer
 * @param {*} hexString è¦è½¬æ¢çš„16进制字符串
 * @returns
 */
common.hexStringToArrayBuffer = function (hexString) {
    return this.hexStringToUint8Array(hexString).buffer;
}
/**
 * å°†16进制字符串转换为Uint8Array
 * @param {string} hexString è¦è½¬æ¢çš„16进制字符串,小写且中间无空隔的十六进制字符串
 * @returns Uint8Array对象
 */
common.hexStringToUint8Array = function (hexString) {
    if (hexString === undefined || hexString === null || (typeof hexString) != 'string' || hexString.length <= 0) {
        throw new Error("dxCommon.hexStringToUint8Array:'hexString' parameter should not be empty")
    }
    let byteString = hexString.match(/.{1,2}/g);
    let byteArray = byteString.map(function (byte) {
        return parseInt(byte, 16);
    });
    let buffer = new Uint8Array(byteArray);
    return buffer;
}
/**
 * å°† ArrayBuffer è½¬æ¢ä¸ºåå…­è¿›åˆ¶å­—符串格式
 * @param {ArrayBuffer} buffer
 * @returns å°å†™ä¸”中间无空隔的十六进制字符串
 */
common.arrayBufferToHexString = function (buffer) {
    return this.uint8ArrayToHexString(new Uint8Array(buffer))
}
/**
 * å°† Uint8Array è½¬æ¢ä¸ºåå…­è¿›åˆ¶å­—符串格式
 * @param {Uint8Array} array
 * @returns å°å†™ä¸”中间无空隔的十六进制字符串
 */
common.uint8ArrayToHexString = function (array) {
    let hexString = '';
    for (let i = 0; i < array.length; i++) {
        const byte = array[i].toString(16).padStart(2, '0');
        hexString += byte;
    }
    return hexString
}
/**
 * è®¾ç½®/获取组件句柄id通用方法
 * @param {string} name ç»„件名,必填
 * @param {string} id å¥æŸ„id,非必填
 * @param {number} pointer å¥æŸ„指针数字,非必填
 * @returns
 */
common.handleId = function (name, id, pointer) {
    // ç»„件名不能为空
    if (name === undefined || name === null || name === "" || typeof name !== 'string') {
        return
    }
    let map = dxMap.get('handleIds')
    // å¥æŸ„id
    if (id === undefined || id === null || id === "" || typeof id !== 'string') {
        id = "__" + name + "_default"
    }
    if (pointer === undefined || pointer === null || typeof pointer !== 'number') {
        // pointer为空则为获取
        return map.get(id)
    } else {
        // pointer不为空则为设置
        let isExist = map.get(id)
        if (isExist) {
            // å¥æŸ„已存在
            return
        }
        map.put(id, pointer)
    }
}
export default common
vf205_access/dxmodules/dxConfig.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
/**
 * å®žçŽ°å¯¹åº”ç”¨æ‰€æœ‰é…ç½®é¡¹ï¼ˆkey/value)的管理:
 * 1. ç”¨æˆ·éœ€è¦æŠŠåˆå§‹çš„配置项保存在项目的 src/config.json ï¼Œé…ç½®æ–‡ä»¶çš„æ ¼å¼è¯·ä¿ç•™key/value格式(支持注释),value只能是字符串和数字类型,例如:
 * {
 *      //mqtt相关配置
 *      "mqtt.ip":"192.168.2.3",
 *      "mqtt.port":6199,
 * }
 * 2. ä¹Ÿæ”¯æŒè‡ªå®šä¹‰é…ç½®æ–‡ä»¶ï¼Œåˆå§‹åŒ–可以传递自定义配置文件的路径和标识,后续读写数据都需要传递这个标识
 * 3. ç”¨æˆ·åœ¨åº”用中第一次使用这个组件,需要先初始化 init,初始化会把 config.json çš„æ•°æ®ä¿å­˜åˆ°å†…存里,以后每次获取都是从内存获取
 * 4. ç”¨æˆ·å¯ä»¥åœ¨ä»»ä½•地方都可以通过 get å’Œ set æ¥è¯»å†™é…ç½®
 * 5. å¦‚果修改配置项的 value åŒæ—¶éœ€è¦ä¿å­˜åˆ°é…ç½®æ–‡ä»¶ï¼ˆä¿è¯é‡å¯åŽæ–°é…ç½®ç”Ÿæ•ˆï¼‰ï¼Œä½¿ç”¨ setAndSave
 * 6. å¦‚果需要恢复所有默认配置,使用 reset
 */
import * as os from 'os';
import dxMap from './dxMap.js'
import common from './dxCommon.js'
import logger from './dxLogger.js'
import std from './dxStd.js'
const map = dxMap.get("default")
const config = {}
const DEFALUT_OPTIONS = { path: '/app/code/src/config.json', savePath: '/app/data/config/config.json', flag: '___config.' }
/**
 * åˆå§‹åŒ–会把 config.json æˆ–自定义的配置文件的数据保存到内存里,以后每次获取都是从内存获取
 * @param {object} custom éžå¿…填,自定义的配置文件
 *          @param {string} custom.path è‡ªå®šä¹‰çš„配置文件完整路径
 *          @param {string} custom.flag è‡ªå®šä¹‰é…ç½®æ–‡ä»¶çš„æ ‡è¯†ï¼Œæ³¨æ„å¦‚果有多个自定义配置文件,这个标识不要重复
 */
config.init = function (custom) {
    if (custom) {
        if (!custom.path || !custom.flag) {
            throw new Error('The path and flag for the custom configuration file cannot be empty.')
        }
    }
    let flag = custom ? DEFALUT_OPTIONS.flag + custom.flag + '.' : DEFALUT_OPTIONS.flag;
    const isInited = map.get('___inited' + flag)
    if (isInited) {//只初始化一次
        return
    }
    let path = custom ? custom.path : DEFALUT_OPTIONS.path
    let savePath = custom ? '/app/data/config/config' + custom.flag + '.json' : DEFALUT_OPTIONS.savePath
    if (!std.exist(path)) {
        throw new Error('The config file not existed:' + path)
    }
    let existed = std.exist(savePath)
    let content = existed ? std.parseExtJSON(std.loadFile(savePath)) : std.parseExtJSON(std.loadFile(path))
    if (!existed) {
        std.saveFile(savePath, JSON.stringify(content))
    }
    for (let [key, value] of Object.entries(content)) {
        map.put(flag + key, value)
    }
    map.put('___inited' + flag, 'ok')
}
/**
 * èŽ·å–æ‰€æœ‰é…ç½®é¡¹
 * @param {string} flag è‡ªå®šä¹‰çš„配置文件标识,可以为空,为空则返回缺省config.json里所有内容
 * @returns json对象
 */
config.getAll = function (flag) {
    let _flag = _getFlag(flag)
    let configInfo = {}
    let keys = map.keys().filter(k => k.startsWith(_flag))
    keys.forEach(k => {
        let key = k.substring(_flag.length)
        let val = map.get(k)
        configInfo[key] = val
    })
    return configInfo
}
/**
 * èŽ·å–é…ç½®ï¼Œåªä»Žmap获取
 * å¦‚果配置项为空,返回所有所有数据;
 * @param {string} key é…ç½®é¡¹
 * @param {string} flag è‡ªå®šä¹‰çš„配置文件标识,可以为空,为空则返回缺省config.json里的配置值
 * @returns
 */
config.get = function (key, flag) {
    if (!key) {
        return this.getAll(flag);
    }
    let _flag = _getFlag(flag)
    return map.get(_flag + key)
}
/**
 * æ›´æ–°é…ç½®ï¼Œåªä¿®æ”¹map
 * @param {string} key é…ç½®é¡¹
 * @param {string} value é…ç½®å€¼
 * @param {string} flag è‡ªå®šä¹‰çš„配置文件标识,可以为空,为空则指向缺省config.json里的配置值
 */
config.set = function (key, value, flag) {
    if (!key || value == null || value == undefined) {
        throw new Error("key or value should not be empty")
    }
    let _flag = _getFlag(flag)
    map.put(_flag + key, value)
}
/**
 * å°†map中的数据持久化到本地
 * @param {string} flag è‡ªå®šä¹‰çš„配置文件标识,可以为空,为空则指向缺省config.json里的配置值
 */
config.save = function (flag) {
    //保存
    std.saveFile(_getSavePath(flag), JSON.stringify(this.getAll(flag)))
}
/**
 * æ›´æ–°é…ç½®ï¼Œä¿®æ”¹map且持久化本地
 * @param {string} key é…ç½®é¡¹
 * @param {string} value é…ç½®å€¼
 * @param {string} flag è‡ªå®šä¹‰çš„配置文件标识,可以为空,为空则指向缺省config.json里的配置值
 */
config.setAndSave = function (key, value, flag) {
    this.set(key, value, flag)
    //保存
    std.saveFile(_getSavePath(flag), JSON.stringify(this.getAll(flag)))
}
/**
 * é‡ç½®ï¼Œé‡ç½®åŽè¯·é‡å¯åŠ¨è®¾å¤‡
 * @param {string} flag è‡ªå®šä¹‰çš„配置文件标识,可以为空,为空则指向缺省config.json里的配置值
 */
config.reset = function (flag) {
    common.systemBrief('rm -rf ' + _getSavePath(flag))
}
//-------------------private-------------------------------
function _getFlag(flag) {
    return flag ? DEFALUT_OPTIONS.flag + flag + '.' : DEFALUT_OPTIONS.flag
}
function _getSavePath(flag) {
    return flag ? '/app/data/config/config' + flag + '.json' : DEFALUT_OPTIONS.savePath
}
export default config;
vf205_access/dxmodules/dxDriver.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
const dxDriver = {}
/*************************************Device Resource Enumeration*************************************/
/**
 * GPIO device pins
 */
dxDriver.GPIO = {
    // Relay
    RELAY0:         44,
}
/**
 * Channel communication
 */
dxDriver.CHANNEL = {
    // 485
    UART_PATH:      "/dev/ttySLB2",
    // USBHID
    USBHID_PATH:    "/dev/hidg1",
}
/**
 * Camera related parameters
 */
dxDriver.CAPTURER = {
    // Camera image width
    RGB_WIDTH:  1280,
    // Camera image height
    RGB_HEIGHT:    800,
    // Camera device files
    RGB_PATH:  "/dev/video3",
    // Camera image width
    NIR_WIDTH:  800,
    // Camera image height
    NIR_HEIGHT:    600,
    // Camera device files
    NIR_PATH:  "/dev/video0"
}
/**
 * PWM channel
 */
dxDriver.PWM = {
    // Fill light
    WHITE_SUPPLEMENT_CHANNEL:       4,
    WHITE_SUPPLEMENT_PERIOD_NS:     255000,
    WHITE_SUPPLEMENT_DUTY:          255000 * 255 / 255,
    NIR_SUPPLEMENT_CHANNEL:         7,
    NIR_SUPPLEMENT_PERIOD_NS:       255000,
    NIR_SUPPLEMENT_DUTY:            255000 * 255 / 255,
}
/**
 * GPIO pin function enumeration
 */
dxDriver.GPIO_FUNC = {
    GPIO_FUNC_3:    0x03,  //0011, GPIO as function 3 / device 3
    GPIO_OUTPUT0:   0x04,  //0100, GPIO output low  level
    GPIO_OUTPUT1:   0x05  //0101, GPIO output high level
};
/**
 * Door opening button
 */
dxDriver.GPIO_KEY_OPEN = 30
/**
 * Door magnetic status
 */
dxDriver.GPIO_KEY_SEN = 48
export default dxDriver
vf205_access/dxmodules/dxEid.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
import { eidClass } from './libvbar-m-dxeid.so'
const eidObj = new eidClass();
const eid = {
    /**
     * @brief  äº‘证激活
     * @param {string} sn       è®¾å¤‡sn
     * @param {string} version  ä¸šåŠ¡è‡ªå®šä¹‰ç‰ˆæœ¬å·
     * @param {string} mac      è®¾å¤‡mac地址
     * @param {string} codeMsg  äº‘证激活码数据
     * @returns
     */
    active: function(sn, version, mac, codeMsg){
        if(!sn){
            throw("sn should not be null or empty")
        }
        if(!version){
            throw("version should not be null or empty")
        }
        if(!mac){
            throw("mac should not be null or empty")
        }
        if(!codeMsg){
            throw("codeMsg should not be null or empty")
        }
        return eidObj.active(sn, version, mac, codeMsg);
    },
    /**
     * @brief   èŽ·å–ä¿¡æ¯
     */
    getInfo: function(){
        if(data == null || data.length < 1){
            throw("data should not be null or empty")
        }
        return eidObj.getInfo(data)
    },
    /**
     * @brief   èŽ·å–ç‰ˆæœ¬å·
     */
    getVersion: function(){
        return eidObj.getVersion()
    },
}
export default eid;
vf205_access/dxmodules/dxEventBus.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,167 @@
//build:20240628
//事件总线,利用quickjs的worker间数据通信来实现线程之间发送事件通知。
//worker和worker之间不能直接通信,需要通过parent(主线程)来转发,所以需要实现5种可能性的事件通知
//1. worker1--->parent--->worker2
//2. worker3--->parent
//3. parent--->worker4
//4. parent<-->parent
//5. worker5<--->worker5,也会通过parent转
//组件依赖 dxLogger,dxCommon
import std from './dxStd.js'
import logger from './dxLogger.js'
import * as os from "os";
//-------------------------variable--------------------
const bus = {}
const all = {}
const subs = {}
const isMain = (os.Worker.parent === undefined)
bus.id = isMain ? '__main' : null
/**
 * åœ¨æ€»çº¿ä¸Šå¯åŠ¨ä¸€ä¸ªworker,给它定义一个唯一的id标识
 * å› ä¸ºworker只能通过主线程创建,所以newWorker函数也只能在主线程里执行
 * æ³¨æ„: worker对应的文件里不能包含while(true)这种死循环,否则就收不到message,可以用setInteval来实现循环
 * @param {string} id worker的唯一标识,不能为空
 * @param {object} file worker对应的文件名,绝对路径,通常以'/app/code/src'开始
 */
bus.newWorker = function (id, file) {
    if (!id) {
        throw new Error("eventbus newWorker:'id' should not be empty")
    } if (!file) {
        throw new Error("eventbus newWorker:'file' should not be empty")
    }
    if (!isMain) {
        throw new Error("evnetbus newWorker should be invoke in main thread")
    }
    if (!std.exist(file)) {
        throw new Error("eventbus newWorker: file not found:" + file)
    }
    let content = std.loadFile(file) + `
import __bus from '/app/code/dxmodules/dxEventBus.js'
__bus.id='${id}'
Object.keys(__bus.handlers).forEach(key => {
    __bus.os.Worker.parent.postMessage({ __sub: key, id: __bus.id })
})
__bus.os.Worker.parent.onmessage = function (e) {
    if(!e.data){
        return
    }
    e = e.data
    if (!e || !e.topic) {
        return
    }
    let fun = __bus.handlers[e.topic]
    if (fun) {
        fun(e.data)
    }
}
    `
    let newfile = file + '_' + id + '.js'
    std.saveFile(newfile, content)
    let worker = new os.Worker(newfile)
    all[id] = worker
    worker.onmessage = function (data) {
        if (data.data) {
            if (data.data.__sub) {
                sub(data.data.__sub, data.data.id)
                return
            }
            //worker发送过来的数据再调用一次主线程的fire,要么主线程自己消费,要么转发到其它worker
            bus.fire(data.data.topic, data.data.data)
        }
    }
}
/**
 * æ ¹æ®id删除对应的worker,这样worker线程就能正常结束
 * @param {string} id
 */
bus.delWorker = function (id) {
    delete all[id]
}
/**
 * è§¦å‘一个事件,这个事件会立刻发送结束,接收到消息的处理如果比较耗时不会影响事件发送的顺序或出现事件丢失
 * åŒæ ·ä¸€ä¸ªäº‹ä»¶å¯ä»¥æœ‰å¤šä¸ªè®¢é˜…者,可以同时通知多个订阅者,同一个topic单位时间内只处理一个事件,
 * åªæœ‰å½“前topic被所有的订阅者处理完之后才允许处理同一topic下一个事件
 *
 * @param {string} topic äº‹ä»¶çš„æ ‡è¯†ã€ä¸»é¢˜
 * @param {*} data äº‹ä»¶é™„带的数据
 */
bus.fire = function (topic, data) {
    if (!topic || (typeof topic) != 'string') {
        throw new Error("eventbus :'topic' should not be null");
    }
    if (isMain) {
        if (subs[topic] && subs[topic].length > 0) {
            for (let i = 0; i < subs[topic].length; i++) {
                const id = subs[topic][i]
                if (id === '__main' && bus.handlers[topic]) {
                    if (Array.isArray(bus.handlers[topic])) {
                        // æ‰§è¡Œæ‰€æœ‰æ³¨å†Œçš„处理函数
                        for (let j = 0; j < bus.handlers[topic].length; j++) {
                            try {
                                bus.handlers[topic][j](data)
                            } catch (error) {
                                logger.error('Error in event handler for topic ' + topic + ': ' + error.message)
                            }
                        }
                    } else {
                        // å…¼å®¹æ—§ç‰ˆæœ¬ï¼Œæ‰§è¡Œå•个处理函数
                        try {
                            bus.handlers[topic](data)
                        } catch (error) {
                            logger.error('Error in event handler for topic ' + topic + ': ' + error.message)
                        }
                    }
                } else {
                    const worker = all[id]
                    if (worker) {
                        worker.postMessage({ topic: topic, data: data })
                    }
                }
            }
        }
    } else {
        os.Worker.parent.postMessage({ topic: topic, data: data })
    }
}
bus.handlers = {}
/**
 * è®¢é˜…一个事件
 * @param {string} topic äº‹ä»¶çš„æ ‡è¯†ã€ä¸»é¢˜ ï¼Œå¿…å¡«
 * @param {function} callback äº‹ä»¶å¤„理的回调函数,必填
 */
bus.on = function (topic, callback) {
    if (!topic || (typeof topic) != 'string') {
        throw new Error("The 'topic' should not be null");
    }
    if (!callback || (typeof callback) != 'function') {
        throw new Error("The 'callback' should be a function");
    }
    sub(topic, bus.id)
    // æ”¯æŒå¤šä¸ªäº‹ä»¶å¤„理函数
    if (!this.handlers[topic]) {
        this.handlers[topic] = []
    }
    if (!Array.isArray(this.handlers[topic])) {
        // å…¼å®¹æ—§ç‰ˆæœ¬ï¼Œå°†å•个函数转换为数组
        this.handlers[topic] = [this.handlers[topic]]
    }
    this.handlers[topic].push(callback)
}
function sub(topic, id) {
    if (isMain) {
        if (!subs[topic]) {
            subs[topic] = []
        }
        if (!subs[topic].includes(id)) {
            subs[topic].push(id)
        }
    } else {
        if (id != null) {
            os.Worker.parent.postMessage({ __sub: topic, id: id })
        }
    }
}
bus.os = os
export default bus
vf205_access/dxmodules/dxFace.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,287 @@
//build: 20250513
//依赖组件:dxDriver,dxStd,dxLogger,dxMap,dxEventBus,dxCommon
import { faceClass } from './libvbar-b-dxface.so'
import dxCommon from './dxCommon.js'
import bus from './dxEventBus.js'
const faceObj = new faceClass();
const face = {}
/**
 * äººè„¸å¤„理初始化
 * @param {object} options é…ç½®å‚数,大部分可以用默认值
 * @param {string} options.rgbPath å¿…填,rgb图像采集设备路径,每种设备有差异,比如DW200对应的值是'/dev/video11', M500对应的'/dev/video0'
 * @param {string} options.nirPath å¿…填,红外图像采集设备路径,每种设备有差异
 * @param {string} options.dbPath æ•°æ®åº“路径,必填
 * @param {number} options.score ç‰¹å¾å€¼å¯¹æ¯”成功所需最低对比得分 ï¼Œéžå¿…填(缺省0.6)
 * @param {number} options.dbMax å†…存中加载的最大特征列表数量,非必填(缺省5000)
 * @param {string} options.mapPath æ ‡å®šç»“果文件路径,非必填
 * @param {string} options.picPath ä¿å­˜å®Œæ•´äººè„¸ç…§ç‰‡è·¯å¾„,非必填
 * @param {string} capturerRgbId å¿…填,rgb取图句柄id
 * @param {string} capturerNirId å¿…填,nir取图句柄id
 * @returns true/false
 */
face.init = function (options, capturerRgbId, capturerNirId) {
    if (options.rgbPath === undefined || options.rgbPath === null || options.rgbPath.length < 1) {
        throw new Error("dxFace.init: 'rgbPath' parameter should not be null or empty")
    }
    if (options.nirPath === undefined || options.nirPath === null || options.nirPath.length < 1) {
        throw new Error("dxFace.init: 'nirPath' parameter should not be null or empty")
    }
    if (capturerRgbId === undefined || capturerRgbId === null || capturerRgbId.length < 1) {
        throw new Error("dxFace.init: 'capturerRgbId' parameter should not be null or empty")
    }
    if (capturerNirId === undefined || capturerNirId === null || capturerNirId.length < 1) {
        throw new Error("dxFace.init: 'capturerNirId' parameter should not be null or empty")
    }
    capturerRgbId = dxCommon.handleId("capturer", capturerRgbId)
    capturerNirId = dxCommon.handleId("capturer", capturerNirId)
    return faceObj.init(options, capturerRgbId, capturerNirId);
}
/**
 * äººè„¸å¤„理去初始化
 * @returns true/false
 */
face.deinit = function () {
    return faceObj.deinit();
}
/**
 * äººè„¸å·¥ä½œæ¨¡å¼
 * @param {number} mode å·¥ä½œæ¨¡å¼ï¼Œå¿…填( 0 äººè„¸è¯†åˆ«æ¨¡å¼ï¼›1 äººè„¸æ³¨å†Œæ¨¡å¼ï¼‰
 * @returns true/false
 */
face.setRecgMode = function (mode) {
    if (mode === undefined || mode === null) {
        throw new Error("dxFace.setRecgMode: 'mode' parameter should not be null or empty")
    }
    return faceObj.setRecgMode(mode)
}
/**
 * äººè„¸æ³¨å†Œ
 * @param {string} userId     äººå‘˜ID,必填
 * @param {string} feature     ç‰¹å¾å€¼ base64字符串,必填
 * @returns 0:成功/非0:失败
 */
face.addFaceFeatures = function (userId, feature) {
    if (userId === undefined || userId === null) {
        throw new Error("dxFace.addFaceFeatures: 'userId' parameter should not be null or empty")
    }
    if (feature === undefined || feature === null) {
        throw new Error("dxFace.addFaceFeatures: 'feature' parameter should not be null or empty")
    }
    return faceObj.addFaceFeatures(userId, feature)
}
/**
 * äººè„¸ç‰¹å¾å€¼å¯¹æ¯”
 * @param {string} feature     ç‰¹å¾å€¼ base64字符串,必填
 * @param {string} score     æ¯”对阈值,非必填,默认0.6
 * @returns string userId
 */
face.faceFeatureCompare = function (feature, score) {
    if (feature === undefined || feature === null) {
        throw new Error("dxFace.faceFeatureCompare: 'feature' parameter should not be null or empty")
    }
    return faceObj.faceFeatureCompare(feature, score)
}
/**
 * æ›´æ–°é…ç½®
 * @param {object} options æ³¨å†Œå‚数,由注册回调获取,参考face.addFaceFeatures方法
 * @returns true/false
 */
face.faceUpdateConfig = function (options) {
    if (options === null || options === undefined) {
        throw new Error("dxFace.faceUpdateConfig: 'options' parameter should not be null or empty")
    }
    return faceObj.faceUpdateConfig(options)
}
/**
 * äººè„¸åˆ é™¤
 * @param {string} userId äººå‘˜ID,必填
 * @returns true/false
 */
face.deleteFaceFeatures = function (userId) {
    if (userId === null || userId === undefined) {
        throw new Error("dxFace.deleteFaceFeatures: 'userId' parameter should not be null or empty")
    }
    return faceObj.deleteFaceFeatures(userId)
}
/**
 * æ ¹æ®ç…§ç‰‡è·¯å¾„å’ŒuserId注册人脸
 * @param {string} userId     ç”¨æˆ·id,必填
 * @param {string} picPath    ç…§ç‰‡è·¯å¾„,必填
 * @returns true/false
 */
face.registerFaceByPicFile = function (userId, picPath) {
    if (userId === undefined || userId === null || userId.length < 1) {
        throw new Error("dxFace.registerFaceByPicFile: 'userId' parameter should not be null or empty")
    }
    if (picPath === undefined || picPath === null || picPath.length < 1) {
        throw new Error("dxFace.registerFaceByPicFile: 'picPath' parameter should not be null or empty")
    }
    return faceObj.registerFaceByPicFile(userId, picPath)
}
/**
 * äººè„¸çº¿ç¨‹å¯ç”¨/禁用,功能开关
 * @param {bool} en å¯ç”¨ã€ç¦ç”¨ï¼Œå¿…å¡«
 * @returns true/false
 */
face.faceSetEnable = function (en) {
    if (en === undefined || en === null) {
        throw new Error("dxFace.faceSetEnable: 'en' parameter should not be null or empty")
    }
    return faceObj.detectSetEnable(en ? 0 : -1)
}
/**
 * é‡ç½®äººè„¸çŠ¶æ€
 * @returns
 */
face.resetFaceStatus = function () {
    return faceObj.resetFaceStatus()
}
/**
 * æ¸…空人脸数据
 * @returns true/false
 */
face.faceFeaturesClean = function () {
    return faceObj.faceFeaturesClean()
}
/**
 * èŽ·å–å±å¹•äº®åº¦
 * @returns number å±å¹•亮度
 */
face.getDisplayBacklight = function () {
    return faceObj.getDisplayBacklight()
}
/**
 * è®¾ç½®å±å¹•亮度
 * @param {number} light å±å¹•亮度,必填
 * @returns true/false
 */
face.setDisplayBacklight = function (light) {
    if (light === undefined || light === null) {
        throw new Error("dxFace.setDisplayBacklight: 'light' parameter should not be null or empty")
    }
    return faceObj.setDisplayBacklight(light)
}
/**
 * èŽ·å–å±å¹•çŠ¶æ€
 * @returns number 0-禁用 1-启用
 */
face.getEnableStatus = function () {
    return faceObj.getEnableStatus()
}
/**
 * è®¾ç½®å±å¹•状态
 * @param {number} enable 0-禁用 1-启用
 * @returns true/false
 */
face.setEnableStatus = function (enable) {
    if (enable === undefined || enable === null) {
        throw new Error("dxFace.setEnableStatus: 'enable' parameter should not be null or empty")
    }
    return faceObj.setEnableStatus(enable)
}
/**
/**
 * èŽ·å–å±å¹•çŠ¶æ€
 * @returns number 0-NORMAL 1-STANDBY
 */
face.getPowerMode = function () {
    return faceObj.getPowerMode()
}
/**
 * è®¾ç½®å±å¹•状态
 * @param {number} mode 0-NORMAL 1-STANDBY
 * @returns true/false
 */
face.setPowerMode = function (mode) {
    if (mode === undefined || mode === null) {
        throw new Error("dxFace.setPowerMode: 'mode' parameter should not be null or empty")
    }
    return faceObj.setPowerMode(mode)
}
/**
 * èŽ·å–çŽ¯å¢ƒå…‰
 * @returns number çŽ¯å¢ƒå…‰äº®åº¦
 */
face.getEnvBrightness = function () {
    return faceObj.getEnvBrightness()
}
/**
 * èŽ·å–äººè„¸åæ ‡
 * @returns string äººè„¸åæ ‡JSON
 */
face.getTrackingBox = function () {
    return faceObj.getTrackingBox()
}
/**
 * æŸ¥è¯¢ç”¨æˆ·æ€»æ•°
 * @returns number
 */
face.selectCount = function () {
    return faceObj.selectFaceFeatures(1)
}
/**
 * æŸ¥è¯¢æ‰€æœ‰userId
 * @returns JSON
 */
face.selectAll = function () {
    return faceObj.selectFaceFeatures(2)
}
/**
 * åˆ¤æ–­ç”¨æˆ·æ˜¯å¦å­˜åœ¨
 * @returns true/false
 */
face.userExist = function (userId) {
    return faceObj.selectFaceFeatures(3, userId)
}
/**
 * åˆ¤æ–­face消息队列是否为空
 * @returns true/false
 */
face.msgIsEmpty = function () {
    return faceObj.msgIsEmpty()
}
/**
 * ä»Žface消息队列中读取数据
 * @returns json
 */
face.msgReceive = function () {
    return JSON.parse(faceObj.msgReceive());
}
face.RECEIVE_MSG = '__face__MsgReceive'
/**
 * ç”¨äºŽç®€åŒ–face组件微光通信协议的使用,把face封装在这个worker里,使用者只需要订阅eventbus的事件就可以监听face
 */
face.run = function () {
    let workerFile = '/app/code/dxmodules/faceWorker.js'
    bus.newWorker('__face', workerFile)
}
/**
 * å¦‚æžœface单独一个线程,可以直接使用run函数,会自动启动一个线程,
 * å¦‚果想加入到其他已有的线程,可以使用以下封装的函数
 */
face.worker = {
    //在while循环前
    beforeLoop: function (options) {
        // äººè„¸ç®—法初始化
        face.init(options, options.capturerRgbId, options.capturerNirId)
    },
    //在while循环里
    loop: function () {
        if (!face.msgIsEmpty()) {
            let res = face.msgReceive();
            bus.fire(face.RECEIVE_MSG, res)
        }
    }
}
export default face;
vf205_access/dxmodules/dxGpio.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
// build : 20240524
// gpio è¾“出,只能输出2种状态,高电平/低电平,如果接入继电器,则高电平是开,低电平是关
import { gpioClass } from './libvbar-b-dxgpio.so'
const gpioObj = new gpioClass();
const gpio = {}
/**
 * åˆå§‹åŒ–,只需要执行一次即可
 * @returns true/false
 */
gpio.init = function () {
    return gpioObj.init();
}
/**
 * é‡Šæ”¾gpio资源
 * @returns true/false
 */
gpio.deinit = function () {
    return gpioObj.exit();
}
/**
 * ç”³è¯·gpio,每个gpio只需要申请一次
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @returns true/false
 */
gpio.request = function (gpio_) {
    let res = gpioObj.request(gpio_)
    if (!res) {
        return res
    }
    gpioObj.setFunc(gpio_, 0x04);
    return true
}
/**
 * é‡Šæ”¾æŒ‡å®šgpio
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @returns true/false
 */
gpio.free = function (gpio_) {
    return gpioObj.free(gpio_);
}
/**
 * æŒ‡å®šgpio输出高/低电平
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @param {number} value åªèƒ½æ˜¯1和0,1表示高电平,0表示低电平,缺省是高电平,必填
 * @returns true/false
 */
gpio.setValue = function (gpio_, value) {
    return gpioObj.setValue(gpio_, value);
}
/**
 * èŽ·å–æŒ‡å®šgpio当前的输出 ï¼šé«˜/低电平
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @returns 1和0,1表示高电平,0表示低电平
 */
gpio.getValue = function (gpio_) {
    return gpioObj.getValue(gpio_);
}
/**
 * ç”³è¯·gpio,每个gpio只需要申请一次
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @returns true/false
 */
gpio.requestGpio = function (gpio_) {
    let res = gpioObj.request(gpio_)
    return res
}
/**
 * è®¾ç½®gpio功能
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @param {number} gpio功能属性,不同的设备不同的功能属性,必填
 * @returns true/false
 */
gpio.setFuncGpio = function (gpio_, func) {
    let res = gpioObj.setFunc(gpio_, func)
    return res
}
/**
 * è®¾ç½®æŒ‡å®šgpio上拉状态
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @param {number} state ä¸Šæ‹‰çŠ¶æ€ï¼Œå¿…å¡«
 * @returns true/false
 */
gpio.setPullState = function (gpio_, state) {
    return gpioObj.setPullState(gpio_, state);
}
/**
 * èŽ·å–æŒ‡å®šgpio上拉状态
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @returns ä¸Šæ‹‰çŠ¶æ€(int)
 */
gpio.getPullState = function (gpio_) {
    return gpioObj.getPullState(gpio_);
}
/**
 * è®¾ç½®æŒ‡å®šgpio的驱动能力
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @param {number} strength èƒ½åŠ›ï¼Œå¿…å¡«
 * @returns true/false
 */
gpio.setDriveStrength = function (gpio_, strength) {
    return gpioObj.setDriveStrength(gpio_, strength);
}
/**
 * èŽ·å–æŒ‡å®šgpio的驱动能力
 * @param {number} gpio的标识,不同的设备不同的标识,必填
 * @returns èƒ½åŠ›(int)
 */
gpio.getDriveStrength = function (gpio_) {
    return gpioObj.getDriveStrength(gpio_);
}
export default gpio;
vf205_access/dxmodules/dxGpioKey.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
//build 20240524
//接受 gpio çš„输入
//依赖组件: dxLogger,dxDriver,dxEventBus
import { gpioKeyClass } from './libvbar-m-dxkey.so'
import bus from './dxEventBus.js'
import * as os from "os";
const gpioKeyObj = new gpioKeyClass();
const gpioKey = {}
/**
 * gpioKey åˆå§‹åŒ–
 * @returns true:成功,false:失败
 */
gpioKey.init = function () {
    const res = gpioKeyObj.init()
    if (res) {
        gpioKeyObj.registerCb("gpioKeyCb")
    }
    return res
}
/**
 * gpioKey å–消初始化
 * @returns true:成功,false:失败
 */
gpioKey.deinit = function () {
    gpioKeyObj.unRegisterCb("gpioKeyCb")
    return gpioKeyObj.deinit()
}
/**
 * åˆ¤æ–­gpioKey消息队列是否为空
 * @returns true:成功,false:失败
 */
gpioKey.msgIsEmpty = function () {
    return gpioKeyObj.msgIsEmpty()
}
/**
 * ä»ŽgpioKey消息队列中读取数据
 * @returns json消息对象,格式:{"code":30,"type":1,"value":1}
 */
gpioKey.msgReceive = function () {
    let msg = gpioKeyObj.msgReceive()
    return JSON.parse(msg);
}
gpioKey.RECEIVE_MSG = '__gpioKey__MsgReceive'
/**
 * ç®€åŒ–gpiokey组件的使用,无需轮询去获取数据,数据会通过eventbus发送出去
 * run åªä¼šæ‰§è¡Œä¸€æ¬¡
 * å¦‚果需要实时获取数据,可以订阅 eventbus的事件,事件的topic是GPIO_KEY,事件的内容是类似{"code":30,"type":1,"value":1}
 * å…¶ä¸­code是gpio的标识,表示是那个gpio有输入,value值只能是0,1通常表示低电平和高电平
 * type是事件类型,遵循Linux的标准输入规定,以下列出常用几个:
    (0x01):按键事件,包括所有的键盘和按钮事件。例如,当按下或释放键盘上的一个键时,将报告此类事件。
    (0x05):开关事件,例如笔记本电脑盖的开关可以报告开合状态。
    (Ox11):LED事件,用于控制设备上的LED指示灯,
    (Ox12):声音事件,用于控制设备上的声音输出,
    (0x16):电源事件,可以用于报告电源按钮事件或电池电量低
 *
 */
gpioKey.run = function () {
    bus.newWorker("__gpiokey", '/app/code/dxmodules/gpioKeyWorker.js')
}
/**
 * å¦‚æžœgpioKey单独一个线程,可以直接使用run函数,会自动启动一个线程,
 * å¦‚果想加入到其他已有的线程,可以使用以下封装的函数
 */
gpioKey.worker = {
    //在while循环前
    beforeLoop: function () {
        gpioKey.init()
    },
    //在while循环里
    loop: function () {
        if (!gpioKey.msgIsEmpty()) {
            let res = gpioKey.msgReceive();
            bus.fire(gpioKey.RECEIVE_MSG, res)
        }
    }
}
export default gpioKey;
vf205_access/dxmodules/dxHttp.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,155 @@
// http客户端组件
import { httpClass } from './libvbar-m-dxhttp.so'
const httpObj = new httpClass();
const http = {
    HTTP_METHOD : {
        GET: "GET",
        POST: "POST",
        PUT: "PUT",
        DELETE: "DELETE",
        HEAD: "HEAD",
        OPTIONS: "OPTIONS",
        PATCH: "PATCH"
    },
    HTTP_FORMAT : {
        JSON: "JSON",
        FORM: "FORM",
        URLENCODE: "URLENCODE"
    },
    HTTP_FORM_TYPE : {
        STRING: "STRING",
        FILE: "FILE"
    },
    /**
     * get请求
     * @param {string} url
     * @param {array} headers éžå¿…填,会有默认填充 request headers
     * @param {number} timeout éžå¿…填, è¶…æ—¶æ—¶é—´
     * @returns
     */
    get: function (url, headers, timeout) {
        if (!url) {
            throw new Error("url should not be null or empty")
        }
        return httpObj.request({ method: 0, url: url, headers: headers, timeout: timeout})
    },
    /**
     * post请求,data为json/form表单数组格式
     * @param {string} url
     * @param {array} data
     * @param {array} headers éžå¿…填,会有默认填充 request headers
     * @param {number} timeout éžå¿…填, è¶…æ—¶æ—¶é—´
     * @returns
     */
    post: function (url, data, headers, timeout, format = "JSON") {
        if (!url) {
            throw new Error("url should not be null or empty")
        }
        if (!data) {
            throw new Error("data should not be null or empty")
        }
        if (typeof data != 'string' && format != "FORM") {
            data = JSON.stringify(data)
        }
        if(format == "JSON"){
            return httpObj.request({ method: 1, url: url, data: data, headers: headers, timeout: timeout})
        }else{
            return httpObj.request({ method: 1, url: url, formData: data, headers: headers, timeout: timeout})
        }
    },
    /**
     * put请求,data为json/form表单数组格式
     * @param {string} url
     * @param {array} data
     * @param {array} headers éžå¿…填,会有默认填充 request headers
     * @param {number} timeout éžå¿…填, è¶…æ—¶æ—¶é—´
     * @returns
     */
    put: function (url, data, headers, timeout, format = "JSON") {
        if (!url) {
            throw new Error("url should not be null or empty")
        }
        if (!data) {
            throw new Error("data should not be null or empty")
        }
        if (typeof data != 'string' && format != "FORM") {
            data = JSON.stringify(data)
        }
        if(format == "JSON"){
            return httpObj.request({ method: 2, url: url, data: data, headers: headers, timeout: timeout})
        }else{
            return httpObj.request({ method: 2, url: url, formData: data, headers: headers, timeout: timeout})
        }
    },
    /**
     * delete请求
     * @param {string} url
     * @param {array} headers éžå¿…填,会有默认填充 request headers
     * @param {number} timeout éžå¿…填, è¶…æ—¶æ—¶é—´
     * @returns
     */
    delete: function (url, headers, timeout) {
        if (!url) {
            throw new Error("url should not be null or empty")
        }
        return httpObj.request({ method: 3, url: url, headers: headers, timeout: timeout})
    },
    /**
     * ä¸‹è½½æ–‡ä»¶ï¼Œæœ¬è´¨æ˜¯get请求
     * @param {string} url
     * @param {string} path ç›®æ ‡è·¯å¾„(绝对路径)
     * @param {array} headers éžå¿…填,会有默认填充 request headers
     * @param {number} timeout éžå¿…填, è¶…æ—¶æ—¶é—´
     * @returns ä¸‹è½½æ–‡ä»¶æœ‰å¯èƒ½è¿”回null,但是下载是成功的
     */
    download: function (url, path, headers, timeout) {
        if (!url) {
            throw new Error("url should not be null or empty")
        }
        if (!path) {
            throw new Error("path should not be null or empty")
        }
        return httpObj.request({ method: 0, url: url, headers: headers, download: path, timeout: timeout })
    },
    /**
     * ä¸Šä¼ æ–‡ä»¶ï¼Œæœ¬è´¨æ˜¯post请求
     * @param {string} url
     * @param {string} path æºè·¯å¾„(绝对路径)
     * @returns
     */
    upload: function (url, path) {
        if (!url) {
            throw new Error("url should not be null or empty")
        }
        if (!path) {
            throw new Error("path should not be null or empty")
        }
        return httpObj.request({
            method: 1,
            url: url,
            headers: ["application/x-www-form-urlencoded; charset=UTF-8"],
            upload: path
        })
    },
    /**
     * åŽŸç”Ÿæ–¹å¼
     * å¿…填参数:method(0:get请求,1:post请求)、url
     * å¯é€‰å‚数:headers(字符串数组,覆盖默认header)、download(文件下载地址)、data(请求报文,post请求必填)、timeout(超时时间/ms,缺省:5000)、dns(缺省:"114.114.114.114,8.8.8.8")、upload
     * é»˜è®¤header:Accept-Charset:utf-8、Content-Type:application/json;charset=utf-8、Connection:close
     * @param {object} param json
     *  å¦‚:let param={
                method:0,
                url:"http://192.168.10.122:8000/DW200_1_0.zip",
                download:"/testNet/aaa"
            }
     * @returns
     */
    request: function (param) {
        return httpObj.request(param)
    }
}
export default http;
vf205_access/dxmodules/dxLogger.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
/**
 * dxLogger module
 * To replace the `console.log` function, allowing logs to be viewed in the corresponding VSCode plugin during debugging,
 * with support for three levels of logging: `debug`,`info`, and `error`.
 * Supports printing various data types in JavaScript.
 */
import dxCommon from './dxCommon.js'
const logger = {}
logger.config = {
    level: 0, // default is all,if<0,no print
}
logger.debug = function (...data) {
    if (this.config.level === 0) {
        log("DEBUG ", data)
    }
}
logger.info = function (...data) {
    if ([0, 1].includes(this.config.level)) {
        log("INFO ", data)
    }
}
logger.error = function (...data) {
    if ([0, 1, 2].includes(this.config.level)) {
        log("ERROR ", data)
    }
}
//-----------------------------------private----------------------
function log(level, messages) {
    let message = messages.map(msg => getContent(msg)).join(' ');
    let content = `[${level}${getTime()}]: ${message}`
    dxCommon.systemBrief(`echo '${content}'`)
}
function getContent(message) {
    if (message === undefined) {
        return 'undefined'
    } else if (message === null) {
        return 'null'
    }
    if ((typeof message) == 'object') {
        if (Object.prototype.toString.call(message) === '[object Error]') {
            return message.message + '\n' + message.stack
        }
        return JSON.stringify(message)
    }
    return message
}
function getTime() {
    const now = new Date();
    const year = now.getFullYear();
    const month = ('0' + (now.getMonth() + 1)).slice(-2);
    const day = ('0' + now.getDate()).slice(-2);
    const hours = ('0' + now.getHours()).slice(-2);
    const minutes = ('0' + now.getMinutes()).slice(-2);
    const seconds = ('0' + now.getSeconds()).slice(-2);
    const milliseconds = ('0' + now.getMilliseconds()).slice(-3);
    return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds + '.' + milliseconds;
}
export default logger
vf205_access/dxmodules/dxMap.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
import { mapClass } from './libvbar-m-dxmap.so'
/**
 * build:20240407
 * map ç»„件,可以在内存里读写key/value
 */
const mapObj = new mapClass();
const map = {
    get: function (name) {
        if (!name || name.length == 0) {
            throw new Error("dxMap.get:name should not be null or empty")
        }
        //第一次put会自动创建实例
        return {
            /**
             * @brief   èŽ·å–Map中的所有键,返回一个数组
             */
            keys: function () {
                let all = mapObj.keys(name)
                return all == null ? [] : all
            },
            /**
             * @brief   æ ¹æ®key获取value
             */
            get: function (key) {
                if (!key || key.length < 1) {
                    throw new Error("The 'key' parameter cannot be null or empty")
                }
                // put空字符串,get会是null
                let value = mapObj.get(name, key)
                if (value === undefined || value === null) {
                    value = ""
                }
                return _parseString(value)
            },
            /**
             * @brief   å‘Map中插入键值对
             */
            put: function (key, value) {
                if (!key || key.length < 1) {
                    throw new Error("The 'key' parameter cannot be null or empty")
                }
                if (value == null || value == undefined) {
                    throw new Error("The 'value' parameter cannot be null or empty")
                }
                return mapObj.insert(name, key, _stringifyValue(value))
            },
            /**
             * @brief   æ ¹æ®Key删除键值对
             */
            del: function (key) {
                if (!key || key.length < 1) {
                    throw new Error("The 'key' parameter cannot be null or empty")
                }
                return mapObj.delete(name, key)
            },
            /**
             * ä¸å†ä½¿ç”¨äº†ï¼Œå°±é”€æ¯
             */
            destroy: function () {
                return mapObj.destroy(name)
            },
        }
    }
}
function _stringifyValue(value) {
    const type = typeof value
    if (type === 'string') {
        return value
    }
    if (type === 'number') {
        return '#n#' + value
    }
    if (type === 'boolean') {
        return '#b#' + value
    }
    if (type === 'object') {
        // å¦‚果是对象,进一步判断是否为数组
        if (Array.isArray(value)) {
            return '#a#' + JSON.stringify(value);
        }// else if (value === null) { å‰é¢å·²ç»è§„避了null的情况
        return '#o#' + JSON.stringify(value)
    }
    if (type === 'function') {
        throw new Error("The 'value' parameter should not be function")
    }
}
function _parseString(str) {
    if (str.startsWith('#n#')) {
        // è§£æžæ•°å­—
        const numberStr = str.substring(3);
        return numberStr.includes('.') ? parseFloat(numberStr) : parseInt(numberStr, 10);
    } else if (str.startsWith('#b#')) {
        // è§£æžå¸ƒå°”值
        return str.substring(3) === 'true';
    } else if (str.startsWith('#a#')) {
        // è§£æžæ•°ç»„
        return JSON.parse(str.substring(3));
    } else if (str.startsWith('#o#')) {
        // è§£æžå¯¹è±¡
        return JSON.parse(str.substring(3));
    } else {
        // é»˜è®¤æƒ…况下,将字符串返回
        return str;
    }
}
export default map;
vf205_access/dxmodules/dxMqtt.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,225 @@
//build:20240411
//利用mqtt协议实现和mqtt服务端的通信或通过mqtt broker实现和其它mqtt客户端的通信
//依赖组件 dxMap,dxLogger,dxDriver,dxCommon,dxEventBus,dxNet
import { mqttClass } from './libvbar-m-dxmqtt.so'
import * as os from "os"
import std from './dxStd.js'
import dxMap from './dxMap.js'
import dxCommon from './dxCommon.js'
import bus from './dxEventBus.js'
const map = dxMap.get("default")
const mqttObj = new mqttClass();
const mqtt = {}
/**
 * åˆå§‹åŒ–mqtt相关属性并创建连接,请在worker里使用dxMqtt组件或使用简化函数dxMqtt.run
 * @param {string} mqttAddr mqtt服务地址,必填,以tcp://开头,格式是tcp://ip:port
 * @param {string} clientId å®¢æˆ·ç«¯id,必填,不同的设备请使用不同的客户端id
 * @param {string} username éžå¿…填,mqtt用户名
 * @param {string} password éžå¿…填,mqtt密码
 * @param {string} prefix éžå¿…填,缺省为空字符串,这个表示自动在主题前加上一个前缀
 * @param {number} qos 0,1,2 éžå¿…填,缺省是1. å…¶ä¸­0表示消息最多发送一次,发送后消息就被丢弃;1表示消息至少发送一次,可以保证消息被接收方收到,但是会存在接收方收到重复消息的情况;2表示消息发送成功且只发送一次,资源开销大
 * @param {string} willTopic éžå¿…填,遗嘱主题,通过broker通信的时候设备断开会自动触发一个mqtt遗嘱消息,这个是遗嘱消息的主题
 * @param {string} willMessage éžå¿…填,遗嘱内容,通过broker通信的时候设备断开会自动触发一个mqtt遗嘱消息,这个是遗嘱消息的内容
 * @param {string} id å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 */
mqtt.init = function (mqttAddr, clientId, username, password, prefix = "", qos = 1, willTopic, willMessage, id) {
    if (mqttAddr === undefined || mqttAddr.length === 0) {
        throw new Error("dxMqtt.init: 'mqttAddr' parameter should not be null or empty")
    }
    if (clientId === undefined || clientId.length === 0) {
        throw new Error("dxMqtt.init: 'clientId' parameter should not be null or empty")
    }
    // å¦‚æžœmqttAddr不以tcp://开头,自动添加前缀
    if (!mqttAddr.startsWith('tcp://')) {
        mqttAddr = 'tcp://' + mqttAddr
    }
    let pointer = mqttObj.init(mqttAddr, clientId, username, password, prefix, qos, willTopic, willMessage);
    if (pointer === undefined || pointer === null) {
        throw new Error("dxMqtt.init: mqtt init failed")
    }
    dxCommon.handleId("mqtt", id, pointer)
}
/**
 * é‡æ–°è¿žæŽ¥,比如连接成功后突然网络断开,无需重新init,直接重连即可
 * @param {string} willTopic éžå¿…填,遗嘱主题,通过broker通信的时候设备断开会自动触发一个mqtt遗嘱消息,这个是遗嘱消息的主题
 * @param {string} willMessage éžå¿…填,遗嘱内容,通过broker通信的时候设备断开会自动触发一个mqtt遗嘱消息,这个是遗嘱消息的内容
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 */
mqtt.reconnect = function (willTopic, willMessage, id) {
    let pointer = dxCommon.handleId("mqtt", id)
    return mqttObj.recreate(pointer, willTopic, willMessage);
}
/**
 * è®¢é˜…多主题
 * @param {array} topics å¿…填, è¦è®¢é˜…的主题数组,可以同时订阅多个
 * @param {number} qos éžå¿…填,缺省是1. å…¶ä¸­0表示消息最多发送一次,发送后消息就被丢弃;1表示消息至少发送一次,可以保证消息被接收方收到,但是会存在接收方收到重复消息的情况;2表示消息发送成功且只发送一次,资源开销大
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns
 */
mqtt.subscribes = function (topics, qos, id) {
    if (topics === undefined || topics.length === 0) {
        throw new Error("dxMqtt.subscribes: 'topics' parameter should not be null or empty")
    }
    if (qos === undefined) {
        qos = 1
    }
    let pointer = dxCommon.handleId("mqtt", id)
    return mqttObj.subscribes(pointer, topics, qos);
}
/**
 * åˆ¤æ–­mqtt是否连接,连接成功后如果网络断开,连接也会断开
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns false失败; true成功
 */
mqtt.isConnected = function (id) {
    let pointer = dxCommon.handleId("mqtt", id)
    return mqttObj.isConnected(pointer);
}
/**
 * æŸ¥è¯¢mqtt配置
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns mqtt配置
 */
mqtt.getConfig = function (id) {
    let pointer = dxCommon.handleId("mqtt", id)
    return mqttObj.getConfig(pointer);
}
/**
 * mqtt配置更新
 * @param {object} options é…ç½®å‚数,大部分可以用默认值
 *      @param {string} options.mqttAddr mqtt服务地址,必填,以tcp://开头,格式是tcp://ip:port
 *      @param {string} options.clientId å®¢æˆ·ç«¯id,必填,不同的设备请使用不同的客户端id
 *      @param {string} options.userName éžå¿…填,mqtt用户名
 *      @param {string} options.password éžå¿…填,mqtt密码
 *      @param {string} options.prefix éžå¿…填,缺省为空字符串,这个表示自动在主题前加上一个前缀
 *      @param {number} options.qos 0,1,2 éžå¿…填,缺省是1. å…¶ä¸­0表示消息最多发送一次,发送后消息就被丢弃;1表示消息至少发送一次,可以保证消息被接收方收到,但是会存在接收方收到重复消息的情况;2表示消息发送成功且只发送一次,资源开销大
 *      @param {string} options.ssl éžå¿…填,ssl配置类
 */
mqtt.updateConfig = function (options, id) {
    if (!options) {
        throw new Error("dxMqtt.updateConfig: 'options' parameter should not be null or empty")
    }
    if (options.mqttAddr === undefined || options.mqttAddr.length === 0) {
        throw new Error("dxMqtt.updateConfig: 'options.mqttAddr' parameter should not be null or empty")
    }
    if (options.clientId === undefined || options.clientId.length === 0) {
        throw new Error("dxMqtt.updateConfig: 'options.clientId' parameter should not be null or empty")
    }
    if (options.qos === undefined || options.qos == null) {
        throw new Error("dxMqtt.updateConfig: 'options.qos' parameter should not be null or empty")
    }
    // å¦‚æžœmqttAddr不以tcp://开头,自动添加前缀
    if (!options.mqttAddr.startsWith('tcp://')) {
        options.mqttAddr = 'tcp://' + options.mqttAddr
    }
    let pointer = dxCommon.handleId("mqtt", id)
    let res = mqttObj.setConfig(pointer, options);
    return res;
}
/**
 * å‘送mqtt请求
 * @param {string} topic ä¸»é¢˜ï¼Œå¿…å¡«
 * @param {string} payload æ¶ˆæ¯ä½“内容,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 */
mqtt.send = function (topic, payload, id) {
    if (topic === undefined || topic.length === 0) {
        throw new Error("dxMqtt.send:'topic' parameter should not be null or empty")
    }
    if (payload === undefined || payload.length === 0) {
        throw new Error("dxMqtt.send:'payload' parameter should not be null or empty")
    }
    let pointer = dxCommon.handleId("mqtt", id)
    return mqttObj.sendMsg(pointer, topic, payload);
}
/**
 * æŽ¥æ”¶mqtt数据,需要轮询去获取
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @return mqtt请求数据,结构是: {topic:'主题',payload:'内容'}
 */
mqtt.receive = function (id) {
    let msg = mqttObj.msgReceive(id);
    return JSON.parse(msg);
}
/**
 * åˆ¤æ–­æ˜¯å¦æœ‰æ–°çš„æ•°æ®ï¼Œä¸€èˆ¬å…ˆåˆ¤æ–­æœ‰æ•°æ®åŽå†è°ƒç”¨receive去获取数据
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns false æœ‰æ•°æ®ï¼›true æ²¡æœ‰æ•°æ®
 */
mqtt.msgIsEmpty = function (id) {
    return mqttObj.msgIsEmpty(id);
}
/**
 * é”€æ¯mqtt实例
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 */
mqtt.destroy = function (id) {
    let pointer = dxCommon.handleId("mqtt", id)
    mqttObj.deinit(pointer);
}
mqtt.RECEIVE_MSG = '__mqtt__MsgReceive'
mqtt.CONNECTED_CHANGED = '__mqtt__Connect_changed'
mqtt.RECONNECT = '__mqtt__Reconnect'
/**
 * ç”¨ç®€å•的方式实现mqtt客户端,只需要调用这一个函数就可以实现mqtt客户端,
 * æ”¶åˆ°æ¶ˆæ¯ä¼šè§¦å‘ç»™ dxEventBus发送一个事件,事件的主题是mqtt.RECEIVE_MQTT_MSG,内容是{topic:'',payload:''}格式
 * å¦‚果需要发送消息,直接使用 mqtt.send方法 mqtt发送的数据格式类似: { topic: "sendtopic1", payload: JSON.stringify({ a: i, b: "ssss" }) }
 * mqtt的连接状态发生变化会触发给 dxEventBus发送一个事件,事件的主题是mqtt.CONNECTED_CHANGED,内容是'connected'或者'disconnect'
 * mqtt需要有网络,所以必须在使用之前确保dxNet组件完成初始化
 * @param {object} options mqtt相关参数,必填
 *      @param {string} options.mqttAddr mqtt服务地址,必填,以tcp://开头,格式是tcp://ip:port
 *      @param {string} options.clientId å®¢æˆ·ç«¯id,必填,不同的设备请使用不同的客户端id
 *      @param {string} options.username éžå¿…填,mqtt用户名
 *      @param {string} options.password éžå¿…填,mqtt密码
 *      @param {string} options.prefix éžå¿…填,缺省为空字符串,这个表示自动在主题前加上一个前缀
 *      @param {number} options.qos 0,1,2 éžå¿…填,缺省是1. å…¶ä¸­0表示消息最多发送一次,发送后消息就被丢弃;1表示消息至少发送一次,可以保证消息被接收方收到,但是会存在接收方收到重复消息的情况;2表示消息发送成功且只发送一次,资源开销大
 *      @param {string} options.willTopic éžå¿…填,遗嘱主题,通过broker通信的时候设备断开会自动触发一个mqtt遗嘱消息,这个是遗嘱消息的主题
 *      @param {string} options.willMessage éžå¿…填,遗嘱内容,通过broker通信的时候设备断开会自动触发一个mqtt遗嘱消息,这个是遗嘱消息的内容
 *      @param {array}  options.subs éžå¿…填,要订阅的主题组
 *      @param {string} options.id  å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 */
mqtt.run = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxmqtt.run:'options' parameter should not be null or empty")
    }
    if (options.id === undefined || options.id === null || typeof options.id !== 'string') {
        // å¥æŸ„id
        options.id = ""
    }
    let oldfilepre = '/app/code/dxmodules/mqttWorker'
    let content = std.loadFile(oldfilepre + '.js').replace("{{id}}", options.id)
    let newfile = oldfilepre + options.id + '.js'
    std.saveFile(newfile, content)
    let init = map.get("__mqtt__run_init" + options.id)
    if (!init) {//确保只初始化一次
        map.put("__mqtt__run_init" + options.id, options)
        bus.newWorker(options.id || "__mqtt", newfile)
    }
}
/**
 * èŽ·å–å½“å‰mqtt连接的状态
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns 'connected' æˆ–者 'disconnected'
 */
mqtt.getConnected = function (id) {
    if (id == undefined || id == null) {
        id = ""
    }
    return mqtt.isConnected(id) ? "connected" : "disconnected"
}
export default mqtt;
vf205_access/dxmodules/dxNet.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,341 @@
//build:20240626
//通过这个组件来配置网络和监听网络状态变化
//依赖组件: dxMap,dxLogger,dxDriver,dxEventBus
import dxMap from './dxMap.js'
import bus from './dxEventBus.js'
import { netClass } from './libvbar-m-dxnet.so'
const netObj = new netClass();
const map = dxMap.get("default")
const net = {}
net.TYPE = {
    "UNKNOWN": 0,
    "ETHERNET": 1,
    "WIFI": 2,
    "4G": 4
}
net.DHCP = {
    STATIC: 1,
    DYNAMIC: 2,
    WIFI_AP: 3 //WiFi AP热点模式
}
/**
 * ç½‘络初始化,wifi或以太网,如果连不上网络会自动不断的重试,无需重复init。但是init后需要轮询去获取网络状态(通过msgReceive)
 * ä¹Ÿå¯ä»¥ç›´æŽ¥ä½¿ç”¨ç®€åŒ–方法dxNet.run,无需轮询
 * @param {object} options åˆå§‹åŒ–网络的参数
 *       @param {number} type å¿…å¡« ç½‘络类型,参考net.TYPE枚举
 *       @param {number} dhcp å¿…å¡« DHCP,参考net.DHCP枚举
 *       @param {string} macAddr å¿…å¡« mac地址,缺省使用dxCommon.getUuid2mac()方法来获取mac地址
 *       @param {string} ip éžå¿…å¡« ç½‘络ip地址
 *       @param {string} gateway éžå¿…å¡« ç½‘关地址
 *       @param {string} netmask éžå¿…å¡« å­ç½‘掩码
 *       @param {string} dns0 éžå¿…å¡« DNS地址
 *       @param {string} dns1 éžå¿…å¡« å¤‡é€‰DNS地址
 * @returns
 */
net.init = function (options) {
    let ret = netObj.init()
    if (!ret) {
        return false
    }
    if (!options) {
        throw new Error("dxNet.init: 'options' parameter should not be null or empty")
    }
    ret = netObj.setMasterCard(options.type)
    if (!ret) {
        return false
    }
    netObj.setMacaddr(options.type, options.macAddr)
    ret = netObj.cardEnable(options.type, true)
    if (!ret) {
        return false
    }
    if (options.dhcp === 1) {
        return netObj.setModeByCard(options.type, 1, {
            ip: options.ip,
            gateway: options.gateway,
            netmask: options.netmask,
            dns0: options.dns0,
            dns1: options.dns1,
        })
    } else if (options.dhcp === 2) {
        return netObj.setModeByCard(options.type, options.dhcp)
    }
    return false
}
/**
 * èŽ·å–Mac地址
 * @param {number} type  å¿…å¡« ç½‘络类型,参考net.TYPE枚举
 * @returns   Mac地址
 */
net.getMacaddr = function (type) {
    return netObj.getMacaddr(type)
}
/**
 * è®¾ç½®Mac地址
 * @param {number} type  å¿…å¡« ç½‘络类型,参考net.TYPE枚举
 * @param {string} addr  Mac地址,必填,格式类似 b2:a1:63:3f:99:b6
 * @returns   true:成功 ä¸»ç½‘卡类型,false å¤±è´¥
 */
net.setMacaddr = function (type, addr) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setMacaddr:'type' paramter should not be null or empty")
    }
    if (addr === null || addr === undefined || addr.length < 1) {
        throw new Error("dxNet.setMacaddr:'addr' paramter should not be null or empty")
    }
    return netObj.setMacaddr(type, addr)
}
/**
 * ä½¿èƒ½ç½‘卡,并添加到网络管理模块
 * @param {number} type  å¿…å¡« ç½‘络类型,参考net.TYPE枚举
 * @param {boolean} on  å¼€å¯/关闭
 * @returns   0:成功 <0 å¤±è´¥
 */
net.cardEnable = function (type, on) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.cardEnable: 'type' parameter should not be null or empty")
    }
    if (on === null) {
        throw new Error("dxNet.cardEnable: 'on' parameter should not be null or empty")
    }
    return netObj.cardEnable(type, on)
}
/**
 * net网络销毁
 * @return true:成功,false å¤±è´¥
 */
net.exit = function () {
    return netObj.exit()
}
/**
 * è®¾ç½®æŒ‡å®šç½‘卡的模式及对应参数网络参数
 * @param {number} type   å¿…å¡« ç½‘络类型,参考net.TYPE枚举
 * @param {number} mode   å¿…å¡« DHCP,参考net.DHCP枚举
 * @param param  ç½‘络参数
 * @return true:成功,false å¤±è´¥
 */
net.setModeByCard = function (type, mode, param) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setModeByCard: 'type' parameter should not be null or empty")
    }
    if (mode === null) {
        throw new Error("dxNet.setModeByCard:'mode' parameter should not be null or empty")
    }
    return netObj.setModeByCard(type, mode, param)
}
/**
 * èŽ·å–æŒ‡å®šç½‘å¡çš„æ¨¡å¼åŠå¯¹åº”å‚æ•°ç½‘ç»œå‚æ•°
 * @param {number} type  å¿…å¡« ç½‘络类型,参考net.TYPE枚举
 * @returns   å¦‚果是静态网络模式,就会返回ip、网关等信息
 */
net.getModeByCard = function (type) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.getModeByCard: 'type' parameter should not be null or empty")
    }
    return netObj.getModeByCard(type)
}
/**
 * è®¾ç½®ä¸»ç½‘卡,应用程序网络状态由次网卡决定
 * @param {number} type  å¿…å¡« ç½‘络类型,参考net.TYPE枚举
 * @returns    true:成功,false å¤±è´¥
 */
net.setMasterCard = function (type) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setMasterCard: 'type' parameter should not be null or empty")
    }
    return netObj.setMasterCard(type)
}
/**
 * èŽ·å–ä¸»ç½‘å¡
 * @returns   >0:成功 ä¸»ç½‘卡类型,<0 å¤±è´¥
 */
net.getMasterCard = function () {
    return netObj.getMasterCard()
}
/**
 * èŽ·å–ç½‘ç»œçŠ¶æ€ ç±»ä¼¼{"status":4,"connected":true} ,其中status如下
 *  0,    æœªåˆå§‹æ€
    1,    ç½‘卡处于关闭状态
    2,    ç½‘卡处于打开状态
    3,    ç½‘线已插入或者wifi已连接ssid ä½†æœªåˆ†é…ip
    4,    å·²æˆåŠŸåˆ†é…ip
    5     å·²è¿žæŽ¥æŒ‡å®šæœåŠ¡æˆ–è€…é€šè¿‡æµ‹è¯•å¯ä»¥è¿žæŽ¥åˆ°å¹¿åŸŸç½‘
 * @returns   ç½‘络状态
 */
net.getStatus = function () {
    let status = netObj.getStatus()
    return { "status": status, "connected": status >= 4 }
}
/**
 * è®¾ç½®ç½‘络状态
 * @param {number} status ç½‘络状态,必填
 * @returns true:成功,false å¤±è´¥
 */
net.setStatus = function (status) {
    if (status === null || status === undefined) {
        throw new Error("dxNet.setStatus: 'status' parameter should not be null or empty")
    }
    return netObj.setStatus(status)
}
/**
 * é‡æ–°ä½¿èƒ½ç½‘卡
 * @param {number} type         ç½‘络类型,必填
 * @param {number} phy_reset    å¼€å¯/关闭,必填
 * @returns true:成功,false å¤±è´¥
 */
net.netCardReset = function (type, phy_reset) {
    if (type === null || type === undefined) {
        throw new Error("dxNet.setStatus: 'status' parameter should not be null or empty")
    }
    if (phy_reset === null || phy_reset === undefined) {
        throw new Error("dxNet.setStatus: 'status' parameter should not be null or empty")
    }
    return netObj.netCardReset(type, phy_reset)
}
/**
 * èŽ·å–wifi列表
 * @param {*} timeout å¿…å¡«
 * @param {*} interval å¿…å¡«
 * @returns wifi列表
 */
net.netGetWifiSsidList = function (timeout, interval) {
    if (timeout === null || timeout === undefined) {
        throw new Error("dxNet.netGetWifiSsidList: 'timeout' parameter should not be null or empty")
    }
    if (interval === null) {
        throw new Error("dxNet.netGetWifiSsidList: 'interval' parameter should not be null or empty")
    }
    return netObj.netGetWifiSsidList(timeout, interval)
}
/**
 * è¿žæŽ¥åˆ°wifi
 * @param {*} ssid å¿…å¡«
 * @param {*} psk å¿…å¡«
 * @param {*} params å¿…å¡«
 * @returns
 */
net.netConnectWifiSsid = function (ssid, psk, params) {
    if (ssid === null) {
        throw new Error("dxNet.netConnectWifiSsid: 'ssid' parameter should not be null or empty")
    }
    if (psk === null) {
        throw new Error("dxNet.netConnectWifiSsid: 'psk' parameter should not be null or empty")
    }
    if (params === null) {
        throw new Error("dxNet.netConnectWifiSsid: 'params' parameter should not be null or empty")
    }
    return netObj.netConnectWifiSsid(ssid, psk, params)
}
/**
 * èŽ·å–å·²ä¿å­˜çš„çƒ­ç‚¹åˆ—è¡¨
 * @returns  å·²ä¿å­˜çš„热点列表
 */
net.netGetWifiSavedList = function () {
    return netObj.netGetWifiSavedList()
}
/**
 * æ–­å¼€å½“前连接的wifi热点
 * @returns
 */
net.netDisconnetWifi = function () {
    return netObj.netDisconnetWifi()
}
/**
 * èŽ·å–å½“å‰çƒ­ç‚¹çš„ä¿¡æ¯
 * @param timeout å¿…å¡«
 * @returns
 */
net.netGetCurrentWifiInfo = function (timeout) {
    if (timeout === null) {
        throw new Error("dxNet.netGetCurrentWifiInfo: 'timeout' parameter should not be null or empty")
    }
    return netObj.netGetCurrentWifiInfo(timeout)
}
/**
 * æ£€æŸ¥æ¶ˆæ¯é˜Ÿåˆ—是否为空
 * @returns true为空 false不为空
 */
net.msgIsEmpty = function () {
    return netObj.msgIsEmpty()
}
/**
 * ä»Žæ¶ˆæ¯é˜Ÿåˆ—中取网络当前状态数据,返回结构类似{"type":1,"status":4,"connected":true}
 * å…¶ä¸­type参考net.TYPE枚举
 * å…¶ä¸­status的值说明如下:
 *  0,    æœªåˆå§‹æ€
    1,    ç½‘卡处于关闭状态
    2,    ç½‘卡处于打开状态
    3,    ç½‘线已插入或者wifi已连接ssid ä½†æœªåˆ†é…ip
    4,    å·²æˆåŠŸåˆ†é…ip
    5     å·²è¿žæŽ¥æŒ‡å®šæœåŠ¡æˆ–è€…é€šè¿‡æµ‹è¯•å¯ä»¥è¿žæŽ¥åˆ°å¹¿åŸŸç½‘
 * @returns   å­—符串类型的消息数据
 */
net.msgReceive = function () {
    let res = JSON.parse(netObj.msgReceive());
    if (res.status >= 4) {
        res.connected = true
    } else {
        res.connected = false
    }
    return res
}
net.STATUS_CHANGE = '__netstatus__changed'
/**
 * ç®€åŒ–网络组件的使用,无需轮询去获取网络状态,网络的状态会通过eventBus发送出去
 * run åªä¼šæ‰§è¡Œä¸€æ¬¡ï¼Œæ‰§è¡Œä¹‹åŽç½‘络基本配置不能修改
 * å¦‚果需要实时获取网络状态变化,可以订阅 eventBus的事件,事件的topic是net.STATUS_CHANGE,事件的内容是类似{"type":1,"status":4,"connected":true}
 * å…¶ä¸­type参考net.TYPE枚举
 * å…¶ä¸­status的值说明如下:
 *  0,    æœªåˆå§‹æ€
    1,    ç½‘卡处于关闭状态
    2,    ç½‘卡处于打开状态
    3,    ç½‘线已插入或者wifi已连接ssid ä½†æœªåˆ†é…ip
    4,    å·²æˆåŠŸåˆ†é…ip
    5     å·²è¿žæŽ¥æŒ‡å®šæœåŠ¡æˆ–è€…é€šè¿‡æµ‹è¯•å¯ä»¥è¿žæŽ¥åˆ°å¹¿åŸŸç½‘
 * @param {object} options å‚考init的options描述
 */
net.run = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxnet.run:'options' parameter should not be null or empty")
    }
    let workerFile = '/app/code/dxmodules/netWorker.js'
    let init = map.get("__net__run_init")
    if (!init) {//确保只初始化一次
        map.put("__net__run_init", options)
        bus.newWorker('__net', workerFile)
    }
}
/**
 * å¦‚æžœnet单独一个线程,可以直接使用run函数,会自动启动一个线程,
 * å¦‚果想加入到其他已有的线程,可以使用以下封装的函数
 */
net.worker = {
    //在while循环前
    beforeLoop: function (options) {
        net.init(options)
    },
    //在while循环里
    loop: function () {
        if (!net.msgIsEmpty()) {
            let res = net.msgReceive();
            if (res.status >= 4) {
                res.connected = true
            } else {
                res.connected = false
            }
            bus.fire(net.STATUS_CHANGE, res)
        }
    }
}
export default net;
vf205_access/dxmodules/dxNfc.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,409 @@
//build:20240715
//通过这个组件来读取卡,包括M1卡,psam卡之类的
//依赖组件: dxDriver,dxMap,dxLogger,dxDriver,dxCommon,dxEventBus
import { nfcClass } from './libvbar-p-dxnfc.so'
import dxCommon from './dxCommon.js'
import bus from './dxEventBus.js'
import dxMap from './dxMap.js'
const nfcObj = new nfcClass();
const map = dxMap.get("default")
const nfc = {}
/**
 * NFC åˆå§‹åŒ–
 * @param {number} useEid éžå¿…填,是否使用云证 0不使用 1使用
 */
nfc.init = function (useEid = 0) {
    let pointer = nfcObj.init(useEid)
    if (pointer === undefined || pointer === null) {
        throw new Error("nfc.init: init failed")
    }
    dxCommon.handleId("nfc", 'nfcid', pointer)
}
/**
 * NFC æ™®é€šå¡æ³¨å†Œå›žè°ƒ
 */
nfc.cbRegister = function (callback) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    return nfcObj.cbRegister(pointer, "nfc_cb", 1, callback)
}
/**
 * NFC PSAM卡注册回调
 */
nfc.psamCbRegister = function (callback) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    return nfcObj.nfcPsamCheckVgcardCallback(pointer, callback)
}
/**
 * NFC å–消初始化
 */
nfc.deinit = function () {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    let ret = nfcObj.cbUnregister(pointer, "nfc_cb")
    if (ret === false) {
        throw new Error("nfc.cbUnregister: cbUnregister failed")
    }
    return nfcObj.deinit(pointer)
}
/**
 * NFC å¡ä¿¡æ¯åˆ›å»º
 * @param {number} cardType å¡èŠ¯ç‰‡ç±»åž‹(原厂定义)
 * @param {ArrayBuffer} cardId å¡å·
 * @param {number} type å¡ç±»åž‹(我们自己定义的)
 * @returns cardInfo(pointer)
 */
nfc.cardInfoCreate = function (cardType, cardId, type) {
    if (!cardType) {
        throw new Error("cardInfoCreate:cardType should not be null or empty")
    }
    if (!cardId) {
        throw new Error("cardInfoCreate:cardId should not be null or empty")
    }
    if (!type) {
        throw new Error("cardInfoCreate:type should not be null or empty")
    }
    return nfcObj.cardInfoCreate(cardType, cardId, type);
}
/**
 * NFC å¡ä¿¡æ¯é”€æ¯
 * @param {pointer} cardInfo å¡ä¿¡æ¯
 * @returns
 */
nfc.cardInfoDestory = function (cardInfo) {
    if (!cardInfo) {
        throw new Error("cardInfoDestory:cardInfo should not be null or empty")
    }
    return nfcObj.cardInfoDestory(cardInfo);
}
/**
 * NFC å¡ä¿¡æ¯å¤åˆ¶
 * @param {pointer} cardInfo å¡ä¿¡æ¯
 * @returns cardInfo(pointer)
 */
nfc.cardInfoCopy = function (cardInfo) {
    if (cardInfo == null) {
        throw new Error("cardInfoCopy:cardInfo should not be null or empty")
    }
    return nfcObj.cardInfoCopy(cardInfo);
}
/**
 * NFC åˆ¤æ–­æ˜¯å¦æœ‰å¡
 * @returns bool
 */
nfc.isCardIn = function () {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    return nfcObj.isCardIn(pointer);
}
/**
 * NFC è¯»M1卡扇区
 * @param {number} taskFlg ä»»åŠ¡æ ‡å¿—ï¼š
 *                    0x00->AUTO å‘ŠçŸ¥æ‰«ç å™¨è¯¥æŒ‡ä»¤å¯å•独执行,无指令间的依赖关系。
 *                    0x01->START å‘ŠçŸ¥æ‰«ç å™¨å¼€å§‹å¯¹å¡æ“ä½œæˆ–对卡操作尚未结束,且指令间可能存在依赖关系。
 *                    0x02->FINISH å‘ŠçŸ¥æ‰«ç å™¨æœ¬æ¡æŒ‡ä»¤æ˜¯æ“ä½œå¡çš„æœ€åŽä¸€æ¡æŒ‡ä»¤ï¼Œå°†å¡ç‰‡æ“ä½œçŽ¯å¢ƒæ¢å¤åˆ°é»˜æ€ã€‚
 * @param {number} secNum æ‰‡åŒºå·
 * @param {number} logicBlkNum å—号(在扇区内的逻辑号0~3)
 * @param {number} blkNums å—æ•°
 * @param {array} key å¯†é’¥, é•¿åº¦6bytes
 * @param {number} keyType å¯†é’¥ç±»åž‹: A:0x60 B:0x61
 * @returns Array è¯»å–结果 undefined:失败
 */
nfc.m1cardReadSector = function (taskFlg, secNum, logicBlkNum, blkNums, key, keyType) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    _validate('m1cardReadSector', taskFlg, secNum, logicBlkNum, blkNums, key, keyType, ' ')
    return nfcObj.m1cardReadSector(pointer, taskFlg, secNum, logicBlkNum, blkNums, key, keyType);
}
/**
 * NFC è¯»M1卡扇区
 * @param {number} taskFlg ä»»åŠ¡æ ‡å¿—ï¼š
 *                    0x00->AUTO å‘ŠçŸ¥æ‰«ç å™¨è¯¥æŒ‡ä»¤å¯å•独执行,无指令间的依赖关系。
 *                    0x01->START å‘ŠçŸ¥æ‰«ç å™¨å¼€å§‹å¯¹å¡æ“ä½œæˆ–对卡操作尚未结束,且指令间可能存在依赖关系。
 *                    0x02->FINISH å‘ŠçŸ¥æ‰«ç å™¨æœ¬æ¡æŒ‡ä»¤æ˜¯æ“ä½œå¡çš„æœ€åŽä¸€æ¡æŒ‡ä»¤ï¼Œå°†å¡ç‰‡æ“ä½œçŽ¯å¢ƒæ¢å¤åˆ°é»˜æ€ã€‚
 * @param {number} secNum æ‰‡åŒºå·
 * @param {number} logicBlkNum å—号(在扇区内的逻辑号0~3)
 * @param {number} blkNums å—æ•°
 * @param {array} key å¯†é’¥, é•¿åº¦6bytes
 * @param {number} keyType å¯†é’¥ç±»åž‹: A:0x60 B:0x61
 * @param {array} data å†™å…¥æ•°æ®
 * @returns int å†™å…¥é•¿åº¦ -1:错误
 */
nfc.m1cardWriteSector = function (taskFlg, secNum, logicBlkNum, blkNums, key, keyType, data) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    _validate('m1cardWriteSector', taskFlg, secNum, logicBlkNum, blkNums, key, keyType, data)
    return nfcObj.m1cardWriteSector(pointer, taskFlg, secNum, logicBlkNum, blkNums, key, keyType, data);
}
/**
 *
 * @param {number} taskFlg ä»»åŠ¡æ ‡å¿—ï¼š
 *                    0x00->AUTO å‘ŠçŸ¥æ‰«ç å™¨è¯¥æŒ‡ä»¤å¯å•独执行,无指令间的依赖关系。
 *                    0x01->START å‘ŠçŸ¥æ‰«ç å™¨å¼€å§‹å¯¹å¡æ“ä½œæˆ–对卡操作尚未结束,且指令间可能存在依赖关系。
 *                    0x02->FINISH å‘ŠçŸ¥æ‰«ç å™¨æœ¬æ¡æŒ‡ä»¤æ˜¯æ“ä½œå¡çš„æœ€åŽä¸€æ¡æŒ‡ä»¤ï¼Œå°†å¡ç‰‡æ“ä½œçŽ¯å¢ƒæ¢å¤åˆ°é»˜æ€ã€‚
 * @param {number} blkNums å—号
 * @param {array} key å¯†é’¥, é•¿åº¦6bytes
 * @param {number} keyType å¯†é’¥ç±»åž‹: A:0x60 B:0x61
 * @returns Array è¯»å–结果 undefined:失败
 */
nfc.m1cardReadBlk = function (taskFlg, blkNum, key, keyType) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    _validate('m1cardReadBlk', taskFlg, 1, 0, blkNum, key, keyType, ' ')
    return nfcObj.m1cardReadBlk(pointer, taskFlg, blkNum, key, keyType);
}
/**
 *
 * @param {number} taskFlg ä»»åŠ¡æ ‡å¿—ï¼š
 *                    0x00->AUTO å‘ŠçŸ¥æ‰«ç å™¨è¯¥æŒ‡ä»¤å¯å•独执行,无指令间的依赖关系。
 *                    0x01->START å‘ŠçŸ¥æ‰«ç å™¨å¼€å§‹å¯¹å¡æ“ä½œæˆ–对卡操作尚未结束,且指令间可能存在依赖关系。
 *                    0x02->FINISH å‘ŠçŸ¥æ‰«ç å™¨æœ¬æ¡æŒ‡ä»¤æ˜¯æ“ä½œå¡çš„æœ€åŽä¸€æ¡æŒ‡ä»¤ï¼Œå°†å¡ç‰‡æ“ä½œçŽ¯å¢ƒæ¢å¤åˆ°é»˜æ€ã€‚
 * @param {number} blkNums å—号
 * @param {array} key å¯†é’¥, é•¿åº¦6bytes
 * @param {number} keyType å¯†é’¥ç±»åž‹: A:0x60 B:0x61
 * @param {array} data å†™å…¥æ•°æ®
 * @returns int å†™å…¥é•¿åº¦ -1:错误
 */
nfc.m1cardWriteBlk = function (taskFlg, blkNum, key, keyType, data) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    _validate('m1cardWriteBlk', taskFlg, 1, 0, blkNum, key, keyType, data)
    return nfcObj.m1cardWriteBlk(pointer, taskFlg, blkNum, key, keyType, data);
}
/**
 * ATS检测
 */
nfc.nfc_iso14443_type_a_get_ats = function () {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    return nfcObj.nfc_iso14443_type_a_get_ats(pointer)
}
/**
 *
 * @param {number} taskFlg ä»»åŠ¡æ ‡å¿—ï¼š
 *                    0x00->AUTO å‘ŠçŸ¥æ‰«ç å™¨è¯¥æŒ‡ä»¤å¯å•独执行,无指令间的依赖关系。
 *                    0x01->START å‘ŠçŸ¥æ‰«ç å™¨å¼€å§‹å¯¹å¡æ“ä½œæˆ–对卡操作尚未结束,且指令间可能存在依赖关系。
 *                    0x02->FINISH å‘ŠçŸ¥æ‰«ç å™¨æœ¬æ¡æŒ‡ä»¤æ˜¯æ“ä½œå¡çš„æœ€åŽä¸€æ¡æŒ‡ä»¤ï¼Œå°†å¡ç‰‡æ“ä½œçŽ¯å¢ƒæ¢å¤åˆ°é»˜æ€ã€‚
 * @param {ArrayBuffer} buffer     è¦å‘送的数据
 * @param {number} bufferLen     è¦å‘送的数据长度
 * @returns buffer
 */
nfc.iso14443Apdu = function (taskFlg, buffer, bufferLen) {
    let pointer = dxCommon.handleId("nfc", "nfcid")
    return nfcObj.iso14443Apdu(pointer, taskFlg, buffer, bufferLen);
}
/**
 * PSAM卡断电
 */
nfc.nfcPsamPowerDown = function () {
    let pointer = dxCommon.handleId("nfc", "nfcid")
    return nfcObj.nfcPsamPowerDown(pointer);
}
/**
 * NFC æ”¹å˜çŠ¶æ€
 */
nfc.nfcPsamChangeBaud = function () {
    let pointer = dxCommon.handleId("nfc", "nfcid")
    return nfcObj.nfcPsamChangeBaud(pointer);
}
/**
 * PSAM卡重置
 */
nfc.nfcPsamCardReset = function (force) {
    let pointer = dxCommon.handleId("nfc", "nfcid")
    return nfcObj.nfcPsamCardReset(pointer, force);
}
/**
 * å‘送PSAM APDU指令
 */
nfc.nfcPsamCardApdu = function (buffer) {
    let pointer = dxCommon.handleId("nfc", "nfcid")
    return nfcObj.nfcPsamCardApdu(pointer, buffer);
}
/**
 * EID æ›´æ–°äº‘证配置
 * @param {object} eidConfig äº‘证配置
 *         @param {string} eidConfig.appid å¹³å°åˆ†é…ç»™åº”用的appid
 *         @param {number} eidConfig.read_len; // å•次读卡长度,默认0x80
 *         @param {number} eidConfig.declevel; // æ˜¯å¦è¯»å–照片,1为不读取,2为读取
 *         @param {number} eidConfig.loglevel; //日志级别,支持0,1,2
 *         @param {number} eidConfig.model; // æ˜¯å¦ç›´æŽ¥æŸ¥å‡ºä¿¡æ¯ 0是  1否 ï¼ˆå³0是原路返回,返回身份信息,1是转发,返回reqid)
 *         @param {number} eidConfig.type; // å¡ç‰‡ç±»åž‹ï¼š0 èº«ä»½è¯ 1电子证照
 *         @param {number} eidConfig.pic_type; // ç…§ç‰‡è§£ç æ•°æ®ç±»åž‹ 0 wlt 1 jpg
 *         @param {number} eidConfig.envCode; // çŽ¯å¢ƒè¯†åˆ«ç 
 *         @param {string} eidConfig.sn[128]; // è®¾å¤‡åºåˆ—号
 *         @param {string} eidConfig.device_model[128]; // è®¾å¤‡åž‹å·
 *         @param {number} eidConfig.info_type; // ä¿¡æ¯è¿”回类型,0 èº«ä»½ä¿¡æ¯ç»“构体 ï¼Œ1原始数据 char
 */
nfc.eidUpdateConfig = function (eidConfig) {
    if (eidConfig == null) {
        throw new Error("eidUpdateConfig:eidConfig should not be null or empty")
    }
    return nfcObj.eidUpdateConfig(eidConfig);
}
/**
 * è¯»NTAG版本号
 *     @param {number}         hdl                   nfc句柄
 *     @returns {ArrayBuffer} buffer
 */
nfc.nfcNtagReadVersion = function () {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    return nfcObj.nfcNtagReadVersion(pointer);
}
/**
 * è¯»NTAG页内容 å›ºå®šè¯»å–4页共16字节
 *     @param {number}         hdl                   nfc句柄
 *     @param {number}         pageNum               èµ·å§‹é¡µåœ°å€ï¼š
 *                                                     æ¯æ¬¡è¯»å–四个页
 *                                                     å¦‚果地址(Addr)是04h,则返回页04h、05h、06h、07h内容
 *     @returns {ArrayBuffer} buffer
 */
nfc.nfcNtagReadPage = function (pageNum) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    if (pageNum == null) {
        throw new Error("nfcNtagReadPage:pageNum should not be null or empty")
    }
    return nfcObj.nfcNtagReadPage(pointer, pageNum);
}
/**
 * è¯»NTAG多页内容 è¯»å–数据的buffer,最小为 é¡µæ•°*4;要读取的数据长度 é¡µæ•°*4
 *     @param {number}         hdl                   nfc句柄
*     @param {number}         start_addr          èµ·å§‹é¡µåœ°å€
 *     @param {number}         end_addr            ç»“束页地址
 *     @returns {ArrayBuffer} buffer
 */
nfc.nfcNtagFastReadPage = function (start_page, end_page) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    if (start_page == null) {
        throw new Error("nfcNtagFastReadPage:start_page should not be null or empty")
    }
    if (end_page == null) {
        throw new Error("nfcNtagFastReadPage:end_page should not be null or empty")
    }
    return nfcObj.nfcNtagFastReadPage(pointer, start_page, end_page);
}
/**
 * å†™NTAG页内容
 *     @param {number}         hdl                   nfc句柄
 *     @param {number}         pageNum               å†™å…¥çš„页号 ï¼šæœ‰æ•ˆAddr参数
 *                                              å¯¹äºŽNTAG213,页地址02h至2Ch
 *                                              å¯¹äºŽNTAG215,页地址02h至86h
 *                                              å¯¹äºŽNTAG216,页地址02h至E6h
 *     @param {ArrayBuffer}     pageData             å†™å…¥é¡µçš„内容:四字节
 *     @returns {boolean} ture/false
 */
nfc.nfcNtagWritePage = function (pageNum, pageData) {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    if (pageNum == null) {
        throw new Error("nfcNtagWritePage:pageNum should not be null or empty")
    }
    if (!pageData) {
        throw new Error("nfcNtagWritePage:pageData should not be null or empty")
    }
    return nfcObj.nfcNtagWritePage(pointer, pageNum, pageData);
}
/**
 * åˆ¤æ–­nfc消息队列是否为空
 * @returns bool
 */
nfc.msgIsEmpty = function () {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    return nfcObj.msgIsEmpty(pointer)
}
/**
 * ä»Žnfc消息队列中读取数据
 * @returns json消息对象
 */
nfc.msgReceive = function () {
    let pointer = dxCommon.handleId("nfc", 'nfcid')
    let msg = nfcObj.msgReceive(pointer)
    return JSON.parse(msg);
}
function _validate(fun, taskFlg, secNum, logicBlkNum, blkNums, key, keyType, data) {
    if (![0x00, 0x01, 0x02].includes(taskFlg)) {
        throw new Error(fun, ":taskFlg error")
    }
    if (!(secNum >= 0)) {
        throw new Error(fun, ":secNum error")
    }
    if (logicBlkNum == null || logicBlkNum == undefined || logicBlkNum < 0 || logicBlkNum > 3) {
        throw new Error(fun, ":logicBlkNum error")
    }
    if (blkNums == null || blkNums == undefined || blkNums < 0 || blkNums > 59) {
        throw new Error(fun, ":blkNums error")
    }
    if (key == null || key === undefined || key.length < 0) {
        throw new Error(fun, ":key error")
    }
    if (![0x60, 0x61].includes(keyType)) {
        throw new Error(fun, ":keyType error")
    }
    if (data === null || data === undefined) {
        throw new Error(fun, ":data error")
    }
}
nfc.RECEIVE_MSG = '__nfc__MsgReceive'
/**
 * ç®€åŒ–NFC组件的使用,无需轮询去获取网络状态,网络的状态会通过eventcenter发送出去
 * run åªä¼šæ‰§è¡Œä¸€æ¬¡ï¼Œæ‰§è¡Œä¹‹åŽç½‘络基本配置不能修改
 * å¦‚果需要实时获取刷卡数据,可以订阅 eventCenter的事件,事件的topic是nfc.CARD,事件的内容是类似
 * {id:'卡id',card_type:卡芯片类型,id_len:卡号长度,type:卡类型,timestamp:'刷卡时间戳',monotonic_timestamp:'相对开机的时间'}
 * @param {*} options
 *         @param {boolean} options.m1 éžå¿…填,普通卡回调开关
 *         @param {boolean} options.psam éžå¿…填,psam卡回调开关
 */
nfc.run = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxnfc.run:'options' parameter should not be null or empty")
    }
    let init = map.get("__nfc__run_init")
    if (!init) {//确保只初始化一次
        map.put("__nfc__run_init", options)
        bus.newWorker("__nfc", '/app/code/dxmodules/nfcWorker.js')
    }
}
/**
 * å¦‚æžœnfc单独一个线程,可以直接使用run函数,会自动启动一个线程,
 * å¦‚果想加入到其他已有的线程,可以使用以下封装的函数
 */
nfc.worker = {
    //在while循环前
    beforeLoop: function (options) {
        nfc.init(options.useEid)
        // PSAM和普通卡回调
        if (options.m1) {
            nfc.cbRegister()
        }
        if (options.psam) {
            nfc.psamCbRegister()
        }
    },
    //在while循环里
    loop: function () {
        if (!nfc.msgIsEmpty()) {
            let res = nfc.msgReceive();
            bus.fire(nfc.RECEIVE_MSG, res)
        }
    }
}
export default nfc;
vf205_access/dxmodules/dxNtp.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
//build: 20240523
//同步时间,运行时需要把这个组件加载到一个线程里的while循环,
//缺省每隔24小时同步一次,也支持事件触发主动同步 eventcetner.fire(ntp.MANUAL_SYNC)
//依赖组件 dxLogger,dxCommon,dxCenter
import common from "./dxCommon.js"
import dxNet from './dxNet.js'
import log from './dxLogger.js'
const ntp = {}
/**
 * åŒæ­¥æ—¶é—´å¾ªçŽ¯åˆå§‹åŒ–
 * @param {string} server åŒæ­¥æ—¶é—´æœåŠ¡å™¨åœ°å€ï¼Œç¼ºçœæ˜¯182.92.12.11
 * @param {number} interval åŒæ­¥æ—¶é—´çš„间隔,单位是分钟,缺省是24小时同步一次
 *
 */
ntp.beforeLoop = function (server = '182.92.12.11', interval = 24 * 60) {
    this.server = server
    this.interval = interval
    this.tempinterval = 1000//第一次隔1秒就开始第一次对时
    this.last = new Date().getTime()
}
/**
 * æ›´æ–°æ—¶åŒºGMT,比如8,表示GMT8北京时间
 * @param {number} gmt  å–值范围是0,1,2....24表示时区,
 */
ntp.updateGmt = function (gmt) {
    if (gmt != undefined && gmt != null) {
        let cmd = `cp /etc/localtimes/localtime${gmt} /etc/localtime`
        common.systemBrief(cmd)
    }
}
/**
 * ç«‹åˆ»åŒæ­¥
 */
ntp.syncnow = false
/**
 * åŒæ­¥æ—¶é—´
 */
ntp.loop = function () {
    if (!dxNet.getStatus().connected) {//没有网络
        return
    }
    const now = new Date().getTime()
    const minus = now - this.last
    if (ntp.syncnow || (minus > (this.tempinterval))) {
        ntp.syncnow = false
        let cmd = `ntpdate -u -t 1 '${this.server}' > /dev/null && echo 'Y' || echo 'N'`
        let res = common.systemWithRes(cmd, 100).split(/\s/)[0]
        if (res != "Y" ) {
            // å¯¹æ—¶å¤±è´¥ï¼Œ1分后重试
            log.error('ntp sync failed')
            this.tempinterval = 60 * 1000
        } else {
            // å¯¹æ—¶æˆåŠŸ
            log.info('ntp sync success')
            this.tempinterval = this.interval * 60 * 1000
            common.systemBrief("hwclock -u -w")
        }
        this.last = new Date().getTime()
    }
}
export default ntp;
vf205_access/dxmodules/dxOta.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,256 @@
//build:20240724
// è´Ÿè´£å›ºä»¶çš„升级
import log from './dxLogger.js'
import com from './dxCommon.js'
import http from './dxHttp.js'
import * as os from 'os';
const ota = {}
//获取当前磁盘剩余大小(k)可能不同的操作系统指令不一样
ota.DF_CMD = `df -k / | awk 'NR==2 {print $4}'`
ota.OTA_ROOT = '/ota'
ota.OTA_RUN = ota.OTA_ROOT + '/run.sh'
/**
 * HTTP升级:网络下载升级包升级
 * @param {string} url å¿…填,下载升级包的http url地址
 * @param {string} md5 å¿…填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串
 * @param {number} size éžå¿…填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载
 * @param {number} timeout éžå¿…填,尝试链接下载地址的超时时间(不是下载完成的时间),缺省是3秒
 * @param {string} password éžå¿…填,下载安装包的密码,可选,如果设置,下载时需要输入密码
 */
ota.updateHttp = function (url, md5, timeout = 3, password, size) {
    if (!url || !md5) {
        throw new Error("The 'url' and 'md5' param should not be null")
    }
    if (size && (typeof size != 'number')) {
        throw new Error("The 'size' param should be a number")
    }
    //1. æŸ¥çœ‹ç£ç›˜è¿˜å‰©ä½™çš„大小
    let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000))
    if (size) {
        if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间
            throw new Error('The upgrade package is too large, and not be enough space on the disk to download it')
        }
    }
    //2. ä¸‹è½½æ–‡ä»¶åˆ°ä¸´æ—¶ç›®å½•
    const firmware = '/upgrades.zip'
    const temp = '/upgrades.temp'
    com.systemBrief(`rm -rf ${firmware} && rm -rf ${temp} `) //先删除ota根目录
    let downloadRet = http.download(url + (password ? "&password=" + password : '') , temp, null, timeout*1000)
    let fileExist = (os.stat(temp)[1] === 0)
    if (!fileExist) {
        log.error("download result" + downloadRet)
        com.systemBrief(`rm -rf ${firmware} && rm -rf ${temp} `)
        throw new Error('Download failed, please check the url:' + url)
    }
    //3. è®¡ç®—并比较md5是否一样
    let md5Hash = com.md5HashFile(temp)
    md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('')
    if (md5Hash != md5) {
        com.systemBrief(`rm -rf ${firmware} && rm -rf ${temp} `)
        throw new Error('Download failed with wrong md5 value')
    }
    //4. md5校验通过,将升级包放到升级目录下,等待重启升级
    com.systemBrief(`mv ${temp} ${firmware} `)
}
/**
 * æ–‡ä»¶å‡çº§ï¼šé€šè¿‡å…¶ä»–方式将升级包放到用户目录下,调用此方法升级
 * @param {string} path å¿…填,下载升级包的http url地址
 * @param {string} md5 å¿…填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串
 * @param {number} size éžå¿…填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载
 */
ota.updateFile = function (path, md5, size) {
    if (!path || !md5) {
        throw new Error("The 'path' and 'md5' param should not be null")
    }
    if (size && (typeof size != 'number')) {
        throw new Error("The 'size' param should be a number")
    }
    //1. æŸ¥çœ‹ç£ç›˜è¿˜å‰©ä½™çš„大小
    let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000))
    if (size) {
        if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间
            throw new Error('The upgrade package is too large, and not be enough space on the disk to download it')
        }
    }
    //2. è®¡ç®—并比较md5是否一样
    let md5Hash = com.md5HashFile(path)
    md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('')
    if (md5Hash != md5) {
        throw new Error('With wrong md5 value')
    }
    //3. md5校验通过,将升级包放到升级目录下,等待重启升级
    const firmware = '/upgrades.zip'
    com.systemBrief(`mv ${path} ${firmware} `)
}
/**
 * æ³¨æ„ï¼šæ­¤æ–¹æ³•即将过期,用于兼容旧版本,新版本不推荐使用
 * å‡çº§åˆ†äºŒå¤§æ­¥éª¤ï¼Œç¬¬ä¸€æ­¥æ˜¯åœ¨åº”用端下载升级包(zip),解压升级包
 * ç¬¬äºŒæ­¥åŒ…括重启设备,利用脚本复制目录和文件或额外一些操作
 * å¦‚果在升级包根目录下放一个custom_update.sh,就会先执行这个shell文件,我们可以在这个文件里放一些自定义的升级动作
 * @param {string} url å¿…填,下载升级包的http url地址
 * @param {string} md5 å¿…填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串
 * @param {number} size éžå¿…填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载
 * @param {string} shell éžå¿…填,重启设备后的升级脚本内容,解压后的文件夹缺省是 /ota/temp,升级会缺省把/ota/temp下所有文件拷贝复制到/app/code/下
 * @param {number} timeout éžå¿…填,尝试链接下载地址的超时时间(不是下载完成的时间),缺省是3秒
 */
ota.update = function (url, md5, size, shell, timeout = 3) {
    if (!url || !md5) {
        throw new Error("The 'url' and 'md5' param should not be null")
    }
    if (size && (typeof size != 'number')) {
        throw new Error("The 'size' param should be a number")
    }
    //1. æŸ¥çœ‹ç£ç›˜è¿˜å‰©ä½™çš„大小
    let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000))
    if (size) {
        if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间
            throw new Error('The upgrade package is too large, and not be enough space on the disk to download it')
        }
    }
    //2. ä¸‹è½½æ–‡ä»¶åˆ°ç‰¹å®šç›®å½•
    const firmware = ota.OTA_ROOT + '/download.zip'
    const temp = ota.OTA_ROOT + '/temp'
    com.systemBrief(`rm -rf ${ota.OTA_ROOT} && mkdir ${ota.OTA_ROOT} `) //先删除ota根目录
    let download = `wget --no-check-certificate --timeout=${timeout} -c "${url}" -O ${firmware} 2>&1`
    com.systemBrief(download, 1000)
    let fileExist = (os.stat(firmware)[1] === 0)
    let downloadRet
    if (!fileExist) {
        downloadRet = http.download(url, firmware, null, timeout*1000)
    }
    fileExist = (os.stat(firmware)[1] === 0)
    if (!fileExist) {
        log.error("download result" + downloadRet)
        throw new Error('Download failed, please check the url:' + url)
    }
    //3. è®¡ç®—并比较md5是否一样
    let md5Hash = com.md5HashFile(firmware)
    md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('')
    if (md5Hash != md5) {
        log.error("download result" + downloadRet)
        throw new Error('Download failed with wrong md5 value')
    }
    //4. è§£åŽ‹
    com.systemBrief(`mkdir ${temp} && unzip -o ${firmware} -d ${temp}`)
    //5. æ‰§è¡Œè‡ªå®šä¹‰çš„升级脚本
    const custom_update = temp+'/custom_update.sh'
    if(os.stat(custom_update)[1] === 0){
        com.systemBrief(`chmod +x ${custom_update}`)
        com.systemWithRes(`${custom_update}`)
    }
    //6. æž„建脚本文件
    if (!shell) {
        //缺省只是拷贝目录并删除ota根目录
        shell = `cp -r ${temp}/* /app/code \n rm -rf ${ota.OTA_ROOT}`
    }
    com.systemBrief(`echo "${shell}" > ${ota.OTA_RUN} && chmod +x ${ota.OTA_RUN}`)
    fileExist = (os.stat(ota.OTA_RUN)[1] === 0)
    if (!fileExist) {
        throw new Error('Build shell file failed')
    }
    com.systemWithRes(`${ota.OTA_RUN}`)
}
/**
 * æ³¨æ„ï¼šæ­¤æ–¹æ³•即将过期,用于兼容旧版本,新版本不推荐使用
 * ç‰¹æ®Šï¼šå…¼å®¹æ—§çš„升级格式,必须是tar.xz格式,且只用来升级资源文件
 * å‡çº§åˆ†äºŒå¤§æ­¥éª¤ï¼Œç¬¬ä¸€æ­¥æ˜¯åœ¨åº”用端下载升级包(zip),解压升级包
 * ç¬¬äºŒæ­¥åŒ…括重启设备,利用脚本复制目录和文件或额外一些操作
 * å¦‚果在升级包根目录下放一个custom_update.sh,就会先执行这个shell文件,我们可以在这个文件里放一些自定义的升级动作
 * @param {string} url å¿…填,下载升级包的http url地址
 * @param {string} md5 å¿…填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串
 * @param {number} size éžå¿…填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载
 * @param {string} shell éžå¿…填,重启设备后的升级脚本内容,解压后的文件夹缺省是 /ota/temp,升级会缺省把/ota/temp下所有文件拷贝复制到/app/code/下
 * @param {number} timeout éžå¿…填,尝试链接下载地址的超时时间(不是下载完成的时间),缺省是3秒
 */
ota.updateResource = function (url, md5, size, shell, timeout = 3) {
    if (!url || !md5) {
        throw new Error("The 'url' and 'md5' param should not be null")
    }
    if (size && (typeof size != 'number')) {
        throw new Error("The 'size' param should be a number")
    }
    //1. æŸ¥çœ‹ç£ç›˜è¿˜å‰©ä½™çš„大小
    let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000))
    if (size) {
        if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间
            throw new Error('The upgrade package is too large, and not be enough space on the disk to download it')
        }
    }
    //2. ä¸‹è½½æ–‡ä»¶åˆ°ç‰¹å®šç›®å½•
    const firmware = ota.OTA_ROOT + '/download.tar.xz'
    const temp = ota.OTA_ROOT + '/temp'
    com.systemBrief(`rm -rf ${ota.OTA_ROOT} && mkdir ${ota.OTA_ROOT} `) //先删除ota根目录
    let download = `wget --no-check-certificate --timeout=${timeout} -c "${url}" -O ${firmware} 2>&1`
    com.systemBrief(download, 1000)
    let fileExist = (os.stat(firmware)[1] === 0)
    if (!fileExist) {
        http.download(url, firmware, null, timeout*1000)
    }
    fileExist = (os.stat(firmware)[1] === 0)
    if (!fileExist) {
        throw new Error('Download failed, please check the url:' + url)
    }
    //3. è®¡ç®—并比较md5是否一样
    let md5Hash = com.md5HashFile(firmware)
    md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('')
    if (md5Hash != md5) {
        throw new Error('Download failed with wrong md5 value')
    }
    //4. è§£åŽ‹
    //tar -xJvf test.tar.xz -C /path/
    com.systemBrief(`mkdir ${temp} && tar -xJvf ${firmware} -C ${temp}`)
    //5. æž„建脚本文件
    if (!shell) {
        shell = `
        source=${temp}/vgapp/res/image/bk.png
        target=/app/code/resource/image/bg.png
        if test -e "\\$source"; then
            cp "\\$source" "\\$target"
        fi
        source=${temp}/vgapp/res/image/bk_90.png
        target=/app/code/resource/image/bg_90.png
        if test -e "\\$source"; then
            cp "\\$source" "\\$target"
        fi
        source=${temp}/vgapp/res/font/AlibabaPuHuiTi-2-65-Medium.ttf
        target=/app/code/resource/font.ttf
        if test -e "\\$source"; then
            cp "\\$source" "\\$target"
        fi
        source=${temp}/vgapp/wav/*.wav
        target=/app/code/resource/wav/
        cp "\\$source" "\\$target"
        rm -rf ${ota.OTA_ROOT}
        `
    }
    com.systemBrief(`echo "${shell}" > ${ota.OTA_RUN} && chmod +x ${ota.OTA_RUN}`)
    fileExist = (os.stat(ota.OTA_RUN)[1] === 0)
    if (!fileExist) {
        throw new Error('Build shell file failed')
    }
    com.systemWithRes(`${ota.OTA_RUN}`)
}
/**
 * ç”±è°ƒç”¨è€…来启动重启,一般是update函数没有错误,运行完成并向北向汇报结果后再调用重启
 */
ota.reboot = function () {
    com.asyncReboot(2)
}
//-------------------------private-------------------
export default ota
vf205_access/dxmodules/dxPwm.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
// build : 20240419
// PWM代表脉冲宽度调制(Pulse Width Modulation)被用来模拟输出电压或功率,以控制蜂鸣器、电机的速度、LED的亮度、温度调节器的温度等等
import { pwmClass } from './libvbar-b-dxpwm.so'
import * as os from "os"
const pwmObj = new pwmClass();
const pwm = {}
/**
 * ç”³è¯·pwm通道,申请一次即可
 * @param {number} channel ç”³è¯·çš„通道号,支持0~7通道
 * @returns true/false
 */
pwm.request = function (channel) {
    return pwmObj.request(channel)
}
/**
 * è®¾ç½®PWM模式
 * @param {number} mode
    0 --> CPU mode,连续波形.
    1 --> DMA mode,指定数量的波形.
    2 --> DMA mode,连续波形.
 * @returns true/false
 */
pwm.setMode = function (mode) {
    return pwmObj.setMode(mode)
}
/**
 * è®¾ç½®PWM周期 æ˜¯æŒ‡ä¸€ä¸ªå®Œæ•´çš„PWM信号周期所花费的时间
 * @param {number} periodNs  å¾…设置的PWM周期值(单位: ns)
 * @returns true/false
 */
pwm.setPeriod = function (periodNs) {
    return pwmObj.setPeriod(periodNs)
}
/**
 * è®¾ç½®PWM占空比 æ˜¯æŒ‡é«˜ç”µå¹³ï¼ˆè„‰å†²ï¼‰åœ¨ä¸€ä¸ªå®Œæ•´çš„周期内所占的时间
 * @param {number} dutyNs  å¾…设置的PWM占空比(设置高电平的时间, å•位: ns)
 * @returns true/false
 */
pwm.setDuty = function (dutyNs) {
    return pwmObj.setDuty(dutyNs)
}
/**
 * è®¾ç½®PWM mode 2,指令数量的波形模式的数量
 * @param {number} dutyNs
 * @returns true/false
 */
pwm.setDmaDuty = function (dutyNs) {
    return pwmObj.setDmaDuty(dutyNs)
}
/**
 * ä½¿èƒ½æŒ‡å®šé€šé“
 * @param {number} channel  ç”³è¯·çš„通道号,支持0~7通道
 * @param {boolean} on
 * @returns true/false
 */
pwm.enable = function (channel, on) {
    return pwmObj.enable(channel, on)
}
/**
 * å…³é—­æ‰€é€‰é€šé“
 * @param {number} channel è¾“入参数, ç”³è¯·çš„通道号,支持0~7通道
 * @returns true/false
 */
pwm.free = function (channel) {
    return pwmObj.free(channel)
}
/**
 * è®¾ç½®æŒ‡å®šé€šé“çš„PWM周期
 * @param {number} channel  ç”³è¯·çš„通道号,支持0~7通道
 * @param {number} periodNs  å¾…设置的PWM周期值(单位: ns)
 * @returns true/false
 */
pwm.setPeriodByChannel = function (channel, periodNs) {
    return pwmObj.setPeriodByChannel(channel, periodNs)
}
/**
 * è®¾ç½®æŒ‡å®šé€šé“çš„PWM占空比
 * @param {number} channel  ç”³è¯·çš„通道号,支持0~7通道
 * @param {number} dutyNs å¾…设置的PWM占空比(设置高电平的时间, å•位: ns)
 * @returns true/false
 */
pwm.setDutyByChannel = function (channel, dutyNs) {
    return pwmObj.setDutyByChannel(channel, dutyNs);
}
/**
* èœ‚鸣,需要先request,setPeriodByChannel和enable之后才可以使用
* @param {object} options èœ‚鸣的参数
*         @param {number} options.channel  ç”³è¯·çš„通道号,支持0~7通道,必填
*         @param {number} options.period  å¾…设置的PWM周期值(单位: ns) ç¼ºçœæ˜¯366166
*         @param {number} options.count èœ‚鸣的次数,缺省是1次
*         @param {number} options.time èœ‚鸣的时间,缺省是50毫秒,如果想长鸣,一般是500毫秒
*         @param {number} options.interval 2次蜂鸣之间的间隔,缺省是50毫秒
*         @param {number} options.volume èœ‚鸣的音量,缺省是50
*/
pwm.beep = function (options) {
    const {
        count = 1,
        time = 50,
        interval = 50,
        volume = 50,
        period = 366166,
    } = options;
    for (let i = 0; i < count; i++) {
        pwm.setDutyByChannel(options.channel, period * volume / 255)
        os.sleep(time)
        pwm.setDutyByChannel(options.channel, 0)
        if (i < (count - 1)) {
            // æœ€åŽä¸€æ¬¡èœ‚鸣无间隔
            os.sleep(interval)
        }
    }
}
export default pwm;
vf205_access/dxmodules/dxQrRule.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,215 @@
//build: 20240301
//微光二维码码制规则,包括101,103 ç­‰
//依赖组件: dxDriver,dxCommon,dxLogger
import common from './dxCommon.js'
import base64 from './dxBase64.js'
import logger from './dxLogger.js'
let sqliteObj
// æ¯”较两个字符串的前N个字符是否相等
function comparePrefix(str1, str2, N) {
    let substring1 = str1.substring(0, N);
    let substring2 = str2.substring(0, N);
    return substring1 === substring2;
}
// 101码值解码
function decode101(str) {
    if (str.length < 5) {
        logger.info("无效二维码")
        throw new Error("code invalid,length too short")
    }
    let decodeBuf = base64.decode(str.slice(0, -4))
    decodeBuf=decodeBuf.substring(4)
    return decodeBuf
}
function hexStringToArrayBuffer (hexString) {
    var byteString = hexString.match(/.{1,2}/g);
    var byteArray = byteString.map(function (byte) {
        return parseInt(byte, 16);
    });
    var buffer = new Uint8Array(byteArray).buffer;
    return buffer;
}
/**
 * 103码值解码
 * 1、base64解码
 * 2、解析组织编号
 * 3、RSA解密
 * 4、解析身份类型
 * 5、解析权限标识
 * 6、解析生码时间
 * 7、解析码过期时间
 * 8、校验通行码是否过期
 * @param {*} str
 * @returns
 */
function decode103(str) {
    // FIXME è¿™ä¸ªpubKey后期需要从配置中查询
    let TLV_T_SIZE = 2, TLV_L_SIZE = 2, offset = 0, code, decryptedData, generationCodeTime, expirationTime
    // 1、base64解码
    let decodeBuf = base64.toHexString(str)
    decodeBuf= hexStringToArrayBuffer(decodeBuf)
    let view = new Uint8Array(decodeBuf)
    let organizationNumber;
    // 2、解析组织编号
    if (view[offset] == 0x01) {
        offset += TLV_T_SIZE
        let orgNumLen = view[offset]
        offset += TLV_L_SIZE
        let orgNumBuf = new Uint8Array(decodeBuf, offset, orgNumLen);
        organizationNumber = String.fromCharCode.apply(null, new Uint8Array(orgNumBuf));
        logger.info("组织编号: " + organizationNumber)
        offset += orgNumLen
    } else {
        throw new Error("code invalid,organization number error")
    }
    // 3、RSA解密
    if (view[offset] == 0x02) {
        // ç»„织编号数据长度
        offset += TLV_T_SIZE
        let cipherTextLen = view[offset]
        offset += TLV_L_SIZE
        // å¯¹å¯†æ–‡è¿›è¡ŒRSA解密
        let encryptedData = decodeBuf.slice(offset, offset + cipherTextLen)
        // TODO ç§˜é’¥å†™æ­»ï¼ŒåŽç»­éœ€è¦æš´éœ²å‡ºæ¥
        // RSA æŸ¥è¯¢å¯†é’¥(也可以固定,也可以写在文本中),根据密钥再次解密
        // RSA解密后的数据
        let arr = sqliteObj.securityFindAllByCodeAndTypeAndTimeAndkey(undefined, undefined, undefined, Math.floor(Date.parse(new Date()) / 1000), organizationNumber)
        if (arr && arr.length > 0) {
            for (let data of arr) {
                decryptedData = common.arrayBufferRsaDecrypt(encryptedData, data.value)
                if (decryptedData != null) {
                    break
                }
            }
        }
        if (!arr && arr.length <= 0 || decryptedData == null) {
            return str
        }
    }
    // ä¸€ä¸ªè®¾å¤‡ä¸€ä¸ªå¯†é’¥ï¼Œç›¸å½“于设备内用于解密的公钥是固定的,可以把公钥放到配置中,这里先默认写死
    // "MTlBODExMDA2MTkwMzQ4Q0I5QUY3QTc4QzAzOTQzNUU5NzNFODAzMEU4QUU1QzBEMkZFOEYwRjEzRjU4M0M5MTU5QUU5MTdDMDIzRDU0RDgxRUY2NTI0NkUyQ0Y2MUMzMTQzNTNENjA2NDU5N0Y2OTY5RUE4QjA5MUY1RTYyODM=";
    // let buf = common.arrayBufferRsaDecrypt(deData, deData.length)
    // 0 3 0 3 0 31 30 33 4 0 a 0 31 30 35 34 33 32 33 33 32 33 5 0 4 0 af 8c fa 5a 6 0 1 0 35 7 0 0 0
    // 0
    // 3 0 3 0 31 30 33
    // 4 0 a 0 31 30 35 34 33 32 33 33 32 33
    // 5 0 4 0 af 8c fa 5a
    // 6 0 1 0 35
    // 7 0 0 0
    offset = 1;
    view = new Uint8Array(decryptedData)
    // 4、解析身份类型(type:103)
    if (view[offset] == 0x03) {
        // èº«ä»½ç±»åž‹æ•°æ®é•¿åº¦
        offset += TLV_T_SIZE
        let identityTypeLength = view[offset]
        // èº«ä»½ç±»åž‹æ•°æ®
        offset += TLV_L_SIZE
        let identityTypeBuf = new Uint8Array(decryptedData, offset, identityTypeLength);
        let identityType = String.fromCharCode.apply(null, identityTypeBuf);
        offset += identityTypeLength
        logger.info("身份类型数据: " + identityType)
    }
    // 5、解析权限标识(code)
    if (view[offset] == 0x04) {
        // æƒé™æ ‡è¯†æ•°æ®é•¿åº¦
        offset += TLV_T_SIZE
        let identityCodeLength = view[offset]
        // æƒé™æ ‡è¯†æ•°æ®
        offset += TLV_L_SIZE
        let identityCodeBuf = new Uint8Array(decryptedData, offset, identityCodeLength);
        offset += identityCodeLength
        code = String.fromCharCode.apply(null, identityCodeBuf);
    }
    // 6、解析生码时间
    if (view[offset] == 0x05) {
        // ç”Ÿç æ—¶é—´æ•°æ®é•¿åº¦
        offset += TLV_T_SIZE
        let createTimeLength = view[offset]
        // ç”Ÿç æ—¶é—´æ•°æ®
        offset += TLV_L_SIZE
        let createTimeBuf = new Uint8Array(decryptedData, offset, createTimeLength);
        offset += createTimeLength
        generationCodeTime = parseInt(common.arrayBufferToHexString(createTimeBuf.reverse()), 16)
    }
    // 7、解析码过期时间
    if (view[offset] == 0x06) {
        // ç è¿‡æœŸæ—¶é—´æ•°æ®é•¿åº¦
        offset += TLV_T_SIZE
        let expireTimeLength = view[offset]
        // ç è¿‡æœŸæ—¶é—´æ•°æ®
        offset += TLV_L_SIZE
        let expireTimeBuf = new Uint8Array(decryptedData, offset, expireTimeLength);
        offset += expireTimeLength
        expirationTime = parseInt(String.fromCharCode.apply(null, expireTimeBuf))
    }
    // 8、校验通行码是否过期
    let timestamp = Date.now();
    expirationTime = generationCodeTime + expirationTime
    if (expirationTime * 1000 > timestamp) {
        return code
    } else {
        return null
    }
}
const code = {
    formatCode: function (msg, sqlObj) {
        if (!msg) {
            throw new Error("msg should not be null or empty")
        }
        if (!sqlObj) {
            throw new Error("sqlObj should not be null or empty")
        }
        if (!sqliteObj) {
            sqliteObj = sqlObj
        }
        let data = {}
        // åˆ¤æ–­ç å€¼
        if (comparePrefix(msg, "&llgy", "&llgy".length) || comparePrefix(msg, "&v101", "&v101".length)) {
            // 101码值
            data.type = 101
            data.code = decode101(msg.substring(5))
        }
        else if (comparePrefix(msg, "vg://v103", "vg://v103".length)) {
            // 103码值
            data.type = 103
            data.code = decode103(msg.substring(9)) ? decode103(msg.substring(9)) : msg.substring(9)
        } else if (comparePrefix(msg, "___VBAR_CONFIG_V1.1.0___", "___VBAR_CONFIG_V1.1.0___".length) || comparePrefix(msg, "___VBAR_KZB_V1.1.0___", "___VBAR_KZB_V1.1.0___".length)) {
            //TODO å…ˆè¿™æ ·å†™ï¼Œè®¨è®ºå¥½åŽæ›´æ”¹æµè½¬é€»è¾‘
            data.type = 'config'
            data.code = msg
        } else {
            data.type = 100
            data.code = msg
        }
        if (data.code) {
            return data
        } else {
            console.log("decode fail")
            return
        }
    }
}
export default code;
vf205_access/dxmodules/dxSqlite.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
//build: 20240525
//依赖组件:dxCommon
import { sqliteClass } from './libvbar-m-dxsqlite.so'
import dxCommon from './dxCommon.js'
const sqliteObj = new sqliteClass();
const sqlite = {}
/**
 * åˆå§‹åŒ–数据库
 * @param {string} path db文件全路径,必填
 * @param {string} id å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 */
sqlite.init = function (path, id) {
    if (path == undefined || path.length == 0) {
        throw new Error("dxsqliteObj.initDb:path should not be null or empty")
    }
    let pointer = sqliteObj.open(path);
    dxCommon.handleId("sqlite", id, pointer)
}
/**
 * æ‰§è¡Œè¯­å¥
 * @param {string} sql è„šæœ¬è¯­å¥ï¼Œå¿…å¡«
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns 0:成功,非0失败
 */
sqlite.exec = function (sql, id) {
    let pointer = dxCommon.handleId("sqlite", id)
    return sqliteObj.sql_exec(pointer, sql)
}
/**
 * æ‰§è¡ŒæŸ¥è¯¢è¯­å¥
 * @param {string} sql è„šæœ¬è¯­å¥ï¼Œå¿…å¡«
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns æŸ¥è¯¢ç»“果,形如:[{"id":"1","type":200,"code":"aad7485a","door":"大门","extra":"","tiemType":0,"beginTime":1716613766,"endTime":1716613766,"repeatBeginTime":1716613766,"repeatEndTime":1716613766,"period":"extra"}]
 */
sqlite.select = function (sql, id) {
    let pointer = dxCommon.handleId("sqlite", id)
    return sqliteObj.select(pointer, sql);
}
/**
 * å…³é—­æ•°æ®åº“连接
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns 0:成功,非0失败
 */
sqlite.close = function (id) {
    let pointer = dxCommon.handleId("sqlite", id)
    return sqliteObj.close(pointer)
}
export default sqlite;
vf205_access/dxmodules/dxStd.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,399 @@
//build 20240222
//quickjs æ ‡å‡†åº“,提供和操作系统相关函数,提供标准IO相关函数
import * as os from "os"
import * as std from "std"
import common from "./dxCommon.js"
const dxstd = {}
/**
 * é€€å‡ºåº”用
 * @param {number} n é€€å‡ºç 
 */
dxstd.exit = function (n) {
    return std.exit(n);
}
/**
 * å¯åŠ¨è®¡æ—¶å™¨ï¼Œå»¶æ—¶å¼‚æ­¥æ‰§è¡Œå‡½æ•°
 * @param {function} func éœ€è¦æ‰§è¡Œçš„函数
 * @param {number} delay å»¶è¿Ÿçš„æ—¶é—´ï¼ˆæ¯«ç§’)
 * @returns timer引用
 */
dxstd.setTimeout = function (func, delay) {
    return os.setTimeout(func, delay)
}
/**
 * æ¸…除指定的计时器
 * @param {*} handle timer引用
 */
dxstd.clearTimeout = function (handle) {
    os.clearTimeout(handle)
}
// è®°å½•定时器id,用于clear,只能在同一线程中clear
let allTimerIdsMap = {}
/**
 * interval定时器
 * @param {function} callback å›žè°ƒå‡½æ•°ï¼Œå¿…å¡«
 * @param {number} interval é—´é𔿗¶é—´ï¼Œå¿…å¡«
 * @param {boolean} once åˆ›å»ºåŽç«‹å³æ‰§è¡Œä¸€æ¬¡ï¼Œéžå¿…å¡«
 * @param {number} timerId å®šæ—¶å™¨id,非必填
 */
dxstd.setInterval = function (callback, interval, once, timerId) {
    if (timerId === null || timerId === undefined) {
        timerId = new Date().getTime() + "_" + this.genRandomStr(5)
        allTimerIdsMap[timerId] = "ready"
    }
    if (once === true) {
        // åˆ›å»ºåŽç«‹å³æ‰§è¡Œä¸€æ¬¡
        os.setTimeout(() => {
            if (allTimerIdsMap[timerId]) {
                callback()
            }
        }, 0);
    }
    if (!allTimerIdsMap[timerId]) {
        return
    }
    allTimerIdsMap[timerId] = os.setTimeout(() => {
        if (allTimerIdsMap[timerId]) {
            callback()
            this.setInterval(callback, interval, false, timerId)
        }
    }, interval);
    return timerId
}
/**
 * æ¸…除interval定时器
 * @param {number} timerId å®šæ—¶å™¨id,必填
 */
dxstd.clearInterval = function (timerId) {
    const timer = allTimerIdsMap[timerId];
    if (timer) {
        os.clearTimeout(timer);
        delete allTimerIdsMap[timerId];
    }
}
/**
 * åˆ é™¤å½“前线程所有interval定时器,注意:只是删除当前线程创建的定时器,若有多个线程,每个线程都需要调用删除
 */
dxstd.clearIntervalAll = function () {
    for (let timerId in allTimerIdsMap) {
        if (allTimerIdsMap.hasOwnProperty(timerId)) {
            os.clearTimeout(allTimerIdsMap[timerId]);
            delete allTimerIdsMap[timerId];
        }
    }
}
/**
 * ç”ŸæˆæŒ‡å®šé•¿åº¦çš„字母和数字组合的随机字符串
 * @param {number} length å­—符串长度,非必填,缺省是6
 * @returns
 */
dxstd.genRandomStr = function (length = 6) {
    const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    let result = ''
    for (let i = 0; i < length; i++) {
        const randomIndex = Math.floor(Math.random() * charset.length)
        result += charset.charAt(randomIndex)
    }
    return result
}
/**
 * æŠŠä¸€æ®µå­—符串作为 javascript è„šæœ¬æ‰§è¡Œ
 * @param {string} str js脚本字符串
 * @param {boolean} async é»˜è®¤ä¸ºfalse,如果为 true,脚本中将接受 await å¹¶è¿”回一个 Promise
 */
dxstd.eval = function (str, async) {
    return std.evalScript(str, { async: async });
}
/**
 * åŠ è½½ä¸€ä¸ªæ–‡ä»¶å†…å®¹ä½œä¸º javascript è„šæœ¬æ‰§è¡Œ
 * @param {string} filename js脚本内容的文件名(绝对路径)
 */
dxstd.loadScript = function (filename) {
    return std.loadScript(filename);
}
/**
 * åŠ è½½æ–‡ä»¶ï¼Œè¯»å–æ–‡ä»¶é‡Œçš„å†…å®¹ï¼ˆä½¿ç”¨utf)
 * @param {string} filename æ–‡ä»¶å
 */
dxstd.loadFile = function (filename) {
    return std.loadFile(filename)
}
/**
 * ä¿å­˜å­—符串到文件
 * @param {string} filename
 * @param {string} content
 */
dxstd.saveFile = function (filename, content) {
    if (!content || (typeof content) != 'string') {
        throw new Error("The 'content' value should be string and not empty")
    }
    if (!filename) {
        throw new Error("The 'filename' should not be empty")
    }
    if (!this.exist(filename)) {
        this.ensurePathExists(filename)
        let fd = os.open(filename, os.O_RDWR | os.O_CREAT | os.O_TRUNC);
        if (fd < 0) {
            throw new Error("Create file failed:" + filename)
        }
        os.close(fd)
    }
    let fd = std.open(filename, "w");
    fd.puts(content)
    fd.flush();
    fd.close();
    common.systemBrief('sync')
    return true
}
/**
 * ç¡®ä¿æ–‡ä»¶å¯¹åº”的目录都存在,不存在就会创建
 * @param {string} filename
 */
dxstd.ensurePathExists = function (filename) {
    const pathSegments = filename.split('/');
    let currentPath = '';
    for (let i = 0; i < pathSegments.length - 1; i++) {
        currentPath += pathSegments[i] + '/';
        if (!this.exist(currentPath)) {
            this.mkdir(currentPath);
        }
    }
}
/**
 * åˆ¤æ–­æ–‡ä»¶æ˜¯å¦å­˜åœ¨
 * @param {string} filename  æ–‡ä»¶å
 * @returns true/false
 */
dxstd.exist = function (filename) {
    return (os.stat(filename)[1] === 0)
}
/**
 * è¿”回一个包含环境变量的键值对的对象。
 */
dxstd.getenviron = function () {
    return std.getenviron();
}
/**
 * è¿”回环境变量名称的值,如果未定义则返回undefined
 * @param {string} name å˜é‡å
 */
dxstd.getenv = function (name) {
    return std.getenv(name);
}
/**
 * å°†çŽ¯å¢ƒå˜é‡åçš„å€¼è®¾ç½®ä¸ºå­—ç¬¦ä¸²å€¼
 * @param {string} name å˜é‡å
 * @param {string} value å˜é‡å€¼
 */
dxstd.setenv = function (name, value) {
    return std.setenv(name, value);
}
/**
 * åˆ é™¤çŽ¯å¢ƒå˜é‡
 * @param {string} name å˜é‡å
 */
dxstd.unsetenv = function (name) {
    return std.unsetenv(name);
}
/**
 * ä½¿ç”¨JSON.parse的超集来解析字符串。可以解析非标准的 JSON å­—符串。接受以下扩展:
 * - å•行和多行注释
 * - æœªåŠ å¼•å·çš„å±žæ€§ï¼ˆä»…ASCII字符的JavaScript标识符)
 * - æ•°ç»„和对象最后可以加逗号
 * - å•引号字符串
 * - \f å’Œ \v è¢«æŽ¥å—为空格字符
 * - æ•°å­—中的前面可以有加号
 * - å…«è¿›åˆ¶ï¼ˆ0o前缀)和十六进制(0x前缀)数字
 * @param {string} str json字符串
 */
dxstd.parseExtJSON = function (str) {
    return std.parseExtJSON(str);
}
/**
 * ä¼‘眠delay_ms毫秒
 */
dxstd.sleep = function (delay_ms) {
    return os.sleep(delay_ms);
}
/**
 * è¿”回表示平台的字符串:"linux"、"darwin"、"win32" æˆ– "js"。
 */
dxstd.platform = function () {
    return os.platform;
}
/**
 * åˆ›å»ºä¸€ä¸ªæ–°çº¿ç¨‹ï¼ˆworker)的构造函数,其API接近于WebWorkers。
 * å¯¹äºŽåŠ¨æ€å¯¼å…¥çš„æ¨¡å—ï¼Œå®ƒç›¸å¯¹äºŽå½“å‰è„šæœ¬æˆ–æ¨¡å—è·¯å¾„ã€‚çº¿ç¨‹é€šå¸¸ä¸å…±äº«ä»»ä½•æ•°æ®ï¼Œå¯ä»¥é€šè¿‡dxMap,dxQueue,dxWpc来共享和传递数据。不支持嵌套的 worker。
 * @param {string} module_filename æŒ‡å®šåœ¨æ–°åˆ›å»ºçš„线程中执行的模块文件名
 */
dxstd.Worker = function (module_filename) {
    return new os.Worker(module_filename)
}
dxstd.O_RDONLY = os.O_RDONLY
dxstd.O_WRONLY = os.O_WRONLY
dxstd.O_RDWR = os.O_RDWR
dxstd.O_APPEND = os.O_APPEND
dxstd.O_CREAT = os.O_CREAT
dxstd.O_EXCL = os.O_EXCL
dxstd.O_TRUNC = os.O_TRUNC
/**
 * æ‰“开一个文件。返回一个句柄,如果出现错误则返回 < 0。
 * @param {string} filename æ–‡ä»¶ç»å¯¹è·¯å¾„
 * @param {number} flags O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_CREAT,O_EXCL,O_TRUNC
 * 1. O_RDONLY ï¼šä»¥åªè¯»æ–¹å¼æ‰“开文件
 * 2. O_WRONLY ï¼šä»¥åªå†™æ–¹å¼æ‰“开文件
 * 3. O_RDWR ï¼šä»¥å¯è¯»å¯å†™æ–¹å¼æ‰“开文件
 * ä»¥ä¸Šä¸‰ä¸ªæ˜¯æ–‡ä»¶è®¿é—®æƒé™æ ‡å¿—,传入的flags å‚数中必须要包含其中一种标志,而且只能包含一种,打开的文件只能按照这种权限来操作,
   è­¬å¦‚使用了 O_RDONLY æ ‡å¿—,就只能对文件进行读取操作,不能写操作。
 * 4. O_APPEND ï¼šè°ƒç”¨ open å‡½æ•°æ‰“开文件,当每次使用 write()函数对文件进行写操作时,都会自动把文件当前位置偏移量移动到文件末尾,
   ä»Žæ–‡ä»¶æœ«å°¾å¼€å§‹å†™å…¥æ•°æ®ï¼Œä¹Ÿå°±æ˜¯æ„å‘³ç€æ¯æ¬¡å†™å…¥æ•°æ®éƒ½æ˜¯ä»Žæ–‡ä»¶æœ«å°¾å¼€å§‹ã€‚
   O_APPEND标志并不会影响读文件,当读取文件时, O_APPEND æ ‡å¿—并不会影响读位置偏移量,
   å³ä½¿ä½¿ç”¨äº† O_APPEND标志,读文件位置偏移量默认情况下依然是文件头,
   ä½¿ç”¨ lseek å‡½æ•°æ¥æ”¹å˜ write()时的写位置偏移量也不会成功,
   å½“执行 write()函数时,检测到 open å‡½æ•°æºå¸¦äº† O_APPEND æ ‡å¿—,所以在 write å‡½æ•°å†…部会自动将写位置偏移量移动到文件末尾
 * 5. O_CREAT:如果 filename å‚数指向的文件不存在则创建此文件
 * 6. O_EXCL :此标志一般结合 O_CREAT æ ‡å¿—一起使用,用于专门创建文件。
   åœ¨ flags å‚数同时使用到了 O_CREAT å’ŒO_EXCL æ ‡å¿—的情况下,如果 filename å‚数指向的文件已经存在,
   åˆ™ open å‡½æ•°è¿”回错误。可以用于测试一个文件是否存在,如果不存在则创建此文件,如果存在则返回错误,这使得测试和创建两者成为一个原子操作。
 * 7. O_TRUNC ï¼šè°ƒç”¨ open å‡½æ•°æ‰“开文件的时候会将文件原本的内容全部丢弃,文件大小变为 0;
 */
dxstd.open = function (filename, flags) {
    return os.open(filename, flags);
}
/**
 * åˆ¤æ–­ç»™å®šè·¯å¾„是否是一个文件夹。
 * @param {string} filename - è¦æ£€æŸ¥çš„路径。
 * @returns {boolean} å¦‚果是文件夹则返回 true,否则返回 false,如果不存在,抛出异常。
 */
dxstd.isDir = function (filename) {
    let stat = os.stat(filename)
    if (stat[1] != 0) {
        throw new Error("No such file:" + filename)
    }
    return ((stat[0].mode & this.S_IFMT) === this.S_IFDIR);
}
/**
 * å…³é—­æ–‡ä»¶
 * @param {*} fd æ–‡ä»¶å¥æŸ„
 */
dxstd.close = function (fd) {
    return os.close(fd)
}
dxstd.SEEK_SET = std.SEEK_SET
dxstd.SEEK_CUR = std.SEEK_CUR
dxstd.SEEK_END = std.SEEK_END
/**
 * åœ¨æ–‡ä»¶ä¸­è¿›è¡Œå®šä½ã€‚使用SEEK_*来表示whence。offset可以是数字或bigint。如果offset是bigint,则返回一个bigint。
 * @param {*} fd æ–‡ä»¶å¥æŸ„
 * @param {number} offset ä¸ºåç§»é‡ï¼Œæ•´æ•°è¡¨ç¤ºæ­£å‘偏移,负数表示负向偏移
 * @param {*} whence è®¾å®šä»Žæ–‡ä»¶çš„哪里开始偏移: SEEK_SET: æ–‡ä»¶å¼€å¤´;SEEK_CUR: å½“前位置;SEEK_END: æ–‡ä»¶ç»“å°¾
 */
dxstd.seek = function (fd, offset, whence) {
    return os.seek(fd, offset, whence)
}
/**
 * ä»Žæ–‡ä»¶å¥æŸ„fd读取length字节到位于字节位置offset的ArrayBuffer缓冲区。返回读取的字节数,如果出现错误则返回 < 0。
 * @param {*} fd æ–‡ä»¶å¥æŸ„
 * @param {*} buffer ArrayBuffer对象
 * @param {number} offset åç§»é‡
 * @param {number} length è¯»å–的字节长度
 */
dxstd.read = function (fd, buffer, offset, length) {
    return os.read(fd, buffer, offset, length);
}
/**
 * ä»ŽArrayBuffer缓冲区的字节位置offset向文件句柄fd写入length字节。返回已写入的字节数,如果出现错误则返回 < 0。
 * @param {*} fd æ–‡ä»¶å¥æŸ„
 * @param {*} buffer ArrayBuffer对象
 * @param {*} offset åç§»é‡
 * @param {*} length å†™çš„字节长度
 */
dxstd.write = function (fd, buffer, offset, length) {
    return os.write(fd, buffer, offset, length);
}
/**
 * åˆ é™¤æ–‡ä»¶ï¼ŒæˆåŠŸè¿”å›ž0否则-errno
 * @param {string} filename æ–‡ä»¶ç»å¯¹è·¯å¾„
 */
dxstd.remove = function (filename) {
    return os.remove(filename)
}
/**
 * ä¿®æ”¹æ–‡ä»¶åç§°ï¼ŒæˆåŠŸè¿”å›ž0否则-errno
 * @param {string} oldname æ—§æ–‡ä»¶ç»å¯¹è·¯å¾„
 * @param {string} newname æ–°æ–‡ä»¶ç»å¯¹è·¯å¾„
 */
dxstd.rename = function (oldname, newname) {
    return os.rename(oldname, newname)
}
/**
 * è¿”回 [str, err],其中 str æ˜¯å½“前工作目录,err æ˜¯é”™è¯¯ä»£ç 
 */
dxstd.getcwd = function () {
    return os.getcwd()
}
/**
 * æ”¹å˜å½“前工作目录
 * @param {string} path ç›®å½•,支持绝对和相对路径
 */
dxstd.chdir = function (path) {
    return os.chdir(path)
}
/**
 * åˆ›å»ºç›®å½•,成功返回0否则-errno
 * @param {string} path ç›®å½•绝对路径
 */
dxstd.mkdir = function (path) {
    return os.mkdir(path)
}
dxstd.S_IFMT = os.S_IFMT
dxstd.S_IFIFO = os.S_IFIFO
dxstd.S_IFCHR = os.S_IFCHR
dxstd.S_IFDIR = os.S_IFDIR
dxstd.S_IFBLK = os.S_IFBLK
dxstd.S_IFREG = os.S_IFREG
dxstd.S_IFSOCK = os.S_IFSOCK
dxstd.S_IFLNK = os.S_IFLNK
dxstd.S_ISGID = os.S_ISGID
dxstd.S_ISUID = os.S_ISUID
/**
 * è¿”回 [obj, err],其中 obj æ˜¯ä¸€ä¸ªåŒ…含路径path的文件状态信息的对象。
 * err æ˜¯é”™è¯¯ä»£ç ã€‚obj ä¸­å®šä¹‰äº†ä»¥ä¸‹å­—段:dev、ino、mode、nlink、uid、gid、rdev、size、blocks、atime、mtime、ctime。
 * æ—¶é—´ä»¥è‡ª1970年以来的毫秒为单位指定。
 * å…¶ä¸­mode的值对应以下枚举,例如,检查一个文件是否是目录可以使用 (mode & S_IFMT) == S_IFDIR çš„æ–¹å¼:
   S_IFMT:位掩码,用于提取文件类型部分的位。这是一个用于屏蔽文件类型位的常量。
   S_IFIFO:表示FIFO(命名管道)。
   S_IFCHR:表示字符设备。
   S_IFDIR:表示目录。
   S_IFBLK:表示块设备。
   S_IFREG:表示常规文件。
   S_IFSOCK:表示套接字。
   S_IFLNK:表示符号链接。
   S_ISGID:设置组ID位。
   S_ISUID:设置用户ID位。
 * @param {string} path æ–‡ä»¶æˆ–目录绝对路径
 */
dxstd.stat = function (path) {
    return os.stat(path)
}
/**
 * lstat() ä¸Ž stat() ç›¸åŒï¼Œåªæ˜¯å®ƒè¿”回关于链接本身的信息。
 * @param {*} path æ–‡ä»¶æˆ–目录绝对路径
 */
dxstd.lstat = function (path) {
    return os.lstat(path)
}
/**
 * è¿”回 [array, err],其中 array æ˜¯åŒ…含目录路径下的文件名的字符串数组。err æ˜¯é”™è¯¯ä»£ç ã€‚
 * @param {string} path ç›®å½•绝对路径
 */
dxstd.readdir = function (path) {
    return os.readdir(path)
}
export default dxstd
vf205_access/dxmodules/dxUart.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,242 @@
//build: 20240715
//数据通信通道,包括串口(Serial port)、USB(Universal Serial Bus)和韦根(Wiegand)
//依赖组件:dxDriver,dxStd,dxLogger,dxMap,dxEventBus,dxCommon
import { channelClass } from './libvbar-m-dxchannel.so'
import std from './dxStd.js'
import dxMap from './dxMap.js'
import dxCommon from './dxCommon.js'
import bus from './dxEventBus.js'
const uartObj = new channelClass();
const map = dxMap.get('default')
const uart = {}
uart.TYPE = {
    USBKBW: 1,//USB Keyboard Wedge通过USB接口连接键盘,并以韦根协议的形式传输数据
    USBHID: 2,//USB人体接口设备(USB Human Interface Device)通道类型
    UART: 3,//表示UART通道类型,即串口通道
    WIEGAND: 4//韦根(Wiegand)通道类型
}
/* å„类通道 IO æŽ§åˆ¶æ“ä½œçš„设置选项枚举 */
uart.IOC_SET_CMD = {
    /* è®¾ç½®KBW通道的配置参数 */
    CHANNEL_IOC_SET_KBW_CONFIG      : 1,
    /* è®¾ç½®KBW通道的上位机参数 */
    CHANNEL_IOC_SET_KBW_UPPER       : 2,
    /* KBW上线时间 */
    CHANNEL_IOC_SET_KBW_UPTIME      : 3,
    /* KBW下线时间 */
    CHANNEL_IOC_SET_KBW_DOWNTIME    : 4,
    /* è®¾ç½®HID通道的报告长度 */
    CHANNEL_IOC_SET_HID_REPORT_LEN  : 5,
    /* è®¾ç½®UART通道的参数 */
    CHANNEL_IOC_SET_UART_PARAM      : 6,
    /* è®¾ç½®éŸ¦æ ¹é€šé“的工作模式 */
    CHANNEL_IOC_SET_WIEGAND_MODE    : 7,
    /* è®¾ç½®éŸ¦æ ¹é€šé“çš„GPIO配置 */
    CHANNEL_IOC_SET_WIEGAND_GPIO    : 8,
    /* è®¾ç½®éŸ¦æ ¹é€šé“的延迟时间 */
    CHANNEL_IOC_SET_WIEGAND_DELAY   : 9,
    /* è®¾ç½®éŸ¦æ ¹é€šé“的日志记录功能 */
    CHANNEL_IOC_SET_WIEGAND_LOG     : 10
};
/* éŸ¦æ ¹é€šé“的不同工作模式 */
uart.WIEGAND_MODE = {
    /* éŸ¦æ ¹æ¨¡å¼åˆå§‹åŒ–值 */
    WIEGAND_MODE_INIT      : 0,
    /* éŸ¦æ ¹ 26 ä½æ¨¡å¼ */
    WIEGAND_MODE_26          : 1,
    /* éŸ¦æ ¹ 34 ä½æ¨¡å¼ */
    WIEGAND_MODE_34          : 2,
    /* éŸ¦æ ¹ 128 ä½æ¨¡å¼ */
    WIEGAND_MODE_128       : 3,
    /* éŸ¦æ ¹ 256 ä½æ¨¡å¼ */
    WIEGAND_MODE_256       : 4,
    /* éŸ¦æ ¹ 2048 ä½æ¨¡å¼ */
    WIEGAND_MODE_2048      : 5,
    /* è‡ªå®šä¹‰çš„韦根模式, æœ€å¤§å‘送 6400 ä½ */
    WIEGAND_MODE_CUSTOM    : 6
};
/**
 * æ‰“开信道
 * @param {number} type é€šé“类型,参考枚举 TYPE,必填
 * @param {string} path ä¸åŒçš„设备或同一设备的不同类型通道对应的path不一样,比如DW200的485对应的值是"/dev/ttyS2",必填
 * @param {string} id å¥æŸ„id,非必填(若打开多个实例需要传入唯一id)
 */
uart.open = function (type, path, id) {
    if (type === undefined || type === null) {
        throw new Error("uart.open:'type' should not be null or empty")
    }
    if (path === undefined || path === null) {
        throw new Error("uart.open:'path' should not be null or empty")
    }
    let pointer = uartObj.open(type, path);
    if (pointer === undefined || pointer === null) {
        throw new Error("uart.open: open failed")
    }
    dxCommon.handleId("uart", id, pointer)
}
/**
 * ä¿¡é“数据发送
 * @param {ArrayBuffer} buffer è¦å‘送的数据,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
uart.send = function (buffer, id) {
    if (buffer === undefined || buffer === null) {
        throw new Error("uart.send: 'buffer' should not be null or empty")
    }
    let pointer = dxCommon.handleId("uart", id)
    return uartObj.send(pointer, buffer);
}
/**
 * ä¿¡é“数据发送,使用微光通信协议格式
 * @param {string/object} data è¦å‘送的数据,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
uart.sendVg = function (data, id) {
    if (!data) {
        return
    }
    if (typeof data === 'string') {
        uart.send(dxCommon.hexStringToArrayBuffer(data), id)
        return
    }
    let pack = '55aa' + data.cmd
    if (data.hasOwnProperty('result')) {
        pack += data.result
    }
    pack += (data.length % 256).toString(16).padStart(2, '0')
    pack += (Math.floor(data.length / 256)).toString(16).padStart(2, '0')
    pack += data.data
    let all = dxCommon.hexToArr(pack)
    let bcc = dxCommon.calculateBcc(all)
    all.push(bcc)
    uart.send(new Uint8Array(all).buffer, id)
}
/**
 * æŽ¥æ”¶æ•°æ®ï¼Œéœ€è¦åœ¨çº¿ç¨‹é‡Œè½®è¯¢åŽ»èŽ·å–,返回Uint8Array类型
 * å¦‚果接收到的数据没有达到size长度,会继续等待直到接收到size长度,但是如果timeout很短,就会有可能没收完就结束这一次操作
 * @param {number} size æŽ¥æ”¶æ•°æ®çš„字节数,必填
 * @param {number} timeout è¶…时时间(毫秒)这个函数会阻塞等待最多这个时间就结束,如果提前接收到了size个数据也会结束,非必填,缺省是10ms
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns Uint8Array,返回值的byteLength表示接收到的长度,如果为0表示没有接收到任何数据
 */
uart.receive = function (size, timeout, id) {
    if (size === undefined || size === null) {
        throw new Error("uart.receive:'size' should not be null or empty")
    }
    if (timeout === undefined || timeout === null) {
        timeout = 10
    }
    let pointer = dxCommon.handleId("uart", id)
    let res = uartObj.receive(pointer, size, timeout)
    if (res === null) {
        return null
    }
    return new Uint8Array(res)
}
/**
 * è°ƒç”¨ä¿¡é“特殊IO接口
 * @param {*} request
 * @param {*} arg
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
uart.ioctl = function (request, arg, id) {
    let pointer = dxCommon.handleId("uart", id)
    return uartObj.ioctl(pointer, request, arg)
}
/**
 * å…³é—­ä¿¡é“
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
uart.close = function (id) {
    let pointer = dxCommon.handleId("uart", id)
    return uartObj.close(pointer)
}
/**
 * åˆ·æ–°ä¿¡é“
 * @param {number} queue_selector å¿…å¡«
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
uart.flush = function (queue_selector, id) {
    if (queue_selector == null) {
        throw new Error("queue_selector should not be null or empty")
    }
    let pointer = dxCommon.handleId("uart", id)
    return uartObj.flush(pointer, queue_selector);
}
uart.VG = {
    RECEIVE_MSG: '__uartvg__MsgReceive',
}
/**
 * ç®€åŒ–微光通信协议的使用,
 * 1. æŽ¥å—数据:把TLV的二进制的数据接受到后解析成对象,并以eventbus的event发送出去(uart.VG.RECEIVE_MSG+options.id)
 * è¿”回的对象格式:{cmd:"2a",result:"01",length:7,data:"0a1acc320fee32",bcc:true}
 * cmd: 1个字节的命令字,16进制字符串
 * result:1个字节的标识字,表示数据处理的结果,成功或失败或其他状态。只有反馈数据才有标识字,16进制字符串
 * length:数据的长度,在TLV里用2个字节来定义,这里直接转成10进制的数字
 * data:多个字节的数据域,16进制字符串
 * bcc: bcc校验成功或失败
 * 2. å‘送数据:把对象转成TLV格式的二进制数据再发送出去,可以通过uart.sendVg('要发送的数据',id),数据格式如下
 * å‘送的数据格式有二种 1.对象格式 ï¼š{cmd:"2a",result:"01",length:7,data:"0a1acc320fee32"} 2. å®Œæ•´çš„16进制字符串'55AA09000000F6'
 * 3. åŒæ ·çš„id,多次调用runvg也只会执行一次
 *
 * @param {object} options å¯åŠ¨çš„å‚æ•°
 *            @param {number} options.type é€šé“类型,参考枚举 TYPE,必填  ï¼ˆå…¼å®¹USBHID块传输,默认1024每块)
 *            @param {string} options.path ä¸åŒçš„设备或同一设备的不同类型通道对应的path不一样,比如DW200的485对应的值是"/dev/ttyS2",必填
 *            @param {number} options.result 0和1(缺省是0),标识是接收的数据还是发送的数据包含标识字节,0表示接受的数据不包括标识字,发送的数据包括,1是反之
 *            @param {number} options.passThrough passThrough为true则接收的数据使用透传模式,非必填
 *          @param {string} options.id  å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 */
uart.runvg = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxuart.runvg:'options' parameter should not be null or empty")
    }
    if (options.id === undefined || options.id === null || typeof options.id !== 'string') {
        // å¥æŸ„id
        options.id = ""
    }
    if (options.type === undefined || options.type === null) {
        throw new Error("dxuart.runvg:'type' should not be null or empty")
    }
    if (options.path === undefined || options.path === null) {
        throw new Error("dxuart.runvg:'path' should not be null or empty")
    }
    let oldfilepre = '/app/code/dxmodules/vgUartWorker'
    let content = std.loadFile(oldfilepre + '.js').replace("{{id}}", options.id)
    let newfile = oldfilepre + options.id + '.js'
    std.saveFile(newfile, content)
    let init = map.get("__vguart__run_init" + options.id)
    if (!init) {//确保只初始化一次
        map.put("__vguart__run_init" + options.id, options)
        bus.newWorker(options.id || "__uart",newfile)
    }
}
export default uart;
vf205_access/dxmodules/dxUi.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,216 @@
//build:20240724
/**
 * UI çš„基础组件,需要先了解一些概念
 * 1. å›¾å±‚:设备具备2个基本图层,主图层(main)和顶部图层(top)
      å…¶ä¸­TOP图层永远在主图层之上,主图层切换页面不会挡住TOP图层,TOP图层用于显示一些状态栏是比较合适的。
      å…¶ä¸­ä¸»å›¾å±‚可以预先在内存中构造多个页面,然后通过loadMain来加载切换不同的页面。而TOP图层不能切换,只能让ui对象隐藏或删除
 * 2. UI对象:有很多种类的UI对象,其中最基础的是 'view' å¯¹è±¡ï¼Œä¸»å›¾å±‚和顶部图层的根ui对象必须是 'view'对象,剩下的 ui å¯¹è±¡éƒ½æ˜¯æŸä¸ª ui å¯¹è±¡çš„子ui。
      ui对象包括常见的 'button'、'label'、'image'等等,所有对象都有一些通用的属性,也有一些独特的属性
      æ‰€æœ‰ ui å¯¹è±¡éƒ½æœ‰å…¨å±€å”¯ä¸€çš„ id ï¼Œä¸èƒ½é‡å¤ã€‚通用的属性还包括
      - type:获取ui对象的类型,字符串
      - parent:获取ui对象的父节点,字符串
      - children:获取ui对象的所有子对象的id,字符串数组
 * 3. dxui文件:以.dxui为扩展名的文件是利用可视化拖拽工具生成的 ui æ ‘,工具会自动生成对应的js文件,可以import对应的js文件来操作
 */
import logger from './dxLogger.js'
import utils from './uiUtils.js'
import button from './uiButton.js'
import font from './uiFont.js'
import image from './uiImage.js'
import label from './uiLabel.js'
import line from './uiLine.js'
import list from './uiList.js'
import dropdown from './uiDropdown.js'
import checkbox from './uiCheckbox.js'
import slider from './uiSlider.js'
import _switch from './uiSwitch.js'
import textarea from './uiTextarea.js'
import keyboard from './uiKeyboard.js'
import style from './uiStyle.js'
import view from './uiView.js'
import buttons from './uiButtons.js'
const dxui = {}
dxui.Button = button
dxui.Font = font
dxui.Image = image
dxui.Label = label
dxui.Line = line
dxui.List = list
dxui.Dropdown = dropdown
dxui.Checkbox = checkbox
dxui.Slider = slider
dxui.Switch = _switch
dxui.Textarea = textarea
dxui.Keyboard = keyboard
dxui.Style = style
dxui.View = view
dxui.Utils = utils
dxui.Buttons = buttons
let orientation = 1 //默认横屏
/**
 * åˆå§‹åŒ–,必须在代码最前面调用
 * @param {object} options åˆå§‹åŒ–参数
 *        @param {number} options.orientation å±å¹•方向 å¯ä»¥ä¸º0,1,2,3,分别表示竖屏,屏幕在左;横屏,屏幕在上;竖屏,屏幕在右;横批,屏幕在下
 * @param {object} context ä¸Šä¸‹æ–‡ï¼Œæ¯ä¸ªåº”用都有唯一的一个上下文变量,不同的js可以都引用dxUi.js,但是context必须一致
*/
dxui.init = function (options, context = {}) {
     this.initContext(context)
     if (options && options.orientation != undefined && options.orientation != null && [0, 1, 2, 3].includes(options.orientation)) {
          orientation = options.orientation
     }
     utils.GG.NativeDisp.lvDispSetRotation(orientation)
}
/**
 * åˆå§‹åŒ–上下文,每个应用都有唯一的一个上下文变量,不同的js可以都引用dxUi.js,但是context必须一致
 * åœ¨æž„建ui前需要初始化
 * @param {object} context åˆå§‹æ˜¯ä¸€ä¸ªç©ºå¯¹è±¡{}
 */
dxui.initContext = function (context) {
     utils.validateObject(context)
     dxui.all = context
     dxui.Button.all = dxui.all
     dxui.Image.all = dxui.all
     dxui.Label.all = dxui.all
     dxui.Line.all = dxui.all
     dxui.List.all = dxui.all
     dxui.Dropdown.all = dxui.all
     dxui.Checkbox.all = dxui.all
     dxui.Slider.all = dxui.all
     dxui.Switch.all = dxui.all
     dxui.Textarea.all = dxui.all
     dxui.Keyboard.all = dxui.all
     dxui.View.all = dxui.all
     dxui.Buttons.all = dxui.all
}
/**
 * æ ¹æ®id获取已经构建的ui对象
 * @param {string} id
 * @returns
 */
dxui.getUi = function (id) {
     return dxui.all[id]
}
/**
 * å¤–部循环需要调用此方法
 */
dxui.handler = function () {
     return utils.GG.NativeTimer.lvTimerHandler()
}
/**
 * èŽ·å–å±å¹•æ–¹å‘ï¼Œä¸åŒçš„å±å¹•æ–¹å‘å¯èƒ½è¦åŠ è½½ä¸åŒçš„ui或不同的处理逻辑
 * @returns å¯ä»¥ä¸º0,1,2,3,分别表示竖屏,屏幕在左;横屏,屏幕在上;竖屏,屏幕在右;横批,屏幕在下
 */
dxui.getOrientation = function () {
     return orientation;
}
/**
 * åˆ›å»ºä¸€ä¸ªå®šæ—¶å™¨ï¼Œæ¯éš”ms毫秒执行一次回调函数,主要用于定时刷新某个ui对象的值
 * å¯ä»¥åœ¨å›žè°ƒå‡½æ•°é‡Œåˆ é™¤å®šæ—¶å™¨(clearInterval)来实现setTimeout的效果
 * @param {string} id å®šæ—¶å™¨çš„唯一标识 å¿…å¡«
 * @param {function} callback å›žè°ƒå‡½æ•°ï¼ˆå¯ä»¥æ˜¯åŒ¿åå‡½æ•°ï¼‰
 * @param {number} ms æ¯«ç§’æ•°
 * @param {object} user_data ç”¨æˆ·æ•°æ®ï¼Œä¼ é€’给回调参数
 * @returns å®šæ—¶å™¨å¥æŸ„
 */
dxui.setInterval = function (id, callback, ms, user_data) {
     if (utils.validateId(dxui.all, id))
          if (!callback || (typeof callback != 'function') || !callback.name || callback.name === '') {
               throw new Error('The callback should not be null and should be named function')
          }
     if (!ms || (typeof ms != 'number')) {
          throw new Error('The interval should not be empty, and should be number')
     }
     if (!this.all.__interval) {
          this.all.__interval = {}
     }
     this.all.__interval[id] = utils.GG.NativeTimer.lvTimerCreate(callback, ms, user_data)
}
/**
 * å®šæ—¶å™¨ä¸å†éœ€è¦åŽï¼Œå¯ä»¥åˆ é™¤è¿™ä¸ªå®šæ—¶å™¨
 * @param {string} id å®šæ—¶å™¨id
 */
dxui.clearInterval = function (id) {
     if (!dxui.all[id]) {
          return
     }
     utils.GG.NativeTimer.lvTimerDel(dxui.all[id])
     delete dxui.all.__interval[id]
}
/**
 * èŽ·å–ui对象的父对象
 * @param {Object} ui
 */
dxui.getParent = function (ui) {
     if (ui.parent) {
          return dxui.getUi(ui.parent)
     }
     return null
}
/**
 * åˆ é™¤å½“前自身ui对象
 */
dxui.del = function (ui) {
     function recursiveDelete(ui) {
          // å¦‚果对象不存在,直接返回
          if (!dxui.all[ui.id]) {
               return;
          }
          // å…ˆé€’归删除所有子对象
          if (ui.children && Array.isArray(ui.children)) {
               // å€’序遍历子节点
               for (let i = ui.children.length - 1; i >= 0; i--) {
                    const childId = ui.children[i];
                    if (dxui.all[childId]) {
                         recursiveDelete(dxui.all[childId]);
                    }
               }
          }
          // ä»Žçˆ¶å¯¹è±¡ä¸­ç§»é™¤å½“前对象
          if (ui.parent && dxui.all[ui.parent] && Array.isArray(dxui.all[ui.parent].children)) {
               const children = dxui.all[ui.parent].children
               let index = children.indexOf(ui.id);
               if (index !== -1) {
                    children.splice(index, 1);
               }
          }
          // åˆ é™¤å½“前对象
          ui.obj.lvObjDel();
          delete dxui.all[ui.id];
     }
     // å¼€å§‹é€’归删除
     recursiveDelete(ui);
}
/**
 * åœ¨ä¸»å›¾å±‚加载(切换)已经构建好的 ui å¯¹è±¡ï¼Œ
 * @param {object} ui ä½¿ç”¨build函数构建的 ui å¯¹è±¡
 */
dxui.loadMain = function (ui) {
     if (!ui || !ui.obj) {
          throw new Error("dxui.loadMain:'ui' paramter should not be null")
     }
     // åŠ è½½ä¸»å±å¹•
     utils.GG.NativeDisp.lvScrLoad(ui.obj)
}
/**
 * ä»Žæœ€åŽä¸€ä¸ªç”¨æˆ·æ´»åŠ¨æ˜¾ç¤º(如点击)经过的时间
 * @returns è¿”回从最后一个活动开始的经过时间(毫秒)
 */
dxui.getIdleDuration = function () {
     return utils.GG.NativeDisp.lvDispGetInactiveTime()
}
/**
 * é‡ç½®ç”¨æˆ·æ´»åŠ¨æ˜¾ç¤º(如点击)经过的时间
 */
dxui.trigActivity = function () {
     utils.GG.NativeDisp.lvDispTrigActivity()
}
export default dxui;
vf205_access/dxmodules/dxWatchdog.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
//build 20240425
//看门狗组件,用于监控应用是否卡死,设置一个超时时间,如果超过这个时间没有喂狗,会自动触发设备重启
//注意使用看门狗之前可能需要先初始化gpio
//依赖组件 dxDriver,dxLogger,dxCommon,dxMap,dxGpio
import { watchdogClass } from './libvbar-b-dxwatchdog.so'
import dxMap from './dxMap.js'
import logger from './dxLogger.js'
import dxCommon from './dxCommon.js'
const map = dxMap.get("___watchdog")
const watchdogObj = new watchdogClass();
const watchdog = {}
watchdog.last = new Date().getTime()
/**
 * æ‰“开看门狗设备
 * @param {number} type å¿…å¡«
 * @param {string} id å¥æŸ„id,非必填(若初始化多个实例需要传入唯一id)
 */
watchdog.open = function (type, id) {
    let pointer = watchdogObj.open(type)
    if (pointer === undefined || pointer === null) {
        throw new Error("watchdog.open: open failed")
    }
    dxCommon.handleId("watchdog", id, pointer)
}
/**
 * æŽ§åˆ¶æŒ‡å®šé€šé“开关
 * @param {number} chan é€šé“id,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
watchdog.enable = function (chan, id) {
    let pointer = dxCommon.handleId("watchdog", id)
    return watchdogObj.enable(pointer, chan)
}
/**
 * å¼€å¯çœ‹é—¨ç‹—总定时器
 * @param {*} timeout å¿…å¡«
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
watchdog.start = function (timeout, id) {
    let pointer = dxCommon.handleId("watchdog", id)
    return watchdogObj.start(pointer, timeout)
}
/**
 * åˆ¤æ–­æ˜¯å¦æ˜¯ä¸Šç”µå¤ä½ï¼Œçœ‹é—¨ç‹—是否已经启动
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
watchdog.isPoweron = function (id) {
    let pointer = dxCommon.handleId("watchdog", id)
    return watchdogObj.isPoweron(pointer)
}
/**
 * å–‚狗指定通道
 * @param {*} chan é€šé“id,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
watchdog.restart = function (chan, id) {
    let pointer = dxCommon.handleId("watchdog", id)
    return watchdogObj.restart(pointer, chan)
}
/**
 * å…³é—­çœ‹é—¨ç‹—总定时器
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
watchdog.stop = function (id) {
    let pointer = dxCommon.handleId("watchdog", id)
    return watchdogObj.stop(pointer)
}
/**
 * å…³é—­çœ‹é—¨ç‹—设备
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 * @returns true/false
 */
watchdog.close = function (id) {
    let pointer = dxCommon.handleId("watchdog", id)
    return watchdogObj.close(pointer)
}
/**
 * å¾ªçŽ¯æ£€æŸ¥æ¯ä¸ªçº¿ç¨‹çš„å–‚ç‹—æƒ…å†µï¼Œä»»ä½•ä¸€ä¸ªçº¿ç¨‹æ²¡æœ‰å–‚ç‹—ï¼Œåˆ™ä¸å¯åŠ¨restart
 * @param {number} chan é€šé“id,必填
 * @param {string} id å¥æŸ„id,非必填(需保持和init中的id一致)
 */
watchdog.loop = function (chan, id) {
    const now = new Date().getTime()
    const minus = now - watchdog.last
    if (minus > 3000 || minus < 0) {//每3秒检查一次或者小于0代表操作了往前改时间
        watchdog.last = now
        let keys = map.keys()
        let check = true
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i]
            let value = map.get(key)
            const temp = now - value.now
            if (temp > value.timeout * 1000 && temp < 1700000000) {
                logger.error(`The worker ${key} did not feed the dog in time.`, temp)
                check = false
                break
            }
        }
        if (check) {
            this.restart(chan, id)
        }
    }
}
/**
 * ä¸åŒçš„线程喂狗
 * @param {string} flag çº¿ç¨‹çš„æ ‡è¯†,必填不能为空
 * @param {number} timeout çº¿ç¨‹å¯ä»¥å¤šé•¿æ—¶é—´ä¸å–‚狗(秒),缺省是10秒
 */
watchdog.feed = function (flag, timeout = 10) {
    if (!flag || flag.length <= 0) {
        return
    }
    map.put(flag, { now: new Date().getTime(), timeout: timeout })
}
export default watchdog;
vf205_access/dxmodules/dxWorkerPool.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,167 @@
//build:20240717
//线程池,里面加载多个worker,线程池接收任务或事务后然后派发给线程池里面空闲的worker来执行任务,用于解决多事务处理的瓶颈
//设备资源有限,线程数量不宜太多,另外也不考虑多个线程池的情况,全局只一个
//组件依赖 dxLogger,dxCommon,dxStd
import std from './dxStd.js'
import logger from './dxLogger.js'
import * as os from "os";
//-------------------------variable--------------------
const pool = {}
const isMain = (os.Worker.parent === undefined)
let queueSize = 100
const queue = []
const all = {}
pool.os = os
/**
 * åˆå§‹åŒ–线程池,设置worker个数和缓存队列大小,有可能多个worker都没有空闲,缓存队列可以缓存来不及处理的事务
 * å› ä¸ºworker只能通过主线程创建,所以init函数也只能在主线程里执行
 * æ³¨æ„: worker对应的文件里不能包含while(true)这种死循环,可以用setInteval来实现循环
 * @param {string} file worker对应的文件名,必填,绝对路径,通常以'/app/code/src'开始
 * @param {Object} bus EventBus对象 å¿…å¡«
 * @param {Array} topics è¦è®¢é˜…的主题组 å¿…å¡«
 * @param {number} count çº¿ç¨‹çš„个数,非必填,不能小于1,缺省2,
 * @param {number} maxsize äº‹åŠ¡ç¼“å­˜çš„å¤§å°ï¼Œéžå¿…å¡«ï¼Œç¼ºçœ100,如果超过100,最老的事务被抛弃
 */
pool.init = function (file, bus, topics, count = 2, maxsize = 100) {
    if (!file) {
        throw new Error("pool init:'file' should not be empty")
    }
    if (!bus) {
        throw new Error("pool init:'bus' should not be empty")
    }
    if (!topics) {
        throw new Error("pool init:'topics' should not be empty")
    }
    if (!isMain) {
        throw new Error("pool init should be invoked in main thread")
    }
    if (!std.exist(file)) {
        throw new Error("pool init: file not found:" + file)
    }
    queueSize = maxsize
    if (count <= 1) {
        count = 1
    }
    for (let i = 0; i < count; i++) {
        const id = 'pool__id' + i
        let content = std.loadFile(file) + `
import __pool from '/app/code/dxmodules/dxWorkerPool.js'
__pool.id = '${id}'
const __parent = __pool.os.Worker.parent
__parent.onmessage = function (e) {
    if (!e.data) {
        return
    }
    let fun = __pool.callbackFunc
    if (fun) {
        try {
            fun(e.data)
            __parent.postMessage({ id: __pool.id })//通知处理完了idle
        } catch (err) {
            __parent.postMessage({ id: __pool.id, error: err.stack })//通知处理完了idle,但是失败了
        }
    }
}
            `
        let newfile = file + '_' + id + '.js'
        std.saveFile(newfile, content)
        let worker = new os.Worker(newfile)
        all[id] = { isIdle: true, worker: worker }
        worker.onmessage = function (data) {
            if (!data.data) {
                return
            }
            const id = data.data.id
            if (id) {//通知处理完成的消息
                all[id].isIdle = true
                if (data.data.error) {
                    logger.error(`worker ${id} callback error:${data.data.error}`)
                }
            } else {
                const topic = data.data.topic
                if (topic) {//bus.fire出来的消息
                    bus.fire(topic, data.data.data)
                }
            }
        }
    }
    for (let topic of topics) {
        bus.on(topic, function (d) {
            push({ topic: topic, data: d })
        })
    }
    std.setInterval(function () {
        Object.keys(all).forEach(key => {
            const obj = all[key]
            if (obj.isIdle) {
                let event = take()
                if (event) {
                    obj.isIdle = false
                    obj.worker.postMessage(event)
                }
            }
        });
    }, 5)
}
/**
 * è¿”回线程的唯一标识id
 * @returns worker唯一标识
 */
pool.getWorkerId = function () {
    if (isMain) {
        return 'main'
    } else {
        return pool.id
    }
}
/**
 * è®¢é˜…EventBus ä¸Šçš„事务主题,可以订阅多个主题,这个函数也只能在主线程里执行
 * @param {Object} bus EventBus对象
 * @param {Array} topics è¦è®¢é˜…的主题组
 */
pool.on = function (bus, topics) {
    if (!bus) {
        throw new Error("pool onEventBus:'bus' should not be empty")
    }
    if (!topics) {
        throw new Error("pool onEventBus:'topics' should not be empty")
    }
    if (!isMain) {
        throw new Error("pool onEventBus should be invoked in main thread")
    }
}
pool.callbackFunc = null
/**
 * worker线程订阅线程池的事件,不用选择特定的主题,线程池关注的所有事件都会处理,
 * è¿™ä¸ªå‡½æ•°å¿…须在worker线程里执行,不能在主线程执行
 * @param {function} cb äº‹ä»¶å¤„理的回调函数,必填
 */
pool.callback = function (cb) {
    if (!cb || (typeof cb) != 'function') {
        throw new Error("pool on :The 'callback' should be a function");
    }
    if (isMain) {
        throw new Error("pool on should not be invoked in main thread")
    }
    pool.callbackFunc = cb
}
function push(item) {
    if (queue.length >= queueSize) {
        const first = JSON.stringify(queue[0])
        logger.error(`pool queue is full,removing oldest element: ${first}`)
        queue.shift(); // ç§»é™¤æœ€è€çš„元素
    }
    queue.push(item);
}
function take() {
    if (queue.length === 0) {
        return null; // é˜Ÿåˆ—为空时返回 null
    }
    return queue.shift(); // ç§»é™¤å¹¶è¿”回最早添加的元素
}
export default pool
vf205_access/dxmodules/faceWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
//build:20240606
//用于简化face组件微光通信协议的使用,把face封装在这个worker里,使用者只需要订阅eventbus的事件就可以监听face
import log from './dxLogger.js'
import face from './dxFace.js'
import std from './dxStd.js'
function run() {
    face.worker.beforeLoop()
    log.info('face start......')
    std.setInterval (function() {
        try {
            face.worker.loop()
        } catch (error) {
            log.error(error)
        }
    },10)
}
try {
    run()
} catch (error) {
    log.error(error)
}
vf205_access/dxmodules/gpioKeyWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
//build:20240524
//用于简化gpioKey组件微光通信协议的使用,把gpioKey封装在这个worker里,使用者只需要订阅eventbus的事件就可以监听gpioKey
import log from './dxLogger.js'
import gpioKey from './dxGpioKey.js'
import * as os from "os";
import std from './dxStd.js'
function run() {
    gpioKey.worker.beforeLoop()
    log.info('gpioKey start......')
    std.setInterval(() => {
        try {
            gpioKey.worker.loop()
        } catch (error) {
            log.error(error)
        }
    },10)
}
try {
    run()
} catch (error) {
    log.error(error)
}
vf205_access/dxmodules/libJLReader.so
Binary files differ
vf205_access/dxmodules/libalc.so
Binary files differ
vf205_access/dxmodules/libid_jpg.so
Binary files differ
vf205_access/dxmodules/libid_jpg_codec.so
Binary files differ
vf205_access/dxmodules/libie_jpg.so
Binary files differ
vf205_access/dxmodules/libie_jpg_codec.so
Binary files differ
vf205_access/dxmodules/liblombo_jpeg.so
Binary files differ
vf205_access/dxmodules/libvbar-b-dxface.so
Binary files differ
vf205_access/dxmodules/libvbar-b-dxgpio.so
Binary files differ
vf205_access/dxmodules/libvbar-b-dxpwm.so
Binary files differ
vf205_access/dxmodules/libvbar-b-dxwatchdog.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-audio_gain.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-capturer.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-capturer_calibration.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-display.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-face.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-gpio.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-memory.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-pwm.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-soc.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-tts.so
Binary files differ
vf205_access/dxmodules/libvbar-drv-watchdog.so
Binary files differ
vf205_access/dxmodules/libvbar-m-alsa.so
Binary files differ
vf205_access/dxmodules/libvbar-m-capturer.so
Binary files differ
vf205_access/dxmodules/libvbar-m-channel.so
Binary files differ
vf205_access/dxmodules/libvbar-m-common.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxalsa.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxcapturer.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxcapturer_calibration.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxchannel.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxcommon.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxeid.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxhttp.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxkey.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxmap.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxmqtt.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxnet.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxsqlite.so
Binary files differ
vf205_access/dxmodules/libvbar-m-dxui.so
Binary files differ
vf205_access/dxmodules/libvbar-m-eid.so
Binary files differ
vf205_access/dxmodules/libvbar-m-key.so
Binary files differ
vf205_access/dxmodules/libvbar-m-net.so
Binary files differ
vf205_access/dxmodules/libvbar-m-vgmqtt.so
Binary files differ
vf205_access/dxmodules/libvbar-p-dxnfc.so
Binary files differ
vf205_access/dxmodules/libvbar-p-nfc.so
Binary files differ
vf205_access/dxmodules/libvccore.so
Binary files differ
vf205_access/dxmodules/libyuv.so
Binary files differ
vf205_access/dxmodules/mqttWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
//build:20240524
//用于简化mqtt组件微光通信协议的使用,把mqtt封装在这个worker里,使用者只需要订阅eventcenter的事件就可以监听mqtt
import log from './dxLogger.js'
import net from './dxNet.js'
import mqtt from './dxMqtt.js'
import dxMap from './dxMap.js'
import std from './dxStd.js'
import * as os from "os";
const map = dxMap.get('default')
const id = "{{id}}"
const options = map.get("__mqtt__run_init" + id)
let connected = false
function run() {
    mqtt.init(options.mqttAddr, options.clientId, options.username, options.password, options.prefix, options.qos, options.willTopic, options.willMessage, options.id)
    log.info('mqtt start......,id =', id)
    os.sleep(2000)//等待2秒
    __bus.on(mqtt.RECONNECT, (options) => {
        mqtt.destroy(options.id)
        mqtt.init(options.mqttAddr, options.clientId, options.username, options.password, options.prefix, options.qos, options.willTopic, options.willMessage, options.id)
    })
    std.setInterval(() => {
        try {
            if (mqtt.isConnected(options.id) && net.getStatus().connected) {
                if (!connected) {
                    _fireChange(true)
                    if (options.subs) {
                        mqtt.subscribes(options.subs, options.qos, options.id)
                    }
                }
            } else {
                if (connected) {
                    _fireChange(false)
                }
                // é‡è¿ž
                mqtt.reconnect(options.willTopic, options.willMessage, options.id)
                os.sleep(2000)//重连后等待2秒
            }
        } catch (error) {
            log.error(error)
        }
    }, 3000)
    std.setInterval(() => {
        // è¿žæŽ¥æˆåŠŸåŽè¿›å…¥æ¶ˆæ¯ç›‘å¬
        if (connected) {
            if (!mqtt.msgIsEmpty(options.id)) {
                let msg = mqtt.receive(options.id)
                __bus.fire(mqtt.RECEIVE_MSG + options.id, msg)//bus.newworker的时候会import eventbus as __bus
            }
        }
    }, 10);
}
try {
    run()
} catch (error) {
    log.error(error)
}
function _fireChange(status) {
    __bus.fire(mqtt.CONNECTED_CHANGED + options.id, status ? 'connected' : 'disconnected')//bus.newworker的时候会import eventbus as __bus
    connected = status
}
vf205_access/dxmodules/netWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
//build:20240525
//用于简化net组件微光通信协议的使用,把net封装在这个worker里,使用者只需要订阅eventcenter的事件就可以监听net
import log from './dxLogger.js'
import net from './dxNet.js'
import dxMap from './dxMap.js'
import std from './dxStd.js'
const map = dxMap.get('default')
const options = map.get("__net__run_init")
function run() {
    net.worker.beforeLoop(options)
    log.info('net worker start......')
    std.setInterval (function() {
        try {
            net.worker.loop()
        } catch (error) {
            log.error(error)
        }
    },100)
}
try {
    run()
} catch (error) {
    log.error(error)
}
vf205_access/dxmodules/nfcWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
//build:20240524
//用于简化nfc组件微光通信协议的使用,把nfc封装在这个worker里,使用者只需要订阅eventcenter的事件就可以监听nfc
import log from './dxLogger.js'
import nfc from './dxNfc.js'
import dxMap from './dxMap.js'
import std from './dxStd.js'
import * as os from "os";
const map = dxMap.get('default')
const options = map.get("__nfc__run_init")
function run() {
    nfc.worker.beforeLoop(options)
    log.info('nfc start......')
    std.setInterval(() => {
        try {
            nfc.worker.loop(options)
        } catch (error) {
            log.error(error)
        }
    }, 10)
}
try {
    run()
} catch (error) {
    log.error(error)
}
vf205_access/dxmodules/uiBase.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,582 @@
//build:20240524
/**
 * UI的基类,其它控件都会继承,子类不允许修改对应的函数行为,这个js不需要直接引用和使用
 */
import utils from "./uiUtils.js"
import logger from './dxLogger.js'
import * as os from "os"
const uibase = {}
/**
* ä¿®æ”¹æˆ–获取控件的宽度
* @param {number} w éžå¿…填,如果不填是获取宽度,否则就是修改宽度
*/
uibase.width = function (w) {
     if (!utils.validateNumber(w)) {
          return this.obj.getWidth()
     }
     this.obj.lvObjSetWidth(w)
}
/**
* ä¿®æ”¹æˆ–获取控件的高度
* @param {number} h éžå¿…填,如果不填就是获取高度,否则就是修改高度
*/
uibase.height = function (h) {
     if (!utils.validateNumber(h)) {
          return this.obj.getHeight()
     }
     this.obj.lvObjSetHeight(h)
}
/**
 * èŽ·å–åŽ»é™¤è¾¹æ¡†ã€å†…è¾¹è·çš„å®½åº¦
 * @returns
 */
uibase.contentWidth = function () {
     return this.obj.lvObjGetContentWidth()
}
/**
 * èŽ·å–åŽ»é™¤è¾¹æ¡†ã€å†…è¾¹è·çš„é«˜åº¦
 * @returns
 */
uibase.contentHeight = function () {
     return this.obj.lvObjGetContentHeight()
}
/**
 * èŽ·å–ä¸Šæ–¹æ»šåŠ¨è·ç¦»
 * @returns
 */
uibase.scrollTop = function () {
     return this.obj.getScrollTop()
}
/**
 * èŽ·å–ä¸‹æ–¹æ»šåŠ¨è·ç¦»
 * @returns
 */
uibase.scrollBottom = function () {
     return this.obj.getScrollBottom()
}
/**
 * èŽ·å–å·¦æ–¹æ»šåŠ¨è·ç¦»
 * @returns
 */
uibase.scrollLeft = function () {
     return this.obj.getScrollLeft()
}
/**
 * èŽ·å–å³æ–¹æ»šåŠ¨è·ç¦»
 * @returns
 */
uibase.scrollRight = function () {
     return this.obj.getScrollRight()
}
/**
* ä¿®æ”¹æŽ§ä»¶çš„宽度和高度
* @param {number} w å¿…å¡«
* @param {number} h å¿…å¡«
*/
uibase.setSize = function (w, h) {
     let err = 'dxui.setSize: width or height should not be empty'
     utils.validateNumber(w, err)
     utils.validateNumber(h, err)
     this.obj.lvObjSetSize(w, h)
}
/**
* ä¿®æ”¹æˆ–获取控件相当于父对象的x坐标
* @param {number} x éžå¿…填,如果不填就是获取x坐标,否则就是修改x坐标
*/
uibase.x = function (x) {
     if (!utils.validateNumber(x)) {
          return this.obj.getX()
     }
     this.obj.lvObjSetX(x)
}
/**
* ä¿®æ”¹æˆ–获取控件相当于父对象的x坐标
* @param {number} y éžå¿…填,如果不填就是获取y坐标,否则就是修改y坐标
*/
uibase.y = function (y) {
     if (!utils.validateNumber(y)) {
          return this.obj.getY()
     }
     this.obj.lvObjSetY(y)
}
/**
* ä¿®æ”¹æŽ§ä»¶ç›¸å¯¹çˆ¶å¯¹è±¡çš„x和y坐标
* @param {number} x å¿…å¡«
* @param {number} y å¿…å¡«
*/
uibase.setPos = function (x, y) {
     let err = 'dxui.setPos: x or y should not be empty'
     utils.validateNumber(x, err)
     utils.validateNumber(y, err)
     this.obj.lvObjSetPos(x, y)
}
/**
 * æŠŠæŽ§ä»¶ç§»åŠ¨åˆ°æœ€ä¸Šå±‚ï¼Œç›¸å½“äºŽçˆ¶å¯¹è±¡æœ€åŽä¸€ä¸ªåˆ›å»ºçš„å­æŽ§ä»¶ï¼Œä¼šè¦†ç›–å…¶å®ƒæ‰€æœ‰å­æŽ§ä»¶
 */
uibase.moveForeground = function () {
     this.obj.moveForeground()
}
/**
 * æŠŠæŽ§ä»¶ç§»åŠ¨åˆ°æœ€åº•å±‚ï¼Œç›¸å½“äºŽçˆ¶å¯¹è±¡ç¬¬ä¸€ä¸ªåˆ›å»ºçš„å­æŽ§ä»¶ï¼Œä¼šè¢«å…¶å®ƒæ‰€æœ‰å­æŽ§ä»¶è¦†ç›–
 */
uibase.moveBackground = function () {
     this.obj.moveBackground()
}
/**
 * è®¢é˜…事件,支持的事件类型参考utils.EVENT
 * @param {number} type æžšä¸¾utils.EVENT,比如点击、长按等
 * @param {function} cb äº‹ä»¶è§¦å‘的回调函数(不能是匿名函数)
 * @param {object} ud ç”¨æˆ·æ•°æ®
 */
uibase.on = function (type, cb, ud) {
     this.obj.addEventCb(() => {
          if (cb) {
               cb({ target: this, ud: ud })
          }
     }, type)
}
/**
 * å‘送事件,比如模拟点击按钮,可以给按钮发送CLICK事件
 * @param {number} type æžšä¸¾utils.EVENT,比如点击、长按等
 */
uibase.send = function (type) {
     NativeObject.APP.NativeComponents.NativeEvent.lvEventSend(this.obj, type)
}
/**
 * éšè—ui对象
 */
uibase.hide = function () {
     if (!this.obj.hasFlag(1)) {
          this.obj.lvObjAddFlag(1);
     }
}
/**
 * åˆ¤æ–­æ˜¯å¦éšè—
 * @returns
 */
uibase.isHide = function () {
     return this.obj.hasFlag(1);
}
/**
 * æ˜¾ç¤ºå·²ç»éšè—çš„ui对象
 */
uibase.show = function () {
     if (this.obj.hasFlag(1)) {
          this.obj.lvObjClearFlag(1);
     }
}
/**
 * ç¦å¯ç”¨å¯¹è±¡
 * @param {*} en false/true,true是禁用,false是启用
 */
uibase.disable = function (en) {
     if (en) {
          this.obj.addState(utils.STATE.DISABLED)
     } else {
          this.obj.clearState(utils.STATE.DISABLED)
     }
}
/**
 * æ˜¯å¦å¯ç‚¹å‡»å¯¹è±¡
 * @param {*} en false/true,true是可点击,false是不可点击
 */
uibase.clickable = function (en) {
     if (en) {
          this.obj.lvObjAddFlag(utils.OBJ_FLAG.CLICKABLE)
     } else {
          this.obj.lvObjClearFlag(utils.OBJ_FLAG.CLICKABLE)
     }
}
/**
 * åˆ¤æ–­æ˜¯å¦ç¦å¯ç”¨
 * @returns true是已禁用,false是已启用
 */
uibase.isDisable = function () {
     return this.obj.hasState(utils.STATE.DISABLED)
}
/**
 * èšç„¦å¯¹è±¡
 * @param {*} en false/true,true是聚焦,false是取消聚焦
 */
uibase.focus = function (en) {
     if (en) {
          this.obj.addState(utils.STATE.FOCUSED)
     } else {
          this.obj.clearState(utils.STATE.FOCUSED)
     }
}
/**
 * åˆ¤æ–­æ˜¯å¦èšç„¦
 * @returns true是已聚焦,false是没聚焦
 */
uibase.isFocus = function () {
     return this.obj.hasState(utils.STATE.FOCUSED)
}
/**
 * è®¾ç½®ui的样式,可以通过一个个样式单独设置,也可以先定义样式对象,然后和ui对象绑定
 * ç»™ui对象和样式对象绑定,可以绑定到不同的部分或不同的状态
 * @param {object} style  style.js build函数返回的对象
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.addStyle = function (style, type) {
     if (!style || !style.obj) {
          throw new Error('dxui.addStyle: style should not be null')
     }
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjAddStyle(style.obj, type);
}
/**
* è®¾ç½®å·¦å³ä¸Šä¸‹çš„内边距都为一个值
* @param {number} pad è¾¹è·å€¼
* @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
*/
uibase.padAll = function (pad, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStylePadAll(pad, type)
}
/**
 * è®¾ç½®/获取右内边距都为一个值
 * @param {number} pad è¾¹è·å€¼
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.padRight = function (pad, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     if (!utils.validateNumber(pad)) {
          return this.obj.getStylePadRight(type)
     }
     this.obj.setStylePadRight(pad, type)
}
/**
  * è®¾ç½®/获取左内边距都为一个值
  * @param {number} pad è¾¹è·å€¼
  * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
  */
uibase.padLeft = function (pad, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     if (!utils.validateNumber(pad)) {
          return this.obj.getStylePadLeft(type)
     }
     this.obj.setStylePadLeft(pad, type)
}
/**
  * è®¾ç½®/获取上内边距都为一个值
  * @param {number} pad è¾¹è·å€¼
  * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
  */
uibase.padTop = function (pad, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     if (!utils.validateNumber(pad)) {
          return this.obj.getStylePadTop(type)
     }
     this.obj.setStylePadTop(pad, type)
}
/**
  * è®¾ç½®/获取下内边距都为一个值
  * @param {number} pad è¾¹è·å€¼
  * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
  */
uibase.padBottom = function (pad, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     if (!utils.validateNumber(pad)) {
          return this.obj.getStylePadBottom(type)
     }
     this.obj.setStylePadBottom(pad, type)
}
/**
 * è®¾ç½®/获取边框宽度
 * @param {number} w
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.borderWidth = function (w, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     if (!utils.validateNumber(w)) {
          return this.obj.lvObjGetStyleBorderWidth(type)
     }
     this.obj.lvObjSetStyleBorderWidth(w, type)
}
/**
 * è®¾ç½®è¾¹æ¡†é¢œè‰²
 * @param {number} color  æ”¯æŒæ•°å­—类型:比如0x34ffaa;字符串类型(#开头),比如:'#34ffaa'
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.setBorderColor = function (color, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.setStyleBorderColor(utils.colorParse(color), type)
}
/**
 * è®¾ç½®è¾¹åœ†è§’
 * @param {number} r
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.radius = function (r, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleRadius(r, type)
}
/**
 * è®¾ç½®èƒŒæ™¯é€æ˜Žåº¦ï¼Œå€¼èŒƒå›´æ˜¯0-100,值越小越好
 * @param {number} opa å¿…须是0-100
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.bgOpa = function (opa, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleBgOpa(utils.OPA_MAPPING(opa), type)
}
/**
 * è®¾ç½®èƒŒæ™¯é¢œè‰²
 * @param {any} color æ”¯æŒæ•°å­—类型:比如0x34ffaa;字符串类型(#开头),比如:'#34ffaa'
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.bgColor = function (color, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleBgColor(utils.colorParse(color), type)
}
/**
 * è®¾ç½®é˜´å½±
 * @param {number} width é˜´å½±å®½åº¦
 * @param {number} x æ°´å¹³åç§»
 * @param {number} y åž‚直偏移
 * @param {number} spread æ‰©æ•£è·ç¦»
 * @param {number} color é¢œè‰²
 * @param {number} opa é€æ˜Žåº¦ï¼Œå¿…须是0-100
 * @param {number} type å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.shadow = function (width, x, y, spread, color, opa, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleShadowWidth(width, type)
     this.obj.lvObjSetStyleShadowOfsX(x, type)
     this.obj.lvObjSetStyleShadowOfsY(y, type)
     this.obj.lvObjSetStyleShadowSpread(spread, type)
     this.obj.setStyleShadowColor(color, type)
     this.obj.setStyleShadowOpa(utils.OPA_MAPPING(opa), type)
}
/**
 * è®¾ç½®æ–‡æœ¬é¢œè‰²
 * @param {any} color  æ”¯æŒæ•°å­—类型:比如0x34ffaa;字符串类型(#开头),比如:'#34ffaa'
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.textColor = function (color, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleTextColor(utils.colorParse(color), type)
}
/**
 * è®¾ç½®æ–‡æœ¬å¯¹é½æ–¹å¼
 * @param {number} align  å‚考utils.TEXT_ALIGN
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.textAlign = function (align, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleTextAlign(align, type)
}
/**
 * è®¾ç½®æ–‡æœ¬å­—体
 * @param {object} font font.js里build返回的对象
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.textFont = function (font, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     if (!font || !font.obj) {
          throw new Error("dxui.textFont: 'font' parameter should not be null")
     }
     this.obj.lvObjSetStyleTextFont(font.obj, type)
}
/**
 * è®¾ç½®çº¿å¯¹è±¡(line)颜色
 * @param {any} color  æ”¯æŒæ•°å­—类型:比如0x34ffaa;字符串类型(#开头),比如:'#34ffaa'
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.lineColor = function (color, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleLineColor(utils.colorParse(color), type)
}
/**
 * è®¾ç½®çº¿å¯¹è±¡(line)宽度
 * @param {number} w
 * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
 */
uibase.lineWidth = function (w, type) {
     if (!utils.validateNumber(type)) {
          type = 0
     }
     this.obj.lvObjSetStyleLineWidth(w, type)
}
/**
 * è®¾ç½®çº¿å¯¹è±¡(line)圆角
 * @param {boolean} enable true/false
 */
uibase.lineRound = function (enable) {
     this.obj.lvObjSetStyleLineRounded(enable)
}
/**
 * è®¾ç½®ui对象的滚动条显示方式
 * @param {boolean} state ture/false
 */
uibase.scrollbarMode = function (state) {
     this.obj.lvObjSetScrollbarMode(state)
}
/**
 * è®¾ç½®ui对象是否支持滚动
 * @param {boolean} state
 */
uibase.scroll = function (state) {
     if (state) {
          this.obj.lvObjAddFlag(16)
     } else {
          this.obj.lvObjClearFlag(16)
     }
}
/**
 * å°†å¯¹è±¡ä¸Žå…¶å®ƒå‚照对象对齐
 * @param {object} ref å‚照对象
 * @param {number} type å¯¹é½çš„æ–¹å‘,参考dxui.Utils.ALIGN枚举
 * @param {number} x åç§»çš„x
 * @param {number} y åç§»çš„y
 */
uibase.alignTo = function (ref, type, x, y) {
     if (!ref || !ref.obj) {
          throw new Error("dxui.alignto: 'ref' parameter should not be null")
     }
     this.obj.lvObjAlignTo(ref.obj, type, x, y)
}
/**
 * å°†å¯¹è±¡ä¸Žçˆ¶å¯¹è±¡å¯¹é½
 * @param {number} type å¯¹é½çš„æ–¹å‘,参考dxui.Utils.ALIGN枚举
 * @param {number} x åç§»çš„x
 * @param {number} y åç§»çš„y
 */
uibase.align = function (type, x, y) {
     this.obj.lvObjAlign(type, x, y)
}
/**
 * ä¼¸ç¼©ç›’布局,可以更加灵活得定位、排列和分布元素,使得创建响应式和可伸缩的布局变得更加容易。
 * å®ƒåŸºäºŽä¸€ä¸ªå®¹å™¨ï¼Œå’Œå†…部的一些弹性项目,下面是使用这种布局的一些概念:
 * 1、容器:容器包含了内部的弹性项目,可以使里面项目从左向右或从右向左等规则排列。
 * 2、主轴和侧轴:主轴,是容器中项目的主要排列方式,通常是水平方向或垂直方向,可以让项目们水平排列或纵向排列。
 *   ä¾§è½´ï¼Œä¸Žä¸»è½´åž‚直的轴向,可以规定项目们在侧轴上的排列方式。
 *   ä¸»è½´å’Œä¾§è½´ç”±flexFlow()设置,主要有ROW(水平方向)、COLUMN(垂直方向)两种,带有WRAP后缀的在项目们超出容器时自动换行,带有REVERSE后缀的与默认排列方向相反,即为从右到左排列(若主轴是垂直方向则为从下到上排列)。
 * 3、主轴对齐方式:START(默认主轴顺序)、END(默认主轴顺序相反)、CENTER(在主轴方向上居中)、SPACE_EVENLY(在主轴上均匀分布,两两之间距离相等)、SPACE_AROUND(在主轴上均匀分布,每个项目平分主轴上的距离)、SPACE_BETWEEN(两端顶格,中间均分),由flexAlign()设置。
 * 4、侧轴对齐方式:将每一行或每一列看作一个项目,在侧轴方向上对齐,对齐方式同主轴,由flexAlign()设置。
 * 5、整体对齐方式:将容器内所有项目看作一个整体,在容器中对齐,对齐方式同主轴,由flexAlign()设置。
 * @param {number} type ä¸»è½´å’Œä¾§è½´çš„设置
 */
uibase.flexFlow = function (type) {
     this.obj.lvObjSetFlexFlow(type)
}
/**
 *
 * @param {number} main å­å…ƒç´ æŒ‰ä¸»è½´æ–¹å‘的对齐方式
 * @param {number} cross å­å…ƒç´ æŒ‰ä¾§è½´æ–¹å‘的对齐方式
 * @param {number} track æ‰€æœ‰å­å…ƒç´ å¯¹äºŽå®¹å™¨çš„对齐方式
 */
uibase.flexAlign = function (main, cross, track) {
     this.obj.lvObjSetFlexAlign(main, cross, track)
}
/**
 * æ›´æ–°ä¸€ä¸ªæŽ§ä»¶çš„尺寸,当获取一个控件的尺寸为0时可以先调用,相当于更新显示缓存。
 */
uibase.update = function () {
     this.obj.lvObjUpdateLayout()
}
/**
 * æ·»åŠ ä¸€ä¸ªæŽ§ä»¶çš„çŠ¶æ€
 * @param {number} state çŠ¶æ€æžšä¸¾
 */
uibase.addState = function (state) {
     this.obj.addState(state)
}
/**
 * åˆ é™¤ä¸€ä¸ªæŽ§ä»¶çš„状态,如果想让一个聚焦输入框失焦,可以调用此方法删除FOCUSED状态
 * @param {number} state çŠ¶æ€æžšä¸¾
 */
uibase.clearState = function (state) {
     this.obj.clearState(state)
}
/**
 * åˆ¤æ–­ä¸€ä¸ªæŽ§ä»¶æ˜¯å¦æ‹¥æœ‰çŠ¶æ€ï¼Œæƒ³åˆ¤æ–­ä¸€ä¸ªè¾“å…¥æ¡†æ˜¯å¦è¢«èšç„¦äº†ï¼Œå¯ä»¥ä½¿ç”¨æ­¤æ–¹æ³•å¹¶ä¼ å…¥FOCUSED参数
 * @param {number} state çŠ¶æ€æžšä¸¾
 * @returns true/false
 */
uibase.hasState = function (state) {
     return this.obj.hasState(state)
}
/**
 * é‡ç»˜ä¸€ä¸ªæŽ§ä»¶ï¼Œå¼ºåˆ¶åˆ·æ–°æŽ§ä»¶çš„缓存,可以强制解决花屏的问题,但是如果死循环中调用会降低性能
 */
uibase.invalidate = function () {
     this.obj.invalidate()
}
/**
 * æ»šåŠ¨æŸä¸ªå­æŽ§ä»¶ç›´è‡³æ˜¾ç¤ºå‡ºæ¥ï¼Œå¦‚æžœæƒ³å°†ä¸€ä¸ªè¢«æ»šåŠ¨è‡³å®¹å™¨å¤–å¯¼è‡´çœ‹ä¸è§çš„é¡¹ç›®æ»šåŠ¨è‡³èƒ½è¢«çœ‹è§çš„ä½ç½®ï¼Œè°ƒç”¨æ­¤æ–¹æ³•ã€‚
 * @param {boolean} en æ˜¯å¦å¼€å¯åŠ¨ç”»ï¼Œå¼€å¯ä¼šç¼“æ…¢æ»šåŠ¨å‡ºæ¥ï¼Œå…³é—­åˆ™ç›´æŽ¥è·³å‡ºã€‚
 * @param {boolean} notRecursive é»˜è®¤é€’归,适用于一般滚动和滚动嵌套控件
 */
uibase.scrollToView = function (en, isRecursive) {
     if (isRecursive) {
          this.obj.scrollToView(en)
     } else {
          this.obj.scrollToViewRecursive(en)
     }
}
/**
 * æ»šåŠ¨ä¸€ä¸ªæŽ§ä»¶çš„x方向
 * @param {number} x æ»šåЍx轴距离
 * @param {boolean} en æ˜¯å¦å¼€å¯åŠ¨ç”»
 */
uibase.scrollToX = function (x, en) {
     this.obj.scrollToX(x, en)
}
/**
 * æ»šåŠ¨ä¸€ä¸ªæŽ§ä»¶çš„y方向
 * @param {number} y æ»šåЍy轴距离
 * @param {boolean} en æ˜¯å¦å¼€å¯åŠ¨ç”»
 */
uibase.scrollToY = function (y, en) {
     this.obj.scrollToY(y, en)
}
/**
 * å…ƒç´ å¿«ç…§ï¼ˆå…¶å®žå°±æ˜¯æˆªå›¾ï¼Œå¦‚果想保存全屏截图,可以对屏幕对象使用此方法)
 * @param {string} fileName å¿…填,保存快照的文件名(注意后缀应与格式对应)
 * @param {number} type éžå¿…填,缺省png,快照格式 0:bmp/1:png/2:jpg(jpeg)
 * @param {number} cf éžå¿…填,一种RGB颜色存储格式
 */
uibase.snapshot = function (fileName, type = 1, cf = NativeObject.APP.NativeComponents.NativeEnum.LV_IMG_CF_TRUE_COLOR_ALPHA) {
     if (!fileName) {
          return
     }
     // é»˜è®¤å­˜å‚¨åœ¨/app/data/snapshot位置
     os.mkdir("/app/data/snapshot/")
     this.obj.lvSnapshotTake(cf, "/app/data/snapshot/" + fileName, type)
}
export default uibase;
vf205_access/dxmodules/uiButton.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
//build:20240311
//button控件 ç›¸å¯¹åŸºç±»æ²¡æœ‰æ–°åŠŸèƒ½
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let button = {}
button.build = function (id, parent) {
    let temp = utils.validateBuild(button.all, id, parent, 'button')
    let my = { type: 'button' }
    my.obj = new utils.GG.NativeButton({ uid: id }, temp)
    my.id = id
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default button;
vf205_access/dxmodules/uiButtons.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
//build:20240314
//按钮组控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let buttons = {}
buttons.build = function (id, parent) {
    let temp = utils.validateBuild(buttons.all, id, parent, 'buttons')
    let my = {type: 'buttons'}
    my.obj = new utils.GG.NativeBtnmatrix({ uid: id }, temp)
    my.id = id
    /**
     * è®¾ç½®button组对应的数据,必须是数组格式,示例如下:表示三行按钮,总共12个按钮
     * ["1", "2", "3", "0", "\n",
     * "4", "5", "6", "取消", "\n",
     *  "7", "8", "9", "确认", ""]
     * @param {array} d éžå¿…填,如果没有填或者不是object类型就是获取数据
     */
    my.data = function (d) {
        if (utils.validateObject(d)) {
            this.obj.lvBtnmatrixSetMap(d)
        } else {
            return this.obj.lvBtnmatrixGetMap()
        }
    }
    /**
     * ç‚¹å‡»æŒ‰é’®ç»„里任何一个按钮,调用selectedData来获取点击按钮的id和文本
     * è¿”回示例: {id:11,text:'取消'}
     */
    my.clickedButton = function () {
        let id = this.obj.lvBtnmatrixGetSelectedBtn();
        if (id == 0xFFFF) {
            // ç‚¹å‡»æŒ‰é’®ç»„边界会出现0xFFFF非法值,返回空
            return { id: null, text: null }
        }
        let txt = this.obj.lvBtnmatrixGetBtnText(id);
        return { id: id, text: txt }
    }
    /**
     * è®¾ç½®æŒ‰é’®ç»„里某一个特定按钮的状态,可以改成选中,不可用之类的
     * @param {number} id æŒ‰é’®çš„索引,从0开始从左到右从上到下,也是点击按钮clickedButton返回的id
     * @param {number} state å‚考dxui.Utils.BUTTONS_STATE
     */
    my.setState = function (id, state) {
        this.obj.lvBtnmatrixSetBtnCtrl(id, state)
    }
    /**
     * æ¸…除按钮组里某一个特定按钮的已经设置好的状态
     * @param {number} id æŒ‰é’®çš„索引,从0开始从左到右从上到下,也是点击按钮clickedButton返回的id
     * @param {number} state å‚考dxui.Utils.BUTTONS_STATE
     */
    my.clearState = function (id, state) {
        this.obj.lvBtnmatrixClearBtnCtrl(id, state)
    }
    /**
     * è®¾ç½®æŒ‰é’®ç»„里所有按钮的状态,可以改成选中,不可用之类的
     * @param {number} state å‚考dxui.Utils.BUTTONS_STATE
     */
    my.setAllState = function (state) {
        this.obj.lvBtnmatrixSetBtnCtrlAll(state)
    }
    /**
     * æ¸…除按钮组里所有按钮的已经设置好的状态
     * @param {number} state å‚考dxui.Utils.BUTTONS_STATE
     */
    my.clearAllState = function (state) {
        this.obj.lvBtnmatrixClearBtnCtrlAll(state)
    }
    /**
     * è®¾ç½®æŸä¸ªid的按钮宽度占用几格
     * @param {number} id æŒ‰é’®åºå·ï¼Œä»Ž0开始编号
     * @param {number} width å®½åº¦è·¨è¶Šæ ¼å­æ•°é‡
     */
    my.setBtnWidth = function (id, width) {
        this.obj.lvBtnmatrixSetBtnWidth(id, width)
    }
    /**
     * è®¾ç½®æŸä¸ªid的按钮图标
     * @param {number} id æŒ‰é’®åºå·ï¼Œä»Ž0开始编号
     * @param {string} src å›¾æ ‡æ–‡ä»¶è·¯å¾„
     */
    my.setBtnIcon = function (id, src) {
        this.obj.addEventCb((e) => {
            // èŽ·å–ç»˜åˆ¶æŽ§ä»¶å¯¹è±¡
            let dsc = e.lvEventGetDrawPartDsc()
            // å¦‚果是绘制第id个按钮
            if (dsc.type == utils.ENUM.LV_BTNMATRIX_DRAW_PART_BTN && dsc.id == id) {
                // èŽ·å–å›¾ç‰‡ä¿¡æ¯
                let header = utils.GG.NativeDraw.lvImgDecoderGetInfo(src)
                // å®šä¹‰ä¸€å—区域,居中显示,注意:尺寸转area需要-1,area转尺寸需要+1
                let x1 = dsc.draw_area.x1 + (dsc.draw_area.x2 - dsc.draw_area.x1 + 1 - header.w) / 2;
                let y1 = dsc.draw_area.y1 + (dsc.draw_area.y2 - dsc.draw_area.y1 + 1 - header.h) / 2;
                let x2 = x1 + header.w - 1;
                let y2 = y1 + header.h - 1;
                let area = utils.GG.NativeArea.lvAreaSet(x1, y1, x2, y2)
                // ç»˜åˆ¶å›¾ç‰‡ä¿¡æ¯
                let img_draw_dsc = utils.GG.NativeDraw.lvDrawImgDscInit()
                // ç»˜åˆ¶å›¾ç‰‡
                utils.GG.NativeDraw.lvDrawImg(dsc.dsc, img_draw_dsc, area, src)
            }
        }, utils.ENUM.LV_EVENT_DRAW_PART_END)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all,comp,parent)
    return comp;
}
export default buttons;
vf205_access/dxmodules/uiCheckbox.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
//build:20240329
//checkbox控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let checkbox = {}
checkbox.build = function (id, parent) {
    let temp = utils.validateBuild(checkbox.all, id, parent, 'checkbox')
    let my = { type: 'checkbox' }
    my.obj = new utils.GG.NativeCheckbox({ uid: id }, temp)
    my.id = id
    /**
     * èŽ·å–/设置文字
     * @param {string} text è®¾ç½®æ–‡å­—
     * @returns èŽ·å–æ–‡å­—
     */
    my.text = function (text) {
        if (text == null || text == undefined) {
            return this.obj.getText()
        } else {
            this.obj.setText(text)
        }
    }
    /**
     * é€‰ä¸­æˆ–不选中
     * @param {boolean} en true/false
     */
    my.select = function (en) {
        if (en) {
            if (!my.obj.hasState(utils.STATE.CHECKED)) {
                my.obj.addState(utils.STATE.CHECKED)
            }
        } else {
            my.obj.clearState(utils.STATE.CHECKED)
        }
    }
    /**
     * åˆ¤æ–­æ˜¯å¦é€‰ä¸­
     * @returns è¿”回true/false
     */
    my.isSelect = function () {
        return my.obj.hasState(utils.STATE.CHECKED)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default checkbox;
vf205_access/dxmodules/uiDropdown.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
//build:20240329
//dropdown控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let dropdown = {}
dropdown.build = function (id, parent) {
    let temp = utils.validateBuild(dropdown.all, id, parent, 'dropdown')
    let my = {type: 'dropdown'}
    my.obj = new utils.GG.NativeDropdown({ uid: id }, temp)
    my.id = id
    /**
     * è®¾ç½®ä¸‹æ‹‰é€‰é¡¹å†…容
     * @param {array} arr é€‰é¡¹å†…容,是个字符串数组,每一项为一个选项
     */
    my.setOptions = function (arr) {
        this.obj.setOptions(arr.join('\n'))
    }
    /**
     * èŽ·å–ä¸‹æ‹‰é€‰é¡¹åˆ—è¡¨
     * @returns è¿”回列表对象,是一个基类对象,可以单独设置它的字体
     */
    my.getList = function () {
        let res = {}
        res.obj = this.obj.getList()
        return Object.assign(res, base)
    }
    /**
     * è®¾ç½®é€‰ä¸­é¡¹ï¼Œé»˜è®¤ä¼šé€‰ä¸­è¿™ä¸ª
     * @param {number} index é€‰ä¸­é¡¹ç´¢å¼•
     */
    my.setSelected = function (index) {
        this.obj.setSelected(index)
    }
    /**
     * èŽ·å–é€‰ä¸­é¡¹ç´¢å¼•
     * @returns è¿”回当前选中的索引
     */
    my.getSelected = function () {
        return this.obj.getSelected()
    }
    /**
     * è®¾ç½®ä¸‹æ‹‰æ¡†é™„属图标,默认是个朝下的箭头
     * @param {string} icon å›¾æ ‡åœ°å€
     */
    my.setSymbol = function (icon) {
        this.obj.setSymbol(icon)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default dropdown;
vf205_access/dxmodules/uiFont.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
//build:20240311
//字体对象(要支持中文,需要使用支持中文的字体ttf文件)
import utils from "./uiUtils.js"
let font = {}
/**
 * æž„建字体
 * @param {string} ttf å­—体ttf文件的完整路径
 * @param {number} size å­—体大小
 * @param {number} style å­—体样式,参考utils.FONT_STYLE
 * @returns
 */
font.build = function (ttf, size, style) {
    let comp = {}
    comp.obj = utils.GG.NativeFont.lvFontInit(ttf, size, style)
    return comp;
}
export default font;
vf205_access/dxmodules/uiImage.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
//build:20240311
//image控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let image = {}
image.build = function (id, parent) {
    let temp = utils.validateBuild(image.all, id, parent, 'image')
    let my = {type: 'image'}
    my.obj = new utils.GG.NativeImage({ uid: id }, temp)
    my.id = id
    /**
     * è®¾ç½®image的来源或获取来源
     * @param {string} path éžå¿…填,图片文件的绝对路径,如果没有填或者不是string类型就是获取
     */
    my.source = function (path) {
        if (utils.validateString(path)) {
            this.obj.lvImgSetSrc(path)
        } else {
            return this.obj.lvImgGetSrc()
        }
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default image;
vf205_access/dxmodules/uiKeyboard.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
//build:20240329
//keyboard控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let keyboard = {}
keyboard.build = function (id, parent) {
    let temp = utils.validateBuild(keyboard.all, id, parent, 'keyboard')
    let my = {type: 'keyboard'}
    my.obj = new utils.GG.NativeKeyboard({ uid: id }, temp)
    // æ‹¼éŸ³è¾“入法会获得一个新对象,与当前键盘绑定,以增强键盘功能,如9键等,用户使用时不用关心,只要操作最初创建的那个键盘对象
    let pinyin = {}
    pinyin.obj = my.obj.lvImePinyinCreate()
    my.obj.lvImePinyinSetKeyboard(pinyin.obj)
    my["__obj"] = Object.assign(pinyin, base)
    my.__mode = "K26"
    my.id = id
    /**
     * è®¾ç½®å…³è”文本框,键盘输出的内容会显示在这里
     * @param {object} textarea æ–‡æœ¬æ¡†æŽ§ä»¶å¯¹è±¡
     */
    my.setTextarea = function (textarea) {
        this.obj.lvKeyboardSetTextarea(textarea.obj)
        my.textarea = textarea
    }
    /**
     * è®¾ç½®/获取模式,纯数字键盘或其他模式
     * @param {any} mode æ¨¡å¼ï¼Œå‚照枚举
     * @returns è¿”回当前模式
     */
    my.mode = function (mode) {
        if (!mode) {
            return my.__mode
        }
        if (mode == "K26" || mode == "K9") {
            this.obj.lvImePinyinSetMode(my["__obj"].obj, mode == "K26" ? 0 : 1)
        } else {
            if (mode == utils.KEYBOARD.NUMBER) {
                this.obj.lvImePinyinSetMode(my["__obj"].obj, 2)
            }
            this.obj.lvKeyboardSetMode(mode)
        }
        my.__mode = mode
    }
    /**
     * è®¾ç½®æ‹¼éŸ³å­—体,和键盘不同,这里设置的是候选字字体
     * @param {object} font font.js里build返回的对象
     * @param {number} type  å‚考utils.STYLE éžå¿…填,缺省是和对象自身绑定
     */
    my.chFont = function (font, type) {
        if (!utils.validateNumber(type)) {
            type = 0
        }
        if (!font || !font.obj) {
            throw new Error("dxui.textFont: 'font' parameter should not be null")
        }
        my.obj.lvImePinyinGetCandPanel(my["__obj"].obj).lvObjSetStyleTextFont(font.obj, type)
    }
    /**
     * æŒ‰ä¸‹æ—¶åœ¨å¼¹å‡ºçª—口中显示按钮标题,即辅助显示的上位框。
     * @param {boolean} en true/false
     */
    my.setPopovers = function (en) {
        this.obj.lvKeyboardSetPopovers(en)
    }
    /**
     * è®¾ç½®è¯åº“
     * @param {object} dict è¯åº“,格式如:{"a": "啊", "ai": "爱",...,"zu":"组"},26个字母都要有,没有候选字就写""
     * @returns
     */
    my.dict = function (dict) {
        if (!dict) {
            return my.obj.lvImePinyinGetDict(my["__obj"].obj)
        } else {
            my.obj.lvImePinyinSetDict(my["__obj"].obj, dict)
        }
    }
    let comp = Object.assign(my, base);
    // é‡å†™æ–¹æ³•
    // ä¿ç•™åŽŸå§‹çš„æ–¹æ³•
    const super_hide = my.hide;
    const super_show = my.show;
    my.hide = function () {
        super_hide.call(this)
        my.obj.lvImePinyinGetCandPanel(my["__obj"].obj).lvObjAddFlag(1);
        if (my.textarea.text() && my.textarea.text().length > 0) {
            my.obj.lvImePinyinClearData(my["__obj"].obj)
        }
    }
    my.show = function () {
        super_show.call(this)
        if (my.obj.lvImePinyinGetCandNum(my["__obj"].obj) > 0) {
            my.obj.lvImePinyinGetCandPanel(my["__obj"].obj).lvObjClearFlag(1);
        }
        my.obj.lvImePinyinGetCandPanel(my["__obj"].obj).lvObjAlignTo(my.obj, utils.ALIGN.OUT_TOP_MID, 0, 0)
    }
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default keyboard;
vf205_access/dxmodules/uiLabel.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
//build:20240311
//label控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let label = {}
label.build = function (id, parent) {
    let temp = utils.validateBuild(label.all, id, parent, 'label')
    let my = {type: 'label'}
    my.obj = new utils.GG.NativeLabel({ uid: id }, temp)
    my.id = id
    /**
     * è®¾ç½®label的文本或获取文本内容
     * @param {string} t éžå¿…填,如果没有填或者不是string类型就是获取文本
     */
    my.text = function (t) {
        if (utils.validateString(t)) {
            this.obj.lvLabelSetText(t)
        } else {
            return this.obj.lvLabelGetText()
        }
    }
    /**
     * è®¾ç½®æ–‡æœ¬è¶…长后显示的模式,比如滚动显示或截断或...等
     * @param {number} mode æžšä¸¾å‚考utils.LABEL_LONG_MODE
     */
    my.longMode = function (mode) {
        this.obj.lvLabelSetLongMode(mode)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default label;
vf205_access/dxmodules/uiLine.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
//build:20240311
//line控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let line = {}
line.build = function (id, parent) {
    let temp = utils.validateBuild(line.all, id, parent, 'line')
    let my = {type: 'line'}
    my.obj = new utils.GG.NativeLine({ uid: id }, temp)
    my.id = id
    /**
     * è®¾ç½®line的所有点的坐标
     * @param {Array} points å¿…填,所有的点组成的数组,比如[[x1,y1],[x2,y2]]
     * @param {number} count å¿…填,要绘制的点的个数,注意这个值可以小于points的长度
     */
    my.setPoints = function (points, count) {
        this.obj.lvLineSetPoints(points, count)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default line;
vf205_access/dxmodules/uiList.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
//build:20240329
//list控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let list = {}
list.build = function (id, parent) {
    let temp = utils.validateBuild(list.all, id, parent, 'list')
    let my = {type: 'list'}
    my.obj = new utils.GG.NativeList({ uid: id }, temp)
    my.id = id
    /**
     * æ·»åŠ å•ä¸ªæ–‡æœ¬é¡¹
     * @param {string} text é¡¹çš„æ–‡æœ¬å†…容
     * @returns é¡¹è‡ªèº«çš„base对象
     */
    my.addText = function (text) {
        let res = {}
        res.obj = this.obj.lvListAddText(text)
        return Object.assign(res, base)
    }
    /**
     * æ·»åŠ å•ä¸ªæŒ‰é’®é¡¹
     * @param {string} src é¡¹å‰é¢çš„图标路径
     * @param {string} text é¡¹çš„æ–‡æœ¬å†…容
     * @returns é¡¹è‡ªèº«çš„base对象
     */
    my.addBtn = function (src, text) {
        let res = {}
        res.obj = this.obj.lvListAddBtn(src, text)
        return Object.assign(res, base)
    }
    /**
     * èŽ·å–æŒ‰é’®é¡¹çš„æ–‡æœ¬å†…å®¹
     * @param {string} btn æŒ‰é’®é¡¹
     * @returns æŒ‰é’®é¡¹çš„æ–‡æœ¬å†…容
     */
    my.getBtnText = function (btn) {
        return this.obj.lvListGetBtnText(btn.obj)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default list;
vf205_access/dxmodules/uiSlider.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
//build:20240329
//slider控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let slider = {}
slider.build = function (id, parent) {
    let temp = utils.validateBuild(slider.all, id, parent, 'slider')
    let my = {type: 'slider'}
    my.obj = new utils.GG.NativeSlider({ uid: id }, temp)
    my.id = id
    /**
     * èŽ·å–/设置值
     * @param {number} v è®¾ç½®å€¼
     * @param {boolean} en è®¾ç½®å€¼æ—¶æ˜¯å¦å¼€å¯åŠ¨ç”»ï¼Œå³ç¼“åŠ¨æ•ˆæžœ
     * @returns èŽ·å–å€¼
     */
    my.value = function (v, en) {
        if (v == null || v == undefined) {
            return this.obj.lvSliderGetValue()
        } else {
            if (!utils.validateNumber(en)) {
                en = false
            }
            this.obj.lvSliderSetValue(v, en)
        }
    }
    /**
     * è®¾ç½®èŒƒå›´
     * @param {number} min æœ€å°å€¼
     * @param {number} max æœ€å¤§å€¼
     */
    my.range = function (min, max) {
        this.obj.lvSliderSetRange(min, max)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default slider;
vf205_access/dxmodules/uiStyle.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,149 @@
//build:20240315
//控件样式 æ¯ä¸ªæŽ§ä»¶å¯ä»¥ç»‘定样式对象,设置多种样式
import utils from "./uiUtils.js"
let style = {}
style.build = function () {
    let comp = {}
    comp.obj = new utils.GG.NativeStyle()
    comp.obj.lvStyleInit()
    /**
     * è®¾ç½®å·¦å³ä¸Šä¸‹çš„内边距都为一个值
     * @param {number} pad è¾¹è·å€¼
     */
    comp.padAll = function (pad) {
        this.obj.lvStyleSetPadAll(pad)
    }
    /**
     * è®¾ç½®å³å†…边距都为一个值
     * @param {number} pad è¾¹è·å€¼
     */
    comp.padRight = function (pad) {
        this.obj.lvStyleSetPadRight(pad)
    }
    /**
     * è®¾ç½®å·¦å†…边距都为一个值
     * @param {number} pad è¾¹è·å€¼
     */
    comp.padLeft = function (pad) {
        this.obj.lvStyleSetPadLeft(pad)
    }
    /**
     * è®¾ç½®ä¸Šå†…边距都为一个值
     * @param {number} pad è¾¹è·å€¼
     */
    comp.padTop = function (pad) {
        this.obj.lvStyleSetPadTop(pad)
    }
    /**
     * è®¾ç½®ä¸‹å†…边距都为一个值
     * @param {number} pad è¾¹è·å€¼
     */
    comp.padBottom = function (pad) {
        this.obj.lvStyleSetPadBottom(pad)
    }
    /**
     * è®¾ç½®åˆ—与列之间的边距都为一个值
     * @param {number} pad è¾¹è·å€¼
     */
    comp.padColumn = function (pad) {
        this.obj.lvStyleSetPadColumn(pad)
    }
    /**
     * è®¾ç½®è¡Œä¸Žè¡Œä¹‹é—´çš„边距都为一个值
     * @param {number} pad è¾¹è·å€¼
     */
    comp.padRow = function (pad) {
        this.obj.lvStyleSetPadRow(pad)
    }
    /**
     * è®¾ç½®è¾¹æ¡†å®½åº¦
     * @param {number} w
     */
    comp.borderWidth = function (w) {
        this.obj.lvStyleSetBorderWidth(w)
    }
    /**
     * è®¾ç½®è¾¹åœ†è§’
     * @param {number} r
     */
    comp.radius = function (r) {
        this.obj.lvStyleSetRadius(r)
    }
    /**
     * è®¾ç½®èƒŒæ™¯é€æ˜Žåº¦ï¼Œå€¼èŒƒå›´æ˜¯0-100,值越小越好
     * @param {number} opa å¿…须是0-100
     */
    comp.bgOpa = function (opa) {
        this.obj.lvStyleSetBgOpa(utils.OPA_MAPPING(opa))
    }
    /**
     * è®¾ç½®è‡ªèº«é€æ˜Žåº¦ï¼Œå€¼èŒƒå›´æ˜¯0-100,值越小越好
     * @param {number} opa å¿…须是0-100
     */
    comp.opa = function (opa) {
        this.obj.lvStyleSetOpa(utils.OPA_MAPPING(opa))
    }
    /**
     * è®¾ç½®èƒŒæ™¯é¢œè‰²
     * @param {any} color æ”¯æŒæ•°å­—类型:比如0x34ffaa;字符串类型(#开头),比如:'#34ffaa'
     */
    comp.bgColor = function (color) {
        this.obj.lvStyleSetBgColor(utils.colorParse(color))
    }
    /**
     * è®¾ç½®æ–‡æœ¬é¢œè‰²
     * @param {any} color  æ”¯æŒæ•°å­—类型:比如0x34ffaa;字符串类型(#开头),比如:'#34ffaa'
     */
    comp.textColor = function (color) {
        this.obj.lvStyleSetTextColor(utils.colorParse(color))
    }
    /**
     * è®¾ç½®æ–‡æœ¬å¯¹é½æ–¹å¼
     * @param {number} type  å‚考utils.TEXT_ALIGN
     */
    comp.textAlign = function (type) {
        this.obj.lvStyleSetTextAlign(type)
    }
    /**
     * è®¾ç½®æ–‡æœ¬å­—体
     * @param {object} font font.js里build返回的对象
     */
    comp.textFont = function (font) {
        if (!font || !font.obj) {
            throw new Error("style.textFont: 'font' parameter should not be null")
        }
        this.obj.lvStyleSetTextFont(font.obj)
    }
    /**
     * è®¾ç½®æ¸å˜è‰²
     * @param {number} color æ¸å˜è‰²ï¼Œä¾‹å¦‚:0xffffff
     */
    comp.bgGradColor = function (color) {
        this.obj.lvStyleSetBgGradColor(color)
    }
    /**
     * è®¾ç½®æ¸å˜è‰²æ–¹å‘
     * @param {number} dir æ–¹å‘,目前只支持水平和垂直
     */
    comp.bgGradDir = function (dir) {
        this.obj.lvStyleSetBgGradDir(dir)
    }
    /**
     * èƒŒæ™¯è‰²çš„结束位置(0-255)
     * @param {number} value è·ç¦»ï¼Œä»Žå·¦ç«¯å¼€å§‹è®¡ç®—
     */
    comp.bgMainStop = function (value) {
        this.obj.lvStyleSetBgMainStop(value)
    }
    /**
     * æ¸å˜è‰²çš„距离(0-255)
     * @param {number} value è·ç¦»ï¼Œä»ŽèƒŒæ™¯è‰²çš„结束位置开始计算
     */
    comp.bgGradStop = function (value) {
        this.obj.lvStyleSetBgGradStop(value)
    }
    return comp;
}
export default style;
vf205_access/dxmodules/uiSwitch.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
//build:20240329
//_switch控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let _switch = {}
_switch.build = function (id, parent) {
    let temp = utils.validateBuild(_switch.all, id, parent, '_switch')
    let my = {type: 'switch'}
    my.obj = new utils.GG.NativeSwitch({ uid: id }, temp)
    my.id = id
    /**
     * èŽ·å–/设置文字
     * @param {string} text è®¾ç½®æ–‡å­—
     * @returns èŽ·å–æ–‡å­—
     */
    my.text = function (text) {
        if (text == null || text == undefined) {
            return this.obj.getText()
        } else {
            this.obj.setText(text)
        }
    }
    /**
     * é€‰ä¸­æˆ–不选中
     * @param {boolean} en true/false
     */
    my.select = function (en) {
        if (en) {
            if (!my.obj.hasState(utils.STATE.CHECKED)) {
                my.obj.addState(utils.STATE.CHECKED)
            }
        } else {
            my.obj.clearState(utils.STATE.CHECKED)
        }
    }
    /**
     * åˆ¤æ–­æ˜¯å¦é€‰ä¸­
     * @returns è¿”回true/false
     */
    my.isSelect = function () {
        return my.obj.hasState(utils.STATE.CHECKED)
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default _switch;
vf205_access/dxmodules/uiTextarea.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
//build:20240330
//textarea控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let textarea = {}
textarea.build = function (id, parent) {
    let temp = utils.validateBuild(textarea.all, id, parent, 'textarea')
    let my = {type: 'textarea'}
    my.obj = new utils.GG.NativeTextarea({ uid: id }, temp)
    my.id = id
    /**
     * è®¾ç½®å•行模式,不能换行
     * @param {boolean} en true/false
     */
    my.setOneLine = function (en) {
        this.obj.lvTextareaSetOneLine(en)
    }
    /**
     * è®¾ç½®å¯†ç æ¨¡å¼ï¼Œå†…容显示为·号
     * @param {boolean} en true/false
     */
    my.setPasswordMode = function (en) {
        this.obj.lvTextareaSetPasswordMode(en)
    }
    /**
     * è®¾ç½®å†…容对齐方式,居中靠左靠右等
     * @param {number} align å¯¹é½æ–¹å¼æžšä¸¾
     */
    my.setAlign = function (align) {
        this.obj.lvTextareaSetAlign(align)
    }
    /**
     * è®¾ç½®å†…容最大长度,字符数限制
     * @param {number} length é•¿åº¦
     */
    my.setMaxLength = function (length) {
        this.obj.lvTextareaSetMaxLength(length)
    }
    /**
     * è®¾ç½®æ˜¯å¦å¯ç”¨å…‰æ ‡å®šä½ï¼Œæ˜¯å¦æ˜¾ç¤º|
     * @param {boolean} en true/false
     */
    my.setCursorClickPos = function (en) {
        this.obj.lvTextareaSetCursorClickPos(en)
    }
    /**
     * åœ¨å½“前光标位置插入文本
     * @param {string} txt æ–‡æœ¬å†…容
     */
    my.lvTextareaAddText = function (txt) {
        this.obj.lvTextareaAddText(txt)
    }
    /**
     * ä»Žå½“前光标位置删除左边的字符
     */
    my.lvTextareaDelChar = function () {
        this.obj.lvTextareaDelChar()
    }
    /**
     * èŽ·å–/设置文本内容
     * @param {string} text è®¾ç½®æ–‡æœ¬å†…容
     * @returns èŽ·å–æ–‡æœ¬å†…å®¹
     */
    my.text = function (text) {
        if (text == null || text == undefined) {
            return this.obj.lvTextareaGetText()
        } else {
            this.obj.lvTextareaSetText(text)
        }
    }
    let comp = Object.assign(my, base);
    utils.setParent(this.all, comp, parent)
    return comp;
}
export default textarea;
vf205_access/dxmodules/uiUtils.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,292 @@
//build:20240315
//公用的一些函数、常量、枚举等
import { uiClass } from '../dxmodules/libvbar-m-dxui.so'
import logger from './dxLogger.js'
const ui = new uiClass();
// åˆå§‹åŒ–ui组件
ui.init()
let utils = {}
utils.GG = NativeObject.APP.NativeComponents
utils.ENUM = utils.GG.NativeEnum
utils.LAYER = {
    "MAIN": 0,
    "SYS": 1,
    "TOP": 2
}
utils.EVENT = {
    "CLICK": 7,
    "LONG_PRESSED": 5,
    "SHORT_PRESSED": 4,
    "PRESSING": utils.ENUM.LV_EVENT_PRESSING,
    "FOCUSED": utils.ENUM.LV_EVENT_FOCUSED,
    "DEFOCUSED": utils.ENUM.LV_EVENT_DEFOCUSED,
    "VALUE_CHANGED": utils.ENUM.LV_EVENT_VALUE_CHANGED,
    "INSERT": utils.ENUM.LV_EVENT_INSERT,
    "REFRESH": utils.ENUM.LV_EVENT_REFRESH,
    "READY": utils.ENUM.LV_EVENT_READY,
    "CANCEL": utils.ENUM.LV_EVENT_CANCEL,
}
utils.TEXT_ALIGN = {
    "AUTO": 0,
    "LEFT": 1,
    "CENTER": 2,
    "RIGHT": 3
}
utils.STATE = {
    "DEFAULT": utils.ENUM.LV_STATE_DEFAULT,
    "CHECKED": utils.ENUM.LV_STATE_CHECKED,
    "FOCUSED": utils.ENUM.LV_STATE_FOCUSED,
    "FOCUS_KEY": utils.ENUM.LV_STATE_FOCUS_KEY,
    "EDITED": utils.ENUM.LV_STATE_EDITED,
    "HOVERED": utils.ENUM.LV_STATE_HOVERED,
    "PRESSED": utils.ENUM.LV_STATE_PRESSED,
    "SCROLLED": utils.ENUM.LV_STATE_SCROLLED,
    "DISABLED": utils.ENUM.LV_STATE_DISABLED,
}
utils.OBJ_FLAG = {
    "CLICKABLE": utils.ENUM.LV_OBJ_FLAG_CLICKABLE,
}
utils.ALIGN = {//相对参照对象的位置,带 OUT çš„在参照对象的边界外
    "OUT_TOP_LEFT": utils.ENUM.LV_ALIGN_OUT_TOP_LEFT,
    "OUT_TOP_MID": utils.ENUM.LV_ALIGN_OUT_TOP_MID,
    "OUT_TOP_RIGHT": utils.ENUM.LV_ALIGN_OUT_TOP_RIGHT,
    "OUT_BOTTOM_LEFT": utils.ENUM.LV_ALIGN_OUT_BOTTOM_LEFT,
    "OUT_BOTTOM_MID": utils.ENUM.LV_ALIGN_OUT_BOTTOM_MID,
    "OUT_BOTTOM_RIGHT": utils.ENUM.LV_ALIGN_OUT_BOTTOM_RIGHT,
    "OUT_LEFT_TOP": utils.ENUM.LV_ALIGN_OUT_LEFT_TOP,
    "OUT_LEFT_MID": utils.ENUM.LV_ALIGN_OUT_LEFT_MID,
    "OUT_LEFT_BOTTOM": utils.ENUM.LV_ALIGN_OUT_LEFT_BOTTOM,
    "OUT_RIGHT_TOP": utils.ENUM.LV_ALIGN_OUT_RIGHT_TOP,
    "OUT_RIGHT_MID": utils.ENUM.LV_ALIGN_OUT_RIGHT_MID,
    "OUT_RIGHT_BOTTOM": utils.ENUM.LV_ALIGN_OUT_RIGHT_BOTTOM,
    "TOP_LEFT": utils.ENUM.LV_ALIGN_TOP_LEFT,
    "TOP_MID": utils.ENUM.LV_ALIGN_TOP_MID,
    "TOP_RIGHT": utils.ENUM.LV_ALIGN_TOP_RIGHT,
    "BOTTOM_LEFT": utils.ENUM.LV_ALIGN_BOTTOM_LEFT,
    "BOTTOM_MID": utils.ENUM.LV_ALIGN_BOTTOM_MID,
    "BOTTOM_RIGHT": utils.ENUM.LV_ALIGN_BOTTOM_RIGHT,
    "LEFT_MID": utils.ENUM.LV_ALIGN_LEFT_MID,
    "RIGHT_MID": utils.ENUM.LV_ALIGN_RIGHT_MID,
    "CENTER": utils.ENUM.LV_ALIGN_CENTER,
    "DEFAULT": utils.ENUM.LV_ALIGN_DEFAULT
}
utils.FLEX_ALIGN = {//flex布局,对齐方式
    "START": utils.ENUM.LV_FLEX_ALIGN_START,
    "END": utils.ENUM.LV_FLEX_ALIGN_END,
    "CENTER": utils.ENUM.LV_FLEX_ALIGN_CENTER,
    "SPACE_EVENLY": utils.ENUM.LV_FLEX_ALIGN_SPACE_EVENLY,
    "SPACE_AROUND": utils.ENUM.LV_FLEX_ALIGN_SPACE_AROUND,
    "SPACE_BETWEEN": utils.ENUM.LV_FLEX_ALIGN_SPACE_BETWEEN,
}
utils.FLEX_FLOW = {//flex布局,主侧轴
    "ROW": utils.ENUM.LV_FLEX_FLOW_ROW,
    "COLUMN": utils.ENUM.LV_FLEX_FLOW_COLUMN,
    "ROW_WRAP": utils.ENUM.LV_FLEX_FLOW_ROW_WRAP,
    "ROW_REVERSE": utils.ENUM.LV_FLEX_FLOW_ROW_REVERSE,
    "ROW_WRAP_REVERSE": utils.ENUM.LV_FLEX_FLOW_ROW_WRAP_REVERSE,
    "COLUMN_WRAP": utils.ENUM.LV_FLEX_FLOW_COLUMN_WRAP,
    "COLUMN_REVERSE": utils.ENUM.LV_FLEX_FLOW_COLUMN_REVERSE,
    "COLUMN_WRAP_REVERSE": utils.ENUM.LV_FLEX_FLOW_COLUMN_WRAP_REVERSE,
}
utils.GRAD = {//渐变色方向
    "NONE": utils.ENUM.LV_GRAD_DIR_NONE,
    "VER": utils.ENUM.LV_GRAD_DIR_VER,
    "HOR": utils.ENUM.LV_GRAD_DIR_HOR,
}
utils.KEYBOARD = {//键盘模式
    "TEXT_LOWER": utils.ENUM.LV_KEYBOARD_MODE_TEXT_LOWER,
    "TEXT_UPPER": utils.ENUM.LV_KEYBOARD_MODE_TEXT_UPPER,
    "SPECIAL": utils.ENUM.LV_KEYBOARD_MODE_SPECIAL,
    "NUMBER": utils.ENUM.LV_KEYBOARD_MODE_NUMBER,
    "K26": "K26",
    "K9": "K9",
}
utils.FONT_STYLE = {
    "NORMAL": utils.ENUM.FT_FONT_STYLE_NORMAL,
    "ITALIC": utils.ENUM.FT_FONT_STYLE_ITALIC,
    "BOLD": utils.ENUM.FT_FONT_STYLE_BOLD,
}
utils.BUTTONS_STATE = {
    "HIDDEN": utils.ENUM.LV_BTNMATRIX_CTRL_HIDDEN,//按钮矩阵中的某个按钮是否隐藏
    "NO_REPEAT": utils.ENUM.LV_BTNMATRIX_CTRL_NO_REPEAT,//按钮矩阵中的按钮是否可以重复按下,不会重复触发按键事件
    "DISABLED": utils.ENUM.LV_BTNMATRIX_CTRL_DISABLED,//按钮矩阵中的某个按钮是否禁用
    "CHECKABLE": utils.ENUM.LV_BTNMATRIX_CTRL_CHECKABLE,//按钮矩阵中的按钮是否可选中
    "CHECKED": utils.ENUM.LV_BTNMATRIX_CTRL_CHECKED,//按钮矩阵中的某个按钮是否已被选中,在界面上呈现为被选中状态
    "CLICK_TRIG": utils.ENUM.LV_BTNMATRIX_CTRL_CLICK_TRIG,//按钮矩阵中的按钮是否可以通过点击触发
    "POPOVER": utils.ENUM.LV_BTNMATRIX_CTRL_POPOVER,//矩阵中的某个按钮是否弹出,被点击后会显示更多的选项或内容
    "RECOLOR": utils.ENUM.LV_BTNMATRIX_CTRL_RECOLOR//矩阵中的按钮是否可重新着色
}
//样式起作用的部分
utils.STYLE_PART = {
    "MAIN": 0, //对象当前样式起作用
    "ITEMS": 327680//对象内部子项起作用,比如buttonMatrix里的按钮组
}
//文本超出控件显示的模式
utils.LABEL_LONG_MODE = {
    "WRAP": utils.ENUM.LV_LABEL_LONG_WRAP,//文本长的时候换行
    "DOT": utils.ENUM.LV_LABEL_LONG_DOT,//文本长的时候用...替代
    "SCROLL": utils.ENUM.LV_LABEL_LONG_SCROLL,//文本长的时候自动滚动
    "SCROLL_CIRCULAR": utils.ENUM.LV_LABEL_LONG_SCROLL_CIRCULAR,//文本长的时候循环滚动
    "CLIP": utils.ENUM.LV_LABEL_LONG_CLIP,//文本长的时候自动截断
}
// å®žçް0-100映射为0-255
utils.OPA_MAPPING = function (value) {
    return Math.round((value / 100) * 255);
}
/**
* æ ¡éªŒæ•°å­—是否为空,是否为number
* @param {number} n å¿…å¡«
* @param {err} é”™è¯¯ä¿¡æ¯ï¼Œéžå¿…填,填了会抛出Error
*/
utils.validateNumber = function (n, err) {
    return _valid(n, 'number', err)
}
/**
* æ ¡éªŒå¯¹è±¡æ˜¯å¦ä¸ºç©ºï¼Œæ˜¯å¦ä¸ºobject
* @param {object} o å¿…å¡«
* @param {err} é”™è¯¯ä¿¡æ¯ï¼Œéžå¿…填,填了会抛出Error
*/
utils.validateObject = function (o, err) {
    return _valid(o, 'object', err)
}
/**
 * æ ¡éªŒui对象的构建参数
 * @param {array} all å¿…å¡«,所有对象引用
 * @param {string} id ä¸èƒ½ä¸ºç©ºï¼Œå¿…å¡«
 * @param {object} parent éžå¿…填,缺省是0
 */
utils.validateBuild = function (all, id, parent, type) {
    this.validateId(all, id)
    if (parent === 0 || parent === 1 || parent === 2) {
        return parent
    }
    if (!parent || !parent.obj) {
        throw new Error(type + ".build: 'parent' paramter should not be null")
    }
    return parent.obj
}
/**
 * æ ¡éªŒæ‰€æœ‰ui控件的id,不能重复
 * @param {array} all
 * @param {string} id
 */
utils.validateId = function (all, id) {
    this.validateString(id, "The 'id' parameter should not be null.")
    if (all[id]) {
        throw new Error("The id(" + id + ") already exists. Please set a different id value.")
    }
}
/**
* æ ¡éªŒå­—符串是否为空
* @param {string} s å¿…å¡«
* @param {err} é”™è¯¯ä¿¡æ¯ï¼Œéžå¿…填,填了会抛出Error
*/
utils.validateString = function (s, err) {
    let res = _valid(s, 'string', err)
    if (!res) {
        return false
    }
    if (s.length <= 0) {
        if (err) {
            throw new Error(err)
        }
        return false
    }
    return true
}
/**
 * è§£æžä¸åŒç±»åž‹çš„颜色值
 * @param {any} value æ”¯æŒæ•°å­—类型:0x34ffaa,字符串类型:'0x34ffaa',字符串类型:'#34ffaa'
 * @returns
 */
utils.colorParse = function (value) {
    if (typeof value == 'string') {
        value = value.replace('#', '0x')
        value = parseInt(value, 16)
    }
    return value
}
/**
 * èŽ·å–è§¦æ‘¸ç‚¹çš„åæ ‡
 * @returns {x:横坐标,y:纵坐标}
 */
utils.getTouchPoint = function () {
    let point = NativeObject.APP.NativeComponents.NativeIndev.lvIndevGetPoint()
    return point
}
/**
 * æä¾›åŠ¨ç”»
 * @param {object} obj åŠ¨ç”»æ“ä½œå¯¹è±¡ï¼Œå¯ä»¥æ˜¯ä»»æ„å¯¹è±¡ï¼Œå›žè°ƒå‚æ•°èŽ·å–
 * @param {number} start åŒºé—´å¼€å§‹å€¼ï¼Œä¸€èˆ¬å’Œend搭配使用,回调参数获取,start在动画过程变化到end
 * @param {number} end åŒºé—´ç»“束值
 * @param {function} cb å›žè°ƒå‡½æ•°(obj, v)=>{},obj即动画操作对象,区间值(start-end)
 * @param {number} duration åŠ¨ç”»æŒç»­æ—¶é—´ï¼Œæ¯«ç§’
 * @param {number} backDuration å¯é€‰ï¼ŒåŠ¨ç”»å›žæ”¾æ—¶é—´ï¼Œæ¯«ç§’ï¼Œç¼ºçœä¸å›žæ”¾
 * @param {number} repeat å¯é€‰ï¼ŒåŠ¨ç”»é‡å¤æ¬¡æ•°ï¼Œç¼ºçœ1次
 * @param {string} mode é€ŸçŽ‡æ›²çº¿ï¼Œå¯é€‰ï¼Œç¼ºçœlinear,内置功能:linear,ease_in,ease_out,ease_in_out,overshoot,bounce,step
 *  linear çº¿æ€§åŠ¨ç”»
    step åœ¨æœ€åŽä¸€æ­¥æ›´æ”¹
    ease_in å¼€å§‹ç¼“æ…¢
    ease_out æœ€åŽç¼“æ…¢
    ease_in_out åœ¨å¼€å§‹å’Œç»“束时都很缓慢
    overshoot è¶…出最终值
    bounce ä»Žæœ€ç»ˆå€¼åå¼¹ä¸€ç‚¹ï¼ˆæ¯”如撞到墙)
 * @returns åŠ¨ç”»å®žä¾‹ï¼Œä¸€å®šå¾—ä¿å­˜åˆ°å…¨å±€
 */
utils.anime = function (obj, start, end, cb, duration, backDuration, repeat, mode) {
    // 1、初始化动画
    let anim = NativeObject.APP.NativeComponents.NativeAnim.lvAnimInit()
    // 2、设置动画对象
    anim.lvAnimSetVar(obj)
    // 3、设置起始和结束值
    anim.lvAnimSetValues(start, end)
    //4、设置动画回调函数
    anim.lvAnimSetExecCb(cb)
    // 5、设置动画时间
    anim.lvAnimSetTime(duration)
    // å¯é€‰ï¼Œè®¾ç½®åŠ¨ç”»å›žæ”¾æ—¶é—´ï¼Œä¸è®¾ç½®å°±ä¸å›žæ”¾
    if (backDuration) {
        anim.lvAnimSetPlaybackTime(backDuration)
    }
    // å¯é€‰ï¼Œè®¾ç½®åŠ¨ç”»é‡å¤æ¬¡æ•°
    if (repeat) {
        anim.lvAnimSetRepeatCount(repeat)
    }
    // å¯é€‰ï¼Œè®¾ç½®åŠ¨ç”»é€ŸçŽ‡æ›²çº¿
    if (mode) {
        anim.lvAnimSetPathCb(mode)
    }
    // 6、运行动画
    anim.lvAnimStart()
    return anim
}
//每个对象设置parent和children
utils.setParent = function (all, child, parent) {
    if (!all || parent == null || parent == undefined || !child) {
        return
    }
    if((typeof parent)=='number'){
    }
    const parentId = ((typeof parent)=='number')?'' + parent:parent.id//把0,1,2转成字符串
    if (!all[parentId]) {
        all[parentId] = { id: parentId }//根节点0,1,2
    }
    if (!all[parentId].children) {
        all[parentId].children = []
    }
    all[parentId].children.push(child.id)
    child.parent = parentId
    all[child.id] = child
}
function _valid(n, type, err) {
    if (n === undefined || n === null || (typeof n) != type) {
        if (err) {
            throw new Error(err)
        }
        return false
    }
    return true
}
export default utils
vf205_access/dxmodules/uiView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
//build:20240314
//基础矩形对象 ç±»ä¼¼div可以加载任何其它控件
import utils from "./uiUtils.js"
import base from "./uiBase.js"
let view = {}
/**
 * åˆ›å»ºä¸€ä¸ªview加载在父控件对象上
 * @param {string} id æŽ§ä»¶id,必填
 * @param {object} parent çˆ¶å¯¹è±¡
 * @returns åˆ›å»ºå®Œçš„view对象
 */
view.build = function (id, parent) {
    let temp = utils.validateBuild(view.all, id, parent, 'view')
    let my = {type: 'view'}
    if (temp === 0 || temp === 1 || temp === 2) {
        my.obj = new utils.GG.NativeBasicComponent({ uid: id }, null, temp)
    }
    else {
        my.obj = new utils.GG.NativeBasicComponent({ uid: id }, temp)
    }
    my.id = id
    let comp = Object.assign(my, base);
    utils.setParent(this.all,comp,parent)
    return comp;
}
export default view;
vf205_access/dxmodules/vgUartWorker.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,166 @@
//build:20240715
//用于简化uart组件微光通信协议的使用,把uart封装在这个worker里,使用者只需要订阅eventcenter的事件就可以监听uart
import log from './dxLogger.js'
import uart from './dxUart.js'
import common from './dxCommon.js';
import dxMap from './dxMap.js'
import * as os from "os";
import std from './dxStd.js'
const map = dxMap.get('default')
const id = "{{id}}"
const options = map.get("__vguart__run_init" + id)
const timeout = 100
const longTimeout = 500
function run() {
    uart.open(options.type, options.path, options.id)
    log.info('vg uart start......,id =', id)
    std.setInterval(() => {
        try {
            // æŽ¥æ”¶æ•°æ®æ¨¡å¼
            if (options.passThrough) {
                // é€ä¼ æ¨¡å¼ï¼Œé€‚配韦根之类
                passThrough()
            }
            if(options.type == uart.TYPE.USBHID){
                receiveUsb()
            } else {
                // å¾®å…‰é€šä¿¡åè®®æ¨¡å¼
                receive()
            }
        } catch (error) {
            log.error(error)
        }
    }, 10)
}
// é€ä¼ æ¨¡å¼
function passThrough() {
    let pack = [];
    let buffer = readOne()
    while (buffer !== null) {
        pack.push(buffer)
        os.sleep(10)
        buffer = readOne()
    }
    if (pack.length !== 0) {
        __bus.fire(uart.VG.RECEIVE_MSG + options.id, pack)//bus.newworker的时候会import eventbus as __bus
    }
}
function receive() {
    //前2个字节必须是55aa
    let buffer = readOne()
    if (buffer === null) {
        return;
    }
    if (buffer == 85) {//0x55
        buffer = readOne()
        if (buffer != 170) {//0xaa
            return;
        }
    } else {
        return;
    }
    let pack = {};
    // è¯»å–命令字(占用1Byte)
    buffer = readOne()
    if (buffer === null) {
        return;
    }
    pack.cmd = buffer
    if (options.result) {
        // è¯»å–结果字(占用1Byte)
        buffer = readOne()
        if (buffer === null) {
            return;
        }
        pack.result = buffer;
    } else {
        pack.result = 0//0不影响bcc的计算结果
    }
    // å‘½ä»¤å¤´å·²è§£æžå®Œï¼Œè¯»å–长度字(占用2Byte)
    let len1 = readOne()
    if (len1 === null) {
        return;
    }
    let len2 = readOne()
    if (len2 === null) {
        return;
    }
    // è§£æžé•¿åº¦å­—,获取数据域长度
    let len = len1 + len2 * 256
    // æ ¹æ®é•¿åº¦å­—读取指定数据长度
    pack.length = len
    if (len > 0) {
        buffer = uart.receive(len, longTimeout, options.id)
        if (buffer === null) {
            return;
        }
        pack.data = Array.from(buffer);
    } else {
        pack.data = 0
    }
    // è¯»å–1Byte的校验位
    buffer = readOne()
    if (buffer === null) {
        return;
    }
    let bcc = valid(pack, buffer)
    let res = { cmd: int2hex(pack.cmd), length: pack.length, bcc: bcc }
    if (pack.length > 0) {
        res.data = common.arrToHex(pack.data)
    }
    if (options.result) {
        res.result = int2hex(pack.result)
    }
    __bus.fire(uart.VG.RECEIVE_MSG + options.id, res)//bus.newworker的时候会import eventbus as __bus
}
function receiveUsb() {
    let arr = uart.receive(1024, 100, options.id)
    if (arr && arr[0] == 0x55 && arr[1] == 0xAA) {
        let cmd = arr[2]
        let dlen = arr[4] * 256 + arr[3]
        if (dlen > (1024 - 6)) {
            let tempLen = dlen - 1024 + 5
            while(tempLen >= 0){
                let tempArr = uart.receive(1024, 100, options.id)
                tempLen = tempLen - tempArr.length
                let newArr = new Uint8Array(arr.length + tempArr.length)
                newArr.set(arr)
                newArr.set(tempArr, arr.length)
                arr = newArr
            }
        }
        let data = (dlen == 0 ? [] : Object.values(arr.slice(5, 5 + dlen)))
        let bcc = common.calculateBcc([0x55, 0xAA, arr[2], arr[3], arr[4]].concat(data))
        data = data.map(v => v.toString(16).padStart(2, '0')).join('')
        if (bcc == arr[5 + dlen]) {
            let res = { "cmd": cmd.toString(16).padStart(2, '0'), "length": dlen, "data": data, "bcc": true }
            __bus.fire(uart.VG.RECEIVE_MSG + options.id, res)//bus.newworker的时候会import eventbus as __bus
        }
    }
}
function valid(pack, bcc) {
    let temp = common.calculateBcc([0x55, 0xaa, pack.cmd, pack.result, pack.length % 256, Math.floor(pack.length / 256)].concat(pack.data))
    return temp === bcc
}
function readOne() {
    let buffer = uart.receive(1, timeout, options.id)
    if (buffer) {
        return parseInt(buffer);
    }
    return null
}
function int2hex(num) {
    return num.toString(16).padStart(2, '0')
}
try {
    run()
} catch (error) {
    log.error(error, error.stack)
}
vf205_access/resource/CN/wav/access_f.wav
Binary files differ
vf205_access/resource/CN/wav/access_s.wav
Binary files differ
vf205_access/resource/CN/wav/btn11.wav
Binary files differ
vf205_access/resource/CN/wav/btn12.wav
Binary files differ
vf205_access/resource/CN/wav/btn13.wav
Binary files differ
vf205_access/resource/CN/wav/btn21.wav
Binary files differ
vf205_access/resource/CN/wav/btn22.wav
Binary files differ
vf205_access/resource/CN/wav/btn23.wav
Binary files differ
vf205_access/resource/CN/wav/btn31.wav
Binary files differ
vf205_access/resource/CN/wav/btn32.wav
Binary files differ
vf205_access/resource/CN/wav/btn33.wav
Binary files differ
vf205_access/resource/CN/wav/calibration_s.wav
Binary files differ
vf205_access/resource/CN/wav/control_f.wav
Binary files differ
vf205_access/resource/CN/wav/door_close.wav
Binary files differ
vf205_access/resource/CN/wav/door_open.wav
Binary files differ
vf205_access/resource/CN/wav/emergency.wav
Binary files differ
vf205_access/resource/CN/wav/emergency_f.wav
Binary files differ
vf205_access/resource/CN/wav/emergency_s.wav
Binary files differ
vf205_access/resource/CN/wav/failed.wav
Binary files differ
vf205_access/resource/CN/wav/light_close.wav
Binary files differ
vf205_access/resource/CN/wav/light_open.wav
Binary files differ
vf205_access/resource/CN/wav/network.wav
Binary files differ
vf205_access/resource/CN/wav/read.wav
Binary files differ
vf205_access/resource/CN/wav/recg_f.wav
Binary files differ
vf205_access/resource/CN/wav/recg_s.wav
Binary files differ
vf205_access/resource/CN/wav/recognition.wav
Binary files differ
vf205_access/resource/CN/wav/recognition_s.wav
Binary files differ
vf205_access/resource/CN/wav/register.wav
Binary files differ
vf205_access/resource/CN/wav/stranger.wav
Binary files differ
vf205_access/resource/CN/wav/user2.wav
Binary files differ
vf205_access/resource/CN/wav/user2_s.wav
Binary files differ
vf205_access/resource/CN/wav/verify.wav
Binary files differ
vf205_access/resource/CN/wav/verify_10x_f.wav
Binary files differ
vf205_access/resource/CN/wav/verify_10x_s.wav
Binary files differ
vf205_access/resource/CN/wav/verify_200_f.wav
Binary files differ
vf205_access/resource/CN/wav/verify_200_s.wav
Binary files differ
vf205_access/resource/CN/wav/verify_300_f.wav
Binary files differ
vf205_access/resource/CN/wav/verify_300_s.wav
Binary files differ
vf205_access/resource/CN/wav/verify_400_f.wav
Binary files differ
vf205_access/resource/CN/wav/verify_400_s.wav
Binary files differ
vf205_access/resource/EN/wav/calibration_s.wav
Binary files differ
vf205_access/resource/EN/wav/network.wav
Binary files differ
vf205_access/resource/EN/wav/read.wav
Binary files differ
vf205_access/resource/EN/wav/recg_f.wav
Binary files differ
vf205_access/resource/EN/wav/recg_s.wav
Binary files differ
vf205_access/resource/EN/wav/recognition.wav
Binary files differ
vf205_access/resource/EN/wav/recognition_s.wav
Binary files differ
vf205_access/resource/EN/wav/register.wav
Binary files differ
vf205_access/resource/EN/wav/stranger.wav
Binary files differ
vf205_access/resource/EN/wav/verify.wav
Binary files differ
vf205_access/resource/EN/wav/verify_10x_f.wav
Binary files differ
vf205_access/resource/EN/wav/verify_10x_s.wav
Binary files differ
vf205_access/resource/EN/wav/verify_200_f.wav
Binary files differ
vf205_access/resource/EN/wav/verify_200_s.wav
Binary files differ
vf205_access/resource/EN/wav/verify_300_f.wav
Binary files differ
vf205_access/resource/EN/wav/verify_300_s.wav
Binary files differ
vf205_access/resource/EN/wav/verify_400_f.wav
Binary files differ
vf205_access/resource/EN/wav/verify_400_s.wav
Binary files differ
vf205_access/resource/font/AlibabaPuHuiTi-2-65-Medium.ttf
Binary files differ
vf205_access/resource/image/4g.png
vf205_access/resource/image/4g_dark.png
vf205_access/resource/image/accessCtrl.png
vf205_access/resource/image/add.png
vf205_access/resource/image/advance.png
vf205_access/resource/image/app.png
vf205_access/resource/image/app_btn.png
vf205_access/resource/image/app_qrcode.png
vf205_access/resource/image/arrow_right.png
vf205_access/resource/image/back.png
vf205_access/resource/image/back_2.png
vf205_access/resource/image/background.jpg
vf205_access/resource/image/backspace.png
vf205_access/resource/image/basic.png
vf205_access/resource/image/black_btn.png
vf205_access/resource/image/card.png
vf205_access/resource/image/close.png
vf205_access/resource/image/close_small.png
vf205_access/resource/image/cloudCert.png
vf205_access/resource/image/co2_f.png
vf205_access/resource/image/co2_s.png
vf205_access/resource/image/commMgmt.png
vf205_access/resource/image/config.png
vf205_access/resource/image/config_btn.png
vf205_access/resource/image/delete.png
vf205_access/resource/image/delete_fill.png
vf205_access/resource/image/devInfo.png
vf205_access/resource/image/deviceInfo.png
vf205_access/resource/image/doorControl.png
vf205_access/resource/image/down.png
vf205_access/resource/image/emergencyOpen.png
vf205_access/resource/image/empty.png
vf205_access/resource/image/enter.png
vf205_access/resource/image/enter_b.png
vf205_access/resource/image/eth_disable.png
vf205_access/resource/image/eth_enable.png
vf205_access/resource/image/ethernet.png
vf205_access/resource/image/ethernet_dark.png
vf205_access/resource/image/eye-fill.png
vf205_access/resource/image/eye-off.png
vf205_access/resource/image/eye_fill.png
vf205_access/resource/image/eye_fill_show.png
vf205_access/resource/image/face.png
vf205_access/resource/image/faceAdd.png
vf205_access/resource/image/faceEmpty.png
vf205_access/resource/image/faceError.png
vf205_access/resource/image/faceRec.png
vf205_access/resource/image/faceRec2.png
vf205_access/resource/image/factoryTest.png
vf205_access/resource/image/failBg.png
vf205_access/resource/image/grey_btn.png
vf205_access/resource/image/help.png
vf205_access/resource/image/idleImage.jpg
vf205_access/resource/image/input_bg.png
vf205_access/resource/image/light_close.png
vf205_access/resource/image/light_open.png
vf205_access/resource/image/localUser.png
vf205_access/resource/image/lock.png
vf205_access/resource/image/logo.png
vf205_access/resource/image/menu_btn.png
vf205_access/resource/image/mini_app.png
vf205_access/resource/image/mini_background.png
vf205_access/resource/image/mini_config.png
vf205_access/resource/image/mini_password.png
vf205_access/resource/image/mqtt.png
vf205_access/resource/image/mqtt_dark.png
vf205_access/resource/image/mqtt_enable.png
vf205_access/resource/image/network.png
vf205_access/resource/image/networkSetting.png
vf205_access/resource/image/network_dark.png
vf205_access/resource/image/o2_f.png
vf205_access/resource/image/o2_s.png
vf205_access/resource/image/ph3_f.png
vf205_access/resource/image/ph3_s.png
vf205_access/resource/image/pwd_btn.png
vf205_access/resource/image/qrcode_small.png
vf205_access/resource/image/recQuery.png
vf205_access/resource/image/recordQuery.png
vf205_access/resource/image/rectangle.png
vf205_access/resource/image/register.png
vf205_access/resource/image/right.png
vf205_access/resource/image/select_arrow.png
vf205_access/resource/image/setting.png
vf205_access/resource/image/setting32.png
vf205_access/resource/image/space.png
vf205_access/resource/image/successBg.png
vf205_access/resource/image/success_fill.png
vf205_access/resource/image/sysSettings.png
vf205_access/resource/image/sys_info.png
vf205_access/resource/image/systemSetting.png
vf205_access/resource/image/title_bg.png
vf205_access/resource/image/trackFace.png
vf205_access/resource/image/unlock.png
vf205_access/resource/image/user.png
vf205_access/resource/image/userGuide.png
vf205_access/resource/image/userMgmt.png
vf205_access/resource/image/user_1.png
vf205_access/resource/image/user_f.png
vf205_access/resource/image/user_s.png
vf205_access/resource/image/user_w.png
vf205_access/resource/image/view_f.png
vf205_access/resource/image/view_s.png
vf205_access/resource/image/vip.png
vf205_access/resource/image/voiceBroadcast.png
vf205_access/resource/image/wifi.png
vf205_access/resource/image/wifi_dark.png
vf205_access/resource/langPack.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,664 @@
// è¯­è¨€åŒ…
const messages = {
  CN: {
    mainView: {
      config: "配置",
      pwd: "密码",
      app: "小程序码",
      success: "通行成功",
      fail: "通行失败",
      passwordDisabled: "密码已禁用",
    },
    idleView: {
      week: {
        0: "周日",
        1: "周一",
        2: "周二",
        3: "周三",
        4: "周四",
        5: "周五",
        6: "周六",
      },
    },
    appView: {
      knowed: "我已知晓",
      appQrcodeLbl: "使用小程序便捷管理",
    },
    pwdView: {
      title: "密码通行",
      pwd: "请输入密码",
      pwdAccess: "确认",
      success: "密码通行成功",
      fail: "密码通行失败",
    },
    newPwdView: {
      title: "设置管理密码",
      pwdAccess: "确认",
      pwd: "请输入密码",
      confirmPwd: "请再次输入密码",
      pwdAccess: "确认",
      tip: "注意:您设置的密码位数应大于或等于 8 ä½ï¼Œå¦‚跳过设置,设备将采用默认密码。",
      skip: "跳过,以后设置",
      success: "密码设置成功",
      fail: "密码设置失败",
      pwdNotMatch: "两次输入密码不一致",
    },
    identityVerificationView: {
      title: "身份验证",
      pwd: "请输入管理密码",
      pwdAccess: "确认",
      success: "人脸验证成功",
      fail: "人脸验证失败",
      pwdLog: "密码登录",
      faceLog: "人脸登录",
      pwdFail: "密码错误",
    },
    configView: {
      title: "设置菜单",
      localUser: "本地用户",
      networkSetting: "网络设置",
      doorControl: "门禁管理",
      systemSetting: "系统设置",
      deviceInfo: "设备信息",
      recordQuery: "记录查询",
      voiceBroadcast: "语音播报",
      cloudCert: "云证功能",
      factoryTest: "工厂测试",
      help: "使用帮助",
      confirmExit: "确认退出",
      confirmExitContent: "是否确认退出设置菜单?",
    },
    cloudCertView: {
      title: "云证功能",
      cloudCertActive: "云证激活",
      inputKey: "请输入密钥",
      key: "密钥",
      tip: "注意:云证可以通过手输密钥或扫描\n专用二维码激活,详情请联系客服。",
      save: "保存",
    },
    doorControlView: {
      title: "门禁管理",
      save: "保存",
      openDoorRelayDelay: "开门继电器延时",
      antiTamperAlarm: "防拆报警",
      input: "请输入",
      success: "保存成功",
      fail: "保存失败",
      mqttAddr: "MQTT地址",
      mqttUser: "MQTT账号",
      mqttPwd: "MQTT密码",
      onlineChecking: "在线验证",
      onlineCheckingTimeout: "在线验证超时",
      ms: "毫秒"
    },
    helpView: {
      title: "使用帮助",
      scanCode: "扫码访问官方教程",
    },
    networkSettingView: {
      title: "网络设置",
      type: "网络类型",
      ip: "IP",
      dhcp: "DHCP",
      mask: "子网掩码",
      gateway: "网关",
      dns: "DNS1",
      dns2: "DNS2",
      mac: "MAC",
      status: "网络状态",
      save: "保存",
      input: "请输入",
      ethernet: "以太网",
      wifi: "WiFi",
      _4G: "4G",
      networkUnconnected: "网络未连接",
      networkConnected: "网络已连接",
      wifiName: "WiFi名称",
      wifiPwd: "WiFi密码",
      wifiList: "WiFi列表",
      close: "关闭",
      confirm: "确认",
      fail: "保存失败",
      success: "保存成功",
    },
    systemSettingView: {
      title: "系统设置",
      displaySetting: "显示界面设置",
      faceRecognitionSetting: "人脸识别设置",
      swipeCardRecognitionSetting: "刷卡识别设置",
      passLogSetting: "通行日志设置",
      passwordOpenDoorSetting: "密码开门设置",
      passwordManagement: "密码管理",
      timeSetting: "时间设置",
      restartDevice: "重启设备",
      restoreDefaultConfig: "恢复默认配置",
      resetDevice: "重置设备",
      restart: "重启",
      restoreDefault: "恢复",
      reset: "重置",
      autoAdjustScreenBrightness: "自动调节屏幕亮度",
      screenBrightness: "屏幕亮度",
      autoTurnOffScreen: "自动熄屏",
      autoTurnOffScreenTime: "自动熄屏时间",
      autoScreenSaver: "自动屏保",
      autoScreenSaverTime: "自动屏保时间",
      displayIp: "显示IP地址",
      displayDeviceSn: "显示设备SN",
      language: "语言",
      displayCode: "显示小程序码",
      themeMode: "工作主题",
      save: "保存",
      input: "请输入",
      faceSimilarityThreshold: "人脸相似度阈值",
      livenessDetectionFunction: "活体检测功能",
      livenessDetectionThreshold: "活体检测阈值",
      infraredImageDisplay: "红外图像显示",
      maskRecognition: "口罩识别",
      maskRecognitionThreshold: "口罩识别阈值",
      recognitionDistance: "识别距离",
      imageSaveType: "图像保存类型",
      saveStrangerImage: "保存陌生人图像",
      fullView: "全景",
      face: "人脸",
      swipeCardRecognition: "刷卡核验",
      passwordOpenDoor: "密码开门",
      inputOriginalPassword: "请输入原管理密码",
      inputNewPassword: "请输入新密码",
      inputRepeatNewPassword: "请重复新密码",
      syncMode: "时区",
      ntpAddress: "NTP地址",
      timeSyncSuccess: "时间与服务器同步成功",
      success: "保存成功",
      fail: "保存失败",
      appMode: "APP模式",
      confirmation: "确认",
      confirmRestart: "确认重启吗?",
      confirmRecoveryConfiguration: "确认恢复默认配置吗?",
      confirmReset: "确认重置吗?",
      min: "分钟"
    },
    deviceInfoView: {
      title: "设备信息",
      systemInfo: "系统信息",
      dataCapacityInfo: "数据容量信息",
      deviceQrCode: "设备二维码",
      miniProgramCode: "小程序码",
      deviceSN: "设备SN号",
      firmwareVersion: "固件版本号",
      firmwareReleaseDate: "固件发布日期",
      deviceTotalSpace: "设备总空间",
      deviceUsedSpace: "已用空间",
      deviceRemainingSpace: "剩余空间",
      registeredPersonNum: "注册人数",
      localFaceWhiteListNum: "本地人脸白名单数量",
      localPasswordWhiteListNum: "本地密码白名单数量",
      localSwipeCardWhiteListNum: "本地刷卡白名单数量",
      passLogTotalNum: "通行记录总数",
      updateDevice: "更新设备",
      currentVersion: "当前已经是最新版本,无需更新固件",
      deviceFreeSpace: "设备空闲空间",
    },
    factoryTestView: {
      title: "工厂测试",
      calibration: "摄像头标定",
    },
    localUserView: {
      title: "本地用户",
      empty: "本地尚未添加人员!",
      add: "新增人员",
      sync: "同步本地人员至小程序",
      search: "姓名或ID",
      searchBtn: "搜索",
      edit: "编辑",
      attention: "注意",
      attentionContent:
        "1、设备将同步设备本地的人员信息\n至小程序的审批列表中,执行成功\n后设备端将清除已经录入的人员信\n息。 \n2、作为管理员,您可以在小程序中\n对本地同步的人员进行审批。审批\n通过并补充完必要的字段后,组织\n内的全部设备,将拥有此次同步人\n员的通行权限。\n此操作无法撤销,请问您确定要同\n步吗?",
      tip: "提示",
      tipContent: "设备尚未连接网络,请先配置网络!",
    },
    recordQueryView: {
      title: "记录查询",
      code: "人员编号",
      time: "通行时间",
      result: "通行结果",
      stranger: "陌生人",
      face: "人脸",
      card: "刷卡",
      password: "密码",
      qrcode: "扫码",
      success: "通行成功",
      fail: "通行失败",
    },
    recordQueryDetailView: {
      title: "通行记录详情",
      id: "第一用户编号",
      name: "第一用户姓名",
      idCard: "第一用户身份证号",
      face: "第一用户人脸抓拍",
      secondId: "第二用户编号",
      secondName: "第二用户姓名",
      secondIdCard: "第二用户身份证号",
      secondFace: "第二用户人脸抓拍",
      time: "通行时间",
      result: "通行结果",
    },
    voiceBroadcastView: {
      title: "语音播报",
      save: "保存",
      strangerVoice: "陌生人语音",
      voiceMode: "语音模式",
      volume: "音量",
      success: "保存成功",
      fail: "保存失败",
    },
    confirm: {
      ok: "确认",
      no: "取消",
      upgrade: "设备升级",
      upgrading: "正在升级",
      upgradeSuccess: "升级成功",
      upgradeFail: "升级失败",
      cloudCertActive: "云证激活",
      cloudCertActiveSuccess: "激活成功",
      cloudCertActiveFail: "激活失败",
      restartDevice: "重启设备",
      restartDeviceDis: "配置已更新,设备即将重启",
    },
    localUserAddView: {
      title: "用户新增",
      title2: "用户编辑",
      save: "保存",
      id: "ID",
      name: "人员姓名",
      idCard: "身份证号",
      face: "人脸凭证",
      pwd: "密码凭证",
      card: "卡片凭证",
      type: "人员类型",
      input: "请输入",
      enter: "录入",
      generate: "生成",
      edit: "修改",
      reset: "重设",
      confirm: "确认",
      confirmDelete: "确认删除",
      confirmDeleteContent: "是否确认删除",
      confirmFace: "确认删除人脸凭证吗?",
      confirmPwd: "确认删除密码凭证吗?",
      confirmCard: "确认删除卡片凭证吗?",
      pwdBoxLbl: "密码生成中",
      pwdBoxSaveBtnLbl: "换一个",
      pwdBoxConfirmBtnLbl: "确定",
      cardBoxResetBtnLbl: "重置",
      cardBoxSaveBtnLbl: "保存",
      cardBoxLbl: "读取卡片中",
      cardBoxInput: "请填写用户卡号",
      delete: "删除",
      success: "成功",
      fail: "失败",
      requiredInfo: "请先填写必填信息",
      preview: "预览",
      failRepeat: "失败,用户ID重复",
      failSimilarity: "失败,人脸相似度过高",
      failCardRepeat: "失败,卡片重复",
      failPwdRepeat: "失败,密码重复",
    },
    faceEnterView: {
      title: "人脸录入",
      faceAdd: "人脸录入中,请正视摄像头",
      recogFace: "识别到人脸",
      recogSuccess: "识别成功",
      faceError: "超时未获取",
    },
    faceService: {
      contrastFailure: "对比失败",
      scalingFailure: "缩放失败",
      failedToSavePicture: "存储图片失败",
      convertToBase64Failed: "特征值转base64失败",
      base64DecodingFailed: "base64解码失败",
      similarityOverheight: "相似度过高",
      fileDoesNotExist: "文件不存在",
      theImageFormatIsNotSupported: "图片格式不支持",
      pictureReadFailure: "图片读取失败",
      thePictureSizeDoesNotMatch: "图片尺寸不符",
      imageParsingFailure: "图片解析失败",
      imageYUVProcessingFailed: "图片YUV处理失败",
      failedToConvertJpegToImage: "jpeg转image失败",
      faceInformationExtractionFailed: "人脸信息提取失败",
      theFaceIsNotUnique: "图片中人脸信息不唯一",
    }
  },
  EN: {
    mainView: {
      config: "Settings",
      pwd: "Password",
      app: "Mini Program Code",//屏蔽小程序码
      success: "Access Granted",
      fail: "Access Denied",
      passwordDisabled: "Password Access Disabled",
    },
    idleView: {
      week: {
        0: "Sun",
        1: "Mon",
        2: "Tue",
        3: "Wed",
        4: "Thu",
        5: "Fri",
        6: "Sat",
      },
    },
    appView: {
      knowed: "Got it",
      appQrcodeLbl: "Manage with Mini Program",
    },
    pwdView: {
      title: "Password Access",
      pwd: "Enter Password",
      pwdAccess: "Confirm",
      success: "Access Granted",
      fail: "Access Denied",
    },
    newPwdView: {
      title: "Set Admin Password",
      pwdAccess: "Confirm",
      pwd: "Enter Password",
      confirmPwd: "Confirm Password",
      pwdAccess: "Confirm",
      tip: "Note: Password must be at least 8 characters long. Default password will be used if skipped.",
      skip: "Skip for Now",
      success: "Password Set Successfully",
      fail: "Failed to Set Password",
      pwdNotMatch: "Passwords Don't Match",
    },
    identityVerificationView: {
      title: "Identity Verification",
      pwd: "Enter Admin Password",
      pwdAccess: "Confirm",
      success: "Face Verification Successful",
      fail: "Face Verification Failed",
      pwdLog: "Password Login",
      faceLog: "Face Login",
      pwdFail: "Wrong Password",
    },
    configView: {
      title: "Settings",
      localUser: "Local Users",
      networkSetting: "Network",
      doorControl: "Access Control",
      systemSetting: "System",
      deviceInfo: "Device Info",
      recordQuery: "Access Logs",
      voiceBroadcast: "Voice Settings",
      cloudCert: "Cloud Certificate",
      factoryTest: "Factory Test",
      help: "Help",
      confirmExit: "Exit Settings",
      confirmExitContent: "Are you sure you want to exit Settings?",
    },
    cloudCertView: {
      title: "Cloud Certificate",
      cloudCertActive: "Activate Certificate",
      inputKey: "Enter Key",
      key: "Key",
      tip: "Note: Activate using key or QR code scan. Contact support for details.",
      save: "Save",
    },
    doorControlView: {
      title: "Access Control",
      save: "Save",
      openDoorRelayDelay: "Door Release Delay",
      antiTamperAlarm: "Tamper Alarm",
      input: "Enter",
      success: "Saved",
      fail: "Save Failed",
      mqttAddr: "MQTT Server",
      mqttUser: "MQTT Username",
      mqttPwd: "MQTT Password",
      onlineChecking: "Online Verification",
      onlineCheckingTimeout: "Verification Timeout",
      ms: "ms"
    },
    helpView: {
      title: "Help",
      scanCode: "Scan for Tutorial",
    },
    networkSettingView: {
      title: "Network",
      type: "Connection Type",
      ip: "IP Address",
      dhcp: "DHCP",
      mask: "Subnet Mask",
      gateway: "Gateway",
      dns: "Primary DNS",
      dns2: "Secondary DNS",
      mac: "MAC Address",
      status: "Status",
      save: "Save",
      input: "Enter",
      ethernet: "Ethernet",
      wifi: "Wi-Fi",
      _4G: "4G",
      networkUnconnected: "Disconnected",
      networkConnected: "Connected",
      wifiName: "Network Name",
      wifiPwd: "Password",
      wifiList: "Available Networks",
      close: "Close",
      confirm: "Confirm",
      fail: "Save Failed",
      success: "Saved",
    },
    systemSettingView: {
      title: "System Settings",
      displaySetting: "Display",
      faceRecognitionSetting: "Face Recognition",
      swipeCardRecognitionSetting: "Card Access",
      passLogSetting: "Access Logs",
      passwordOpenDoorSetting: "Password Access",
      passwordManagement: "Password Management",
      timeSetting: "Date & Time",
      restartDevice: "Restart",
      restoreDefaultConfig: "Reset to Default",
      resetDevice: "Factory Reset",
      restart: "Restart",
      restoreDefault: "Reset",
      reset: "Reset",
      autoAdjustScreenBrightness: "Auto Brightness",
      screenBrightness: "Brightness",
      autoTurnOffScreen: "Auto Screen Off",
      autoTurnOffScreenTime: "Screen Off Timer",
      autoScreenSaver: "Screen Saver",
      autoScreenSaverTime: "Screen Saver Timer",
      displayIp: "Show IP Address",
      displayDeviceSn: "Show Device SN",
      language: "Language",
      displayCode: "Show Program Code",
      themeMode: "Work Theme",
      save: "Save",
      input: "Enter",
      faceSimilarityThreshold: "Face Match Threshold",
      livenessDetectionFunction: "Liveness Detection",
      livenessDetectionThreshold: "Liveness Threshold",
      infraredImageDisplay: "IR Display",
      maskRecognition: "Mask Detection",
      maskRecognitionThreshold: "Mask Detection Threshold",
      recognitionDistance: "Detection Range",
      imageSaveType: "Image Storage Type",
      saveStrangerImage: "Save Stranger Photos",
      fullView: "Full View",
      face: "Face Only",
      swipeCardRecognition: "Card Verification",
      passwordOpenDoor: "Password Access",
      inputOriginalPassword: "Enter Current Password",
      inputNewPassword: "Enter New Password",
      inputRepeatNewPassword: "Confirm New Password",
      syncMode: "Time Zone",
      ntpAddress: "NTP Server",
      timeSyncSuccess: "Time Synced Successfully",
      success: "Saved",
      fail: "Failed",
      confirmation: "Confirm",
      confirmRestart: "Confirm Restart?",
      confirmRecoveryConfiguration: "Reset to Default Settings?",
      confirmReset: "Confirm Factory Reset?",
      min: "min"
    },
    deviceInfoView: {
      title: "Device Info",
      systemInfo: "System Info",
      dataCapacityInfo: "Storage Info",
      deviceQrCode: "Device QR Code",
      miniProgramCode: "Mini Program Code",
      deviceSN: "Serial Number",
      firmwareVersion: "Firmware Version",
      firmwareReleaseDate: "Release Date",
      deviceTotalSpace: "Total Storage",
      deviceUsedSpace: "Used Storage",
      deviceRemainingSpace: "Available Storage",
      registeredPersonNum: "Registered Users",
      localFaceWhiteListNum: "Face Whitelist Count",
      localPasswordWhiteListNum: "Password Whitelist Count",
      localSwipeCardWhiteListNum: "Card Whitelist Count",
      passLogTotalNum: "Total Access Logs",
      updateDevice: "Update Device",
      currentVersion: "Device is up to date",
      deviceFreeSpace: "Free Space",
    },
    factoryTestView: {
      title: "Factory Test",
      calibration: "Camera Calibration",
      appMode: "APP Mode",
    },
    localUserView: {
      title: "Local Users",
      empty: "No Users Added",
      add: "Add User",
      sync: "Sync to Mini Program",
      search: "Search by Name or ID",
      searchBtn: "Search",
      edit: "Edit",
      attention: "Notice",
      attentionContent:
        "1. Local user data will be synced to the Mini Program approval list. After successful sync, local user data will be cleared.\n2. As an admin, you can approve synced users in the Mini Program. Once approved and required fields are completed, users will have access to all organization devices.\nThis action cannot be undone. Continue?",
      tip: "Note",
      tipContent: "Please connect to network first",
    },
    recordQueryView: {
      title: "Access Logs",
      code: "User ID",
      time: "Time",
      result: "Result",
      stranger: "Unknown",
      face: "Face",
      card: "Card",
      password: "Password",
      success: "Granted",
      fail: "Denied",
    },
    recordQueryDetailView: {
      title: "Access Log Details",
      id: "User ID",
      name: "Name",
      idCard: "ID Number",
      face: "First Face Photo",
      secondId: "Second User ID",
      secondName: "Second Name",
      secondIdCard: "Second ID Number",
      secondFace: "Second Face Photo",
      time: "Access Time",
      result: "Result",
    },
    voiceBroadcastView: {
      title: "Voice Settings",
      save: "Save",
      strangerVoice: "Stranger Alert",
      voiceMode: "Voice Mode",
      volume: "Volume",
      success: "Saved",
      fail: "Save Failed",
    },
    confirm: {
      ok: "OK",
      no: "Cancel",
      upgrade: "Update Device",
      upgrading: "Updating...",
      upgradeSuccess: "Update Complete",
      upgradeFail: "Update Failed",
      cloudCertActive: "Activate Certificate",
      cloudCertActiveSuccess: "Activate Success",
      cloudCertActiveFail: "Activate Failed",
      restartDevice: "Restart Device",
      restartDeviceDis: "Configuration updated, device will restart",
    },
    localUserAddView: {
      title: "Add User",
      title2: "Edit User",
      save: "Save",
      id: "ID",
      name: "Name",
      idCard: "ID Number",
      face: "Face ID",
      pwd: "Password",
      card: "Access Card",
      type: "User Type",
      input: "Enter",
      enter: "Add",
      generate: "Generate",
      edit: "Edit",
      reset: "Reset",
      confirm: "Confirm",
      confirmDelete: "Confirm Delete",
      confirmDeleteContent: "Delete this item?",
      confirmFace: "Delete Face ID?",
      confirmPwd: "Delete Password?",
      confirmCard: "Delete Access Card?",
      pwdBoxLbl: "Generating...",
      pwdBoxSaveBtnLbl: "Generate New",
      pwdBoxConfirmBtnLbl: "OK",
      cardBoxResetBtnLbl: "Reset",
      cardBoxSaveBtnLbl: "Save",
      cardBoxLbl: "Reading Card...",
      cardBoxInput: "Enter Card Number",
      delete: "Delete",
      success: "Success",
      fail: "Failed",
      requiredInfo: "Required Fields Missing",
      preview: "Preview",
      failRepeat: "ID Already Exists",
      failSimilarity: "Face Too Similar",
      failCardRepeat: "Card Already Exists",
      failPwdRepeat: "Password Already Exists",
    },
    faceEnterView: {
      title: "Face Enrollment",
      faceAdd: "Look at Camera",
      recogFace: "Face Detected",
      recogSuccess: "Enrollment Complete",
      faceError: "No Face Detected",
    },
    calibrationView: {
      firstCalibration: "Initial Calibration",
      secondCalibration: "Secondary Calibration"
    },
    faceService: {
      contrastFailure: "Comparison Failed",
      scalingFailure: "Scaling Failed",
      failedToSavePicture: "Failed to Save Image",
      convertToBase64Failed: "Base64 Conversion Failed",
      base64DecodingFailed: "Base64 Decode Failed",
      similarityOverheight: "Face Too Similar",
      fileDoesNotExist: "File Not Found",
      theImageFormatIsNotSupported: "Unsupported Image Format",
      pictureReadFailure: "Failed to Read Image",
      thePictureSizeDoesNotMatch: "the picture size does not match",
      imageParsingFailure: "image parsing failure",
      imageYUVProcessingFailed: "image yuv processing failed",
      failedToConvertJpegToImage: "failed to convert jpeg to image",
      faceInformationExtractionFailed: "face information extraction failed",
      theFaceIsNotUnique: "the face is not unique",
    }
  },
};
export default messages;
vf205_access/resource/wav/alarm.wav
Binary files differ
vf205_access/src/common/consts/configConst.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
/**
 * é…ç½®å¸¸é‡æ–‡ä»¶
 * å®šä¹‰ç³»ç»Ÿä¸­æ‰€æœ‰é…ç½®é¡¹çš„键值映射
 * ç”¨äºŽç»Ÿä¸€ç®¡ç†é…ç½®é¡¹çš„路径,方便在系统中引用
 */
const configConst = {}
/**
 * é…ç½®é¡¹æ˜ å°„表
 * åŒ…含系统所有可配置项的键值对
 * é”®ä¸ºé…ç½®é¡¹åç§°ï¼Œå€¼ä¸ºé…ç½®é¡¹åœ¨é…ç½®æ–‡ä»¶ä¸­çš„路径
 */
configConst.setConfig = {
    // åŸºç¡€é…ç½®
    language: "base.language",           // è¯­è¨€è®¾ç½®ï¼šCN/EN
    password: "base.password",           // ç®¡ç†å‘˜å¯†ç 
    screenOff: "base.screenOff",         // ç†„屏时间,单位分钟,0表示从不
    screensaver: "base.screensaver",     // å±å¹•保护,单位分钟,0表示从不
    brightness: "base.brightness",       // å±å¹•亮度
    brightnessAuto: "base.brightnessAuto", // è‡ªåŠ¨äº®åº¦å¼€å…³
    volume: "base.volume",               // éŸ³é‡è®¾ç½®
    showIp: "base.showIp",               // æ˜¯å¦æ˜¾ç¤ºIP地址
    showSn: "base.showSn",               // æ˜¯å¦æ˜¾ç¤ºåºåˆ—号
    showProgramCode: "base.showProgramCode", // æ˜¯å¦æ˜¾ç¤ºç¨‹åºä»£ç 
    showIdentityCard: "base.showIdentityCard", // æ˜¯å¦æ˜¾ç¤ºèº«ä»½è¯ä¿¡æ¯
    appMode: "base.appMode",             // åº”用模式
    luminanceWhite: "base.luminanceWhite", // ç™½å…‰äº®åº¦
    luminanceNir: "base.luminanceNir",   // çº¢å¤–光亮度
    // äººè„¸è¯†åˆ«é…ç½®
    similarity: "face.similarity",       // äººè„¸è¯†åˆ«ç›¸ä¼¼åº¦é˜ˆå€¼
    livenessOff: "face.livenessOff",     // æ´»ä½“检测开关
    livenessVal: "face.livenessVal",     // æ´»ä½“检测阈值
    showNir: "face.showNir",             // æ˜¯å¦æ˜¾ç¤ºçº¢å¤–图像
    detectMask: "face.detectMask",       // æ˜¯å¦æ£€æµ‹å£ç½©
    stranger: "face.stranger",           // é™Œç”Ÿäººè¯­éŸ³æç¤ºï¼š["无语音", "播放请先注册", "播放陌生人你好"]
    voiceMode: "face.voiceMode",         // è¯­éŸ³æ¨¡å¼ï¼š["无语音", "播放名字", "播放问候语"]
    voiceModeDate: "face.voiceModeDate", // è¯­éŸ³æ¨¡å¼æ—¥æœŸè®¾ç½®
    // MQTT配置
    addr: "mqtt.addr",                   // MQTT服务器地址
    mqttclientId: "mqtt.clientId",       // MQTT客户端ID
    mqttusername: "mqtt.username",       // MQTT用户名
    mqttpassword: "mqtt.password",       // MQTT密码
    mqttqos: "mqtt.qos",                 // MQTT QoS级别
    mqttprefix: "mqtt.prefix",           // MQTT主题前缀
    onlinecheck: "mqtt.onlinecheck",     // åœ¨çº¿æ£€æŸ¥å¼€å…³
    timeout: "mqtt.timeout",             // MQTT连接超时
    willTopic: "mqtt.willTopic",         // MQTT遗嘱主题
    // ç½‘络配置
    type: "net.type",                    // ç½‘络类型
    ssid: "net.ssid",                    // WiFi SSID
    psk: "net.psk",                      // WiFi密码
    dhcp: "net.dhcp",                    // DHCP开关
    ip: "net.ip",                        // IP地址
    gateway: "net.gateway",              // ç½‘关地址
    mask: "net.mask",                    // å­ç½‘掩码
    dns: "net.dns",                      // DNS服务器
    mac: "net.mac",                      // MAC地址
    // NTP时间同步配置
    ntp: "ntp.ntp",                      // NTP开关
    server: "ntp.server",                // NTP服务器地址
    ntpInterval: "ntp.interval",         // NTP同步间隔
    gmt: "ntp.gmt",                      // GMT时区设置
    // ç³»ç»Ÿé…ç½®
    version: "sys.version",              // ç³»ç»Ÿç‰ˆæœ¬
    appVersion: "sys.appVersion",        // åº”用版本
    releaseTime: "sys.releaseTime",      // å‘布时间
    heart_en: "sys.heart_en",            // å¿ƒè·³å¼€å…³ï¼š1开 0关
    heart_time: "sys.heart_time",        // å¿ƒè·³é—´éš”
    nfc: "sys.nfc",                      // åˆ·å¡å¼€å…³ï¼š1开 0关
    pwd: "sys.pwd",                      // å¯†ç å¼€é—¨å¼€å…³ï¼š1开 0关
    emergencyPwd: "sys.emergencyPwd",        // åº”急开仓密码
    interval: "sys.interval",            // ç³»ç»Ÿé—´éš”设置
    strangerImage: "sys.strangerImage",  // é™Œç”Ÿäººä¿å­˜å›¾ç‰‡å¼€å…³ï¼š1开 0关
    accessImageType: "sys.accessImageType", // é€šè¡Œå›¾ç‰‡ç±»åž‹ï¼š1人脸 0全景
    com_passwd: "sys.com_passwd",        // é…ç½®ç å¯†ç æ ¡éªŒ
    nfcIdentityCardEnable: "sys.nfcIdentityCardEnable", // äº‘证开关:3云证获取 1物理卡号
    // é—¨ç¦é…ç½®
    offlineAccessNum: "access.offlineAccessNum", // ç¦»çº¿å¼€é—¨æ¬¡æ•°
    relayTime: "access.relayTime",       // ç»§ç”µå™¨åŠ¨ä½œæ—¶é—´
    tamperAlarm: "access.tamperAlarm"    // é˜²æ‹†æŠ¥è­¦å¼€å…³
}
/**
 * æ ¹æ®é”®èŽ·å–é…ç½®é¡¹è·¯å¾„
 * @param {string} key - é…ç½®é¡¹åç§°
 * @returns {string|undefined} é…ç½®é¡¹åœ¨é…ç½®æ–‡ä»¶ä¸­çš„路径,如果不存在则返回undefined
 */
configConst.getValueByKey = function (key) {
    return this.setConfig[key] || undefined;
}
export default configConst
vf205_access/src/common/utils/utils.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
/**
 * å·¥å…·å‡½æ•°æ¨¡å—
 * æä¾›ç³»ç»Ÿå¸¸ç”¨çš„工具函数,包括文件下载、字符串处理、系统操作等
 */
import * as os from "os"
import common from '../../../dxmodules/dxCommon.js'
import logger from "../../../dxmodules/dxLogger.js"
const utils = {}
/**
 * èŽ·å–URL文件的下载大小(字节数)
 * @param {string} url - æ–‡ä»¶çš„URL地址
 * @returns {number} æ–‡ä»¶å¤§å°ï¼ˆå­—节),如果获取失败则返回0
 */
utils.getUrlFileSize = function (url) {
    // ä½¿ç”¨wget命令获取文件大小信息
    let actualSize = common.systemWithRes(`wget --spider -S ${url} 2>&1 | grep 'Length' | awk '{print $2}'`, 100).match(/\d+/g)
    return actualSize ? parseInt(actualSize) : 0
}
/**
 * åˆ¤æ–­å€¼æ˜¯å¦ä¸ºç©ºï¼ˆç©ºå­—符串、null或undefined)
 * @param {*} str - è¦åˆ¤æ–­çš„值
 * @returns {boolean} å¦‚果值为空则返回true,否则返回false
 */
utils.isEmpty = function (str) {
    return (str === "" || str === null || str === undefined)
}
/**
 * è§£æžå­—符串为JSON对象
 * æ³¨æ„ï¼švalue内不能有"号
 * @param {string} inputString - è¦è§£æžçš„字符串,格式为{key1=value1, key2=value2}
 * @returns {Object} è§£æžåŽçš„JSON对象
 */
utils.parseString = function (inputString) {
    // èŽ·å–{}及其之间的内容
    inputString = inputString.slice(inputString.indexOf("{"), inputString.lastIndexOf("}") + 1)
    // key=value正则,key是\w+(字母数字下划线,区别大小写),=两边可有空格,value是\w+或相邻两个"之间的内容(包含")
    const keyValueRegex = /(\w+)\s*=\s*("[^"]*"|\w+(\.\w+)?)/g;
    let jsonObject = {};
    let match;
    // éåŽ†åŒ¹é…ç»“æžœï¼Œæž„å»ºJSON对象
    while ((match = keyValueRegex.exec(inputString)) !== null) {
        let key = match[1];
        let value = match[2]
        // æ ¹æ®å€¼çš„类型进行转换
        if (/^\d+$/.test(value)) {
            // æ•°å­—类型
            value = parseInt(value)
        } else if (/^\d+\.\d+$/.test(value)) {
            // å°æ•°ç±»åž‹
            value = parseFloat(value)
        } else if (value == 'true') {
            // å¸ƒå°”值true
            value = true
        } else if (value == 'false') {
            // å¸ƒå°”值false
            value = false
        } else {
            // å­—符串类型,去除引号和空格
            value = value.replace(/"/g, '').trim()
        }
        jsonObject[key] = value;
    }
    return jsonObject;
}
/**
 * ç­‰å¾…文件下载完成并进行MD5校验
 * æ³¨æ„ï¼šè¶…时时间不得超过喂狗时间,否则下载慢会导致系统重启
 * @param {string} update_addr - ä¸‹è½½åœ°å€
 * @param {string} downloadPath - å­˜å‚¨è·¯å¾„
 * @param {number} timeout - è¶…时时间(毫秒)
 * @param {string} update_md5 - MD5校验值
 * @param {number} fileSize - æ–‡ä»¶å¤§å°ï¼ˆå­—节)
 * @returns {boolean} ä¸‹è½½ç»“果:true表示成功,false表示失败
 */
utils.waitDownload = function (update_addr, downloadPath, timeout, update_md5, fileSize) {
    // åˆ é™¤åŽŸæ–‡ä»¶
    common.systemBrief(`rm -rf "${downloadPath}"`)
    // å¼‚步下载文件
    common.systemBrief(`wget -c "${update_addr}" -O "${downloadPath}" &`)
    let startTime = new Date().getTime()
    // å¾ªçŽ¯æ£€æŸ¥ä¸‹è½½è¿›åº¦
    while (true) {
        // è®¡ç®—已下载的文件大小
        let size = parseInt(common.systemWithRes(`file="${downloadPath}"; [ -e "$file" ] && wc -c "$file" | awk '{print $1}' || echo "0"`, 100).split(/\s/g)[0])
        // å¦‚果文件大小达到预期,进行MD5校验
        if (size == fileSize) {
            let ret = common.md5HashFile(downloadPath)
            if (ret) {
                let md5 = ret.map(v => v.toString(16).padStart(2, '0')).join('')
                if (md5 == update_md5) {
                    // MD5校验成功
                    return true
                }
            }
            // MD5校验失败,删除文件
            common.systemBrief(`rm -rf "${downloadPath}"`)
            return false
        }
        // å¦‚果下载超时,删除下载的文件并且重启,停止异步继续下载
        if (new Date().getTime() - startTime > timeout) {
            vf203.pwm.fail()
            common.systemBrief(`rm -rf "${downloadPath}"`)
            // ç«‹å³é‡å¯
            this.restart()
            return false
        }
        // æš‚停100毫秒后继续检查
        os.sleep(100)
    }
}
/**
 * ç«‹å³é‡å¯
 */
utils.restart = function () {
    common.systemBrief("reboot -f")
}
export default utils
vf205_access/src/config.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
{
    // CN/EN
    "base.language": "CN",
    // ç®¡ç†å‘˜å¯†ç 
    "base.password": "1",
    //是否第一次登录后台 0 æœªç™»å½• 1 å·²ç™»å½•
    "base.firstLogin": 0,
    // äººè„¸è¯†åˆ«ç›¸ä¼¼åº¦
    "face.similarity": 0.6,
    // æ´»ä½“检测
    "face.livenessOff": 0,
    // æ´»ä½“检测阈值(0-100)
    "face.livenessVal": 0,
    "face.showNir": 0,
    "face.detectMask": 0,
    // ["无语音", "播放请先注册", "播放陌生人你好"]
    "face.stranger": 1,
    // ["无语音", "播放名字", "播放问候语"]
    "face.voiceMode": 1,
    "face.voiceModeDate": "欢迎光临",
    "mqtt.addr": "192.168.1.78:1883",
    "mqtt.clientId": "",
    "mqtt.username": "admin",
    "mqtt.password": "123456",
    "mqtt.qos": 1,
    "mqtt.prefix": "",
    "mqtt.onlinecheck": 0,
    "mqtt.timeout": 5000,
    "mqtt.willTopic": "access_device/v2/event/offline",
    "net.type": 2,
    "net.ssid": "",
    "net.psk": "Fzzy@#$432..K",
    "net.dhcp": 1,
    "net.ip": "192.168.1.188",
    "net.gateway": "192.168.1.1",
    "net.mask": "255.255.255.0",
    "net.dns": "218.4.4.4,218.2.2.2",
    "net.mac": "",
    // 0关闭,1定时同步
    "ntp.ntp": 2,
    "ntp.server": "192.168.1.78",
    // å®šæ—¶åŒæ­¥ï¼Œ24小时制
    "ntp.hour": 3,
    "ntp.gmt": 8,
     // mac地址
    "sys.mac": "",
    "sys.uuid": "",
    "sys.sn": "",
    "sys.model": "vf105",
    "sys.version": "",
    "sys.appVersion": "vf105_v11_access_2.0.0",
    "sys.releaseTime": "2024-09-13 09:52:00",
    "sys.heart_en": 0, //心跳1开 0 å…³
    "sys.heart_time": 30,
    "sys.nfc": 1, //1开 0 å…³    åˆ·å¡å¼€å…³
    "sys.pwd": 1, //1开 0 å…³    å¯†ç å¼€é—¨å¼€å…³
    "sys.strangerImage": 1, //1开 0 å…³   é™Œç”Ÿäººä¿å­˜å›¾ç‰‡å¼€å…³
    "sys.accessImageType": 1, //1人脸 0 å…¨æ™¯   é€šè¡Œå›¾ç‰‡ç±»åž‹
    "sys.com_passwd": "1234567887654321", // é…ç½®ç å¯†ç æ ¡éªŒ
    //云证开关 3:云证获取 1:物理卡号
    "sys.nfcIdentityCardEnable": 1,
    "sys.interval": 1000, //扫码间隔模式间隔时间
    "access.offlineAccessNum": 2000,
    "access.relayTime": 2000,
    "access.tamperAlarm": 1,
    // ç†„屏时间,单位分钟,0从不
    "base.screenOff": 5,
    // å±å¹•保护,单位分钟,0从不
    "base.screensaver": 5,
    //屏幕亮度
    "base.brightness": 70,
    //自动调节亮度  1 è‡ªåŠ¨è°ƒèŠ‚
    "base.brightnessAuto": 1,
    //音量
    "base.volume": 80,
    // ip æ˜¾ç¤º  1 æ˜¾ç¤º 0 éšè—
    "base.showIp": 0,
    // sn æ˜¾ç¤º  1 æ˜¾ç¤º 0 éšè—
    "base.showSn": 0,
    //小程序码 æ˜¾ç¤º  1 æ˜¾ç¤º 0 éšè—
    "base.showProgramCode": 0,
    //云证功能菜单 æ˜¾ç¤º  1 æ˜¾ç¤º 0 éšè—
    "base.showIdentityCard": 0,
    // 0: æ ‡å‡†æ¨¡å¼ 1: ç®€çº¦æ¨¡å¼
    "base.appMode": 1,
    // ç™½è‰²è¡¥å…‰ç¯äº®åº¦
    "base.luminanceWhite": 80,
    // çº¢å¤–补光灯亮度
    "base.luminanceNir": 80,
    // 0:wifi ç‰ˆæœ¬  1:4G版本  2:以太网版本 é»˜è®¤ 3未读取状态
    "sys.devType": 0,
    // ä¸šåŠ¡ç¼–ç å®šä¹‰ï¼ˆFunctionId)
    "functionId": {
        "gasDetection": "1000",
        "safeInputControl": "2000",
        "lightControl": "3000"
    },
    // æŽ¥å£è¿”回编码(respCode)
    "respCode": {
        "success": "200",
        "badRequest": "400",
        "unauthorized": "401",
        "forbidden": "403",
        "notFound": "404",
        "serverError": "500"
    },
    // ä»“廒名称
    "houseName": "01号仓",
    // åº“区名称
    "GranaryName": "中央储备粮某某直属库",
    // HTTP接口路径
    "http": {
        "safeInputAccess": "http://192.168.1.119:80/cgi-bin/safeInputAccess"
    }
}
vf205_access/src/controller.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
/**
 * æŽ§åˆ¶å™¨æ–‡ä»¶
 * è´Ÿè´£å®šæœŸæ‰§è¡Œç³»ç»Ÿå„驱动的循环操作,确保系统正常运行
 */
import log from '../dxmodules/dxLogger.js'
import std from '../dxmodules/dxStd.js'
import face from '../dxmodules/dxFace.js'
import bus from '../dxmodules/dxEventBus.js'
import driver from './driver.js'
/**
 * è¿è¡ŒæŽ§åˆ¶å™¨
 * è®¾ç½®å¤šä¸ªå®šæ—¶å™¨ï¼Œå®šæœŸæ‰§è¡Œä¸åŒçš„循环任务
 */
function run() {
    // æ¯5ms执行一次主循环
    std.setInterval(() => {
        try {
            driver.watchdog.feed("controller", 30) // å–‚狗,设置30秒超时
            loop() // æ‰§è¡Œä¸»å¾ªçޝ
        } catch (error) {
            log.error(error) // è®°å½•错误
        }
    }, 5)
    // æ¯500ms执行一次网络循环
    std.setInterval(() => {
        try {
            driver.watchdog.feed("controller1", 30) // å–‚狗,设置30秒超时
            driver.net.loop() // æ‰§è¡Œç½‘络循环
        } catch (error) {
            log.error(error) // è®°å½•错误
        }
    }, 500)
    // æ¯1000ms执行一次NTP循环
    std.setInterval(() => {
        try {
            driver.watchdog.feed("controller2", 30) // å–‚狗,设置30秒超时
            driver.ntp.loop() // æ‰§è¡ŒNTP循环
        } catch (error) {
            log.error(error) // è®°å½•错误
        }
    }, 1000)
}
/**
 * å¯åŠ¨æŽ§åˆ¶å™¨
 */
try {
    run()
} catch (error) {
    log.error(error)
}
/**
 * ä¸»å¾ªçŽ¯å‡½æ•°
 * æ‰§è¡Œå„驱动的循环操作
 */
function loop() {
    driver.capturer.loop()      // æ‰§è¡Œæ‘„像头循环
    driver.face.loop()          // æ‰§è¡Œäººè„¸è¯†åˆ«å¾ªçޝ
    driver.nfc.loop()           // æ‰§è¡ŒNFC循环
    driver.mqtt.heartbeat()     // æ‰§è¡ŒMQTT心跳
    driver.gpiokey.loop()       // æ‰§è¡ŒGPIO按键循环
}
vf205_access/src/driver.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1226 @@
/**
 * é©±åŠ¨æ¨¡å—æ–‡ä»¶
 * åŒ…含系统所有硬件驱动的初始化和操作方法,是系统与硬件交互的核心模块
 */
import * as os from "os"
import capturer from '../dxmodules/dxCapturer.js'
import cameraCalibration from '../dxmodules/dxCameraCalibration.js'
import face from '../dxmodules/dxFace.js'
import std from '../dxmodules/dxStd.js'
import common from '../dxmodules/dxCommon.js'
import utils from './common/utils/utils.js'
import alsa from '../dxmodules/dxAlsa.js'
import config from '../dxmodules/dxConfig.js'
import pwm from '../dxmodules/dxPwm.js'
import net from '../dxmodules/dxNet.js'
import ntp from '../dxmodules/dxNtp.js'
import mqtt from '../dxmodules/dxMqtt.js'
import dxMap from '../dxmodules/dxMap.js'
import logger from '../dxmodules/dxLogger.js'
import sqliteService from "./service/sqliteService.js"
import mqttService from "./service/mqttService.js"
import gpio from "../dxmodules/dxGpio.js"
import map from "../dxmodules/dxMap.js"
import eid from "../dxmodules/dxEid.js"
import nfc from "../dxmodules/dxNfc.js"
import bus from "../dxmodules/dxEventBus.js"
import dxUart from "../dxmodules/dxUart.js"
import watchdog from "../dxmodules/dxWatchdog.js"
import base64 from "../dxmodules/dxBase64.js"
import dxGpioKey from "../dxmodules/dxGpioKey.js"
import dxDriver from "../dxmodules/dxDriver.js"
const driver = {}
/**
 * éŸ³é¢‘驱动模块
 * è´Ÿè´£éŸ³é¢‘播放和语音合成
 */
driver.alsa = alsa
/**
 * é…ç½®é©±åŠ¨æ¨¡å—
 * è´Ÿè´£åˆå§‹åŒ–系统配置,设置系统基本信息
 */
driver.config = {
    /**
     * åˆå§‹åŒ–配置
     * åˆå§‹åŒ–配置模块,设置系统MAC、UUID、SN等基本信息
     */
    init: function () {
        config.init()
        let mac = common.getUuid2mac(19)
        let uuid = common.getSn(19)
        if (!config.get('sys.mac') && mac) {
            config.set('sys.mac', mac)
        }
        if (!config.get('sys.uuid') && uuid) {
            config.set('sys.uuid', uuid)
        }
        //如果 sn ä¸ºç©ºå…ˆç”¨è®¾å¤‡ uuid
        if (!config.get('sys.sn') && uuid) {
            config.set('sys.sn', uuid)
        }
        if (!config.get('mqtt.clientId') && uuid) {
            config.set('mqtt.clientId', uuid)
        }
        config.save()
    }
}
/**
 * å±å¹•驱动模块
 * è´Ÿè´£å±å¹•相关的事件触发和操作
 */
driver.screen = {
    /**
     * é€šè¡Œå¤±è´¥
     * è§¦å‘通行失败事件
     */
    accessFail: function () {
        bus.fire('accessRes', false)
    },
    /**
     * é€šè¡ŒæˆåŠŸ
     * è§¦å‘通行成功事件
     */
    accessSuccess: function () {
        bus.fire('accessRes', true)
    },
    /**
     * å‡çº§
     * è§¦å‘升级事件
     * @param {object} data - å‡çº§æ•°æ®
     */
    upgrade: function (data) {
        bus.fire('upgrade', data)
    },
    /**
     * èŽ·å–å¡ç‰‡
     * æ’­æ”¾è¯»å¡å£°éŸ³å¹¶è§¦å‘获取卡片事件
     * @param {string} card - å¡ç‰‡ä¿¡æ¯
     */
    getCard: function (card) {
        driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/read.wav`)
        bus.fire('getCard', card)
    },
    /**
     * éšè—SN
     * è§¦å‘隐藏SN事件
     * @param {object} data - äº‹ä»¶æ•°æ®
     */
    hideSn: function (data) {
        bus.fire('hideSn', data)
    },
    /**
     * åº”用模式
     * è§¦å‘应用模式事件
     * @param {object} data - æ¨¡å¼æ•°æ®
     */
    appMode: function (data) {
        bus.fire('appMode', data)
    },
    /**
     * éšè—IP
     * è§¦å‘隐藏IP事件
     * @param {object} data - äº‹ä»¶æ•°æ®
     */
    hideIp: function (data) {
        bus.fire('hideIp', data)
    },
    /**
     * åˆ‡æ¢è¯­è¨€
     * è§¦å‘语言切换事件
     */
    changeLanguage: function () {
        bus.fire('changeLanguage')
    }
}
/**
 * SQLite驱动模块
 * è´Ÿè´£æ•°æ®åº“初始化和管理
 */
driver.sqlite = {
    /**
     * åˆå§‹åŒ–数据库
     * ç¡®ä¿æ•°æ®åº“路径存在并初始化SQLite服务
     */
    init: function () {
        std.ensurePathExists('/app/data/db/app.db')
        sqliteService.init('/app/data/db/app.db')
    }
}
/**
 * PWM驱动模块
 * è´Ÿè´£æŽ§åˆ¶è¡¥å…‰ç¯çš„亮度
 */
driver.pwm = {
    /**
     * åˆå§‹åŒ–PWM
     * åˆå§‹åŒ–白色补光灯和红外补光灯的PWM通道
     */
    init: function () {
        // ç™½ç¯
        let luminanceWhite = config.get('base.luminanceWhite') ?? 80
        pwm.request(dxDriver.PWM.WHITE_SUPPLEMENT_CHANNEL);
        pwm.setPeriodByChannel(dxDriver.PWM.WHITE_SUPPLEMENT_CHANNEL, dxDriver.PWM.WHITE_SUPPLEMENT_PERIOD_NS)
        pwm.enable(dxDriver.PWM.WHITE_SUPPLEMENT_CHANNEL, true);
        pwm.setDutyByChannel(dxDriver.PWM.WHITE_SUPPLEMENT_CHANNEL, dxDriver.PWM.WHITE_SUPPLEMENT_PERIOD_NS - (dxDriver.PWM.WHITE_SUPPLEMENT_PERIOD_NS * (luminanceWhite / 100)))
        // çº¢å¤–
        let luminanceNir = config.get('base.luminanceNir') ?? 80
        pwm.request(dxDriver.PWM.NIR_SUPPLEMENT_CHANNEL);
        pwm.setPeriodByChannel(dxDriver.PWM.NIR_SUPPLEMENT_CHANNEL, dxDriver.PWM.NIR_SUPPLEMENT_PERIOD_NS)
        pwm.enable(dxDriver.PWM.NIR_SUPPLEMENT_CHANNEL, true);
        pwm.setDutyByChannel(dxDriver.PWM.NIR_SUPPLEMENT_CHANNEL, dxDriver.PWM.NIR_SUPPLEMENT_PERIOD_NS - (dxDriver.PWM.NIR_SUPPLEMENT_PERIOD_NS * (luminanceNir / 100)))
    },
    /**
     * è°ƒèŠ‚ç™½è‰²è¡¥å…‰ç¯äº®åº¦
     * @param {number} value - äº®åº¦å€¼ï¼ŒèŒƒå›´0-100
     */
    luminanceWhite: function (value) {
        if (value < 0 || value > 100) {
            logger.error("[driver.pwm]: value should be between 0 and 100")
            return
        }
        pwm.setDutyByChannel(dxDriver.PWM.WHITE_SUPPLEMENT_CHANNEL, dxDriver.PWM.WHITE_SUPPLEMENT_PERIOD_NS - (dxDriver.PWM.WHITE_SUPPLEMENT_PERIOD_NS * (value / 100)))
    },
    /**
     * è°ƒèŠ‚çº¢å¤–è¡¥å…‰ç¯äº®åº¦
     * @param {number} value - äº®åº¦å€¼ï¼ŒèŒƒå›´0-100
     */
    luminanceNir: function (value) {
        if (value < 0 || value > 100) {
            logger.error("[driver.pwm]: value should be between 0 and 100")
            return
        }
        pwm.setDutyByChannel(dxDriver.PWM.NIR_SUPPLEMENT_CHANNEL, dxDriver.PWM.NIR_SUPPLEMENT_PERIOD_NS - (dxDriver.PWM.NIR_SUPPLEMENT_PERIOD_NS * (value / 100)))
    }
}
/**
 * ALSA驱动模块
 * è´Ÿè´£éŸ³é¢‘播放和音量控制
 */
driver.alsa = {
    /**
     * åˆå§‹åŒ–音频
     * åˆå§‹åŒ–ALSA音频模块并设置音量
     */
    init: function () {
        alsa.init()
        this.volume(config.get("base.volume"))
    },
    /**
     * æ’­æ”¾éŸ³é¢‘文件
     * @param {string} src - éŸ³é¢‘文件路径
     */
    play: function (src) {
        alsa.play(src)
    },
    /**
     * æ–‡æœ¬è½¬è¯­éŸ³æ’­æ”¾
     * @param {string} text - è¦æ’­æ”¾çš„æ–‡æœ¬
     */
    ttsPlay: function (text) {
        alsa.ttsPlay(text)
    },
    /**
     * èŽ·å–æˆ–è®¾ç½®éŸ³é‡
     * @param {number} [volume] - éŸ³é‡å€¼ï¼ŒèŒƒå›´1-100
     * @returns {number} å½“前音量值(当未提供参数时)
     */
    volume: function (volume) {
        if (volume === undefined || volume === null) {
            return alsa.getVolume()
        } else {
            function mapScore(input) {
                // ç¡®ä¿è¾“入值在1-100之间
                if (input < 1 || input > 100) {
                    throw new Error('输入值必须在1到100之间');
                }
                if (input < 60 && input > 30) {
                    input = input * 1.2
                }
                if (input < 30 && input > 1) {
                    input = input * 2
                }
                return input
            }
            alsa.setVolume(mapScore(volume))
        }
    }
}
/**
 * æ‘„像头驱动模块
 * è´Ÿè´£æ‘„像头的初始化和操作,包括彩色摄像头和红外摄像头
 */
driver.capturer = {
    /**
     * å½©è‰²æ‘„像头配置
     */
    options1: {
        id: "rgb",
        path: dxDriver.CAPTURER.RGB_PATH,
        width: dxDriver.CAPTURER.RGB_WIDTH,
        height: dxDriver.CAPTURER.RGB_HEIGHT,
        preview_width: dxDriver.CAPTURER.RGB_HEIGHT,
        preview_height: dxDriver.CAPTURER.RGB_WIDTH,
        preview_mode: 2,
        preview_screen_index: 0 // å…ˆåŽé¡ºåºï¼Œæ•°å­—越大越在前面
    },
    /**
     * çº¢å¤–摄像头配置
     */
    options2: {
        id: "nir",
        path: dxDriver.CAPTURER.NIR_PATH,
        width: dxDriver.CAPTURER.NIR_WIDTH,
        height: dxDriver.CAPTURER.NIR_HEIGHT,
        preview_width: 150,
        preview_height: 200,
        preview_mode: 1,
        preview_left: 605,
        preview_top: 80,
        preview_screen_index: 1 // å…ˆåŽé¡ºåºï¼Œæ•°å­—越大越在前面
    },
    /**
     * åˆå§‹åŒ–摄像头
     * åˆå§‹åŒ–彩色摄像头和红外摄像头
     */
    init: function () {
        capturer.worker.beforeLoop(this.options1)
        capturer.worker.beforeLoop(this.options2)
        this.showNir(config.get("face.showNir"))
    },
    /**
     * æ˜¾ç¤ºæˆ–隐藏红外摄像头预览
     * @param {boolean} enable - æ˜¯å¦å¯ç”¨çº¢å¤–摄像头预览
     */
    showNir: function (enable) {
        capturer.capturerEnable(enable, this.options2.id)
    },
    /**
     * å°†å›¾ç‰‡æ•°æ®è½¬æ¢ä¸ºå›¾åƒ
     * @param {string} base64Data - Base64编码的图片数据
     * @returns {number} å›¾åƒID
     */
    pictureDataToImage: function (base64Data) {
        return capturer.pictureDataToImage(base64Data, base64Data.length, 1)
    },
    /**
     * å°†å›¾åƒä¿å­˜ä¸ºæ–‡ä»¶
     * @param {number} imageId - å›¾åƒID
     * @param {string} savePath - ä¿å­˜è·¯å¾„
     * @returns {boolean} æ˜¯å¦ä¿å­˜æˆåŠŸ
     */
    imageToPictureFile: function (imageId, savePath) {
        return capturer.imageToPictureFile(imageId, 1, 0, 24, savePath)
    },
    /**
     * å°†å›¾åƒä¿å­˜ä¸ºæ–‡ä»¶ï¼ˆé«˜è´¨é‡ï¼‰
     * @param {number} imageId - å›¾åƒID
     * @param {string} savePath - ä¿å­˜è·¯å¾„
     * @returns {boolean} æ˜¯å¦ä¿å­˜æˆåŠŸ
     */
    imageToPictureFile2: function (imageId, savePath) {
        return capturer.imageToPictureFile(imageId, 1, 0, 100, savePath)
    },
    /**
     * è°ƒæ•´å›¾åƒåˆ†è¾¨çއ
     * @param {number} imageId - å›¾åƒID
     * @param {number} width - ç›®æ ‡å®½åº¦
     * @param {number} height - ç›®æ ‡é«˜åº¦
     * @returns {number} è°ƒæ•´åŽçš„图像ID
     */
    imageResizeResolution: function (imageId, width, height) {
        return capturer.imageResizeResolution(imageId, width, height, 0)
    },
    /**
     * æ‘„像头循环
     * æ‰§è¡Œæ‘„像头的循环操作
     */
    loop: function () {
        capturer.worker.loop(this.options1)
        capturer.worker.loop(this.options2)
    }
}
/**
 * NFC驱动模块
 * è´Ÿè´£NFC卡片的读取和处理
 */
driver.nfc = {
    /**
     * NFC配置选项
     */
    options: { m1: true, psam: false },
    /**
     * åˆå§‹åŒ–NFC
     * åˆå§‹åŒ–NFC模块,根据配置决定是否启用
     */
    init: function () {
        if (!config.get('sys.nfc')) {
            logger.debug("刷卡已关闭")
            return
        }
        this.options.useEid = config.get("sys.nfcIdentityCardEnable") == 3 ? 1 : 0
        nfc.worker.beforeLoop(this.options)
    },
    /**
     * åˆå§‹åŒ–EID(电子身份证)
     * æ›´æ–°EID配置
     */
    eidInit: function () {
        if (!config.get('sys.nfc')) {
            return
        }
        if (config.get("sys.nfcIdentityCardEnable") == 3) {
            nfc.eidUpdateConfig({ appid: "1621503", sn: config.get("sys.sn"), device_model: config.get("sys.appVersion") })
        }
    },
    /**
     * NFC循环
     * æ‰§è¡ŒNFC的循环操作,根据配置决定是否启用
     */
    loop: function () {
        if (!config.get('sys.nfc')) {
            this.loop = () => { }
        } else {
            this.loop = () => nfc.worker.loop(this.options)
        }
    }
}
/**
 * äººè„¸è¯†åˆ«é©±åŠ¨æ¨¡å—
 * è´Ÿè´£äººè„¸è¯†åˆ«ã€æ³¨å†Œå’Œç›¸å…³åŠŸèƒ½çš„ç®¡ç†
 */
driver.face = {
    /**
     * åˆå§‹åŒ–人脸识别
     * åˆå§‹åŒ–人脸模块,设置相关参数和配置
     */
    init: function () {
        common.systemBrief('mkdir -p /app/data/user/temp/')
        let options = {
            dbPath: "/app/data/db/face.db",
            rgbPath: "/dev/video3",
            nirPath: "/dev/video0",
            capturerRgbId: "rgb",
            capturerNirId: "nir",
            dbMax: 5000, //人脸注册上限
            score: config.get("face.similarity"),
            picPath: "/app/data/user/temp",
            gThumbnailHeight: 1280 / 6,
            gThumbnailWidth: 800 / 6,
            // æ˜¯å¦å¼€å¯é‡æ£€
            recgFaceattrEnable: 1,
            // æ´»ä½“开关
            livingCheckEnable: config.get("face.livenessOff"),
            // æ´»ä½“检测阈值
            livingScore: config.get("face.livenessVal"),
            // å£ç½©æ£€æµ‹å¼€å…³
            detectMaskEnable: config.get("face.detectMask"),
            // é‡æ£€é—´éš”
            recheckIntervalTime: 5000,
            // æ£€æµ‹è¶…æ—¶
            detectTimeoutTime: 1000
        }
        face.worker.beforeLoop(options)
        // é»˜è®¤ä¸ºäººè„¸è¯†åˆ«æ¨¡å¼
        this.mode(0)
        // å…³é—­æ‰€æœ‰äººè„¸åŠŸèƒ½
        this.status(false)
        // å±å¹•亮度
        this.setDisplayBacklight(config.get("base.brightness"))
        this.screenStatus(1)
        // è¡¥å…‰ç¯çŠ¶æ€è·Ÿè¸ª
        let isLightOn = true
        // å±å¹•亮度自动调节
        std.setInterval(() => {
            // ç†„屏判断
            let screenOff = map.get("screenOff")
            if (screenOff.get("status") == 1) {
                this.setDisplayBacklight(0)
                this.screenStatus(0)
                // å…³é—­è¡¥å…‰ç¯ï¼ˆä»…当补光灯当前是开启状态时)
                if (isLightOn) {
                    driver.pwm.luminanceWhite(0)
                    driver.pwm.luminanceNir(0)
                    logger.info("[driver.face]: ç†„屏,关闭补光灯")
                    isLightOn = false
                }
            }
            // åœæ­¢ç†„屏
            if (screenOff.get("status") != 1) {
                if (config.get("base.brightnessAuto") == 1) {
                    // è‡ªåŠ¨è°ƒèŠ‚å±å¹•äº®åº¦
                    let brightness = Math.floor(face.getEnvBrightness() / 10)
                    brightness = brightness > 100 ? 100 : brightness
                    this.setDisplayBacklight(brightness)
                } else {
                    this.setDisplayBacklight(config.get("base.brightness"))
                }
                // å¼€å¯è¡¥å…‰ç¯ï¼ˆä»…当补光灯当前是关闭状态时)
                if (!isLightOn) {
                    let luminanceWhite = config.get('base.luminanceWhite') ?? 80
                    let luminanceNir = config.get('base.luminanceNir') ?? 80
                    driver.pwm.luminanceWhite(luminanceWhite)
                    driver.pwm.luminanceNir(luminanceNir)
                    logger.info(`[driver.face]: é€€å‡ºç†„屏,开启补光灯(白灯: ${luminanceWhite}%, çº¢å¤–: ${luminanceNir}%)`)
                    isLightOn = true
                }
            }
        }, 1000)
    },
    /**
     * èŽ·å–äººè„¸è·Ÿè¸ªæ¡†
     * @returns {object} è·Ÿè¸ªæ¡†ä¿¡æ¯
     */
    getTrackingBox: function () {
        return face.getTrackingBox()
    },
    /**
     * äººè„¸è¯†åˆ«å¾ªçޝ
     * æ‰§è¡Œäººè„¸è¯†åˆ«çš„循环操作
     */
    loop: function () {
        // æ£€æŸ¥å±å¹•是否处于熄屏状态
        let screenOff = map.get("screenOff")
        if (screenOff && screenOff.get("status") == 1) {
            // ç†„屏状态下不进行人脸识别
            return
        }
        face.worker.loop()
    },
    /**
     * äººè„¸çº¿ç¨‹å¯ç”¨å¼€å…³
     * @param {boolean} flag - æ˜¯å¦å¯ç”¨äººè„¸æ£€æµ‹
     */
    status: function (flag) {
        console.log('---人脸检测' + (flag ? '开启' : '暂停') + '---');
        face.faceSetEnable(flag)
    },
    /**
     * è®¾ç½®äººè„¸è¯†åˆ«æ¨¡å¼
     * @param {number} value - æ¨¡å¼å€¼ï¼Œ0为识别模式,1为注册模式
     */
    mode: function (value) {
        console.log('---人脸' + (value ? '注册' : '识别') + '模式---');
        face.setRecgMode(value)
    },
    /**
     * äººè„¸æ³¨å†Œ
     * @param {string} id - ç”¨æˆ·ID
     * @param {string} feature - äººè„¸ç‰¹å¾
     * @returns {boolean} æ˜¯å¦æ³¨å†ŒæˆåŠŸ
     */
    reg: function (id, feature) {
        return face.addFaceFeatures(id, feature);
    },
    /**
     * æ›´æ–°äººè„¸é…ç½®
     * @param {object} options - é…ç½®é€‰é¡¹
     */
    faceUpdateConfig: function (options) {
        console.log("更新人脸配置", JSON.stringify(options));
        face.faceUpdateConfig(options)
    },
    /**
     * è®¾ç½®å±å¹•亮度
     * @param {number} brightness - äº®åº¦å€¼
     */
    setDisplayBacklight: function (brightness) {
        brightness = brightness < 2 ? 2 : brightness
        face.setDisplayBacklight(brightness)
    },
    /**
     * é€šè¿‡å›¾ç‰‡æ–‡ä»¶æ³¨å†Œäººè„¸
     * @param {string} userId - ç”¨æˆ·ID
     * @param {string} picPath - å›¾ç‰‡è·¯å¾„
     * @returns {boolean} æ˜¯å¦æ³¨å†ŒæˆåŠŸ
     */
    registerFaceByPicFile: function (userId, picPath) {
        return face.registerFaceByPicFile(userId, picPath)
    },
    /**
     * æ¸…空人脸数据
     * @returns {boolean} æ˜¯å¦æ¸…空成功
     */
    clean: function () {
        // æ¸…空人脸,需要在初始化人脸组件之前才能执行,否则报错
        face.faceFeaturesClean()
        common.systemBrief("rm -rf /app/data/db/face.db")
        return !std.exist("/app/data/db/face.db")
    },
    /**
     * åˆ é™¤æŒ‡å®šç”¨æˆ·çš„人脸数据
     * @param {string} userId - ç”¨æˆ·ID
     * @returns {boolean} æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    delete: function (userId) {
        return face.deleteFaceFeatures(userId)
    },
    /**
     * è®¾ç½®å±å¹•状态
     * @param {boolean} status - æ˜¯å¦å¯ç”¨å±å¹•
     */
    screenStatus: function (status) {
        if (status) {
            face.setPowerMode(0)
        } else {
            face.setPowerMode(1)
        }
        face.setEnableStatus(status)
    },
    /**
     * å°†æ–‡ä»¶è½¬æ¢ä¸ºbase64编码
     * @param {string} filePath - æ–‡ä»¶è·¯å¾„
     * @returns {string} Base64编码的文件内容
     */
    fileToBase64: function (filePath) {
        function fileToUint8Array(filename) {
            // è¯»å–文件
            const file = std.open(filename, "rb");
            if (!file) {
                throw new Error("无法打开文件");
            }
            // èŽ·å–æ–‡ä»¶å¤§å°
            const size = std.seek(file, 0, std.SEEK_END)
            std.seek(file, 0, std.SEEK_SET)
            // æ£€æŸ¥æ–‡ä»¶å¤§å°æ˜¯å¦æœ‰æ•ˆ
            if (size <= 0) {
                std.close(file);
                throw new Error("文件大小无效");
            }
            // åˆ›å»º ArrayBuffer å¹¶è¯»å–文件内容
            const buffer = new ArrayBuffer(size);
            const array = new Uint8Array(buffer);
            std.read(file, array.buffer, 0, size);
            std.close(file);
            return array;
        }
        try {
            // æ£€æŸ¥æ–‡ä»¶è·¯å¾„是否存在
            if (!filePath) {
                throw new Error("文件路径为空");
            }
            const data = fileToUint8Array(filePath);
            return base64.fromUint8Array(data);
        } catch (error) {
            logger.info("文件转Base64失败: " + error.message);
            return "";
        }
    }
}
/**
 * ç½‘络驱动模块
 * è´Ÿè´£ç½‘络连接和管理
 */
driver.net = {
    /**
     * åˆå§‹åŒ–网络
     * åˆå§‹åŒ–网络模块,设置网络配置
     */
    init: function () {
        let dns = config.get("net.dns").split(",")
        let option = {
            type: config.get("net.type"),
            dhcp: config.get("net.dhcp"),
            ip: config.get("net.ip"),
            gateway: config.get("net.gateway"),
            netmask: config.get("net.mask"),
            dns0: dns[0],
            dns1: dns[1],
            macAddr: common.getUuid2mac()
        }
        logger.info("更新联网配置:", JSON.stringify(option));
        net.worker.beforeLoop(option)
        config.set("net.mac", common.getUuid2mac())
        if (config.get("net.type") == 2) {
            //wifi取配置文件去连接
            let ssid = utils.isEmpty(config.get('net.ssid')) ? "ssid" : config.get('net.ssid')
            let psk = utils.isEmpty(config.get('net.psk')) ? "psk" : config.get('net.psk')
            driver.net.netConnectWifiSsid(ssid, psk)
        }
        // è§£å†³ç½‘络切换状态不对
        std.setInterval(() => {
            let status = net.getStatus()
            if (status.status != map.get("NET").get("status")) {
                status.type = config.get("net.type")
                bus.fire(net.STATUS_CHANGE, status)
            }
        }, 1000)
    },
    /**
     * åˆ‡æ¢ç½‘络类型
     * åˆ‡æ¢ç½‘络类型并配置相应的网络参数
     */
    changeNetType: function () {
        // åР锁
        if (map.get("NET").get("changeType") == "Y") {
            return
        }
        map.get("NET").put("changeType", "Y")
        let type = config.get("net.type")
        logger.info("切换网络", type);
        [1, 2, 4].filter(v => v != type).forEach(v => {
            logger.info("关闭网卡", v, net.cardEnable(v, false));
        })
        logger.info("设置主网卡", type, net.setMasterCard(type));
        logger.info("开启网卡", type, net.cardEnable(type, true));
        if (type == 2) {
            //wifi取配置文件去连接
            let ssid = utils.isEmpty(config.get('net.ssid')) ? "ssid" : config.get('net.ssid')
            let psk = utils.isEmpty(config.get('net.psk')) ? "psk" : config.get('net.psk')
            logger.info("连接wifi", ssid, psk);
            net.netConnectWifiSsid(ssid, psk)
            // ç­‰å¾…WiFi连接成功后再设置网络模式
            std.setTimeout(() => {
                let dns = config.get("net.dns").split(",")
                net.setModeByCard(type, config.get("net.dhcp"), config.get("net.dhcp") == 1 ? {
                    ip: config.get("net.ip"),
                    gateway: config.get("net.gateway"),
                    netmask: config.get("net.mask"),
                    dns0: dns[0],
                    dns1: dns[1],
                } : undefined)
            }, 3000); // ç­‰å¾…3秒让WiFi连接成功
        } else if (type == 1) {
            // ä»¥å¤ªç½‘直接设置网络模式
            let dns = config.get("net.dns").split(",")
            net.setModeByCard(type, config.get("net.dhcp"), config.get("net.dhcp") == 1 ? {
                ip: config.get("net.ip"),
                gateway: config.get("net.gateway"),
                netmask: config.get("net.mask"),
                dns0: dns[0],
                dns1: dns[1],
            } : undefined)
        }
        map.get("NET").del("changeType")
    },
    /**
     * åˆå§‹åŒ–EID网络
     * é€€å‡ºç½‘络并重启相关服务
     */
    eidInit: function () {
        net.exit();
        common.systemWithRes(`pkill -9 -f 'wpa_supplicant|udhcpc'`, 5)
    },
    /**
     * èŽ·å–ç½‘ç»œçŠ¶æ€
     * @returns {boolean} æ˜¯å¦è¿žæŽ¥æˆåŠŸ
     */
    getStatus: function () {
        let status = net.getStatus()
        if (status.connected == true && status.status == 4) {
            return true
        } else {
            return false
        }
    },
    /**
     * è¿žæŽ¥WiFi
     * @param {string} ssid - WiFi名称
     * @param {string} psk - WiFi密码
     */
    netConnectWifiSsid: function (ssid, psk) {
        net.netConnectWifiSsid(ssid, psk, "")
    },
    /**
     * èŽ·å–WiFi列表
     * @returns {array} WiFi列表
     */
    netGetWifiSsidList: function () {
        if (!driver.net.getStatus()) {
            //如果 wifi è¿žæŽ¥å¤±è´¥  èŽ·å–åˆ—è¡¨ä¼šå¤±è´¥éœ€è¦å…ˆé”€æ¯
            net.netDisconnetWifi()
        }
        let result = net.netGetWifiSsidList(1000, 5)
        if (!result || !result.results || result.results.length === 0) {
            return [];
        }
        let wifiList = []; // åˆå§‹åŒ–wifiList为数组
        result.results.forEach(element => wifiList.push(element.ssid)); // ä½¿ç”¨push方法添加ssid到数组
        return wifiList;
    },
    /**
     * é‡ç½®ç½‘卡
     */
    cardReset: function () {
        // net.netCardReset(2,1)
    },
    /**
     * ç½‘络循环
     * æ‰§è¡Œç½‘络的循环操作
     */
    loop: function () {
        net.worker.loop()
    }
}
/**
 * NTP驱动模块
 * è´Ÿè´£ç½‘络时间同步
 */
driver.ntp = {
    /**
     * NTP循环
     * åˆå§‹åŒ–NTP模块并执行时间同步操作
     */
    loop: function () {
        // æ¯ç§’钟判断时间,如果时差大于2秒则进行了对时
        let last = new Date().getTime()
        dxMap.get("NTP_SYNC").put("syncTime", last)
        std.setInterval(() => {
            let now = new Date().getTime()
            let diff = now - last
            if (diff > 2000) {
                dxMap.get("NTP_SYNC").put("syncTime", now)
                last = now
            }
        }, 1000)
        ntp.beforeLoop(config.get("ntp.server"), 9999999999999)
        this.ntpHour = config.get('ntp.hour')
        this.flag = true
        driver.ntp.loop = () => {
            if (config.get("ntp.ntp")) {
                ntp.loop()
                if (new Date().getHours() == this.ntpHour && this.flag) {
                    // å®šæ—¶åŒæ­¥ï¼Œç«‹å³åŒæ­¥ä¸€æ¬¡æ—¶é—´
                    ntp.syncnow = true
                    this.flag = false
                }
                if (new Date().getHours() != this.ntpHour) {
                    // ç­‰è¿‡äº†è¿™ä¸ªå°æ—¶å†æ¬¡å…è®¸å¯¹æ—¶
                    this.flag = true
                }
            }
        }
    }
}
/**
 * åŒæ­¥é©±åŠ¨æ¨¡å—
 * æä¾›å¼‚步转同步的实现
 */
driver.sync = {
    /**
     * å¼‚步转同步请求
     * @param {string} topic - ä¸»é¢˜
     * @param {number} timeout - è¶…时时间(毫秒)
     * @returns {any} å“åº”数据
     */
    request: function (topic, timeout) {
        let map = dxMap.get("SYNC")
        let count = 0
        let data = map.get(topic)
        while (utils.isEmpty(data) && count * 10 < timeout) {
            data = map.get(topic)
            std.sleep(10)
            count += 1
        }
        let res = map.get(topic)
        map.del(topic)
        return res
    },
    /**
     * åŒæ­¥å“åº”
     * @param {string} topic - ä¸»é¢˜
     * @param {any} data - å“åº”数据
     */
    response: function (topic, data) {
        let map = dxMap.get("SYNC")
        map.put(topic, data)
    }
}
/**
 * MQTT驱动模块
 * è´Ÿè´£MQTT通信和消息处理
 */
driver.mqtt = {
    /**
     * åˆå§‹åŒ–MQTT
     * åˆå§‹åŒ–MQTT模块,设置连接参数
     */
    init: function () {
        mqtt.run({ mqttAddr: config.get("mqtt.addr"), clientId: config.get('mqtt.clientId'), subs: mqttService.getTopics(), username: config.get("mqtt.username"), password: config.get("mqtt.password"), qos: config.get("mqtt.qos"), willTopic: config.get("mqtt.willTopic"), willMessage: JSON.stringify({ "uuid": config.get("sys.uuid") }) })
    },
    /**
     * åˆå§‹åŒ–EID的MQTT
     * é”€æ¯MQTT连接
     */
    eidInit: function () {
        mqtt.destroy()
    },
    /**
     * å‘送MQTT消息
     * @param {string} topic - æ¶ˆæ¯ä¸»é¢˜
     * @param {any} payload - æ¶ˆæ¯è½½è·
     */
    send: function (topic, payload,) {
        logger.info("[driver.mqtt] send :", topic)
        mqtt.send(topic, payload)
    },
    /**
     * èŽ·å–åœ¨çº¿æ£€æŸ¥
     * @returns {any} åœ¨çº¿æ£€æŸ¥ç»“æžœ
     */
    getOnlinecheck: function () {
        let timeout = config.get("mqtt.timeout")
        timeout = utils.isEmpty(timeout) ? 2000 : timeout
        return driver.sync.request("mqtt.getOnlinecheck", timeout)
    },
    /**
     * åœ¨çº¿æ£€æŸ¥å›žå¤
     * @param {any} data - å›žå¤æ•°æ®
     */
    getOnlinecheckReply: function (data) {
        driver.sync.response("mqtt.getOnlinecheck", data)
    },
    /**
     * èŽ·å–MQTT连接状态
     * @returns {boolean} æ˜¯å¦è¿žæŽ¥æˆåŠŸ
     */
    getStatus: function () {
        return mqtt.isConnected()
    },
    /**
     * MQTT心跳
     * å‘送心跳消息,保持连接
     */
    heartbeat: function () {
        if (utils.isEmpty(this.heart_en)) {
            let heart_en = config.get('sys.heart_en')
            this.heart_en = utils.isEmpty(heart_en) ? 0 : heart_en
            let heart_time = config.get('sys.heart_time')
            this.heart_time = utils.isEmpty(heart_time) ? 30 : heart_time < 30 ? 30 : heart_time
        }
        if (utils.isEmpty(this.lastHeartbeat)) {
            this.lastHeartbeat = 0
        }
        if (this.heart_en === 1 && (new Date().getTime() - this.lastHeartbeat >= (this.heart_time * 1000))) {
            this.lastHeartbeat = new Date().getTime()
            driver.mqtt.send("access_device/v2/event/heartbeat", JSON.stringify(mqttService.mqttReply(std.genRandomStr(10), undefined, mqttService.CODE.S_000)))
        }
    }
}
/**
 * GPIO驱动模块
 * è´Ÿè´£GPIO设备的控制,主要是继电器控制
 */
driver.gpio = {
    /**
     * åˆå§‹åŒ–GPIO
     * åˆå§‹åŒ–GPIO模块并请求继电器引脚
     */
    init: function () {
        gpio.init()
        gpio.request(dxDriver.GPIO.RELAY0)
    },
    /**
     * æ‰“开继电器
     * æ‰“开继电器并在指定时间后自动关闭
     */
    open: function () {
        logger.info("[GPIO]: æ‰“开继电器")
        let result = gpio.setValue(dxDriver.GPIO.RELAY0, 1);
        logger.info("[GPIO]: æ‰“开继电器结果: " + result)
        let relayTime = config.get("access.relayTime")
        std.setTimeout(() => {
            logger.info("[GPIO]: å…³é—­ç»§ç”µå™¨")
            let closeResult = gpio.setValue(dxDriver.GPIO.RELAY0, 0);
            logger.info("[GPIO]: å…³é—­ç»§ç”µå™¨ç»“æžœ: " + closeResult)
        }, relayTime)
    },
    /**
     * å…³é—­ç»§ç”µå™¨
     * ç«‹å³å…³é—­ç»§ç”µå™¨
     */
    close: function () {
        gpio.setValue(dxDriver.GPIO.RELAY0, 0)
    }
}
/**
 * UART485驱动模块
 * è´Ÿè´£UART485通信
 */
driver.uart485 = {
    /**
     * UART485 ID
     */
    id: 'uart485',
    /**
     * åˆå§‹åŒ–UART485
     * åˆå§‹åŒ–UART485模块并设置通信参数
     */
    init: function () {
        dxUart.runvg({ id: this.id, type: dxUart.TYPE.UART, path: '/dev/ttySLB2', result: 0, passThrough: false })
        std.sleep(2000)
        dxUart.ioctl(6, '115200-8-N-1', this.id)
    },
    /**
     * æŽ§åˆ¶UART485
     * @param {string} data - æŽ§åˆ¶æ•°æ®
     */
    ioctl: function (data) {
        dxUart.ioctl(6, data, this.id)
    },
    /**
     * å‘送数据
     * @param {string} data - è¦å‘送的数据
     */
    send: function (data) {
        dxUart.send(data, this.id)
    },
    /**
     * å‘送VG数据
     * @param {object|string} data - è¦å‘送的数据
     */
    sendVg: function (data) {
        if (typeof data == 'object') {
            data.length = data.length ? data.length : (data.data ? data.data.length / 2 : 0)
        }
        dxUart.sendVg(data, this.id)
    }
}
/**
 * UARTCode驱动模块
 * è´Ÿè´£UART条码扫描通信
 */
driver.uartCode = {
    /**
     * UARTCode ID
     */
    id: 'uartCode',
    /**
     * åˆå§‹åŒ–UARTCode
     * åˆå§‹åŒ–UARTCode模块并设置通信参数
     */
    init: function () {
        dxUart.runvg({ id: this.id, type: dxUart.TYPE.UART, path: '/dev/ttySLB1', result: 0, passThrough: false })
        std.sleep(500)
        dxUart.ioctl(6, '115200-8-N-1', this.id)
    },
    /**
     * æŽ§åˆ¶UARTCode
     * @param {string} data - æŽ§åˆ¶æ•°æ®
     */
    ioctl: function (data) {
        dxUart.ioctl(6, data, this.id)
    },
    /**
     * å‘送数据
     * @param {string} data - è¦å‘送的数据
     */
    send: function (data) {
        dxUart.send(data, this.id)
    },
    /**
     * å‘送VG数据
     * @param {object|string} data - è¦å‘送的数据
     */
    sendVg: function (data) {
        if (typeof data == 'object') {
            data.length = data.length ? data.length : (data.data ? data.data.length / 2 : 0)
        }
        dxUart.sendVg(data, this.id)
    },
}
/**
 * EID驱动模块
 * è´Ÿè´£ç”µå­èº«ä»½è¯ç›¸å…³æ“ä½œ
 */
driver.eid = {
    /**
     * EID ID
     */
    id: "eid",
    /**
     * æ¿€æ´»EID
     * @param {string} sn - è®¾å¤‡SN
     * @param {string} version - ç‰ˆæœ¬å·
     * @param {string} mac - MAC地址
     * @param {string} codeMsg - æ¿€æ´»ç 
     * @returns {boolean} æ˜¯å¦æ¿€æ´»æˆåŠŸ
     */
    active: function (sn, version, mac, codeMsg) {
        return eid.active(sn, version, mac, codeMsg)
    },
    /**
     * èŽ·å–EID版本
     * @returns {string} EID版本
     */
    getVerion: function () {
        return eid.getVersion()
    }
}
/**
 * GPIO按键驱动模块
 * è´Ÿè´£GPIO按键的初始化和循环处理
 */
driver.gpiokey = {
    /**
     * åˆå§‹åŒ–GPIO按键
     * åˆå§‹åŒ–GPIO按键模块
     */
    init: function () {
        dxGpioKey.worker.beforeLoop()
    },
    /**
     * GPIO按键循环
     * æ‰§è¡ŒGPIO按键的循环操作
     */
    loop: function () {
        dxGpioKey.worker.loop()
    },
}
/**
 * çœ‹é—¨ç‹—驱动模块
 * è´Ÿè´£ç³»ç»Ÿçœ‹é—¨ç‹—的初始化和喂狗操作
 */
driver.watchdog = {
    /**
     * åˆå§‹åŒ–看门狗
     * åˆå§‹åŒ–看门狗模块
     */
    init: function () {
        // watchdog.open(1)
        // watchdog.enable(1)
        // watchdog.start(20000)
    },
    /**
     * çœ‹é—¨ç‹—循环
     * æ‰§è¡Œçœ‹é—¨ç‹—的循环操作
     */
    loop: function () {
        // watchdog.loop(1)
    },
    /**
     * å–‚ç‹—
     * å‘看门狗发送喂狗信号,防止系统重启
     * @param {string} flag - å–‚狗标志
     * @param {number} timeout - è¶…æ—¶æ—¶é—´
     */
    feed: function (flag, timeout) {
        // if (utils.isEmpty(this["feedTime" + flag]) || new Date().getTime() - this["feedTime" + flag] > 2000) {
        //     // é™ä½Žå–‚狗频率,间隔2秒喂一次
        //     this["feedTime" + flag] = new Date().getTime()
        //     watchdog.feed(flag, timeout)
        // }
    }
}
/**
 * è‡ªåŠ¨é‡å¯é©±åŠ¨æ¨¡å—
 * è´Ÿè´£ç³»ç»Ÿçš„自动重启功能
 */
driver.autoRestart = {
    /**
     * ä¸Šæ¬¡é‡å¯æ£€æŸ¥çš„小时数
     */
    lastRestartCheck: new Date().getHours(),  // åˆå§‹åŒ–为当前小时数,而不是0
    /**
     * åˆå§‹åŒ–自动重启
     * åˆå§‹åŒ–自动重启模块,设置定时重启功能
     */
    init: function () {
        // std.setInterval(() => {        // æ£€æŸ¥æ˜¯å¦éœ€è¦æ•´ç‚¹é‡å¯
        //     const now = new Date()
        //     const currentHour = now.getHours()
        //     // åªæœ‰å½“小时数等于设定值,且不是上次检查过的小时时才执行
        //     if (currentHour === 3 && currentHour !== this.lastRestartCheck && now.getMinutes() === 0) {
        //         common.systemBrief('reboot')
        //     }
        //     // æ›´æ–°ä¸Šæ¬¡æ£€æŸ¥çš„小时数
        //     this.lastRestartCheck = currentHour
        // }, 60000)
    }
}
export default driver
vf205_access/src/main.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
/**
 * åº”用主入口文件
 * è´Ÿè´£åˆå§‹åŒ–系统各组件、驱动和服务,启动应用程序
 */
import log from '../dxmodules/dxLogger.js'
import std from '../dxmodules/dxStd.js'
import bus from '../dxmodules/dxEventBus.js'
import screen from './screen.js'
import driver from './driver.js'
import pool from '../dxmodules/dxWorkerPool.js'
import config from '../dxmodules/dxConfig.js'
import face from '../dxmodules/dxFace.js'
import net from '../dxmodules/dxNet.js'
import mqtt from '../dxmodules/dxMqtt.js'
import dxNfc from '../dxmodules/dxNfc.js'
import dxUart from '../dxmodules/dxUart.js'
import dxGpioKey from '../dxmodules/dxGpioKey.js'
/**
 * äº‹ä»¶æ€»çº¿ä¸»é¢˜åˆ—表
 * åŒ…含系统中所有需要监听的事件主题
 */
let topics = ["getCode", face.RECEIVE_MSG, dxGpioKey.RECEIVE_MSG, "netGetWifiSsidList", "switchNetworkType", "access", "setConfig", dxNfc.RECEIVE_MSG, net.STATUS_CHANGE, mqtt.CONNECTED_CHANGED, mqtt.RECEIVE_MSG, dxUart.VG.RECEIVE_MSG + driver.uart485.id, dxUart.VG.RECEIVE_MSG + driver.uartCode.id, "trackResult"]
/**
 * åˆå§‹åŒ–控制器
 * åˆå§‹åŒ–系统所有驱动模块
 */
function initController() {
    driver.gpio.init()         // åˆå§‹åŒ–GPIO
    driver.watchdog.init()     // åˆå§‹åŒ–看门狗
    driver.config.init()       // åˆå§‹åŒ–配置
    driver.gpiokey.init()      // åˆå§‹åŒ–GPIO按键
    driver.net.init()          // åˆå§‹åŒ–网络
    driver.sqlite.init()       // åˆå§‹åŒ–数据库
    driver.alsa.init()         // åˆå§‹åŒ–音频
    driver.nfc.init()          // åˆå§‹åŒ–NFC
    driver.nfc.eidInit()       // åˆå§‹åŒ–EID(电子身份证)
    driver.uart485.init()      // åˆå§‹åŒ–UART485
    driver.uartCode.init()     // åˆå§‹åŒ–UART码
    driver.capturer.init()     // åˆå§‹åŒ–摄像头
    std.sleep(100)             // ç­‰å¾…100ms
    driver.face.init()         // åˆå§‹åŒ–人脸识别
    std.sleep(100)             // ç­‰å¾…100ms
    driver.pwm.init()          // åˆå§‹åŒ–PWM
    std.sleep(100)             // ç­‰å¾…100ms
    driver.mqtt.init()         // åˆå§‹åŒ–MQTT
    driver.autoRestart.init()  // åˆå§‹åŒ–自动重启
}
/**
 * åº”用程序启动函数
 * åˆå§‹åŒ–控制器、屏幕、创建工作线程和服务池
 */
(function () {
    initController()                      // åˆå§‹åŒ–控制器
    screen.init()                         // åˆå§‹åŒ–屏幕
    bus.newWorker('controller', '/app/code/src/controller.js')  // åˆ›å»ºæŽ§åˆ¶å™¨å·¥ä½œçº¿ç¨‹
    pool.init('/app/code/src/services.js', bus, topics, 5, 100) // åˆå§‹åŒ–服务池
    const appVersion = 'vf105_v12_access_2.0.1.1'  // åº”用版本号
    config.setAndSave('sys.version', appVersion)    // ä¿å­˜ç‰ˆæœ¬å·åˆ°é…ç½®
    config.setAndSave('sys.appVersion', appVersion) // ä¿å­˜åº”用版本号到配置
    log.info("=================== version:" + appVersion + " ====================")
})();
/**
 * ä¸»å¾ªçޝ
 * å®šæœŸå–‚狗、执行看门狗循环和屏幕循环
 */
std.setInterval(() => {
    try {
        driver.watchdog.feed("main", 30) // å–‚狗,设置30秒超时
        driver.watchdog.loop()           // æ‰§è¡Œçœ‹é—¨ç‹—循环
        screen.loop()                    // æ‰§è¡Œå±å¹•循环
    } catch (error) {
        log.error(error)                 // è®°å½•错误
    }
}, 5) // æ¯5ms执行一次
vf205_access/src/screen.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1470 @@
/**
 * å±å¹•管理模块
 * è´Ÿè´£å±å¹•显示、UI控制、用户界面管理等功能
 */
import dxui from '../dxmodules/dxUi.js'
import dxMap from '../dxmodules/dxMap.js'
import log from '../dxmodules/dxLogger.js'
import net from '../dxmodules/dxNet.js'
import viewUtils from './view/viewUtils.js'
import i18n from './view/i18n.js'
import grainService from './service/grainService.js'
import sqliteService from './service/sqliteService.js'
import pinyin from './view/pinyin/pinyin.js'
import mainView from './view/mainView.js'
import idleView from './view/idleView.js'
import topView from './view/topView.js'
import appView from './view/appView.js'
import pwdView from './view/pwdView.js'
import emergencyPwdView from './view/emergencyPwdView.js'
import newPwdView from './view/config/newPwdView.js'
import identityVerificationView from './view/config/identityVerificationView.js'
import configView from './view/config/configView.js'
import cloudCertView from './view/config/menu/cloudCertView.js'
import doorControlView from './view/config/menu/doorControlView.js'
import helpView from './view/config/menu/helpView.js'
import networkSettingView from './view/config/menu/networkSettingView.js'
import systemSettingView from './view/config/menu/systemSettingView.js'
import deviceInfoView from './view/config/menu/deviceInfoView.js'
import factoryTestView from './view/config/menu/factoryTestView.js'
import localUserView from './view/config/menu/localUserView.js'
import recordQueryView from './view/config/menu/recordQueryView.js'
import voiceBroadcastView from './view/config/menu/voiceBroadcastView.js'
import dockingSettingView from './view/config/menu/dockingSetting.js'
import localUserAddView from './view/config/menu/localUser/localUserAddView.js'
import faceEnterView from './view/config/menu/localUser/faceEnterView.js'
import displaySettingView from './view/config/menu/systemSetting/displaySettingView.js'
import faceRecognitionSettingView from './view/config/menu/systemSetting/faceRecognitionSettingView.js'
import swipeCardRecognitionSettingView from './view/config/menu/systemSetting/swipeCardRecognitionSettingView.js'
import passLogSettingView from './view/config/menu/systemSetting/passLogSettingView.js'
import passwordOpenDoorSettingView from './view/config/menu/systemSetting/passwordOpenDoorSettingView.js'
import passwordManagementView from './view/config/menu/systemSetting/passwordManagementView.js'
import timeSettingView from './view/config/menu/systemSetting/timeSettingView.js'
import systemInfoView from './view/config/menu/deviceInfo/systemInfoView.js'
import dataCapacityInfoView from './view/config/menu/deviceInfo/dataCapacityInfoView.js'
import recordQueryDetailView from './view/config/menu/recordQuery/recordQueryDetailView.js'
import std from '../dxmodules/dxStd.js'
import bus from '../dxmodules/dxEventBus.js'
import driver from './driver.js'
import config from '../dxmodules/dxConfig.js'
import common from '../dxmodules/dxCommon.js'
import configService from './service/configService.js'
import codeService from './service/codeService.js'
import face from '../dxmodules/dxFace.js'
import faceService from './service/faceService.js'
/**
 * å±å¹•管理对象
 */
const screen = {}
/**
 * å±å¹•尺寸配置
 */
screen.screenSize = {
    width: 800,
    height: 1280
}
/**
 * UI上下文
 */
const context = {}
/**
 * åˆå§‹åŒ–屏幕管理模块
 * åˆå§‹åŒ–所有UI组件、设置语言、启动屏保计时器等
 * æ³¨æ„ï¼šåœ¨main.js中调用,只允许调用一次
 */
screen.init = function () {
    const loadMethod = dxui.loadMain
    dxui.loadMain = function (view) {
        if (screen.screenNow && screen.screenNow.id == view.id) {
            return
        }
        screen.screenNow = view
        pinyin.hide(true)
        loadMethod.call(dxui, view)
    }
    dxui.init({ orientation: 0 }, context);
    // åˆå§‹åŒ–所有组件
    pinyin.init(800, 400)
    viewUtils.confirmInit()
    mainView.init()
    idleView.init()
    topView.init()
    appView.init()
    pwdView.init()
    emergencyPwdView.init()
    newPwdView.init()
    identityVerificationView.init()
    configView.init()
    cloudCertView.init()
    doorControlView.init()
    helpView.init()
    networkSettingView.init()
    systemSettingView.init()
    deviceInfoView.init()
    factoryTestView.init()
    localUserView.init()
    recordQueryView.init()
    voiceBroadcastView.init()
    dockingSettingView.init()
    localUserAddView.init()
    faceEnterView.init()
    displaySettingView.init()
    faceRecognitionSettingView.init()
    swipeCardRecognitionSettingView.init()
    passLogSettingView.init()
    passwordOpenDoorSettingView.init()
    passwordManagementView.init()
    timeSettingView.init()
    systemInfoView.init()
    dataCapacityInfoView.init()
    recordQueryDetailView.init()
    // è®¾ç½®è¯­è¨€
    // i18n.setLanguage("en-US")
    i18n.setLanguage(config.get("base.language"))
    dxui.loadMain(mainView.screenMain)
    // dxui.loadMain(networkSettingView.screenMain)
    // å¯åŠ¨å±ä¿è®¡æ—¶å™¨
    idleTimerStart()
    // bus事件
    busEvents()
    // å®žæ—¶èŽ·å–ç‚¹å‡»åæ ‡
    getClickPoint()
    // éšè—é”®ç›˜
    hidePinyin()
    // äººè„¸è·Ÿè¸ªæ¡†
    faceTrackingBox()
}
/**
 * äººè„¸è·Ÿè¸ªæ¡†å¤„理
 * å®šæœŸèŽ·å–äººè„¸è·Ÿè¸ªæ•°æ®å¹¶æ›´æ–°è·Ÿè¸ªæ¡†æ˜¾ç¤º
 */
function faceTrackingBox() {
    std.setInterval(() => {
        let data = driver.face.getTrackingBox()
        try {
            if (data && typeof data === 'string') {
                // æ£€æŸ¥æ•°æ®æ˜¯å¦æ˜¯æœ‰æ•ˆçš„JSON字符串
                try {
                    data = JSON.parse(data)
                    // æœ€å¤§10个人
                    if (data.type == "track" && data.faces && data.faces.length <= 10) {
                        for (let i = 0; i < data.faces.length; i++) {
                            let item = data.faces[i]
                            if (item && item.rect_render && item.rect_render.length === 4) {
                                screen.trackUpdate({ w: item.rect_render[2] - item.rect_render[0], h: item.rect_render[3] - item.rect_render[1], x: item.rect_render[0], y: item.rect_render[1] }, item.id, item.is_living)
                            }
                        }
                    }
                } catch (parseError) {
                    log.info('screen.faceTrackingBox: æ— æ•ˆçš„JSON数据:', data);
                    log.error("screen.faceTrackingBox: JSON解析错误:", parseError)
                }
            }
        } catch (error) {
            log.info('screen.faceTrackingBox:', data);
            log.error("screen.faceTrackingBox:", error)
        }
    }, 110)
}
/**
 * ç‚¹å‡»åæ ‡å˜åŒ–标记
 */
let changedClickPoint
/**
 * ä¸Šæ¬¡ç‚¹å‡»åæ ‡
 */
let lastClickPoint = { x: 0, y: 0 }
/**
 * å½“前点击坐标
 */
let clickPoint
/**
 * å®žæ—¶èŽ·å–ç‚¹å‡»åæ ‡
 * å®šæœŸèŽ·å–ç”¨æˆ·è§¦æ‘¸å±å¹•çš„åæ ‡ä½ç½®
 */
function getClickPoint() {
    const indev = NativeObject.APP.NativeComponents.NativeIndev
    std.setInterval(() => {
        clickPoint = {
            x: Math.abs(800 - indev.lvIndevGetPointVg().x),
            y: indev.lvIndevGetPointVg().y
        }
        if (lastClickPoint.x != clickPoint.x || lastClickPoint.y != clickPoint.y) {
            changedClickPoint = clickPoint
        } else {
            changedClickPoint = null
        }
        lastClickPoint = clickPoint
    }, 5)
}
/**
 * éšè—æ‹¼éŸ³é”®ç›˜
 * å¤„理拼音键盘的显示和隐藏逻辑,当用户点击屏幕其他区域时自动隐藏键盘
 */
function hidePinyin() {
    /**
     * é”®ç›˜æ˜¾ç¤ºæ—¶çš„点击坐标
     */
    let showPoint
    /**
     * åŽŸå§‹çš„éšè—æ–¹æ³•
     */
    const hideMethod = pinyin.hide
    /**
     * åŽŸå§‹çš„æ˜¾ç¤ºæ–¹æ³•
     */
    const showMethod = pinyin.show
    /**
     * é”å®šæ ‡å¿—,防止重复操作
     */
    let lock = false
    // é‡å†™éšè—æ–¹æ³•
    pinyin.hide = function (isForce) {
        if (isForce) {
            hideMethod.call(pinyin)
            lock = false
            return
        }
        if (lock) {
            return
        }
        lock = true
        hideMethod.call(pinyin)
        lock = false
    }
    // é‡å†™æ˜¾ç¤ºæ–¹æ³•
    pinyin.show = function (...args) {
        if (lock) {
            return
        }
        lock = true
        showMethod.call(pinyin, ...args)
        showPoint = clickPoint
        lock = false
    }
    // å®šæœŸæ£€æŸ¥æ˜¯å¦éœ€è¦éšè—é”®ç›˜
    std.setInterval(() => {
        if (showPoint && (Math.abs(showPoint.x - clickPoint.x) > 5 && Math.abs(showPoint.y - clickPoint.y) > 5)) {
            if (clickPoint.y < (1280 - (pinyin.getMode() == 1 ? 400 + 70 : 400))) {
                let defocus = dxMap.get("INPUT_KEYBOARD").get("defocus")
                if (defocus == "defocus") {
                    dxMap.get("INPUT_KEYBOARD").del("defocus")
                    showPoint = null
                    pinyin.hide()
                }
            }
        }
    }, 5)
}
/**
 * å±å¹•管理器类
 * è´Ÿè´£å±å¹•状态管理,包括屏保和熄屏功能
 */
class ScreenManager {
    /**
     * æž„造函数
     * @param {object} callbacks - å›žè°ƒå‡½æ•°å¯¹è±¡
     */
    constructor(callbacks = {}) {
        /**
         * å®šæ—¶å™¨å¯¹è±¡
         */
        this.timers = {
            screenSaver: null, // å±ä¿å®šæ—¶å™¨
            screenOff: null    // ç†„屏定时器
        };
        /**
         * é…ç½®å¯¹è±¡
         */
        this.config = {
            screenSaverDelay: 0, // å±ä¿å»¶è¿Ÿï¼ˆæ¯«ç§’)
            screenOffDelay: 0    // ç†„屏延迟(毫秒)
        };
        /**
         * å›žè°ƒå‡½æ•°
         */
        this.callbacks = {
            onScreenSaverStart: callbacks.onScreenSaverStart || (() => { }),
            onScreenSaverEnd: callbacks.onScreenSaverEnd || (() => { }),
            onScreenOff: callbacks.onScreenOff || (() => { }),
            onScreenOn: callbacks.onScreenOn || (() => { })
        };
        this.resetTimers = this.resetTimers.bind(this);
    }
    /**
     * é…ç½®æ—¶é—´
     * @param {number} screenSaverDelay - å±ä¿å»¶è¿Ÿï¼ˆæ¯«ç§’)
     * @param {number} screenOffDelay - ç†„屏延迟(毫秒)
     */
    configure({ screenSaverDelay = 0, screenOffDelay = 0 }) {
        this.config.screenSaverDelay = screenSaverDelay;
        this.config.screenOffDelay = screenOffDelay;
        this.resetTimers();
    }
    /**
     * é‡ç½®å®šæ—¶å™¨
     * æ¸…除现有定时器并设置新的定时器
     */
    resetTimers() {
        // æ¸…除现有定时器
        if (this.timers.screenSaver) {
            std.clearTimeout(this.timers.screenSaver);
        }
        if (this.timers.screenOff) {
            std.clearTimeout(this.timers.screenOff);
        }
        // é€€å‡ºå½“前状态
        this.exitScreenStates();
        // è®¾ç½®æ–°çš„定时器
        if (this.config.screenOffDelay > 0) {
            this.timers.screenOff = std.setTimeout(() => {
                this.enterScreenOff();
            }, this.config.screenOffDelay);
        }
        // åªæœ‰å½“熄屏时间大于屏保时间时才设置屏保定时器
        if (this.config.screenSaverDelay > 0 &&
            (this.config.screenSaverDelay < this.config.screenOffDelay || this.config.screenOffDelay == 0)) {
            this.timers.screenSaver = std.setTimeout(() => {
                this.enterScreenSaver();
            }, this.config.screenSaverDelay);
        }
    }
    /**
     * è¿›å…¥å±ä¿çŠ¶æ€
     */
    enterScreenSaver() {
        const mapUI = dxMap.get("UI")
        if (!mapUI.get("isScreenOff")) {
            mapUI.put("isScreenSaver", true)
            this.callbacks.onScreenSaverStart();
        }
    }
    /**
     * è¿›å…¥ç†„屏状态
     */
    enterScreenOff() {
        const mapUI = dxMap.get("UI")
        mapUI.put("isScreenOff", true)
        mapUI.put("isScreenSaver", false)
        this.callbacks.onScreenOff();
    }
    /**
     * é€€å‡ºæ‰€æœ‰å±å¹•状态
     */
    exitScreenStates() {
        const mapUI = dxMap.get("UI")
        const previousState = { isScreenOff: mapUI.get("isScreenOff"), isScreenSaver: mapUI.get("isScreenSaver") };
        mapUI.put("isScreenOff", false)
        mapUI.put("isScreenSaver", false)
        // å¦‚果状态发生改变,触发相应回调
        if (previousState.isScreenSaver) {
            this.callbacks.onScreenSaverEnd();
        }
        if (previousState.isScreenOff) {
            this.callbacks.onScreenOn();
        }
    }
    /**
     * èŽ·å–å½“å‰çŠ¶æ€
     * @returns {object} å½“前屏幕状态
     */
    getState() {
        const mapUI = dxMap.get("UI")
        return { isScreenOff: mapUI.get("isScreenOff"), isScreenSaver: mapUI.get("isScreenSaver") };
    }
    /**
     * æ¸…理资源
     * æ¸…除所有定时器
     */
    destroy() {
        if (this.timers.screenSaver) {
            std.clearTimeout(this.timers.screenSaver);
        }
        if (this.timers.screenOff) {
            std.clearTimeout(this.timers.screenOff);
        }
    }
}
/**
 * å±å¹•管理器实例
 */
let screenManager
/**
 * å¯åŠ¨å±ä¿è®¡æ—¶å™¨
 * åˆå§‹åŒ–屏幕管理器,设置屏保和熄屏时间,检测用户触摸
 */
function idleTimerStart() {
    // åˆ›å»ºå®žä¾‹ï¼Œä¼ å…¥å›žè°ƒå‡½æ•°
    screenManager = new ScreenManager({
        /**
         * å±ä¿å¼€å§‹å›žè°ƒ
         */
        onScreenSaverStart: () => {
            screen.enterIdle()
        },
        /**
         * å±ä¿ç»“束回调
         */
        onScreenSaverEnd: () => {
            screen.exitIdle(true)
        },
        /**
         * ç†„屏回调
         */
        onScreenOff: () => {
            dxMap.get("screenOff").put("status", 1)
            // åœæ­¢äººè„¸è¯†åˆ«
            driver.face.status(false)
            screen.screenNow.hide()
            topView.screenMain.hide()
        },
        /**
         * äº®å±å›žè°ƒ
         */
        onScreenOn: () => {
            screen.exitIdle(true)
            dxMap.get("screenOff").put("status", 0)
            // æ¢å¤äººè„¸è¯†åˆ«
            driver.face.status(true)
            screen.screenNow.show()
            topView.screenMain.show()
            // é€€å‡ºç†„屏时获取并更新气体浓度和状态信息
            try {
                log.info('[screen]: é€€å‡ºç†„屏,获取气体浓度和状态信息')
                grainService.checkGasConcentration()
                grainService.checkDevConcentration()
            } catch (error) {
                log.error(`[screen]: èŽ·å–æ°”ä½“æµ“åº¦å’ŒçŠ¶æ€ä¿¡æ¯é”™è¯¯: ${error.message}`)
            }
        }
    });
    // é…ç½®æ—¶é—´ï¼ˆæ¯«ç§’)
    screenManager.configure({
        // screenSaverDelay: 10000,  // å±ä¿
        // screenOffDelay: 5000     // ç†„屏
        screenSaverDelay: config.get("base.screensaver") * 60 * 1000,  // å±ä¿
        screenOffDelay: config.get("base.screenOff") * 60 * 1000     // ç†„屏
    });
    // æ£€æµ‹ç”¨æˆ·è§¦æ‘¸
    let touchCount = 0
    std.setInterval(() => {
        let count = dxui.Utils.GG.NativeDisp.lvDispGetInactiveTime()
        if (count < touchCount) {
            screenManager.resetTimers();
        }
        touchCount = count
    }, 100);
}
/**
 * åˆ·æ–°å±å¹•管理器配置
 * æ›´æ–°å±ä¿å’Œç†„屏时间配置
 */
screen.screenManagerRefresh = function () {
    screenManager.configure({
        screenSaverDelay: config.get("base.screensaver") * 60 * 1000,  // å±ä¿
        screenOffDelay: config.get("base.screenOff") * 60 * 1000     // ç†„屏
    });
    screenManager.resetTimers();
}
/**
 * è¿›å…¥å±ä¿å®šæ—¶å™¨
 */
let enterIdleTimer
/**
 * è¿›å…¥å±ä¿
 * æ˜¾ç¤ºå±ä¿ç•Œé¢
 */
screen.enterIdle = function () {
    // å»¶è¿Ÿ1秒,防止进入屏保和退出屏保同时触发,1秒内没有触发退出屏保,则认为进入屏保
    enterIdleTimer = std.setTimeout(() => {
        if (idleView.screenMain.isHide()) {
            viewUtils.confirmClose()
            dxui.loadMain(mainView.screenMain)
            idleView.screenMain.show()
            topView.changeTheme(false)
        }
    }, 1000)
}
/**
 * é€€å‡ºå±ä¿
 * éšè—å±ä¿ç•Œé¢
 * @param {boolean} isSelf - æ˜¯å¦ç”±ç³»ç»Ÿè‡ªèº«è§¦å‘
 */
screen.exitIdle = function (isSelf) {
    if (enterIdleTimer) {
        std.clearTimeout(enterIdleTimer)
        enterIdleTimer = null
    }
    if (!isSelf) {
        screenManager.resetTimers();
    }
    if (!idleView.screenMain.isHide()) {
        idleView.screenMain.hide()
    }
}
/**
 * å±å¹•循环处理
 * å¤„理UI事件
 * @returns {boolean} å¤„理结果
 */
screen.loop = function () {
    return dxui.handler()
}
/**
 * äº‘证激活
 * æ¿€æ´»ç”µå­èº«ä»½è¯
 * @param {string} code - æ¿€æ´»ç 
 * @returns {number} æ¿€æ´»ç»“果,0表示成功,非0表示失败
 */
screen.nfcIdentityCardActivation = function (code) {
    return driver.eid.active(config.get("sys.sn"), config.get("sys.appVersion"), config.get("sys.mac"), code);
}
/**
 * åˆ é™¤äººå‘˜
 * åˆ é™¤ç”¨æˆ·ä¿¡æ¯ã€æƒé™ã€å‡­è¯å’Œäººè„¸æ•°æ®
 * @param {object} user - ç”¨æˆ·å¯¹è±¡
 * @returns {boolean} åˆ é™¤ç»“æžœ
 */
screen.deleteUser = function (user) {
    // åˆ é™¤äººå‘˜ä¿¡æ¯
    sqliteService.d1_person.deleteByUserId(user.userId)
    // åˆ é™¤æƒé™ä¿¡æ¯
    sqliteService.d1_permission.deleteByUserId(user.userId)
    // åˆ é™¤å‡­è¯ä¿¡æ¯
    sqliteService.d1_voucher.deleteByUserId(user.userId)
    // åˆ é™¤äººè„¸æ•°æ®
    let res = driver.face.delete(user.userId)
    return true
}
/**
 * æ›´æ–°äººå‘˜ä¿¡æ¯
 * æ›´æ–°ç”¨æˆ·ä¿¡æ¯ã€å¯†ç ã€å¡ç‰‡å’Œäººè„¸æ•°æ®
 * @param {object} user - ç”¨æˆ·å¯¹è±¡
 * @returns {boolean|string} æ›´æ–°ç»“果,成功返回true,失败返回错误信息
 */
screen.updateUser = function (user) {
    //修改人员信息
    let res = sqliteService.d1_person.updatenameAndExtraByUserId(user.userId, user.name, JSON.stringify({ type: user.type, idCard: user.idCard }))
    if (res != 0) {
        return false
    }
    //处理凭证
    let ret
    //处理密码凭证
    if (user.pwd) {
        //判断库表是否存在这个凭证
        let pwdData = sqliteService.d1_voucher.findByCodeAndType(user.pwd, "400");
        if (pwdData.length > 0 && pwdData[0].userId != user.userId) {
            //存在不能添加返回失败
            log.info("密码重复");
            return "localUserAddView.failPwdRepeat"
        }
        //查询是否有密码凭证有更新没有新增
        let countByuserIdAndType = sqliteService.d1_voucher.findByuserIdAndType(user.userId, "400");
        if (countByuserIdAndType.length > 0) {
            ret = sqliteService.d1_voucher.updatecodeByuserIdAndtype(user.userId, "400", user.pwd)
            if (ret != 0) {
                return false
            }
        } else {
            //新增一个
            ret = sqliteService.d1_voucher.save({ keyId: std.genRandomStr(32), type: "400", code: user.pwd, userId: user.userId })
            if (ret != 0) {
                return false
            }
        }
    } else {
        //没有内容去数据库表删除一下
        sqliteService.d1_voucher.deleteByuserIdAndtype(user.userId, "400")
    }
    //处理卡片凭证
    if (user.card) {
        //判断库表是否存在这个凭证
        let cardData = sqliteService.d1_voucher.findByCodeAndType(user.card, "200");
        if (cardData.length > 0 && cardData[0].userId != user.userId) {
            //存在不能添加返回失败
            log.info("卡重复");
            return "localUserAddView.failCardRepeat"
        }
        //查询是否有密码凭证有更新没有新增
        let countByuserIdAndType = sqliteService.d1_voucher.countByuserIdAndType(user.userId, "200");
        if (countByuserIdAndType > 0) {
            ret = sqliteService.d1_voucher.updatecodeByuserIdAndtype(user.userId, "200", user.card)
            if (ret != 0) {
                return false
            }
        } else {
            //新增一个
            ret = sqliteService.d1_voucher.save({ keyId: std.genRandomStr(32), type: "200", code: user.card, userId: user.userId })
            if (ret != 0) {
                return false
            }
        }
    } else {
        //没有内容去数据库表删除一下
        sqliteService.d1_voucher.deleteByuserIdAndtype(user.userId, "200")
    }
    //处理人脸凭证
    if (user.face) {
        let findByuserIdAndType = sqliteService.d1_voucher.findByuserIdAndType(user.userId, "300");
        if (findByuserIdAndType.length <= 0) {
            let ret = driver.face.registerFaceByPicFile(user.userId, user.face)
            log.info("2注册人脸,ret:", ret)
            if (ret != 0) {
                return faceService.regErrorEnum.picture[ret + '']
            }
            //注册成功后需要吧原来图片移动到 user å¯¹åº”目录下
            let src = "/app/data/user/" + user.userId + "/register.jpg"
            std.ensurePathExists(src)
            common.systemBrief('mv ' + user.face + " " + src)
            //新增一个
            ret = sqliteService.d1_voucher.save({ keyId: std.genRandomStr(32), type: "300", code: src, userId: user.userId })
            if (ret != 0) {
                return false
            }
        } else {
            //原来有又传入 å…ˆåˆ é™¤åŽæ–°å¢ž
            if (findByuserIdAndType[0].code != user.face) {
                //删除老人脸
                driver.face.delete(user.userId)
                //注册新人脸
                let res = driver.face.registerFaceByPicFile(user.userId, user.face)
                log.info("3注册人脸,res:", res)
                if (res != 0) {
                    return faceService.regErrorEnum.picture[res + '']
                }
                let src = "/app/data/user/" + user.userId + "/register.jpg"
                std.ensurePathExists(src)
                //把临时目录人脸移动到 user å¯¹åº”的文件夹中
                common.systemBrief('mv ' + user.face + " " + src)
                ret = sqliteService.d1_voucher.updatecodeAndExtraByuserIdAndtype(user.userId, "300", src, JSON.stringify({ faceType: 0 }))
            }
        }
    } else {
        //没有内容去数据库表删除一下
        sqliteService.d1_voucher.deleteByuserIdAndtype(user.userId, "300")
        driver.face.delete(user.userId)
        common.systemBrief("rm -rf /app/data/user/" + user.userId)
    }
    return true
}
/**
 * æ–°å¢žäººå‘˜
 * æ·»åŠ æ–°ç”¨æˆ·ä¿¡æ¯ã€å¯†ç ã€å¡ç‰‡å’Œäººè„¸æ•°æ®
 * @param {object} user - ç”¨æˆ·å¯¹è±¡
 * @returns {boolean|string} æ·»åŠ ç»“æžœï¼ŒæˆåŠŸè¿”å›žtrue,失败返回错误信息
 */
screen.insertUser = async function (user) {
    /**
     * ä¿å­˜å‡­è¯
     * @param {string} type - å‡­è¯ç±»åž‹
     * @param {string} code - å‡­è¯ä»£ç 
     * @returns {boolean|string} ä¿å­˜ç»“æžœ
     */
    const saveVoucher = async (type, code) => {
        // æ£€æŸ¥å¡ç‰‡å‡­è¯æ˜¯å¦é‡å¤
        if (type == "200") {
            let cardData = sqliteService.d1_voucher.findByCodeAndType(code, "200");
            if (cardData.length > 0 && cardData[0].userId != user.userId) {
                //存在不能添加返回失败
                log.info("卡重复");
                return "localUserAddView.failCardRepeat"
            }
        }
        // å½“ type ä¸º "300" æ—¶ï¼Œé¦–先调用特定方法检查是否可以继续保存凭证
        if (type === "300") {
            let preCheckResult = await preSaveCheck(code); // å‡è®¾è¿™æ˜¯æ‚¨æåˆ°çš„需要调用的方法
            if (preCheckResult !== true) { // å¦‚果预检查不通过,则直接返回 false
                return preCheckResult;
            }
            code = "/app/data/user/" + user.userId + "/register.jpg"
        }
        // æ£€æŸ¥å¯†ç å‡­è¯æ˜¯å¦é‡å¤
        if (type == "400") {
            let pwdData = sqliteService.d1_voucher.findByCodeAndType(code, "400");
            if (pwdData.length > 0 && pwdData[0].userId != user.userId) {
                //存在不能添加返回失败
                log.info("密码重复");
                return "localUserAddView.failPwdRepeat"
            }
        }
        let keyId = std.genRandomStr(32);
        let extra = type == 300 ? JSON.stringify({ faceType: 0 }) : JSON.stringify({})
        let voucherRet = await sqliteService.d1_voucher.save({
            keyId: keyId,
            type: type,
            code: code,
            userId: user.userId,
            extra: extra
        });
        if (voucherRet != 0) {
            // å¦‚果凭证保存失败,则删除已保存的用户信息及可能已保存的其他凭证
            await sqliteService.d1_person.deleteByUserId(user.userId);
            await sqliteService.d1_voucher.deleteByUserId(user.userId);
            return false;
        }
        return true;
    };
    /**
     * ä¿å­˜å‰æ£€æŸ¥
     * @param {string} code - äººè„¸å›¾ç‰‡è·¯å¾„
     * @returns {boolean|string} æ£€æŸ¥ç»“æžœ
     */
    async function preSaveCheck(code) {
        let ret = driver.face.registerFaceByPicFile(user.userId, code)
        log.info("1注册人脸,ret:", ret)
        if (ret != 0) {
            return faceService.regErrorEnum.picture[ret + '']
        }
        //注册成功后需要吧原来图片移动到 user å¯¹åº”目录下
        let src = "/app/data/user/" + user.userId + "/register.jpg"
        std.ensurePathExists(src)
        common.systemBrief('mv ' + code + " " + src)
        return true;
    }
    let success = true;
    // ä¿å­˜äººè„¸å‡­è¯
    if (success === true && user.face && !(success = await saveVoucher("300", user.face)));
    // ä¿å­˜å¯†ç å‡­è¯
    if (success === true && user.pwd && !(success = await saveVoucher("400", user.pwd)));
    // ä¿å­˜å¡ç‰‡å‡­è¯
    if (success === true && user.card && !(success = await saveVoucher("200", user.card)));
    if (success === true) {
        //{"id":"423","userId":"423","name":"微光互联","idCard":"123","pwd":"251574","card":"123"}
        //保存人员信息
        let personRet = await sqliteService.d1_person.save({
            userId: user.userId,
            name: user.name,
            extra: JSON.stringify({ type: user.type == 1 ? 1 : 0, idCard: user.idCard })
        });
        if (personRet != 0) {
            sqliteService.d1_voucher.deleteByUserId(user.userId);
            return "localUserAddView.failRepeat"
        }
        //新增一条永久权限
        sqliteService.d1_permission.save({ permissionId: user.userId, userId: user.userId, timeType: 0 })
    } else {
        await sqliteService.d1_voucher.deleteByUserId(user.userId);
    }
    return success;
}
/**
 * èŽ·å–æœ¬åœ°äººå‘˜ä¿¡æ¯
 * èŽ·å–ç”¨æˆ·çš„å‡­è¯ä¿¡æ¯ï¼ŒåŒ…æ‹¬èº«ä»½è¯ã€å¡ç‰‡ã€å¯†ç å’Œäººè„¸æ•°æ®
 * @param {string} userId - ç”¨æˆ·ID
 * @returns {object} ç”¨æˆ·å‡­è¯ä¿¡æ¯
 */
screen.getVoucher = function (userId) {
    // èŽ·å–ç”¨æˆ·ä¿¡æ¯
    let person = sqliteService.d1_person.find({ userId: userId });
    if (person.length < 0) {
        return
    }
    // èŽ·å–å¯†ç å‡­è¯
    let pwd_voucher = sqliteService.d1_voucher.find({ userId: userId, type: "400" })[0] || undefined
    // èŽ·å–å¡ç‰‡å‡­è¯
    let card_voucher = sqliteService.d1_voucher.find({ userId: userId, type: "200" })[0] || undefined
    // èŽ·å–äººè„¸å‡­è¯
    let face_voucher = sqliteService.d1_voucher.find({ userId: userId, type: "300" })[0] || undefined
    // èŽ·å–èº«ä»½è¯ä¿¡æ¯
    let idCard_voucher
    try {
        idCard_voucher = JSON.parse(person[0].extra).idCard
    } catch (error) {
    }
    return {
        id: userId,
        idCard: idCard_voucher ? idCard_voucher : undefined,
        card: card_voucher ? card_voucher.code : undefined,
        pwd: pwd_voucher ? pwd_voucher.code : undefined,
        face: face_voucher ? face_voucher.code : undefined,
        type: JSON.parse(person[0].extra).type || 0
    }
}
/**
 * èŽ·å–ç”¨æˆ·åˆ—è¡¨
 * æ ¹æ®æ¡ä»¶æŸ¥è¯¢ç”¨æˆ·åˆ—表,支持分页和搜索
 * @param {number} page - é¡µç ï¼Œé»˜è®¤0
 * @param {number} size - æ¯é¡µå¤§å°ï¼Œé»˜è®¤6
 * @param {string} userId - ç”¨æˆ·ID,用于精确搜索
 * @param {string} name - ç”¨æˆ·å§“名,用于模糊搜索
 * @returns {object} ç”¨æˆ·åˆ—表数据,包含分页信息
 */
screen.getUsers = function (page = 0, size = 6, userId, name) {
    // æŒ‰ç”¨æˆ·ID或姓名搜索
    if (userId || name) {
        // æŒ‰ç”¨æˆ·ID搜索
        let user = sqliteService.d1_person.findByUserId(userId)[0]
        if (user) {
            user.id = user.userId
            return { data: [user], totalPage: 1, totalSize: 1, currentPage: 1 }
        }
        // æŒ‰ç”¨æˆ·å§“名搜索
        let users = sqliteService.d1_person.findByName(name)
        if (users && users.length > 0) {
            users.map(v => {
                v.id = v.userId
            })
            /**
             * æ•°ç»„分块
             * @param {array} arr - æºæ•°ç»„
             * @param {number} size - å—大小
             * @returns {array} åˆ†å—后的数组
             */
            function chunkArray(arr, size) {
                // å¦‚果数组为空或者大小为零,返回空数组
                if (arr.length === 0 || size <= 0) {
                    return [];
                }
                const result = [];
                // ä½¿ç”¨å¾ªçŽ¯éåŽ†æ•°ç»„ï¼Œå¹¶æŒ‰ç…§å¤§å°åˆ‡å‰²
                for (let i = 0; i < arr.length; i += size) {
                    result.push(arr.slice(i, i + size));  // slice æˆªå–指定范围的元素
                }
                return result;
            }
            const chunkedArray = chunkArray(users, size);
            return { data: chunkedArray[page], totalPage: Math.ceil(users.length / size), totalSize: users.length, currentPage: page + 1 }
        }
        return { data: [], totalPage: 0, totalSize: 0, currentPage: 1 }
    }
    // èŽ·å–æ‰€æœ‰ç”¨æˆ·
    let userCount = sqliteService.d1_person.count()
    let users = sqliteService.d1_person.findOrderByUserIdAsc({ page, size })
    if (users.length > 0) {
        users.forEach(element => { element.id = element.userId });
    }
    // æ€»é¡µæ•°
    let totalPage = Math.ceil(userCount / size)
    return { data: users, totalPage: totalPage, totalSize: userCount, currentPage: page + 1 }
}
/**
 * èŽ·å–é€šè¡Œè®°å½•
 * èŽ·å–ç”¨æˆ·é€šè¡Œè®°å½•ï¼Œæ”¯æŒåˆ†é¡µ
 * @param {number} page - é¡µç ï¼Œé»˜è®¤0
 * @param {number} size - æ¯é¡µå¤§å°ï¼Œé»˜è®¤6
 * @returns {object} é€šè¡Œè®°å½•数据,包含分页信息
 */
screen.getPassRecord = function (page = 0, size = 6) {
    let passCount = sqliteService.d1_pass_record.count()
    let datas = sqliteService.d1_pass_record.findOrderByTimeDesc({ page, size })
    // æ€»é¡µæ•°
    let totalPage = Math.ceil(passCount / size)
    return { data: datas, totalPage: totalPage, totalSize: passCount, currentPage: page + 1 }
}
/**
 * äººè„¸å½•入开始
 * å¼€å§‹äººè„¸å½•入,UI控制
 * @param {string} userId - ç”¨æˆ·ID
 */
screen.faceEnterStart = function (userId) {
    dxMap.get("UI").put("faceEnterStart", userId)
    driver.face.status(1)
    driver.face.mode(1)
}
/**
 * äººè„¸å½•入结束
 * ç»“束人脸录入,UI控制
 */
screen.faceEnterEnd = function () {
    dxMap.get("UI").del("faceEnterStart")
    driver.face.status(0)
    // driver.face.mode(0)
}
/**
 * èŽ·å–å¡å·å¼€å§‹
 * å¼€å§‹èŽ·å–å¡å·ï¼ŒUI控制
 */
screen.getCardStart = function () {
    dxMap.get("UI").put("getCardStart", true)
}
/**
 * èŽ·å–å¡å·ç»“æŸ
 * ç»“束获取卡号,UI控制
 */
screen.endCardEnd = function () {
    dxMap.get("UI").del("getCardStart")
}
/**
 * å¼€å¯äººè„¸è¯†åˆ«
 * å¼€å§‹äººè„¸è¯†åˆ«åŠŸèƒ½
 */
screen.faceRecgStart = function () {
    driver.face.status(1)
    driver.face.mode(0)
}
/**
 * äººè„¸è¯†åˆ«æš‚停
 * æš‚停人脸识别功能
 */
screen.faceRecgPause = function () {
    driver.face.status(0)
}
/**
 * äººè„¸å½•入结果
 * å¤„理人脸录入结果
 * @param {string} facePic - äººè„¸å›¾ç‰‡è·¯å¾„
 */
screen.faceEnterResult = function (facePic) {
    if (facePic) {
        faceEnterView.successFlag = true
        // æˆåŠŸï¼Œæ˜¾ç¤ºäººè„¸ç…§ç‰‡
        localUserAddView.addFace(facePic)
        dxui.loadMain(localUserAddView.screenMain)
        faceEnterView.backCb()
    } else {
        // å¤±è´¥ï¼ŒæŠ¥é”™
        faceEnterView.timeout()
    }
}
/**
 * éžè¯†åˆ«é¡µé¢äººè„¸è®¤è¯å¼€å§‹
 * å¼€å§‹äººè„¸è®¤è¯ï¼ŒUI控制
 */
screen.faceAuthStart = function () {
    dxMap.get("UI").put("faceAuthStart", "Y")
    driver.face.status(1)
    driver.face.mode(0)
}
/**
 * éžè¯†åˆ«é¡µé¢äººè„¸è®¤è¯ç»“束
 * ç»“束人脸认证,UI控制
 */
screen.faceAuthEnd = function () {
    dxMap.get("UI").del("faceAuthStart")
    driver.face.status(0)
}
/**
 * éžè¯†åˆ«é¡µé¢äººè„¸è®¤è¯ç»“æžœ
 * å¤„理人脸认证结果
 * @param {boolean} bool - è®¤è¯ç»“果,true表示成功,false表示失败
 */
screen.faceAuthResult = function (bool) {
    if (bool) {
        // æˆåŠŸï¼Œè¿›å…¥è®¾ç½®èœå•
        driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/recg_s.wav`)
        dxui.loadMain(configView.screenMain)
    } else {
        // å¤±è´¥ï¼ŒæŠ¥é”™
        driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/recg_f.wav`)
        identityVerificationView.statusPanel.fail()
    }
}
/**
 * ä¿å­˜é…ç½®
 * ä¿å­˜ç³»ç»Ÿé…ç½®
 * @param {object} configAll - é…ç½®å¯¹è±¡
 * @returns {boolean} ä¿å­˜ç»“æžœ
 */
screen.saveConfig = function (configAll) {
    // ç›´æŽ¥è°ƒç”¨é…ç½®éªŒè¯å’Œä¿å­˜å‡½æ•°
    return configService.configVerifyAndSave(configAll)
}
/**
 * èŽ·å–é…ç½®
 * èŽ·å–ç³»ç»Ÿé…ç½®
 * @returns {object} é…ç½®å¯¹è±¡
 */
screen.getConfig = function () {
    let config1 = config.getAll()
    return config1
}
/**
 * å¯†ç é€šè¡Œ
 * ä½¿ç”¨å¯†ç è¿›è¡Œé€šè¡ŒéªŒè¯
 * @param {string} pwd - å¯†ç 
 * @returns {boolean} éªŒè¯ç»“果,true表示成功,false表示失败
 */
screen.pwdAccess = function (pwd) {
    // æ£€æŸ¥æ•°æ®åº“中的应急开仓密码
    try {
        const passwords = sqliteService.d1_emergency_password.findAll({ status: 1 })
        for (let i = 0; i < passwords.length; i++) {
            if (passwords[i].password === pwd) {
                return true
            }
        }
    } catch (error) {
        logger.error('检查应急开仓密码失败:', error)
    }
    // å…¶ä»–密码验证逻辑
    bus.fire("access", { data: { type: "400", code: pwd } })
    return false
}
/**
 * åˆ‡æ¢ç½‘络类型
 * åˆ‡æ¢ç½‘络连接类型
 * @param {object} data - ç½‘络类型数据
 */
screen.switchNetworkType = function (data) {
    bus.fire("switchNetworkType", data)
}
/**
 * èŽ·å–WiFi列表
 * èŽ·å–å¯ç”¨çš„WiFi网络列表
 */
screen.netGetWifiSsidList = function () {
    bus.fire("netGetWifiSsidList")
}
/**
 * WiFi列表数据
 * å¤„理WiFi列表数据
 * @param {array} data - WiFi列表数据
 */
screen.netWifiSsidList = function (data) {
    if (data.length == 0 && config.get("net.type") == 2) {
        //无线网
        std.setTimeout(() => {
            screen.netGetWifiSsidList()
        }, 1000)
        return
    }
    networkSettingView.wifiListData = data
    networkSettingView.wifiList.refresh()
}
/**
 * è¿žæŽ¥WiFi
 * è¿žæŽ¥åˆ°æŒ‡å®šçš„WiFi网络
 * @param {string} ssid - WiFi名称
 * @param {string} psk - WiFi密码
 * @returns {boolean} è¿žæŽ¥ç»“æžœ
 */
screen.netConnectWifiSsid = function (ssid, psk) {
    return driver.net.netConnectWifiSsid(ssid, psk)
}
/**
 * èŽ·å–å¡å·
 * å¤„理获取到的卡号
 * @param {string} card - å¡å·
 */
screen.getCard = function (card) {
    localUserAddView.cardBoxInput.text(card)
}
/**
 * äº‹ä»¶æ€»çº¿äº‹ä»¶å¤„理
 * æ³¨å†Œå’Œå¤„理系统事件
 */
function busEvents() {
    // ç½‘络状态
    bus.on('netStatus', (data) => {
        console.log(JSON.stringify(data));
        let type = config.get("net.type")
        if (data.connected) {
            let ip = net.getModeByCard(type).param.ip
            mainView.overlayIpLbl.text("IP:" + ip)
            config.setAndSave("net.ip", ip)
            config.setAndSave("net.mac", net.getMacaddr(type))
            topView.ethConnectState(true, type)
            networkSettingView.netInfo[10].label.dataI18n = "networkSettingView.networkConnected"
        } else {
            topView.ethConnectState(false, type)
            networkSettingView.netInfo[10].label.dataI18n = "networkSettingView.networkUnconnected"
        }
        i18n.refreshObj(networkSettingView.netInfo[10].label)
    })
    // mqtt连接状态
    bus.on('mqttStatus', (data) => {
        if (data == "connected") {
            topView.mqttConnectState(true)
        } else {
            topView.mqttConnectState(false)
        }
    })
    // äººè„¸å½•入开始
    bus.on("beginAddFace", screen.beginAddFace)
    // äººè„¸å½•入结果
    bus.on("faceEnterResult", screen.faceEnterResult)
    // é€€å‡ºå±ä¿
    bus.on("exitIdle", screen.exitIdle)
    // èŽ·å–WiFi列表 - ç§»é™¤äº‹ä»¶ç›‘听器,避免无限循环
    // èŽ·å–å¡å·
    bus.on("getCard", screen.getCard)
    // äººè„¸è®¤è¯ç»“æžœ
    bus.on("faceAuthResult", screen.faceAuthResult)
    // é€šè¡Œç»“æžœ
    bus.on("accessRes", screen.accessRes)
    // è·Ÿè¸ªæ›´æ–°
    // bus.on("trackUpdate", screen.trackUpdate)
    // éšè—SN
    bus.on("hideSn", screen.hideSn)
    // åˆ‡æ¢è¯­è¨€
    bus.on("changeLanguage", screen.changeLanguage)
    // éšè—IP
    bus.on("hideIp", screen.hideIp)
    // åˆ·æ–°å±å¹•管理器
    bus.on("screenManagerRefresh", screen.screenManagerRefresh)
    // WiFi列表数据
    bus.on("netWifiSsidList", screen.netWifiSsidList)
    // APP模式
    bus.on("appMode", screen.appMode)
    // å‡çº§
    bus.on("upgrade", screen.upgrade)
    // å¡ç‰‡é‡ç½®
    // bus.on("cardReset", screen.cardReset)
    // è·Ÿè¸ªç»“æžœ
    bus.on("trackResult", screen.trackResult)
    bus.on("showAccessResult", screen.showAccessResult)
}
/**
 * å¡ç‰‡é‡ç½®å®šæ—¶å™¨
 */
let setTimeout
/**
 * å¡ç‰‡é‡ç½®
 * å¤„理卡片重置逻辑
 * @param {object} msg - æ¶ˆæ¯å¯¹è±¡
 */
screen.cardReset = function (msg) {
    if (msg.type == 2 && msg.status == 3) {
        setTimeout = std.setTimeout(() => {
            driver.net.cardReset()
        }, 30 * 1000);
    } else {
        if (setTimeout) {
            std.clearTimeout(setTimeout)
        }
    }
}
/**
 * å¼€å§‹æ³¨å†Œäººè„¸
 * å¤„理人脸注册开始逻辑
 * @param {object} data - äººè„¸æ•°æ®å¯¹è±¡
 */
screen.beginAddFace = function (data) {
    log.info('screen.beginAddFace', JSON.stringify(data));
    if (!data.fileName) {
        return screen.faceEnterResult()
    }
    driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/recognition_s.wav`)
    faceEnterView.statusPanel.success("faceEnterView.recogSuccess")
    // ä¿å­˜å›¾ç‰‡åˆ°æœ¬åœ°
    let src = `/app/data/user/register.jpg`
    common.systemBrief(`mv ${data.fileName} ${src}`)
    common.systemBrief(`rm -rf /app/data/user/temp/*`)
    screen.faceEnterResult(src)
}
/**
 * é€šè¡Œç»“æžœ
 * å¤„理通行成功或失败的结果
 * @param {boolean} bool - é€šè¡Œç»“果,true表示成功,false表示失败
 */
screen.accessRes = function (bool) {
    if (bool) {
        if (mainView.smallStatusPanel && mainView.smallStatusPanel.success) {
            mainView.smallStatusPanel.success()
        }
    } else {
        if (mainView.smallStatusPanel && mainView.smallStatusPanel.fail) {
            mainView.smallStatusPanel.fail()
        }
    }
}
/**
 * åˆ‡æ¢APP模式
 * åˆ‡æ¢åº”用程序显示模式(标准模式或简洁模式)
 * @param {number} mode - æ¨¡å¼å€¼ï¼Œ0表示标准模式,1表示简洁模式
 */
screen.appMode = function (mode) {
    if (mode == 0) {
        // åˆ‡æ¢åˆ°æ ‡å‡†æ¨¡å¼
    } else if (mode == 1) {
        // åˆ‡æ¢åˆ°ç®€æ´æ¨¡å¼
    }
}
/**
 * æ›´æ–°äººè„¸è·Ÿè¸ªæ¡†
 * æ›´æ–°äººè„¸è·Ÿè¸ªæ¡†çš„位置和状态
 * @param {object} data - åæ ‡ä¿¡æ¯
 * @param {number} id - face_id,用于关联识别姓名用
 * @param {boolean} isLiving - æ˜¯å¦æ´»ä½“
 */
screen.trackUpdate = function (data, id, isLiving) {
    let item = mainView.trackFaces[0]
    for (let i = 0; i < 10; i++) {
        let ele = mainView.trackFaces[i]
        if (ele.face_id == id) {
            item = ele
            break
        }
    }
    item.face_id = id
    if (item && item.timer) {
        std.clearTimeout(item.timer)
        item.timer = null
    }
    item.timer = std.setTimeout(() => {
        item.trackFace.hide()
        // item.trackFaceName.hide()
        if (item.timer) {
            std.clearTimeout(item.timer)
            item.timer = null
        }
    }, 300)
    let edge = data.w > data.h ? data.w : data.h
    let offset = Math.abs(data.w - data.h) / 2
    item.trackFace.show()
    item.trackFace.setSize(edge, edge)
    item.trackFace.radius(edge / 2)
    if (data.w > data.h) {
        item.trackFace.setPos(data.x, data.y - offset)
    } else {
        item.trackFace.setPos(data.x - offset, data.y)
    }
    item.trackFaceName.text(" ")
    if (item.result && item.result.result === true && item.result.id == id) {
        item.trackFace.setBorderColor(viewUtils.color.success)
        let user = sqliteService.d1_person.findByUserId(item.result.userId)[0]
        item.trackFaceName.text(user ? user.name : "")
    } else if (item.result && item.result.result === false && item.result.id == id) {
        item.trackFace.setBorderColor(viewUtils.color.fail)
    } else if (isLiving) {
        item.trackFace.setBorderColor(0xf3e139)
    } else {
        item.trackFace.setBorderColor(0xffffff)
    }
}
/**
 * è®¤è¯ç»“æžœ
 * å¤„理人脸识别认证结果
 * @param {object} data - è®¤è¯ç»“果数据
 */
screen.trackResult = function (data) {
    for (let i = 0; i < 10; i++) {
        let ele = mainView.trackFaces[i]
        if (ele.face_id == data.id) {
            ele.result = data
            return
        }
    }
}
/**
 * éšè—SN按钮
 * æŽ§åˆ¶SN按钮的显示和隐藏
 * @param {boolean} bool - æ˜¯å¦æ˜¾ç¤ºï¼Œtrue表示显示,false表示隐藏
 */
screen.hideSn = function (bool) {
    if (bool) {
        mainView.bottomSnBtn.show()
    } else {
        mainView.bottomSnBtn.hide()
    }
}
/**
 * éšè—IP标签
 * æŽ§åˆ¶IP标签的显示和隐藏
 * @param {boolean} bool - æ˜¯å¦æ˜¾ç¤ºï¼Œtrue表示显示,false表示隐藏
 */
screen.hideIp = function (bool) {
    // ä¸å†éœ€è¦æ­¤å‡½æ•°ï¼Œå› ä¸ºIP信息现在显示在半透明覆盖图层中
}
/**
 * éšè—åº•部框
 * æŽ§åˆ¶åº•部框的显示和隐藏
 * @param {boolean} bool - æ˜¯å¦æ˜¾ç¤ºï¼Œtrue表示显示,false表示隐藏
 */
screen.hideBottomBox = function (bool) {
    if (bool) {
        mainView.bottomBox.hide()
    } else {
        mainView.bottomBox.show()
    }
}
/**
 * åˆ‡æ¢è¯­è¨€
 * åˆ‡æ¢ç³»ç»Ÿè¯­è¨€è®¾ç½®
 */
screen.changeLanguage = function () {
    i18n.setLanguage(screen.getConfig()['base.language'])
}
/**
 * å‡çº§æç¤º
 * æ˜¾ç¤ºç³»ç»Ÿå‡çº§æç¤ºä¿¡æ¯
 * @param {object} data - å‡çº§ä¿¡æ¯å¯¹è±¡ï¼ŒåŒ…含title和content
 */
screen.upgrade = function (data) {
    const { title, content } = data
    viewUtils.confirmOpen(title, content)
}
/**
 * æ˜¾ç¤ºé€šè¡Œç»“果弹窗
 * @param {object} data - é€šè¡Œç»“果数据
 * @param {boolean} data.faceAuth - äººè„¸è¯†åˆ«æ˜¯å¦é€šè¿‡
 * @param {boolean} data.gasConcentration - æ°”体浓度是否合格
 * @param {boolean} data.accessAllowed - æ˜¯å¦å…è®¸é€šè¡Œ
 * @param {string} data.message - å¼¹çª—消息
 */
screen.showAccessResult = function (data) {
    // // æ˜¾ç¤ºçŠ¶æ€é¢æ¿ï¼Œ3秒后自动关闭
    // if (data.accessAllowed) {
    //     mainView.statusPanel.success(data.message)
    // } else {
    //     mainView.statusPanel.fail(data.message)
    // }
    // æ˜¾ç¤ºå°åž‹çŠ¶æ€é¢æ¿ï¼Œ3秒后自动关闭
    if (data.accessAllowed) {
        mainView.smallStatusPanel.success(data.message)
    } else {
        mainView.smallStatusPanel.fail(data.message)
    }
}
export default screen
vf205_access/src/service/accessService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,501 @@
/**
 * é—¨ç¦é€šè¡ŒæœåŠ¡æ¨¡å—
 * å¤„理门禁通行相关的业务逻辑,包括人脸/密码白名单校验、权限判断、通行记录保存等
 */
import logger from "../../dxmodules/dxLogger.js"
import std from "../../dxmodules/dxStd.js"
import config from "../../dxmodules/dxConfig.js"
import common from "../../dxmodules/dxCommon.js"
import map from '../../dxmodules/dxMap.js'
import bus from '../../dxmodules/dxEventBus.js'
import driver from "../driver.js"
import mqttService from "./mqttService.js"
import sqliteService from "./sqliteService.js"
import utils from '../common/utils/utils.js'
import http from "../../dxmodules/dxHttp.js"
import grainService from './grainService.js'
const accessService = {}
/**
 * å°†åè¿›åˆ¶æ•°è½¬æ¢ä¸ºå°ç«¯åºåå…­è¿›åˆ¶å­—符串
 * @param {number} decimalNumber - åè¿›åˆ¶æ•°
 * @param {number} byteSize - å­—节大小
 * @returns {string} å°ç«¯åºåå…­è¿›åˆ¶å­—符串
 */
function decimalToLittleEndianHex(decimalNumber, byteSize) {
    const littleEndianBytes = [];
    for (let i = 0; i < byteSize; i++) {
        littleEndianBytes.push(decimalNumber & 0xFF);
        decimalNumber >>= 8; // ç›¸å½“于除以256
    }
    const littleEndianHex = littleEndianBytes
        .map((byte) => byte.toString(16).padStart(2, '0'))
        .join('');
    return littleEndianHex;
}
/**
 * å°†æ•°æ®åŒ…转换为字符串格式
 * @param {object} pack - æ•°æ®åŒ…对象
 * @returns {string} è½¬æ¢åŽçš„字符串
 */
function pack2str(pack) {
    pack.data = (!pack.data) ? [] : pack.data.match(/.{2}/g)
    let len = decimalToLittleEndianHex(pack.data.length, 2)
    let str = "55aa" + pack.cmd + pack.result + len + pack.data.join('')
    let crc = common.calculateBcc([0x55, 0xaa, parseInt(pack.cmd, 16), parseInt(pack.result, 16), pack.data.length % 256, pack.data.length / 256].concat(pack.data.map(v => parseInt(v, 16))))
    return str + crc.toString(16).padStart(2, '0')
}
/**
 * äººè„¸/密码白名单校验
 * @param {object} data - é€šè¡Œæ•°æ®ï¼ŒåŒ…含type(码制)和code(码内容)
 * @param {string} fileName - é€šè¡Œå›¾ç‰‡æ–‡ä»¶è·¯å¾„
 * @param {boolean|undefined} similarity - ç›¸ä¼¼åº¦éªŒè¯ç»“果,false表示验证失败
 * @returns {number|string|boolean} -1(参数错误),0(通行成功),1(在线验证),string(校验失败的原因),false(通行加锁)
 */
accessService.access = function (data, fileName, similarity) {
    // é€šè¡ŒåŠ é”ï¼Œé˜²æ­¢é‡å¤éªŒè¯
    let lockMap = map.get("access_lock")
    if (lockMap.get("access_lock")) {
        logger.error("[access]: é€šè¡ŒåŠ é”ï¼Œè¯·ç¨åŽå†è¯•")
        return false
    }
    lockMap.put("access_lock", true)
    try {
        // è®°å½•通行时间戳
        data.time = Math.floor(Date.parse(new Date()) / 1000)
        // æ ¹æ®code查询凭证
        let res
        if (data.type == "300") {
            // äººè„¸ç±»åž‹ï¼Œä½¿ç”¨userId查询
            res = sqliteService.d1_voucher.findByUserIdAndType(data.code, data.type)
        } else {
            // å…¶ä»–类型,使用code查询
            res = sqliteService.d1_voucher.findByCodeAndType(data.code, data.type)
        }
        // æƒé™è®¤è¯ç»“æžœ
        let ret = true
        // æ˜¯å¦æ˜¯é™Œç”Ÿäºº
        let isStranger = false
        if (similarity === false) {
            // å¦‚果相似度验证失败,则不进行认证
            ret = false
            isStranger = true
        } else {
            // éªŒè¯å‡­è¯æ˜¯å¦å­˜åœ¨
            if (res.length == 0) {
                logger.error("[access]: é€šè¡Œå¤±è´¥ï¼Œæ²¡æŸ¥è¯¢åˆ°å‡­è¯ï¼")
                ret = false
                isStranger = true
            } else {
                // å‡­è¯å­˜åœ¨ï¼ŒèŽ·å–ç”¨æˆ·ä¿¡æ¯
                data.userId = res[0].userId
                data.keyId = res[0].id
                // æ ¹æ®userId查询人员信息
                res = sqliteService.d1_person.findByUserId(data.userId)
                if (res.length == 0) {
                    logger.error("[access]: é€šè¡Œå¤±è´¥ï¼Œæ²¡æŸ¥è¯¢åˆ°äººå‘˜ï¼")
                    ret = false
                    isStranger = true
                } else {
                    // èŽ·å–äººå‘˜å§“åã€èº«ä»½è¯å·å’Œèº«ä»½ç±»åž‹
                    let idCard
                    let userType = 0
                    try {
                        idCard = JSON.parse(res[0].extra).idCard
                        userType = JSON.parse(res[0].extra).type || 0
                    } catch (error) {
                        logger.error("无身份证号或类型")
                    }
                    data.extra = { name: res[0].name, idCard: idCard, type: userType }
                }
                // å¤„理双人认证信息
                if (data.dualAuthInfo) {
                    logger.info("[access]: å¤„理双人认证信息: " + JSON.stringify(data.dualAuthInfo))
                    // å­˜å‚¨ç¬¬ä¸€ç”¨æˆ·ä¿¡æ¯
                    let firstUserId = data.dualAuthInfo.firstUserId
                    // æŸ¥è¯¢ç¬¬ä¸€ç”¨æˆ·çš„详细信息
                    let res1 = sqliteService.d1_person.findByUserId(firstUserId)
                    if (res1.length > 0) {
                        // èŽ·å–ç¬¬ä¸€ç”¨æˆ·çš„å§“åã€èº«ä»½è¯å·å’Œèº«ä»½ç±»åž‹
                        let idCard1
                        let firstUserType = 0
                        try {
                            idCard1 = JSON.parse(res1[0].extra).idCard
                            firstUserType = JSON.parse(res1[0].extra).type || 0
                        } catch (error) {
                            logger.error("无第一用户身份证号或类型")
                        }
                        data.userId = firstUserId
                        data.extra = { name: res1[0].name, idCard: idCard1, type: firstUserType }
                    } else {
                        // å¦‚果没有查询到第一用户信息,使用默认值
                        data.userId = firstUserId
                        data.extra = { name: data.dualAuthInfo.firstUserName, idCard: "", type: 0 }
                    }
                    // å­˜å‚¨ç¬¬äºŒç”¨æˆ·ä¿¡æ¯
                    data.userId2 = data.dualAuthInfo.secondUserId
                    // æŸ¥è¯¢ç¬¬äºŒç”¨æˆ·çš„详细信息
                    let res2 = sqliteService.d1_person.findByUserId(data.dualAuthInfo.secondUserId)
                    if (res2.length > 0) {
                        // èŽ·å–ç¬¬äºŒç”¨æˆ·çš„å§“åå’Œèº«ä»½è¯å·
                        let idCard2
                        let secondUserType = 0
                        try {
                            idCard2 = JSON.parse(res2[0].extra).idCard
                            secondUserType = JSON.parse(res2[0].extra).type || 0
                        } catch (error) {
                            logger.error("无第二用户身份证号或类型")
                        }
                        data.extra2 = { name: res2[0].name, idCard: idCard2 }
                        // å­˜å‚¨ç¬¬äºŒç”¨æˆ·çš„æƒé™ID(身份类型)
                        data.permissionId2 = secondUserType.toString()
                    } else {
                        // å¦‚果没有查询到第二用户信息,使用默认值
                        data.extra2 = { name: data.dualAuthInfo.secondUserName, idCard: "" }
                        data.permissionId2 = ""
                    }
                    // å¤„理第一用户的人脸图片
                    if (data.firstUserFileName) {
                        let firstUserSrc = `/app/data/passRecord/${firstUserId}_${data.time}_1.jpg`
                        std.ensurePathExists(firstUserSrc) // ç¡®ä¿ç›®å½•存在
                        if (std.exist(data.firstUserFileName)) {
                            // ç§»åŠ¨å›¾ç‰‡åˆ°æŒ‡å®šç›®å½•
                            common.systemBrief(`mv ${data.firstUserFileName} ${firstUserSrc}`)
                            // æ›´æ–°data中的code为第一用户的图片路径
                            data.code = firstUserSrc
                        }
                    }
                    // å¤„理第二用户的人脸图片
                    if (fileName) {
                        let secondUserSrc = `/app/data/passRecord/${data.userId2}_${data.time}_2.jpg`
                        std.ensurePathExists(secondUserSrc) // ç¡®ä¿ç›®å½•存在
                        if (std.exist(fileName)) {
                            // ç§»åŠ¨å›¾ç‰‡åˆ°æŒ‡å®šç›®å½•
                            common.systemBrief(`mv ${fileName} ${secondUserSrc}`)
                            // æ›´æ–°data中的code2为第二用户的图片路径
                            data.code2 = secondUserSrc
                        }
                    }
                }
            }
            // éªŒè¯æƒé™
            if (ret) {
                // æ ¹æ®userId查询权限
                res = sqliteService.d1_permission.findByUserId(data.userId)
                if (res && res.length > 0 && judgmentPermission(res)) {
                    logger.info("[access]: æƒé™è®¤è¯é€šè¿‡")
                    ret = true
                    // å­˜å‚¨ç”¨æˆ·èº«ä»½ç±»åž‹ä½œä¸ºæƒé™ID(本系统中身份即权限)
                    let userType = 0
                    try {
                        if (data.extra) {
                            userType = data.extra.type || 0
                        } else {
                            // ä»Žæ•°æ®åº“查询用户信息获取身份类型
                            let userRes = sqliteService.d1_person.findByUserId(data.userId)
                            if (userRes && userRes.length > 0) {
                                userType = JSON.parse(userRes[0].extra).type || 0
                            }
                        }
                    } catch (error) {
                        logger.error("解析用户类型失败")
                    }
                    data.permissionId = userType.toString()
                    // æš‚停人脸认证功能
                    driver.face.status(false)
                    logger.info("[access]: æš‚停人脸认证功能")
                } else {
                    logger.info("[access]: æ— æƒé™")
                    ret = false
                }
            }
            // æ— æƒé™æ—¶ï¼Œå°è¯•在线验证
            if (!ret && config.get('mqtt.onlinecheck') == 1 && driver.mqtt.getStatus()) {
                logger.info("[access]: æ— æƒé™ï¼Œèµ°åœ¨çº¿éªŒè¯")
                let serialNo = std.genRandomStr(10)
                driver.mqtt.send("access_device/v2/event/access_online", JSON.stringify(mqttService.mqttReply(serialNo, data, mqttService.CODE.S_000)))
                driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/verify.wav`)
                // ç­‰å¾…在线验证结果
                let payload = driver.mqtt.getOnlinecheck()
                if (payload && payload.serialNo == serialNo && payload.code == '000000') {
                    ret = true
                    // æš‚停人脸认证功能
                    driver.face.status(false)
                    logger.info("[access]: æš‚停人脸认证功能")
                } else {
                    logger.info("[access]: åœ¨çº¿éªŒè¯å¤±è´¥")
                    ret = false
                }
            }
        }
        // ç¡®å®šè¯­éŸ³æ–‡ä»¶åç§°
        let alsaFile = (data.type).toString().startsWith("10") ? '10x' : data.type
        //暂停人脸认证功能
        if (ret == true) {
            // éªŒè¯æ°”体浓度
            grainService.checkGasConcentration(function() {
                // ä»Žå­˜å‚¨çš„æ°”体数据中获取验证结果
                const gasData = grainService.getGasData()
                if(gasData && gasData.data && gasData.data.status === "0") {
                    logger.info("[access]: æ°”体浓度验证合格")
                    // é€šè¡ŒæˆåŠŸå¤„ç†
                    driver.screen.accessSuccess()
                    logger.info("[access]: é€šè¡ŒæˆåŠŸ")
                    // æ˜¾ç¤ºé€šè¡ŒæˆåŠŸç»“æžœ
                    bus.fire("showAccessResult", {
                        faceAuth: true,
                        gasConcentration: true,
                        accessAllowed: true,
                        message: "*仓内气体浓度合格,允许通行*"
                    })
                    // è§¦å‘通行成功事件,通知UI更新
                    bus.fire("accessSuccess", { data: data, fileName: fileName })
                    driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/access_s.wav`)
                    driver.gpio.open() // å¼€é—¨
                    savePassPic(data, fileName) // ä¿å­˜é€šè¡Œå›¾ç‰‡
                    reply(data, true) // ä¸ŠæŠ¥é€šè¡Œè®°å½•
                } else {
                    logger.info("[access]: æ°”体浓度验证不合格")
                    // é€šè¡Œå¤±è´¥å¤„理
                    driver.screen.accessFail()
                    logger.error("[access]: é€šè¡Œå¤±è´¥")
                    // è§¦å‘失败弹窗
                    bus.fire("showAccessResult", {
                        faceAuth: true,
                        gasConcentration: false,
                        accessAllowed: false,
                        message: "*仓内气体浓度不合格,禁止通行*"
                    })
                    if (utils.isEmpty(similarity)) {
                        driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/access_f.wav`)
                    }
                    // if (isStranger && !config.get("sys.strangerImage")) {
                    //     // é™Œç”Ÿäººä¸ä¿å­˜ç…§ç‰‡
                    // } else {
                    //     savePassPic(data, fileName)
                    // }
                    savePassPic(data, fileName)
                    // æ·»åŠ æ°”ä½“æµ“åº¦å¤±è´¥ä¿¡æ¯
                    data.message = "气体浓度不合格"
                    reply(data, false) // ä¸ŠæŠ¥é€šè¡Œè®°å½•
                }
            })
        } else {
            // é€šè¡Œå¤±è´¥å¤„理
            driver.screen.accessFail()
            logger.error("[access]: é€šè¡Œå¤±è´¥")
            // è§¦å‘失败弹窗
            bus.fire("showAccessResult", {
                faceAuth: true,
                gasConcentration: false,
                accessAllowed: false,
                message: "*权限认证失败,禁止通行*"
            })
            if (utils.isEmpty(similarity)) {
                driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/recg_f.wav`)
            }
            // if (isStranger && !config.get("sys.strangerImage")) {
            //     // é™Œç”Ÿäººä¸ä¿å­˜ç…§ç‰‡
            // } else {
            //     savePassPic(data, fileName)
            // }
            savePassPic(data, fileName)
            reply(data, false) // ä¸ŠæŠ¥é€šè¡Œè®°å½•
        }
    } catch (error) {
        logger.error(error)
    }
    // è¯­éŸ³æ’­æŠ¥éœ€è¦æ—¶é—´ï¼Œæ‰€ä»¥å»¶è¿Ÿ1秒解锁
    std.sleep(1000)
    lockMap.put("access_lock", false)
    logger.error("[access]: è§£é”æˆåŠŸ")
    // è§¦å‘通行解锁完成事件,通知UI重置
    // bus.fire("accessUnlockComplete")
}
/**
 * ä¿å­˜é€šè¡Œå›¾ç‰‡
 * @param {object} data - é€šè¡Œæ•°æ®
 * @param {string} fileName - å›¾ç‰‡æ–‡ä»¶è·¯å¾„
 */
function savePassPic(data, fileName) {
    if (data.type == "300") { // ä»…处理人脸类型
        // å¦‚果是双人认证,已经在处理双人认证信息时保存了图片,这里跳过
        if (data.dualAuthInfo) {
            return
        }
        let src = `/app/data/passRecord/${data.userId}_${data.time}.jpg`
        std.ensurePathExists(src) // ç¡®ä¿ç›®å½•存在
        if (std.exist(fileName)) {
            // ç§»åŠ¨å›¾ç‰‡åˆ°æŒ‡å®šç›®å½•
            common.systemBrief(`mv ${fileName} ${src}`)
            // åªæ¸…理原始临时图片文件(以时间戳命名的文件),保留调整后的图片文件
            common.systemBrief(`rm -rf /app/data/user/temp/[0-9]*.jpg`)
            // æ›´æ–°data中的code为图片路径
            data.code = src
        } else {
            logger.error("[access]: é€šè¡Œå¤±è´¥ï¼Œå›¾ç‰‡ä¸å­˜åœ¨ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼" + fileName)
        }
    }
}
/**
 * æ ¡éªŒæƒé™æ—¶é—´æ˜¯å¦å¯ä»¥é€šè¡Œ
 * @param {array} permissions - æƒé™è®°å½•数组
 * @returns {boolean} true表示有权限,false表示无权限
 */
function judgmentPermission(permissions) {
    let currentTime = Math.floor(Date.now() / 1000)
    for (let permission of permissions) {
        if (permission.timeType == 0) {
            // æ°¸ä¹…权限
            return true
        } else if (permission.beginTime <= currentTime && currentTime <= permission.endTime) {
            if (permission.timeType == 1) {
                // æ—¶é—´æ®µæƒé™
                return true
            }
            if (permission.timeType == 2) {
                // æ¯æ—¥æƒé™
                let seconds = Math.floor((new Date() - new Date().setHours(0, 0, 0, 0)) / 1000);
                if (permission.repeatBeginTime <= seconds && seconds <= permission.repeatEndTime) {
                    return true
                }
            }
            if (permission.timeType == 3 && permission.period) {
                // å‘¨é‡å¤æƒé™
                let dayTimes = JSON.parse(permission.period)[new Date().getDay() + 1]
                if (dayTimes && dayTimes.split("|").some((dayTime) => isCurrentTimeInTimeRange(dayTime))) {
                    return true
                }
            }
        }
    }
    return false
}
/**
 * åˆ¤æ–­å½“前时间是否在时间段内
 * @param {string} time - æ—¶é—´æ®µå­—符串,格式如"15:00-19:00"
 * @returns {boolean} true表示当前时间在时间段内,false表示不在
 */
function isCurrentTimeInTimeRange(time) {
    // åˆ†å‰²å¼€å§‹æ—¶é—´å’Œç»“束时间
    let [startTime, endTime] = time.split('-');
    // è§£æžå¼€å§‹æ—¶é—´çš„小时和分钟
    let [startHour, startMinute] = startTime.split(':');
    // è§£æžç»“束时间的小时和分钟
    let [endHour, endMinute] = endTime.split(':');
    // èŽ·å–å½“å‰æ—¶é—´
    let currentTime = new Date();
    // åˆ›å»ºå¼€å§‹æ—¶é—´çš„æ—¥æœŸå¯¹è±¡
    let startDate = new Date();
    startDate.setHours(parseInt(startHour, 10));
    startDate.setMinutes(parseInt(startMinute, 10));
    // åˆ›å»ºç»“束时间的日期对象
    let endDate = new Date();
    endDate.setHours(parseInt(endHour, 10));
    endDate.setMinutes(parseInt(endMinute, 10));
    // æ£€æŸ¥å½“前时间是否在时间范围内
    return currentTime >= startDate && currentTime <= endDate;
}
/**
 * é€šè¡Œè®°å½•上报
 * @param {object} data - é€šè¡Œæ•°æ®
 * @param {boolean} result - é€šè¡Œç»“果,true表示成功,false表示失败
 */
function reply(data, result) {
    // ä½¿ç”¨çº¿ç¨‹å¤„理整个reply函数,避免堵塞主进程
    std.setTimeout(() => {
        try {
            // æž„建通行记录
            let record = {
                id: std.genRandomStr(16),
                result: result ? 0 : 1, // 0表示成功,1表示失败
            }
            // å¤åˆ¶data中的字段,排除extra和extra2
            for (const key in data) {
                if (key !== 'extra' && key !== 'extra2' && !(key in record)) {
                    record[key] = data[key]
                }
            }
            // å¤„理extra字段
            if (data.extra) {
                record.extra = JSON.stringify(data.extra)
            }
            // å¤„理extra2字段
            if (data.extra2) {
                record.extra2 = JSON.stringify(data.extra2)
            }
            // å­˜å‚¨é€šè¡Œè®°å½•,判断上限
            let count = sqliteService.d1_pass_record.count()
            let configNum = config.get("access.offlineAccessNum");
            configNum = configNum ? configNum : 2000;
            if (configNum > 0) {
                if (count >= configNum) {
                    // è¾¾åˆ°æœ€å¤§å­˜å‚¨æ•°é‡ï¼Œåˆ é™¤æœ€æ—§çš„记录
                    let lastRecord = sqliteService.d1_pass_record.findAllOrderBytimeDesc({ page: 0, size: 1 })
                    if (lastRecord && lastRecord.length == 1) {
                        // å¦‚果是人脸记录,删除对应的图片文件
                        if (lastRecord[0].type == 300) {
                            common.systemBrief(`rm -rf ${lastRecord[0].code}`)
                        }
                        sqliteService.d1_pass_record.deleteByid(lastRecord[0].id)
                    }
                }
                // ä¿å­˜æ–°è®°å½•
                sqliteService.d1_pass_record.save(record)
            }
            // // ç”Ÿæˆåºåˆ—号
            // let serialNo = std.genRandomStr(10)
            // // å¤„理人脸图片
            // if (record.type == 300) {
            //     let m = map.get("faceAccesss")
            //     m.del(serialNo)
            //     m.put(serialNo, record.code ? record.code : "")
            //     // å°†å›¾ç‰‡è½¬æ¢ä¸ºbase64格式
            //     record.code = driver.face.fileToBase64(record.code)
            // }
            // // æž„建MQTT消息并发送
            // let payload = mqttService.mqttReply(serialNo, [record], mqttService.CODE.S_000)
            // driver.mqtt.send("access_device/v2/event/access", JSON.stringify(payload))
        } catch (error) {
            logger.error(`[accessService]: å¤„理通行记录失败: ${error.message}`)
        }
    }, 0)
}
export default accessService
vf205_access/src/service/codeService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,205 @@
/**
 * äºŒç»´ç /条形码处理服务模块
 * å¤„理各类二维码/条形码的解析和处理,包括配置码、云证激活码和通行码
 */
import common from '../../dxmodules/dxCommon.js';
import log from '../../dxmodules/dxLogger.js'
import qrRule from '../../dxmodules/dxQrRule.js'
import std from '../../dxmodules/dxStd.js'
import config from '../../dxmodules/dxConfig.js'
import base64 from '../../dxmodules/dxBase64.js'
import dxMap from '../../dxmodules/dxMap.js'
import ota from "../../dxmodules/dxOta.js";
import bus from "../../dxmodules/dxEventBus.js"
import sqliteService from "./sqliteService.js";
import driver from '../driver.js';
import utils from '../common/utils/utils.js';
import configConst from '../common/consts/configConst.js';
import configService from './configService.js';
import accessService from './accessService.js';
import logger from '../../dxmodules/dxLogger.js';
const codeService = {}
/**
 * æŽ¥æ”¶æ¶ˆæ¯å¹¶å¤„理
 * @param {ArrayBuffer} data - æŽ¥æ”¶åˆ°çš„原始数据
 */
codeService.receiveMsg = function (data) {
    log.info('[codeService] receiveMsg :' + JSON.stringify(data))
    // å°†ArrayBuffer转换为十六进制字符串,再转换为UTF8字符串
    let str = common.utf8HexToStr(common.arrayBufferToHexString(data))
    // è°ƒç”¨code方法处理字符串
    this.code(str)
}
/**
 * æ¯”较两个字符串的前N个字符是否相等
 * @param {string} str1 - ç¬¬ä¸€ä¸ªå­—符串
 * @param {string} str2 - ç¬¬äºŒä¸ªå­—符串
 * @param {number} N - æ¯”较的字符数
 * @returns {boolean} å‰N个字符是否相等
 */
function comparePrefix (str1, str2, N) {
    let substring1 = str1.substring(0, N);
    let substring2 = str2.substring(0, N);
    return substring1 === substring2;
}
/**
 * å¤„理二维码/条形码数据
 * @param {string} data - äºŒç»´ç /条形码的原始数据
 */
codeService.code = function (data) {
    log.info('[codeService] code :' + data)
    // æ ¼å¼åŒ–二维码数据
    data = qrRule.formatCode(data, sqliteService)
    // åˆ¤æ–­äºŒç»´ç ç±»åž‹
    if (data.type == 'config' || comparePrefix(data.code, "___VF102_CONFIG_V1.1.0___", "___VF102_CONFIG_V1.1.0___".length)) {
        // é…ç½®ç å¤„理
        configCode(data.code)
    } else if (comparePrefix(data.code, "___VBAR_ID_ACTIVE_V", "___VBAR_ID_ACTIVE_V".length)) {
        // äº‘证激活码处理
        let activeResute = driver.eid.active(config.get("sys.sn"), config.get("sys.appVersion"), config.get("sys.mac"), data.code);
        log.info("[codeService] code: activeResute " + activeResute)
        if (activeResute === 0) {
            log.info("[codeService] code: äº‘证激活成功")
            driver.screen.upgrade({ title: "confirm.cloudCertActive", content: "confirm.cloudCertActiveSuccess" })
        } else {
            log.info("[codeService] code: äº‘证激活失败")
            driver.screen.upgrade({ title: "confirm.cloudCertActive", content: "confirm.cloudCertActiveFail" })
        }
    } else {
        // é€šè¡Œç å¤„理
        log.info("解析通行码:", JSON.stringify(data))
        bus.fire("access", { data })
    }
}
/**
 * é…ç½®ç å¤„理
 * @param {string} code - é…ç½®ç æ•°æ®
 * @param {boolean} configType - é…ç½®ç±»åž‹æ ‡å¿—
 */
function configCode (code, configType) {
    // é…ç½®ç æ ¡éªŒæš‚时注释
    // if (!checkConfigCode(code)) {
    //     log.error("配置码校验失败")
    //     return
    // }
    // è§£æžé…ç½®ç ä¸ºJSON对象
    let json = utils.parseString(code)
    if (Object.keys(json).length <= 0) {
        try {
            json = JSON.parse(code.slice(code.indexOf("{"), code.lastIndexOf("}") + 1))
        } catch (error) {
            log.error(error)
        }
    }
    log.info("解析配置码:", JSON.stringify(json))
    // æ¨¡å¼åˆ‡æ¢å¤„理
    if (!utils.isEmpty(json.w_model)) {
        try {
            common.setMode(json.w_model)
            common.asyncReboot(1)
        } catch (error) {
            log.error(error, error.stack)
            log.info('切换失败不做任何处理');
        }
        return
    }
    let map = dxMap.get("UPDATE")
    // æ‰«ç å‡çº§å¤„理
    if (json.update_flag === 1) {
        if (!driver.net.getStatus()) {
            driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/network.wav`)
            return
        }
        if (map.get("updateFlag")) {
            return
        }
        map.put("updateFlag", true)
        try {
            driver.screen.upgrade({ title: "confirm.upgrade", content: "confirm.upgrading" })
            ota.updateHttp(json.update_addr, json.update_md5, 300)
            driver.screen.upgrade({ title: "confirm.upgrade", content: "confirm.upgradeSuccess" })
        } catch (error) {
            logger.info(error.message)
            driver.screen.upgrade({ title: "confirm.upgrade", content: "confirm.upgradeFail" })
        } finally {
            map.del("updateFlag")
        }
        common.asyncReboot(3)
        return
    }
    // è®¾å¤‡é…ç½®å¤„理
    let configData = {}
    for (let key in json) {
        let transKey
        if (configType == true) {
            transKey = key
        } else {
            transKey = configConst.getValueByKey(key)
        }
        if (transKey == undefined) {
            continue
        }
        let keys = transKey.split(".")
        if (utils.isEmpty(configData[keys[0]])) {
            configData[keys[0]] = {}
        }
        configData[keys[0]][keys[1]] = json[key]
    }
    let res = false
    if (Object.keys(configData).length > 0) {
        res = configService.configVerifyAndSave(configData)
    }
    if (typeof res != 'boolean') {
        log.error(res)
        return
    }
    if (res) {
        log.info("配置成功")
    } else {
        log.error("配置失败")
    }
    // é…ç½®åŽé‡å¯
    if (json.reboot === 1) {
        driver.screen.warning({ msg: config.get("sysInfo.language") == 1 ? "Rebooting" : "重启中", beep: false })
        common.asyncReboot(1)
    }
}
/**
 * æ ¡éªŒé…ç½®ç 
 * @param {string} code - é…ç½®ç æ•°æ®
 * @returns {boolean} æ ¡éªŒæ˜¯å¦æˆåŠŸ
 */
function checkConfigCode (code) {
    let password = config.get('sysInfo.com_passwd') || '1234567887654321'
    let lastIndex = code.lastIndexOf("--");
    if (lastIndex < 0) {
        lastIndex = code.lastIndexOf("__");
    }
    let firstPart = code.substring(0, lastIndex);
    let secondPart = code.substring(lastIndex + 2);
    let res
    try {
        res = base64.fromHexString(common.arrayBufferToHexString(common.hmac(firstPart, password)))
    } catch (error) {
        log.error(error)
        return false
    }
    return res == secondPart;
}
export default codeService
vf205_access/src/service/configService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,273 @@
/**
 * é…ç½®æœåŠ¡æ¨¡å—
 * è´Ÿè´£é…ç½®çš„æ ¡éªŒã€ä¿å­˜å’Œåº”用,包括各种配置项的规则验证和回调处理
 */
import utils from '../common/utils/utils.js'
import config from '../../dxmodules/dxConfig.js'
import mqtt from '../../dxmodules/dxMqtt.js'
import std from '../../dxmodules/dxStd.js'
import ntp from '../../dxmodules/dxNtp.js'
import common from '../../dxmodules/dxCommon.js'
import driver from '../driver.js'
import bus from '../../dxmodules/dxEventBus.js'
import mqttService from './mqttService.js'
import logger from '../../dxmodules/dxLogger.js'
const configService = {}
/**
 * IP地址校验正则
 * åŒ¹é…ä»¥ç‚¹åˆ†åè¿›åˆ¶å½¢å¼è¡¨ç¤ºçš„ IP åœ°å€ï¼Œä¾‹å¦‚:192.168.0.1
 */
const ipCheck = v => /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(v)
/**
 * IP地址或域名校验正则(带端口)
 * åŒ¹é…IP地址或域名,可选择带有端口号
 */
const ipOrDomainCheckWithPort = v => /^(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(:\d{1,5})?$/.test(v);
/**
 * æ­£æ•´æ•°æ ¡éªŒæ­£åˆ™
 */
const regpCheck = v => /^[1-9]\d*$/.test(v)
/**
 * éžè´Ÿæ•´æ•°æ ¡éªŒæ­£åˆ™
 */
const regnCheck = v => /^([1-9]\d*|0{1})$/.test(v)
/**
 * æ‰€æœ‰æ”¯æŒçš„配置项的检验规则以及设置成功后的回调
 * rule:校验规则,返回true校验成功,false校验失败
 * callback:配置修改触发回调
 */
const supported = {
    // äººè„¸é…ç½®
    face: {
        similarity: { rule: v => typeof v == 'number' && v >= 0 && v <= 1, callback: v => driver.face.faceUpdateConfig({ score: v }) },
        livenessOff: { rule: v => [0, 1].includes(v), callback: v => driver.face.faceUpdateConfig({ livingCheckEnable: v }) },
        livenessVal: { rule: v => typeof v == 'number' && v >= 0 && v <= 1, callback: v => driver.face.faceUpdateConfig({ livingScore: v }) },
        showNir: { rule: v => [0, 1].includes(v), callback: v => driver.capturer.showNir(v) },
        detectMask: { rule: v => [0, 1].includes(v), callback: v => driver.face.faceUpdateConfig({ detectMaskEnable: v }) },
        stranger: { rule: v => [0, 1, 2].includes(v) },
        voiceMode: { rule: v => [0, 1, 2].includes(v) },
        voiceModeDate: { rule: v => typeof v == 'string' },
    },
    // MQTT配置
    mqtt: {
        addr: { rule: ipOrDomainCheckWithPort },
        clientId: { rule: v => typeof v == 'string' },
        username: { rule: v => typeof v == 'string' },
        password: { rule: v => typeof v == 'string' },
        qos: { rule: v => [0, 1, 2].includes(v) },
        prefix: { rule: v => typeof v == 'string' },
        willtopic: { rule: v => typeof v == 'string' },
        onlinecheck: { rule: v => [0, 1, 2].includes(v) },
        timeout: { rule: regpCheck },
    },
    // ç½‘络配置
    net: {
        type: { rule: v => [0, 1, 2, 4].includes(v) }, // ç½‘络类型
        dhcp: { rule: v => [1, 2, 3].includes(v) }, // DHCP设置
        ip: { rule: ipCheck }, // IP地址
        gateway: { rule: ipCheck }, // ç½‘关地址
        dns: { rule: v => !v.split(",").some(ip => !ipCheck(ip)) }, // DNS服务器
        mask: { rule: ipCheck }, // å­ç½‘掩码
        mac: { rule: v => typeof v == 'string' }, // MAC地址
        ssid: { rule: v => typeof v == 'string' }, // WiFi SSID
        psk: { rule: v => typeof v == 'string' }, // WiFi密码
    },
    // NTP时间同步配置
    ntp: {
        ntp: { rule: v => [0, 1].includes(v) }, // NTP开关
        server: { rule: ipCheck }, // NTP服务器
        gmt: { rule: v => typeof v == 'number' && v >= 0 && v <= 24, callback: v => ntp.updateGmt(v) }, // æ—¶åŒºè®¾ç½®
    },
    // ç³»ç»Ÿé…ç½®
    sys: {
        uuid: { rule: v => typeof v == 'string' }, // è®¾å¤‡UUID
        model: { rule: v => typeof v == 'string' }, // è®¾å¤‡åž‹å·
        mode: { rule: v => typeof v == 'string', callback: v => setMode(v) }, // è®¾å¤‡æ¨¡å¼
        sn: { rule: v => typeof v == 'string' }, // è®¾å¤‡åºåˆ—号
        version: { rule: v => typeof v == 'string' }, // ç³»ç»Ÿç‰ˆæœ¬
        releaseDate: { rule: v => typeof v == 'string' }, // å‘布日期
        nfc: { rule: v => [0, 1].includes(v) }, // NFC开关
        nfcIdentityCardEnable: { rule: v => [1, 3].includes(v) }, // äº‘证开关
        pwd: { rule: v => [0, 1].includes(v) }, // å¯†ç å¼€é—¨å¼€å…³
        strangerImage: { rule: v => [0, 1].includes(v) }, // é™Œç”Ÿäººä¿å­˜å›¾ç‰‡å¼€å…³
        accessImageType: { rule: v => [0, 1].includes(v) }, // é€šè¡Œå›¾ç‰‡ç±»åž‹
        interval: { rule: regnCheck }, // ç³»ç»Ÿé—´éš”设置
    },
    // é—¨ç¦é…ç½®
    access: {
        relayTime: { rule: regpCheck }, // ç»§ç”µå™¨åŠ¨ä½œæ—¶é—´
        offlineAccessNum: { rule: regpCheck }, // ç¦»çº¿å¼€é—¨æ¬¡æ•°
        tamperAlarm: { rule: v => [0, 1].includes(v) }, // é˜²æ‹†æŠ¥è­¦å¼€å…³
    },
    // åŸºç¡€é…ç½®
    base: {
        firstLogin: { rule: v => [0, 1].includes(v) }, // é¦–次登录标志
        brightness: { rule: regnCheck, callback: v => driver.face.setDisplayBacklight(v) }, // å±å¹•亮度
        brightnessAuto: { rule: v => [0, 1].includes(v) }, // è‡ªåŠ¨äº®åº¦å¼€å…³
        showIp: { rule: v => [0, 1].includes(v), callback: v => driver.screen.hideIp(v) }, // æ˜¯å¦æ˜¾ç¤ºIP地址
        showSn: { rule: v => [0, 1].includes(v), callback: v => driver.screen.hideSn(v) }, // æ˜¯å¦æ˜¾ç¤ºåºåˆ—号
        appMode: { rule: v => [0, 1].includes(v), callback: v => driver.screen.appMode(v) }, // åº”用模式
        screenOff: { rule: regnCheck, callback: v => bus.fire("screenManagerRefresh") }, // ç†„屏时间
        screensaver: { rule: regnCheck, callback: v => bus.fire("screenManagerRefresh") }, // å±å¹•保护
        volume: { rule: regnCheck, callback: v => driver.alsa.volume(v) }, // éŸ³é‡è®¾ç½®
        password: { rule: v => typeof v == 'string' && v.length >= 8 }, // ç®¡ç†å‘˜å¯†ç 
        language: { rule: v => ["EN", "CN"].includes(v), callback: v => driver.screen.changeLanguage() }, // è¯­è¨€è®¾ç½®
        showProgramCode: { rule: v => [0, 1].includes(v) }, // æ˜¯å¦æ˜¾ç¤ºç¨‹åºä»£ç 
        showIdentityCard: { rule: v => [0, 1].includes(v) }, // æ˜¯å¦æ˜¾ç¤ºèº«ä»½è¯ä¿¡æ¯
        luminanceWhite: { rule: v => typeof v == 'number' && v >= 0 && v <= 100, callback: v => driver.pwm.luminanceWhite(v) }, // ç™½å…‰äº®åº¦
        luminanceNir: { rule: v => typeof v == 'number' && v >= 0 && v <= 100, callback: v => driver.pwm.luminanceNir(v) }, // çº¢å¤–光亮度
    },
    // å¯†ç é€šè¡Œé…ç½®
    passwordAccess: {
        passwordAccess: { rule: v => [0, 1].includes(v) }, // å¯†ç é€šè¡Œå¼€å…³
        emergencyPassword: { rule: v => typeof v == 'string' && v.length >= 8 }, // åº”急开仓密码
    },
    // HTTP配置
    http: {
        gas: { rule: v => typeof v == 'string' }, // HTTP_气体
        status: { rule: v => typeof v == 'string' }, // HTTP_状态
        safeInputAccess: { rule: v => typeof v == 'string' } // HTTP接口路径
    },
    // ä»“廒名称
    houseName: {},
    // åº“区名称
    GranaryName: {},
    // æ›´æ–°é…ç½®
    update: {
        gasTime: { rule: regpCheck }, // æ°”体浓度更新时间(秒)
        statusTime: { rule: regpCheck } // çŠ¶æ€ä¿¡æ¯æ›´æ–°æ—¶é—´ï¼ˆç§’ï¼‰
    }
}
/**
 * éœ€è¦é‡å¯çš„配置项
 */
const needReboot = ["sys.nfc", "sys.nfcIdentityCardEnable", "ntp"]
configService.needReboot = needReboot
/**
 * ä¿®æ”¹è®¾å¤‡æ¨¡å¼
 * @param {string} params - æ¨¡å¼å‚æ•°
 */
function setMode(params) {
    common.systemWithRes(`echo 'app' > /etc/.app_v1`, 2)
    common.setMode(params)
}
/**
 * é…ç½®json校验并保存
 * @param {object} data - é…ç½®json对象
 * @returns {boolean|string} true表示校验并保存成功,string表示错误信息
 */
configService.configVerifyAndSave = function (data) {
    let netFlag = false // ç½‘络配置标志
    let mqttFlag = false // MQTT配置标志
    let isReboot = false // æ˜¯å¦éœ€è¦é‡å¯
    for (const key in data) {
        if (key == 'net') {
            netFlag = true
        }
        if (key == 'mqtt') {
            mqttFlag = true
        }
        // æ£€æŸ¥é…ç½®ç»„是否支持
        if (!supported[key]) {
            return key + " not supported"
        }
        const item = data[key];
        if (typeof item != 'object') {
            // é¡¶å±‚配置项,直接保存
            config.set(key, item)
            continue
        }
        // æ£€æŸ¥æ˜¯å¦éœ€è¦é‡å¯
        if (needReboot.includes(key)) {
            isReboot = true
        }
        for (const subKey in item) {
            let option = supported[key][subKey]
            if (isEmpty(option)) {
                return subKey + " not supported"
            }
            const value = item[subKey];
            // æ£€æŸ¥æ˜¯å¦éœ€è¦é‡å¯
            if (needReboot.includes(key + "." + subKey)) {
                isReboot = true
            }
            // æ ¡éªŒé…ç½®å€¼
            if (!option.rule || option.rule(value)) {
                // æ²¡æœ‰æ ¡éªŒè§„则默认校验通过
                config.set(key + "." + subKey, value)
                if (option.callback) {
                    // æ‰§è¡Œé…ç½®è®¾ç½®å›žè°ƒ
                    option.callback(value)
                }
            } else {
                return value + " check failure"
            }
        }
    }
    // ä¿å­˜é…ç½®
    config.save()
    // æ£€æŸ¥éœ€è¦é‡å¯çš„配置,3秒后重启
    if (isReboot) {
        driver.screen.upgrade({ title: "confirm.restartDevice", content: "confirm.restartDeviceDis" })
        common.asyncReboot(3)
    }
    // å¤„理网络配置变更
    if (netFlag) {
        // ç­‰å¾… 1 ç§’ å› ä¸ºéœ€è¦è¿”回 mqtt
        std.setTimeout(() => {
            bus.fire("switchNetworkType", config.get("net.type"))
        }, 1000);
    }
    // å¤„理MQTT配置变更
    if (mqttFlag) {
        let option = {
            mqttAddr: config.get("mqtt.addr"),
            clientId: config.get('mqtt.clientId') + std.genRandomStr(3),
            subs: mqttService.getTopics(),
            username: config.get("mqtt.username"),
            password: config.get("mqtt.password"),
            qos: config.get("mqtt.qos"),
            willTopic: config.get("mqtt.willTopic"),
            willMessage: JSON.stringify({ "uuid": config.get("sys.uuid") })
        }
        logger.info("重启mqtt", JSON.stringify(option))
        // é”€æ¯ mqtt é‡æ–° init
        bus.fire(mqtt.RECONNECT, option)
    }
    // è§¦å‘配置更新事件,通知其他模块配置已更新
    bus.fire('configUpdated')
    return true
}
/**
 * åˆ¤ç©ºå‡½æ•°
 * @param {*} value - è¦åˆ¤æ–­çš„值
 * @returns {boolean} true表示值为空,false表示值不为空
 */
function isEmpty(value) {
    return value === undefined || value === null
}
export default configService
vf205_access/src/service/faceService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,447 @@
/**
 * äººè„¸è¯†åˆ«æœåŠ¡æ¨¡å—
 * å¤„理人脸识别相关的业务逻辑,包括人脸注册和人脸比对
 */
import logger from "../../dxmodules/dxLogger.js";
import dxCommon from "../../dxmodules/dxCommon.js";
import bus from "../../dxmodules/dxEventBus.js";
import dxMap from "../../dxmodules/dxMap.js";
import driver from "../driver.js";
import config from "../../dxmodules/dxConfig.js";
import sqliteService from "./sqliteService.js";
import std from "../../dxmodules/dxStd.js";
import pool from "../../dxmodules/dxWorkerPool.js";
let map = dxMap.get("LOGIN")
const faceService = {}
// è¯­éŸ³æ’­æ”¾é˜²æŠ–控制
let lastPlayTime = 0
const PLAY_DELAY = 2000 // 2秒内不重复播放相同语音
/**
 * è¯­éŸ³æ’­æ”¾é˜²æŠ–函数
 * @param {function} playFunction - æ’­æ”¾å‡½æ•°
 * @returns {boolean} æ˜¯å¦æ‰§è¡Œäº†æ’­æ”¾
 */
function debouncePlay(playFunction) {
    const now = Date.now()
    if (now - lastPlayTime > PLAY_DELAY) {
        lastPlayTime = now
        playFunction()
        return true
    }
    return false
}
// åŒäººè®¤è¯çŠ¶æ€ç®¡ç†
let dualAuthState = {
    firstUserId: null, // ç¬¬ä¸€ä¸ªç”¨æˆ·ID
    firstUserName: null, // ç¬¬ä¸€ä¸ªç”¨æˆ·å§“名
    firstUserFileName: null, // ç¬¬ä¸€ä¸ªç”¨æˆ·äººè„¸å›¾ç‰‡æ–‡ä»¶è·¯å¾„
    firstUserIdCard: null, // ç¬¬ä¸€ä¸ªç”¨æˆ·èº«ä»½è¯å·
    firstUserType: null, // ç¬¬ä¸€ä¸ªç”¨æˆ·èº«ä»½ç±»åž‹
    secondUserId: null, // ç¬¬äºŒä¸ªç”¨æˆ·ID
    secondUserName: null, // ç¬¬äºŒä¸ªç”¨æˆ·å§“名
    secondUserIdCard: null, // ç¬¬äºŒä¸ªç”¨æˆ·èº«ä»½è¯å·
    secondUserType: null, // ç¬¬äºŒä¸ªç”¨æˆ·èº«ä»½ç±»åž‹
    startTime: 0, // å¼€å§‹æ—¶é—´
    timeout: null, // å®šæ—¶å™¨
    authComplete: false // è®¤è¯æ˜¯å¦å®Œæˆ
}
/**
 * æŽ¥æ”¶äººè„¸è¯†åˆ«æ¶ˆæ¯å¹¶å¤„理
 * @param {object} data - äººè„¸è¯†åˆ«æ•°æ®
 */
faceService.receiveMsg = function (data) {
    logger.info('[faceService] receiveMsg :' + JSON.stringify(data))
    // è§¦å‘退出空闲状态事件(锁屏和息屏)
    bus.fire("exitIdle")
    switch (data.type) {
        case "register":
            // æ³¨å†Œäººè„¸å¤„理
            for (let i = 0; i < data.faces.length; i++) {
                const element = data.faces[i];
                bus.fire("beginAddFace", element)
            }
            break;
        case "compare":
            // äººè„¸æ¯”对处理
            // æ˜¾ç¤ºå§“名,代表有注册过人脸,但是权限不一定有效,需要进一步认证
            for (let i = 0; i < data.faces.length; i++) {
                const element = data.faces[i];
                // åªæœ‰åœ¨è®¤è¯æœªå®Œæˆæ—¶æ‰è§¦å‘trackResult事件
                if (!dualAuthState.authComplete) {
                    // åˆ¤æ–­å½“前是第几个用户(用于双人认证)
                    let userIndex = 1 // 0表示未知,1表示第一个用户,2表示第二个用户
                    if (!dualAuthState.firstUserId) {
                        // ç¬¬ä¸€ä¸ªç”¨æˆ·è¿˜æœªè®¾ç½®ï¼Œå½“前用户就是第一个用户
                        userIndex = 1
                    } else if (element.userId === dualAuthState.firstUserId) {
                        userIndex = 1
                    } else if (element.userId !== dualAuthState.firstUserId) {
                        userIndex = 2
                    }
                    logger.info('[faceService]: å‡†å¤‡è§¦å‘trackResult事件, userIndex=' + userIndex + ', result=' + element.result + ', userId=' + element.userId + ', authComplete=' + dualAuthState.authComplete + ', fileName=' + element.fileName)
                    // ç›´æŽ¥ä½¿ç”¨bus.fire触发trackResult事件
                    bus.fire("trackResult", { id: element.id, result: element.result, userId: element.userId, userIndex: userIndex, fileName: element.fileName })
                }
                if (element.result) {
                    // äººè„¸ç›¸ä¼¼åº¦éªŒè¯é€šè¿‡
                    let ret = sqliteService.d1_person.find({ userId: element.userId })
                    if (dxMap.get("UI").get("faceAuthStart") == "Y") {
                        // æ­£åœ¨äººè„¸ç™»å½•
                        if (JSON.parse(ret[0].extra).type != 0) {
                            bus.fire("faceAuthResult", true)
                        } else {
                            bus.fire("faceAuthResult", false)
                        }
                        return
                    }
                    // æ£€æŸ¥ç”¨æˆ·ç±»åž‹ï¼ˆç®¡ç†å‘˜çº§åˆ«ï¼‰
                    let userType = 0
                    try {
                        userType = JSON.parse(ret[0].extra).type || 0
                    } catch (error) {
                        logger.error("解析用户类型失败")
                    }
                    // ç®¡ç†å‘˜çº§åˆ«ç”¨æˆ·ï¼ˆtype != 0)直接认证通过
                    if (userType != 0) {
                        // æ¸…除双人认证状态
                        clearDualAuthState()
                        // æ ¹æ®è¯­éŸ³æ¨¡å¼æ’­æ”¾ç›¸åº”的语音
                        switch (config.get("face.voiceMode")) {
                            case 0:
                                // æ— è¯­éŸ³
                                break;
                            case 1:
                                // æ’­æ”¾åå­—
                                driver.alsa.ttsPlay(ret[0].name)
                                break;
                            case 2:
                                // æ’­æ”¾é—®å€™è¯­
                                driver.alsa.ttsPlay(config.get("face.voiceModeDate") ? config.get("face.voiceModeDate") : "欢迎光临")
                                break;
                            default:
                                break;
                        }
                        // é€šè¡Œè®¤è¯å¤„理
                        bus.fire("access", { data: { type: "300", code: element.userId }, fileName: element.fileName })
                    } else {
                        // éžç®¡ç†å‘˜çº§åˆ«ç”¨æˆ·ï¼Œéœ€è¦åŒäººè®¤è¯
                        handleDualAuth(element.userId, ret[0].name, element.fileName)
                    }
                } else {
                    // äººè„¸ç›¸ä¼¼åº¦éªŒè¯å¤±è´¥
                    if (dxMap.get("UI").get("faceAuthStart") == "Y") {
                        // äººè„¸ç™»å½•失败
                        bus.fire("faceAuthResult", false)
                    } else {
                        // é™Œç”Ÿäººå¤„理
                        switch (config.get("face.stranger")) {
                            case 0:
                                // æ— è¯­éŸ³
                                break;
                            case 1:
                                // æ’­æ”¾è¯·å…ˆæ³¨å†Œï¼ˆé˜²æŠ–控制)
                                debouncePlay(() => {
                                    driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/register.wav`)
                                })
                                break;
                            case 2:
                                // æ’­æ”¾é™Œç”Ÿäººä½ å¥½ï¼ˆé˜²æŠ–控制)
                                debouncePlay(() => {
                                    driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/stranger.wav`)
                                })
                                break;
                            default:
                                break;
                        }
                        // é€šè¡Œè®¤è¯å¤„理(相似度验证失败)
                        bus.fire("access", { data: { type: "300", code: element.userId }, fileName: element.fileName, similarity: false })
                    }
                }
            }
            break;
        default:
            break;
    }
}
// ç›‘听通行解锁完成事件,重置双人认证状态
bus.on('accessUnlockComplete', () => {
    logger.info('[faceService]: accessUnlockComplete事件触发,重置双人认证状态')
    clearDualAuthState()
})
/**
 * æ¸…除双人认证状态
 */
function clearDualAuthState() {
    dualAuthState.firstUserId = null
    dualAuthState.firstUserName = null
    dualAuthState.firstUserFileName = null
    dualAuthState.firstUserIdCard = null
    dualAuthState.firstUserType = null
    dualAuthState.secondUserId = null
    dualAuthState.secondUserName = null
    dualAuthState.secondUserIdCard = null
    dualAuthState.secondUserType = null
    dualAuthState.startTime = 0
    dualAuthState.authComplete = false // é‡ç½®è®¤è¯å®Œæˆæ ‡å¿—
    if (dualAuthState.timeout) {
        std.clearTimeout(dualAuthState.timeout)
        dualAuthState.timeout = null
    }
}
/**
 * å¤„理双人认证超时
 */
function handleDualAuthTimeout() {
    try {
        // ä¿å­˜è®¤è¯è®°å½•
        if (dualAuthState.firstUserId && !dualAuthState.authComplete) {
            // æŸ¥è¯¢ç¬¬ä¸€ç”¨æˆ·çš„详细信息,获取身份类型
            let firstUserType = dualAuthState.firstUserType || 0
            if (!firstUserType) {
                let res = sqliteService.d1_person.findByUserId(dualAuthState.firstUserId)
                if (res.length > 0) {
                    try {
                        firstUserType = JSON.parse(res[0].extra).type || 0
                    } catch (error) {
                        logger.error("无第一用户类型")
                    }
                }
            }
            // æŸ¥è¯¢ç¬¬äºŒç”¨æˆ·çš„详细信息,获取身份类型
            let secondUserType = dualAuthState.secondUserType || 0
            if (dualAuthState.secondUserId && !secondUserType) {
                let res = sqliteService.d1_person.findByUserId(dualAuthState.secondUserId)
                if (res.length > 0) {
                    try {
                        secondUserType = JSON.parse(res[0].extra).type || 0
                    } catch (error) {
                        logger.error("无第二用户类型")
                    }
                }
            }
            // åˆ›å»ºè®¤è¯è®°å½•
            const record = {
                id: std.genRandomStr(16),
                userId: dualAuthState.firstUserId,
                userId2: dualAuthState.secondUserId || "",
                type: "300", // äººè„¸è®¤è¯ï¼ˆå­—符串格式)
                code: dualAuthState.firstUserFileName, // äººè„¸æŠ“拍图片路径
                result: 1, // è®¤è¯å¤±è´¥ï¼ˆ0成功,1失败)
                time: Math.floor(Date.now() / 1000), // æ—¶é—´æˆ³ï¼ˆç§’)
                message: "双人认证超时",
                extra: JSON.stringify({ name: dualAuthState.firstUserName, idCard: dualAuthState.firstUserIdCard, type: firstUserType }),
                extra2: dualAuthState.secondUserId ? JSON.stringify({ name: dualAuthState.secondUserName || "", idCard: dualAuthState.secondUserIdCard || "", type: secondUserType }) : "",
                permissionId: firstUserType.toString(),
                permissionId2: dualAuthState.secondUserId ? secondUserType.toString() : ""
            }
            // ä¿å­˜åˆ°æ•°æ®åº“
            sqliteService.d1_pass_record.save(record)
            logger.info(`[faceService]: ä¿å­˜åŒäººè®¤è¯è¶…时记录: ${JSON.stringify(record)}`)
        }
        // æ¸…除认证状态
        clearDualAuthState()
        // æ’­æ”¾å¤±è´¥æç¤ºéŸ³
        driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/verify_300_f.wav`)
        // è¯­éŸ³æç¤ºè®¤è¯å¤±è´¥
        // driver.alsa.ttsPlay("双人认证超时,认证失败")
        // è§¦å‘认证失败事件
        bus.fire("showAccessResult", {
            faceAuth: false,
            gasConcentration: true,
            accessAllowed: false,
            message: "*双人认证超时,认证失败*"
        })
        // è§¦å‘通行失败事件,通知UI重置
        bus.fire("accessRes", false)
    } catch (error) {
        logger.error(`[faceService]: å¤„理双人认证超时错误: ${error.message}`)
    }
}
/**
 * å¤„理双人认证逻辑
 * @param {string} userId - å½“前用户ID
 * @param {string} userName - å½“前用户姓名
 * @param {string} fileName - äººè„¸å›¾ç‰‡æ–‡ä»¶è·¯å¾„
 */
function handleDualAuth(userId, userName, fileName) {
    try {
        if (!dualAuthState.firstUserId) {
            // ç¬¬ä¸€æ¬¡è®¤è¯
            dualAuthState.firstUserId = userId
            dualAuthState.firstUserName = userName
            dualAuthState.firstUserFileName = fileName
            // æŸ¥è¯¢ç¬¬ä¸€ç”¨æˆ·çš„详细信息,获取身份证号和身份类型
            let res = sqliteService.d1_person.findByUserId(userId)
            if (res.length > 0) {
                let idCard
                let userType = 0
                try {
                    idCard = JSON.parse(res[0].extra).idCard
                    userType = JSON.parse(res[0].extra).type || 0
                } catch (error) {
                    logger.error("无第一用户身份证号或类型")
                }
                dualAuthState.firstUserIdCard = idCard
                dualAuthState.firstUserType = userType
            }
            dualAuthState.startTime = Date.now()
            // æ¸…除之前的定时器
            if (dualAuthState.timeout) {
                std.clearTimeout(dualAuthState.timeout)
            }
            // è®¾ç½®1分钟超时
            dualAuthState.timeout = std.setTimeout(() => {
                // è¶…时处理,视为认证失败
                handleDualAuthTimeout()
            }, 60000)
            // è¯­éŸ³æç¤º-请第二用户进行认证
            driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/user2.wav`)
        } else {
            // ç¬¬äºŒæ¬¡è®¤è¯
            if (userId !== dualAuthState.firstUserId) {
                // ä¸åŒç”¨æˆ·ï¼Œè®¤è¯é€šè¿‡
                // æŸ¥è¯¢ç¬¬äºŒç”¨æˆ·çš„详细信息,获取身份证号和身份类型
                let secondUserIdCard = ""
                let secondUserType = 0
                let res = sqliteService.d1_person.findByUserId(userId)
                if (res.length > 0) {
                    try {
                        secondUserIdCard = JSON.parse(res[0].extra).idCard
                        secondUserType = JSON.parse(res[0].extra).type || 0
                    } catch (error) {
                        logger.error("无第二用户身份证号或类型")
                    }
                }
                // ä¿å­˜åŒäººè®¤è¯çŠ¶æ€ä¿¡æ¯ï¼Œç”¨äºŽUI更新
                let dualAuthInfo = {
                    firstUserId: dualAuthState.firstUserId,
                    firstUserName: dualAuthState.firstUserName,
                    secondUserId: userId,
                    secondUserName: userName,
                    secondUserIdCard: secondUserIdCard,
                    secondUserType: secondUserType
                }
                // ä¿å­˜ç¬¬ä¸€ç”¨æˆ·çš„人脸图片路径
                let firstUserFileName = dualAuthState.firstUserFileName
                // è®¾ç½®è®¤è¯å®Œæˆæ ‡å¿—
                dualAuthState.authComplete = true
                clearDualAuthState()
                // è¯­éŸ³æç¤º-双人认证通过
                driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/user2_s.wav`)
                // é€šè¡Œè®¤è¯å¤„理,传递双人认证信息
                bus.fire("access", { data: { type: "300", code: userId, dualAuthInfo: dualAuthInfo, authComplete: true, firstUserFileName: firstUserFileName }, fileName: fileName })
            } else {
                // åŒä¸€ç”¨æˆ·ï¼Œé‡æ–°å¼€å§‹
                dualAuthState.firstUserId = userId
                dualAuthState.firstUserName = userName
                // æŸ¥è¯¢ç”¨æˆ·çš„详细信息,获取身份证号和身份类型
                let idCard
                let userType = 0
                let res = sqliteService.d1_person.findByUserId(userId)
                if (res.length > 0) {
                    try {
                        idCard = JSON.parse(res[0].extra).idCard
                        userType = JSON.parse(res[0].extra).type || 0
                    } catch (error) {
                        logger.error("无用户身份证号或类型")
                    }
                    dualAuthState.firstUserIdCard = idCard
                    dualAuthState.firstUserType = userType
                }
                dualAuthState.startTime = Date.now()
                // æ¸…除之前的定时器
                if (dualAuthState.timeout) {
                    std.clearTimeout(dualAuthState.timeout)
                }
                // é‡æ–°è®¾ç½®1分钟超时
                dualAuthState.timeout = std.setTimeout(() => {
                    // è¶…时处理,视为认证失败
                    handleDualAuthTimeout()
                }, 60000)
                // è¯­éŸ³æç¤º-请第二用户进行认证
                driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/user2.wav`)
            }
        }
    } catch (error) {
        logger.error(`[faceService]: å¤„理双人认证错误: ${error.message}`)
        // æ¸…除认证状态
        clearDualAuthState()
    }
}
/**
 * äººè„¸æ³¨å†Œé”™è¯¯æžšä¸¾
 * åŒ…含各种注册失败的错误码和对应的错误信息
 */
faceService.regErrorEnum = {
    "callback": {
        title: "注册回调状态枚举",
        "-1": "faceService.contrastFailure",        // å¯¹æ¯”失败
        "-2": "faceService.scalingFailure",         // ç¼©æ”¾å¤±è´¥
        "-3": "faceService.failedToSavePicture",    // ä¿å­˜å›¾ç‰‡å¤±è´¥
        "-4": "faceService.convertToBase64Fail",    // è½¬æ¢ä¸ºbase64失败
    },
    "feature": {
        title: "特征值注册状态枚举",
        "-1": "faceService.base64DecodingFail",     // base64解码失败
        "-10": "faceService.contrastFailure",       // å¯¹æ¯”失败
        "-11": "faceService.similarityOverheight",  // ç›¸ä¼¼åº¦è¿‡é«˜
    },
    "picture": {
        title: "图片注册状态枚举",
        "-1": "faceService.fileDoesNotExist",               // æ–‡ä»¶ä¸å­˜åœ¨
        "-2": "faceService.theImageFormatIsNotSupported",    // å›¾ç‰‡æ ¼å¼ä¸æ”¯æŒ
        "-3": "faceService.pictureReadFailure",              // å›¾ç‰‡è¯»å–失败
        "-4": "faceService.thePictureSizeDoesNotMatch",      // å›¾ç‰‡å°ºå¯¸ä¸åŒ¹é…
        "-5": "faceService.imageParsingFailure",             // å›¾ç‰‡è§£æžå¤±è´¥
        "-6": "faceService.imageYUVProcessingFailed",        // å›¾ç‰‡YUV处理失败
        "-7": "faceService.failedToConvertJpegToImage",      // JPEG转换为图片失败
        "-8": "faceService.faceInformationExtractionFailed", // äººè„¸ä¿¡æ¯æå–失败
        "-9": "faceService.theFaceIsNotUnique",              // äººè„¸ä¸å”¯ä¸€
        "-10": "faceService.contrastFailure",                // å¯¹æ¯”失败
        "-11": "faceService.similarityOverheight",           // ç›¸ä¼¼åº¦è¿‡é«˜
    }
}
export default faceService
vf205_access/src/service/gpiokeyService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
/**
 * GPIO按键服务模块
 * å¤„理GPIO按键相关的业务逻辑,主要包括防拆报警功能
 */
import logger from "../../dxmodules/dxLogger.js";
import config from "../../dxmodules/dxConfig.js";
import driver from "../driver.js";
import bus from "../../dxmodules/dxEventBus.js";
const gpiokeyService = {}
/**
 * æŽ¥æ”¶GPIO按键消息并处理
 * @param {object} data - GPIO按键数据
 * @param {number} data.code - GPIO的标识,表示是哪个GPIO有输入  256-防拆,257-门磁
 * @param {number} data.type - æŒ‰é”®ç±»åž‹ï¼Œ1表示防拆/门磁状态变化
 * @param {number} data.value - æŒ‰é”®å€¼ï¼Œ1表示触发/门开,0表示未触发/门关
 */
gpiokeyService.receiveMsg = function (data) {
    logger.info('[gpiokeyService] æŽ¥æ”¶åˆ°GPIO消息:', JSON.stringify(data))
    // é˜²æ‹†æŠ¥è­¦å¤„理
    if (data.code === 256) {
        if (config.get("access.tamperAlarm") && data.type == 1 && data.value == 1) {
            logger.info('[gpiokeyService] é˜²æ‹†æŠ¥è­¦è§¦å‘')
            driver.alsa.play("/app/code/resource/wav/alarm.wav")
        }
    }
    // é—¨ç£çŠ¶æ€å˜åŒ–å¤„ç†
    if (data.code === 257) {
        const doorStatus = data.value == 1 ? "门开" : "门关"
        logger.info(`[gpiokeyService] é—¨ç£çŠ¶æ€å˜åŒ–: ${doorStatus}`)
        if (data.value === 1) {
            // é—¨å¼€
            driver.alsa.play("/app/code/resource/CN/wav/door_open.wav")
        } else {
            // é—¨å…³
            driver.alsa.play("/app/code/resource/CN/wav/door_close.wav")
        }
        // å‘送门磁状态变化通知事件
        bus.fire('doorStatusChanged', {
            status: data.value,
            statusText: doorStatus,
            time: Math.floor(Date.now() / 1000)
        })
    }
}
export default gpiokeyService
vf205_access/src/service/grainService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,676 @@
/**
 * ç²®æƒ…服务模块
 * è´Ÿè´£æ°”体浓度数据获取和状态信息数据获取
 */
import logger from "../../dxmodules/dxLogger.js"
import config from "../../dxmodules/dxConfig.js"
import http from "../../dxmodules/dxHttp.js"
import bus from '../../dxmodules/dxEventBus.js'
import std from "../../dxmodules/dxStd.js"
import driver from "../driver.js"
const grainService = {}
// ä»Žé…ç½®ä¸­èŽ·å–ä¸šåŠ¡ç¼–ç å®šä¹‰
const functionId = {
    gasDetection: config.get('functionId.gasDetection') || "1000",
    safeInputControl: config.get('functionId.safeInputControl') || "2000",
    lightControl: config.get('functionId.lightControl') || "3000",
    doorStatus: config.get('functionId.doorStatus') || "4000"
}
// ä»Žé…ç½®ä¸­èŽ·å–æŽ¥å£è¿”å›žç¼–ç 
const respCode = {
    success: config.get('respCode.success') || "200",
    badRequest: config.get('respCode.badRequest') || "400",
    unauthorized: config.get('respCode.unauthorized') || "401",
    forbidden: config.get('respCode.forbidden') || "403",
    notFound: config.get('respCode.notFound') || "404",
    serverError: config.get('respCode.serverError') || "500"
}
// é”™è¯¯ç å¯¹åº”的错误信息
const errorMessages = {
    "400": "请求参数有误",
    "401": "未登录,用户不存在",
    "403": "用户无权限",
    "404": "请求功能不存在",
    "500": "请求执行异常,请联系管理员"
}
// æ“ä½œæŒ‰é’®å¯¹åº”的语音文件
const voiceFiles = {
    // å…è®¸è¿›ä»“模式
    "11": "btn11.wav", // å…è®¸è¿›ä»“模式
    "12": "btn12.wav", // å…¥ä»“
    "13": "btn13.wav", // å‡ºä»“
    // å†¬å­£é€šé£Žæ¨¡å¼
    "21": "btn21.wav", // å†¬å­£é€šé£Žæ¨¡å¼
    "22": "btn22.wav", // å¯åЍ
    "23": "btn23.wav", // å…³é—­
    // ç¦æ­¢è¿›ä»“模式
    "31": "btn31.wav", // ç¦æ­¢è¿›ä»“模式
    "32": "btn32.wav", // ç´§æ€¥å…¥ä»“
    "33": "btn33.wav", // å‡ºä»“
    // ç…§æ˜ŽæŽ§åˆ¶
    "light_open": "light_open.wav", // å¼€ç¯
    "light_close": "light_close.wav" // å…³ç¯
}
/**
 * æ ¹æ®é”™è¯¯ç èŽ·å–é”™è¯¯ä¿¡æ¯
 * @param {string} code - é”™è¯¯ç 
 * @returns {string} é”™è¯¯ä¿¡æ¯
 */
function getErrorMessage(code) {
    return errorMessages[code] || "操作失败"
}
/**
 * æ ¹æ®æ“ä½œæŒ‰é’®å’ŒåŠŸèƒ½ç èŽ·å–è¯­éŸ³æ–‡ä»¶
 * @param {string} functionId - åŠŸèƒ½ç 
 * @param {object} params - è¯·æ±‚参数,包含mode和btn
 * @returns {string} è¯­éŸ³æ–‡ä»¶è·¯å¾„
 */
function getVoiceFile(functionId, params) {
    // ç…§æ˜ŽæŽ§åˆ¶åŠŸèƒ½
    if (functionId === "3000") {
        // å¼€ç¯
        if (String(params.btn) === "1") return voiceFiles["light_open"]
        // å…³ç¯
        if (String(params.btn) === "2") return voiceFiles["light_close"]
    }
    // å®‰å…¨å…¥ä»“控制功能
    if (functionId === "2000" && params) {
        const { mode, btn } = params
        // æ¨¡å¼1: å…è®¸è¿›ä»“模式
        if (mode === 1) {
            if (!btn) return voiceFiles["11"] // å…è®¸è¿›ä»“模式按钮
            if (String(btn) === "1") return voiceFiles["12"] // å…¥ä»“按钮
            if (String(btn) === "2") return voiceFiles["13"] // å‡ºä»“按钮
        }
        // æ¨¡å¼2: å†¬å­£é€šé£Žæ¨¡å¼
        if (mode === 2) {
            if (!btn) return voiceFiles["21"] // å†¬å­£é€šé£Žæ¨¡å¼æŒ‰é’®
            if (String(btn) === "1") return voiceFiles["22"] // å¯åŠ¨æŒ‰é’®
            if (String(btn) === "2") return voiceFiles["23"] // å…³é—­æŒ‰é’®
        }
        // æ¨¡å¼3: ç¦æ­¢è¿›ä»“模式
        if (mode === 3) {
            if (!btn) return voiceFiles["31"] // ç¦æ­¢è¿›ä»“模式按钮
            if (String(btn) === "1") return voiceFiles["32"] // ç´§æ€¥å…¥ä»“按钮
            if (String(btn) === "2") return voiceFiles["33"] // å‡ºä»“按钮
        }
    }
    return null
}
// ä»Žé…ç½®ä¸­èŽ·å–HTTP接口路径
function getHttpUrl() {
    return config.get('http.safeInputAccess') || "http://192.168.1.199:80/cgi-bin/safeInputAccess"
}
// å­˜å‚¨æ°”体浓度数据
let gasDataStorage = null
/**
 * æ£€æŸ¥æ°”体浓度
 * @param {function} callback - å›žè°ƒå‡½æ•°ï¼Œåœ¨æ°”体浓度检测完成后调用
 * @returns {boolean} true表示气体浓度合格,false表示气体浓度不合格
 */
grainService.checkGasConcentration = function(callback) {
    // ä½¿ç”¨setTimeout将HTTP请求放入后台执行,避免阻塞主线程
    std.setTimeout(() => {
        try {
            // ç¡®ä¿URL格式正确,添加http://前缀
            let url = getHttpUrl()
            if (!url.startsWith('http://') && !url.startsWith('https://')) {
                url = 'http://' + url
            }
            const timeout = 3000 // 3秒超时
            logger.info(`[grain]: æ­£åœ¨èŽ·å–æ°”ä½“æµ“åº¦æ•°æ®: ${url}`)
            // æž„建POST请求数据
            const postData = {
                sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                houseId: "0000", // ä»“廒编码,默认填充"0000"
                outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                functionId: functionId.gasDetection, // æ°”体浓度检测功能码
                timestamp: Date.now().toString() // æ—¶é—´æˆ³
            }
            logger.info(`[grain]: å‘送POST请求数据: ${JSON.stringify(postData)}`)
            // å‘送HTTP POST请求
            let response = http.post(url, postData, timeout)
            // logger.info(`[grain]: æ°”体浓度数据响应: ${JSON.stringify(response)}`)
            // è§£æžå“åº”数据
            // æ£€æŸ¥response是否为字符串,如果是则解析为对象
            if (typeof response === 'string') {
                response = JSON.parse(response)
            }
            if (response && response.body) {
                // è§£æžå“åº”体
                let gasData
                try {
                    gasData = JSON.parse(response.body)
                    logger.info(`[grain]: è§£æžåŽçš„æ°”体浓度数据: ${JSON.stringify(gasData)}`)
                    // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                    if (gasData.respCode === respCode.success) {
                        logger.info(`[grain]: æ°”体浓度检测接口调用成功`)
                    } else {
                        logger.error(`[grain]: æ°”体浓度检测接口调用失败,返回编码: ${gasData.respCode}, è¿”回信息: ${gasData.respMsg}`)
                    }
                } catch (parseError) {
                    logger.error(`[grain]: è§£æžæ°”体浓度数据失败: ${parseError.message}`)
                    // å°è¯•清理响应体中的转义字符
                    try {
                        // ç§»é™¤å¤šä½™çš„转义字符
                        const cleanedBody = response.body.replace(/\\r\\n/g, '').replace(/\\t/g, '').replace(/\"/g, '"')
                        gasData = JSON.parse(cleanedBody)
                        logger.info(`[grain]: æ¸…理后解析的气体浓度数据: ${JSON.stringify(gasData)}`)
                        // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                        if (gasData.respCode === respCode.success) {
                            logger.info(`[grain]: æ°”体浓度检测接口调用成功`)
                        } else {
                            logger.error(`[grain]: æ°”体浓度检测接口调用失败,返回编码: ${gasData.respCode}, è¿”回信息: ${gasData.respMsg}`)
                        }
                    } catch (cleanError) {
                        logger.error(`[grain]: æ¸…理后解析气体浓度数据仍失败: ${cleanError.message}`)
                        // è°ƒç”¨å›žè°ƒå‡½æ•°
                        if (callback) {
                            callback()
                        }
                        return
                    }
                }
                // å­˜å‚¨æ°”体数据
                gasDataStorage = gasData
                // æ£€æŸ¥æ°”体浓度是否合格
                // æ–°æ ¼å¼: {"value":[{"o2":"20.5","statusO2":"0","co2":"401","statusCo2":"0","ph3":"0","statusPh3":"0"}],"status":"1"}
                // statusO2/statusCo2/statusPh3: "0"表示合格,"1"表示不合格(仅用于UI显示)
                // data.status: "0"表示最终合格,"1"表示最终不合格
                const gasValue = gasData.data && gasData.data.value ? gasData.data.value[0] : null
                const isO2Qualified = gasValue && gasValue.statusO2 === "0"
                const isCo2Qualified = gasValue && gasValue.statusCo2 === "0"
                const isPh3Qualified = gasValue && gasValue.statusPh3 === "0"
                // è§¦å‘事件更新UI
                bus.fire('gasConcentrationUpdated', gasData)
                // æœ€ç»ˆåˆæ ¼åˆ¤æ–­åªä¾èµ–status字段值
                const isAllQualified = gasData.data && gasData.data.status === "0"
                if (isAllQualified) {
                    logger.info("[grain]: æ°”体浓度检测合格")
                } else {
                    logger.info("[grain]: æ°”体浓度检测不合格")
                }
                // è°ƒç”¨å›žè°ƒå‡½æ•°
                if (callback) {
                    callback()
                }
            } else {
                logger.error(`[grain]: æ°”体浓度数据获取失败: ${response ? "无响应体" : "无响应"}`)
                // è°ƒç”¨å›žè°ƒå‡½æ•°
                if (callback) {
                    callback()
                }
            }
        } catch (error) {
            logger.error(`[grain]: æ°”体浓度检测错误: ${error.message}`)
            // è°ƒç”¨å›žè°ƒå‡½æ•°
            if (callback) {
                callback()
            }
        }
    }, 0)
    // ç«‹å³è¿”回,避免阻塞主线程
    return true
}
/**
 * èŽ·å–å­˜å‚¨çš„æ°”ä½“æµ“åº¦æ•°æ®
 * @returns {object|null} å­˜å‚¨çš„æ°”体浓度数据,如果没有则返回null
 */
grainService.getGasData = function() {
    return gasDataStorage
}
/**
 * æ£€æŸ¥è®¾å¤‡çŠ¶æ€ä¿¡æ¯
 * @param {object} params - è¯·æ±‚参数
 * @param {string} params.user1 - äººè„¸è¯†åˆ«ç”¨æˆ·1
 * @param {string} params.user2 - äººè„¸è¯†åˆ«ç”¨æˆ·2
 * @param {string} params.mode - é€‰æ‹©æ¨¡å¼ï¼ˆ1-允许入仓;2-冬季通风;3-禁止入仓)
 * @param {string} params.btn - æ“ä½œæŒ‰é’®ï¼ˆ1-入仓/开启;2-出仓/关闭)
 * @returns {boolean} true表示状态信息获取成功,false表示失败
 */
grainService.checkDevConcentration = function(params) {
    // ä½¿ç”¨setTimeout将HTTP请求放入后台执行,避免阻塞主线程
    std.setTimeout(() => {
        try {
            // ç¡®ä¿URL格式正确,添加http://前缀
            let url = getHttpUrl()
            if (!url.startsWith('http://') && !url.startsWith('https://')) {
                url = 'http://' + url
            }
            const timeout = 3000 // 3秒超时
            // logger.info(`[grain]: æ­£åœ¨èŽ·å–çŠ¶æ€ä¿¡æ¯æ•°æ®: ${url}`)
            // æž„建POST请求数据
            const postData = {
                sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                houseId: "0000", // ä»“廒编码,默认填充"0000"
                outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                functionId: params && params.functionId ? params.functionId : functionId.safeInputControl, // åŠŸèƒ½ç ï¼Œé»˜è®¤ä¸ºå®‰å…¨å…¥ä»“è”åŠ¨æŽ§åˆ¶
                timestamp: Date.now().toString(), // æ—¶é—´æˆ³
                data: {}
            }
            // æ·»åŠ å¯é€‰å‚æ•°
            if (params) {
                if (params.user1) postData.data.user1 = params.user1
                if (params.user2) postData.data.user2 = params.user2
                if (params.mode) postData.data.mode = params.mode
                if (params.btn) postData.data.btn = params.btn
            }
            logger.info(`[grain]: å‘送POST请求数据: ${JSON.stringify(postData)}`)
            // å‘送HTTP POST请求
            let response = http.post(url, postData, timeout)
            // logger.info(`[grain]: çŠ¶æ€ä¿¡æ¯æ•°æ®å“åº”: ${JSON.stringify(response)}`)
            // è§£æžå“åº”数据
            // æ£€æŸ¥response是否为字符串,如果是则解析为对象
            if (typeof response === 'string') {
                response = JSON.parse(response)
            }
            if (response && response.body) {
                // è§£æžå“åº”体
                let statusData
                try {
                    statusData = JSON.parse(response.body)
                    logger.info(`[grain]: è§£æžåŽçš„状态信息数据: ${JSON.stringify(statusData)}`)
                    // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                    if (statusData.respCode === respCode.success) {
                        logger.info(`[grain]: çŠ¶æ€ä¿¡æ¯æŽ¥å£è°ƒç”¨æˆåŠŸ`)
                        // æ£€æŸ¥data是否为空,不为空才触发通知
                        if (Object.keys(postData.data).length > 0) {
                            // è§¦å‘成功弹窗
                            bus.fire('showAccessResult', {
                                faceAuth: true,
                                gasConcentration: true,
                                accessAllowed: true,
                                message: '*执行成功*'
                            })
                            // æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º
                            try {
                                const voiceFile = getVoiceFile(postData.functionId, postData.data)
                                if (voiceFile) {
                                    driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/${voiceFile}`)
                                    logger.info(`[grain]: æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º: ${voiceFile}`)
                                }
                            } catch (error) {
                                logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                            }
                        }
                    } else {
                        logger.error(`[grain]: çŠ¶æ€ä¿¡æ¯æŽ¥å£è°ƒç”¨å¤±è´¥ï¼Œè¿”å›žç¼–ç : ${statusData.respCode}, è¿”回信息: ${statusData.respMsg}`)
                        // æ£€æŸ¥data是否为空,不为空才触发通知
                        if (Object.keys(postData.data).length > 0) {
                            // è§¦å‘失败弹窗
                            bus.fire('showAccessResult', {
                                faceAuth: true,
                                gasConcentration: true,
                                accessAllowed: false,
                                message: statusData.respMsg || getErrorMessage(statusData.respCode)
                            })
                            // æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º
                            try {
                                driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/failed.wav`)
                                logger.info(`[grain]: æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º: failed.wav`)
                            } catch (error) {
                                logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                            }
                        }
                    }
                } catch (parseError) {
                    logger.error(`[grain]: è§£æžçŠ¶æ€ä¿¡æ¯æ•°æ®å¤±è´¥: ${parseError.message}`)
                    // å°è¯•清理响应体中的转义字符
                    try {
                        // ç§»é™¤å¤šä½™çš„转义字符
                        const cleanedBody = response.body.replace(/\\r\\n/g, '').replace(/\\t/g, '').replace(/\"/g, '"')
                        statusData = JSON.parse(cleanedBody)
                        logger.info(`[grain]: æ¸…理后解析的状态信息数据: ${JSON.stringify(statusData)}`)
                        // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                        if (statusData.respCode === respCode.success) {
                            logger.info(`[grain]: çŠ¶æ€ä¿¡æ¯æŽ¥å£è°ƒç”¨æˆåŠŸ`)
                            // æ£€æŸ¥data是否为空,不为空才触发通知
                            if (Object.keys(postData.data).length > 0) {
                                // è§¦å‘成功弹窗
                                bus.fire('showAccessResult', {
                                    faceAuth: true,
                                    gasConcentration: true,
                                    accessAllowed: true,
                                    message: '*执行成功*'
                                })
                                // æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º
                                try {
                                    const voiceFile = getVoiceFile(postData.functionId, postData.data)
                                    if (voiceFile) {
                                        driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/${voiceFile}`)
                                        logger.info(`[grain]: æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º: ${voiceFile}`)
                                    }
                                } catch (error) {
                                    logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                                }
                            }
                        } else {
                            logger.error(`[grain]: çŠ¶æ€ä¿¡æ¯æŽ¥å£è°ƒç”¨å¤±è´¥ï¼Œè¿”å›žç¼–ç : ${statusData.respCode}, è¿”回信息: ${statusData.respMsg}`)
                            // æ£€æŸ¥data是否为空,不为空才触发通知
                            if (Object.keys(postData.data).length > 0) {
                                // è§¦å‘失败弹窗
                                bus.fire('showAccessResult', {
                                    faceAuth: true,
                                    gasConcentration: true,
                                    accessAllowed: false,
                                    message: statusData.respMsg || getErrorMessage(statusData.respCode)
                                })
                                // æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º
                                try {
                                    driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/failed.wav`)
                                    logger.info(`[grain]: æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º: failed.wav`)
                                } catch (error) {
                                    logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                                }
                            }
                        }
                    } catch (cleanError) {
                        logger.error(`[grain]: æ¸…理后解析状态信息数据仍失败: ${cleanError.message}`)
                        return false
                    }
                }
                // è§¦å‘事件更新UI
                bus.fire('statusInfoUpdated', statusData)
                return true
            } else {
                logger.error(`[grain]: çŠ¶æ€ä¿¡æ¯æ•°æ®èŽ·å–å¤±è´¥: ${response ? "无响应体" : "无响应"}`)
                // ç½‘络请求失败时,默认返回false(安全起见)
                return false
            }
        } catch (error) {
            logger.error(`[grain]: çŠ¶æ€ä¿¡æ¯æ£€æµ‹é”™è¯¯: ${error.message}`)
            // å‘生错误时,默认返回false(安全起见)
            return false
        }
    }, 0)
    // ç«‹å³è¿”回,避免阻塞主线程
    return true
}
/**
 * ä»“内照明联动控制
 * @param {object} params - è¯·æ±‚参数
 * @param {string} params.user1 - äººè„¸è¯†åˆ«ç”¨æˆ·1
 * @param {string} params.user2 - äººè„¸è¯†åˆ«ç”¨æˆ·2
 * @param {string} params.btn - æ“ä½œæŒ‰é’®ï¼ˆ1-开灯;2-关灯)
 * @returns {boolean} true表示控制成功,false表示控制失败
 */
grainService.controlLight = function(params) {
    // ä½¿ç”¨setTimeout将HTTP请求放入后台执行,避免阻塞主线程
    std.setTimeout(() => {
        try {
            // ç¡®ä¿URL格式正确,添加http://前缀
            let url = getHttpUrl()
            if (!url.startsWith('http://') && !url.startsWith('https://')) {
                url = 'http://' + url
            }
            const timeout = 3000 // 3秒超时
            // æž„建POST请求数据
            const postData = {
                sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                houseId: "0000", // ä»“廒编码,默认填充"0000"
                outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                functionId: functionId.lightControl, // ä»“内照明联动控制功能码
                timestamp: Date.now().toString(), // æ—¶é—´æˆ³
                data: {}
            }
            // æ·»åŠ å¯é€‰å‚æ•°
            if (params) {
                if (params.user1) postData.data.user1 = params.user1
                if (params.user2) postData.data.user2 = params.user2
                if (params.btn) postData.data.btn = params.btn
            }
            logger.info(`[grain]: å‘送仓内照明联动控制请求数据: ${JSON.stringify(postData)}`)
            // å‘送HTTP POST请求
            let response = http.post(url, postData, timeout)
            // è§£æžå“åº”数据
            // æ£€æŸ¥response是否为字符串,如果是则解析为对象
            if (typeof response === 'string') {
                response = JSON.parse(response)
            }
            if (response && response.body) {
                // è§£æžå“åº”体
                let statusData
                try {
                    statusData = JSON.parse(response.body)
                    logger.info(`[grain]: è§£æžåŽçš„仓内照明联动控制响应数据: ${JSON.stringify(statusData)}`)
                    // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                    if (statusData.respCode === respCode.success) {
                        logger.info(`[grain]: ä»“内照明联动控制接口调用成功`)
                        // è§¦å‘成功弹窗
                        bus.fire('showAccessResult', {
                            faceAuth: true,
                            gasConcentration: true,
                            accessAllowed: true,
                            message: '*执行成功*'
                        })
                        // æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º
                        try {
                            const voiceFile = getVoiceFile(postData.functionId, postData.data)
                            if (voiceFile) {
                                driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/${voiceFile}`)
                                logger.info(`[grain]: æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º: ${voiceFile}`)
                            }
                        } catch (error) {
                            logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                        }
                    } else {
                        logger.error(`[grain]: ä»“内照明联动控制接口调用失败,返回编码: ${statusData.respCode}, è¿”回信息: ${statusData.respMsg}`)
                        // è§¦å‘失败弹窗
                        bus.fire('showAccessResult', {
                            faceAuth: true,
                            gasConcentration: true,
                            accessAllowed: false,
                            message: statusData.respMsg || getErrorMessage(statusData.respCode)
                        })
                        // æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º
                        try {
                            driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/failed.wav`)
                            logger.info(`[grain]: æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º: failed.wav`)
                        } catch (error) {
                            logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                        }
                    }
                } catch (parseError) {
                    logger.error(`[grain]: è§£æžä»“内照明联动控制响应数据失败: ${parseError.message}`)
                    // å°è¯•清理响应体中的转义字符
                    try {
                        // ç§»é™¤å¤šä½™çš„转义字符
                        const cleanedBody = response.body.replace(/\\r\\n/g, '').replace(/\\t/g, '').replace(/\"/g, '"')
                        statusData = JSON.parse(cleanedBody)
                        logger.info(`[grain]: æ¸…理后解析的仓内照明联动控制响应数据: ${JSON.stringify(statusData)}`)
                        // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                        if (statusData.respCode === respCode.success) {
                            logger.info(`[grain]: ä»“内照明联动控制接口调用成功`)
                            // è§¦å‘成功弹窗
                            bus.fire('showAccessResult', {
                                faceAuth: true,
                                gasConcentration: true,
                                accessAllowed: true,
                                message: '*执行成功*'
                            })
                            // æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º
                            try {
                                const voiceFile = getVoiceFile(postData.functionId, postData.data)
                                if (voiceFile) {
                                    driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/${voiceFile}`)
                                    logger.info(`[grain]: æ’­æ”¾æˆåŠŸè¯­éŸ³æç¤º: ${voiceFile}`)
                                }
                            } catch (error) {
                                logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                            }
                        } else {
                            logger.error(`[grain]: ä»“内照明联动控制接口调用失败,返回编码: ${statusData.respCode}, è¿”回信息: ${statusData.respMsg}`)
                            // è§¦å‘失败弹窗
                            bus.fire('showAccessResult', {
                                faceAuth: true,
                                gasConcentration: true,
                                accessAllowed: false,
                                message: statusData.respMsg || getErrorMessage(statusData.respCode)
                            })
                            // æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º
                            try {
                                driver.alsa.play(`/app/code/resource/${config.get("base.language") == "CN" ? "CN" : "EN"}/wav/failed.wav`)
                                logger.info(`[grain]: æ’­æ”¾å¤±è´¥è¯­éŸ³æç¤º: failed.wav`)
                            } catch (error) {
                                logger.error(`[grain]: æ’­æ”¾è¯­éŸ³æç¤ºå¤±è´¥: ${error.message}`)
                            }
                        }
                    } catch (cleanError) {
                        logger.error(`[grain]: æ¸…理后解析仓内照明联动控制响应数据仍失败: ${cleanError.message}`)
                        return false
                    }
                }
                // è§¦å‘事件更新UI
                bus.fire('statusInfoUpdated', statusData)
                return true
            } else {
                logger.error(`[grain]: ä»“内照明联动控制请求失败: ${response ? "无响应体" : "无响应"}`)
                // ç½‘络请求失败时,默认返回false(安全起见)
                return false
            }
        } catch (error) {
            logger.error(`[grain]: ä»“内照明联动控制错误: ${error.message}`)
            // å‘生错误时,默认返回false(安全起见)
            return false
        }
    }, 0)
    // ç«‹å³è¿”回,避免阻塞主线程
    return true
}
/**
 * å‘送门磁状态消息
 * @param {object} doorStatusData - é—¨ç£çŠ¶æ€æ•°æ®
 * @param {number} doorStatusData.status - é—¨ç£çŠ¶æ€ï¼Œ0表示门关,1表示门开
 * @param {string} doorStatusData.statusText - é—¨ç£çŠ¶æ€æ–‡æœ¬
 * @param {number} doorStatusData.time - æ—¶é—´æˆ³
 */
grainService.sendDoorStatusMessage = function(doorStatusData) {
    // ä½¿ç”¨setTimeout将HTTP请求放入后台执行,避免阻塞主线程
    std.setTimeout(() => {
        try {
            // ç¡®ä¿URL格式正确,添加http://前缀
            let url = getHttpUrl()
            if (!url.startsWith('http://') && !url.startsWith('https://')) {
                url = 'http://' + url
            }
            const timeout = 3000 // 3秒超时
            logger.info(`[grain]: æ­£åœ¨å‘送门磁状态消息: ${url}`)
            // æž„建POST请求数据
                const postData = {
                    sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                    houseId: "0000", // ä»“廒编码,默认填充"0000"
                    outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                    functionId: functionId.doorStatus, // é—¨ç£çŠ¶æ€åŠŸèƒ½ç 
                    timestamp: Date.now().toString(), // æ—¶é—´æˆ³
                    data: {
                        doorStatus: doorStatusData.status // é—¨ç£çŠ¶æ€
                    }
                }
            logger.info(`[grain]: å‘送门磁状态消息数据: ${JSON.stringify(postData)}`)
            // å‘送HTTP POST请求
            let response = http.post(url, postData, timeout)
            // è§£æžå“åº”数据
            if (typeof response === 'string') {
                response = JSON.parse(response)
            }
            if (response && response.body) {
                let respData
                try {
                    respData = JSON.parse(response.body)
                    logger.info(`[grain]: é—¨ç£çŠ¶æ€æ¶ˆæ¯å“åº”: ${JSON.stringify(respData)}`)
                } catch (error) {
                    logger.error(`[grain]: è§£æžé—¨ç£çŠ¶æ€æ¶ˆæ¯å“åº”å¤±è´¥: ${error.message}`)
                }
            } else {
                logger.info(`[grain]: é—¨ç£çŠ¶æ€æ¶ˆæ¯å“åº”ä¸ºç©º`)
            }
        } catch (error) {
            logger.error(`[grain]: å‘送门磁状态消息失败: ${error.message}`)
        }
    }, 0)
}
// ç›‘听门磁状态变化事件
bus.on('doorStatusChanged', (doorStatusData) => {
    logger.info(`[grain]: æŽ¥æ”¶åˆ°é—¨ç£çŠ¶æ€å˜åŒ–äº‹ä»¶: ${JSON.stringify(doorStatusData)}`)
    // è°ƒç”¨å‘送门磁状态消息的函数
    try {
        if (typeof grainService.sendDoorStatusMessage === 'function') {
            grainService.sendDoorStatusMessage(doorStatusData)
        } else {
            logger.error('[grain]: sendDoorStatusMessage ä¸æ˜¯ä¸€ä¸ªå‡½æ•°')
            logger.error('[grain]: grainService.sendDoorStatusMessage =', grainService.sendDoorStatusMessage)
        }
    } catch (error) {
        logger.error('[grain]: è°ƒç”¨sendDoorStatusMessage出错:', error.message)
    }
})
export default grainService
vf205_access/src/service/mqttService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1832 @@
/**
 * MQTT服务模块
 * å¤„理MQTT消息的接收和发送,包括设备管理、人员管理、权限管理等功能
 */
import common from "../../dxmodules/dxCommon.js";
import config from "../../dxmodules/dxConfig.js";
import logger from "../../dxmodules/dxLogger.js";
import ota from "../../dxmodules/dxOta.js";
import std from "../../dxmodules/dxStd.js";
import dxMap from '../../dxmodules/dxMap.js'
import driver from "../driver.js";
import configService from "./configService.js";
import sqliteService from "./sqliteService.js";
import sqlite from "../../dxmodules/dxSqlite.js";
import utils from '../common/utils/utils.js'
const mqttService = {}
let map = dxMap.get("faceAccesss")
/**
 * æŽ¥æ”¶MQTT消息并处理
 * @param {object} data - MQTT消息数据
 * @param {string} data.topic - æ¶ˆæ¯ä¸»é¢˜
 * @param {string} data.payload - æ¶ˆæ¯è½½è·
 */
mqttService.receiveMsg = function (data) {
    // {"topic":"ddddd","payload":"{\n  \"msg\": \"world\"\n}"}
    logger.info('[mqttService] receiveMsg :' + JSON.stringify(data.topic))
    // æå–主题的最后部分作为函数名
    if (typeof mqttService[data.topic.match(/[^/]+$/)[0]] == 'function') {
        mqttService[data.topic.match(/[^/]+$/)[0]](data)
    } else {
        logger.error("未实现的topic", data.topic)
    }
}
// =================================权限增删改查=================================
/**
 * æ·»åŠ æƒé™
 * @param {object} event - MQTT事件对象
 */
mqttService.insertPermission = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°insertPermission命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.insertPermissionAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] insertPermission失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] insertPermission成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] insertPermission error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æ·»åŠ æƒé™é€šç”¨åè®®æ ¼å¼
 * @param {array} data - æƒé™æ•°æ®æ•°ç»„
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.insertPermissionAgreement = function (data) {
    let permissions = []
    for (let i = 0; i < data.length; i++) {
        const permission = data[i];
        if (!permission.permissionId || !permission.userId) {
            return "id or userId cannot be empty"
        }
        if (!permission.extra) {
            permission.extra = ""
        }
        if (!permission.time) {
            return "time and type cannot be empty"
        }
        if (permission.time.type != 0 && permission.time.type != 1 && permission.time.type != 2 && permission.time.type != 3) {
            return "time type is not supported"
        }
        let record = {}
        record.permissionId = permission.permissionId
        record.userId = permission.userId
        record.door = isEmpty(permission.index) ? 0 : permission.index
        record.extra = isEmpty(permission.extra) ? JSON.stringify({}) : JSON.stringify(permission.extra)
        record.timeType = permission.time.type
        record.beginTime = permission.time.type == 0 ? 0 : permission.time.range.beginTime
        record.endTime = permission.time.type == 0 ? 0 : permission.time.range.endTime
        record.repeatBeginTime = permission.time.type != 2 ? 0 : permission.time.beginTime
        record.repeatEndTime = permission.time.type != 2 ? 0 : permission.time.endTime
        record.period = permission.time.type != 3 ? 0 : JSON.stringify(permission.time.weekPeriodTime)
        let ret = sqliteService.d1_permission.save(record)
        if (ret != 0) {
            // å¦‚果保存失败,尝试删除后重新保存
            sqliteService.d1_permission.deleteByPermissionId(record.permissionId)
            ret = sqliteService.d1_permission.save(record)
            if (ret != 0) {
                return "sql error ret:" + ret
            } else {
                continue
            }
        }
    }
    return true
}
/**
 * æŸ¥è¯¢æƒé™
 * @param {object} event - MQTT事件对象
 */
mqttService.getPermission = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°getPermission命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„æŸ¥è¯¢å‚æ•°:', JSON.stringify(data))
        let res = this.getPermissionAgreement(data)
        logger.info('[mqttService] æŸ¥è¯¢ç»“æžœ:', JSON.stringify(res))
        return reply(event, res)
    } catch (error) {
        logger.error('[mqttService] getPermission error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æŸ¥è¯¢æƒé™é€šç”¨åè®®æ ¼å¼
 * @param {object} data - æŸ¥è¯¢å‚æ•°
 * @returns {object} æŸ¥è¯¢ç»“æžœ
 */
mqttService.getPermissionAgreement = function (data) {
    try {
        // ç¡®ä¿data参数不为undefined
        data = data || {}
        data.page = isEmpty(data.page) ? 0 : data.page
        data.size = isEmpty(data.size) ? 10 : data.size
        let totalCount = sqliteService.d1_permission.count(data)
        let permissions = sqliteService.d1_permission.findAll(data)
        // æž„建返回结果
        let content = permissions.map(permission => ({
            permissionId: permission.permissionId,
            userId: permission.userId,
            extra: JSON.parse(permission.extra ? permission.extra : "{}"),
            time: {
                type: permission.timeType,
                beginTime: permission.timeType != 2 ? undefined : permission.repeatBeginTime,
                endTime: permission.timeType != 2 ? undefined : permission.repeatEndTime,
                range: permission.timeType === 0 ? undefined : { beginTime: permission.beginTime, endTime: permission.endTime },
                weekPeriodTime: permission.timeType != 3 ? undefined : JSON.parse(permission.period)
            }
        }))
        return {
            content: content,
            page: data.page,
            size: data.size,
            total: totalCount,
            totalPage: Math.ceil(totalCount / data.size),
            count: content.length
        }
    } catch (error) {
        logger.error('[mqttService] getPermissionAgreement error:', error)
        throw error
    }
}
/**
 * åˆ é™¤æƒé™
 * @param {object} event - MQTT事件对象
 */
mqttService.delPermission = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°delPermission命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.delPermissionAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] delPermission失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] delPermission成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] delPermission error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * åˆ é™¤æƒé™é€šç”¨åè®®æ ¼å¼
 * @param {object} data - åˆ é™¤å‚æ•°
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.delPermissionAgreement = function (data) {
    if (data.permissionIds && data.permissionIds.length > 0) {
        let ret = sqliteService.d1_permission.deleteByPermissionIdInBatch(data.permissionIds)
        if (ret != 0) {
            return "sql error ret:" + ret
        }
    }
    if (data.userIds && data.userIds.length > 0) {
        let ret = sqliteService.d1_permission.deleteByUserIdInBatch(data.userIds)
        if (ret != 0) {
            return "sql error ret:" + ret
        }
    }
    return true
}
/**
 * æ¸…空权限
 * @param {object} event - MQTT事件对象
 */
mqttService.clearPermission = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°clearPermission命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let ret = sqliteService.d1_permission.deleteAll()
        if (ret == 0) {
            logger.info('[mqttService] clearPermission成功')
            return reply(event)
        } else {
            logger.error('[mqttService] clearPermission失败:', "sql error ret:" + ret)
            return reply(event, "sql error ret:" + ret, CODE.E_100)
        }
    } catch (error) {
        logger.error('[mqttService] clearPermission error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
// =================================人员增删改查=================================
/**
 * æ·»åŠ äººå‘˜
 * @param {object} event - MQTT事件对象
 */
mqttService.insertUser = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°insertUser命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : []
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.insertUserAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] insertUser失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] insertUser成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] insertUser error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æ·»åŠ äººå‘˜é€šç”¨åè®®æ ¼å¼
 * @param {array} data - äººå‘˜æ•°æ®æ•°ç»„
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.insertUserAgreement = function (data) {
    let persons = []
    for (let i = 0; i < data.length; i++) {
        const person = data[i];
        // ä¸¥æ ¼æ£€æŸ¥æ•°æ®æ ¼å¼
        if (!person.userId || !person.name || person.type === undefined || !person.idCard) {
            return "数据格式错误,缺少必要字段(userId、name、type、idCard)"
        }
        // æ£€æŸ¥type字段类型
        if (typeof person.type !== 'number') {
            return "数据格式错误,type字段必须是数字"
        }
        // æ£€æŸ¥type字段值范围
        if (person.type < 0 || person.type > 1) {
            return "数据格式错误,type字段值必须在0-1之间"
        }
        let record = {}
        record.userId = person.userId
        record.name = person.name
        // å¤„理人员类型字段和身份证号
        let extra = {}
        extra.type = person.type
        extra.idCard = person.idCard
        record.extra = JSON.stringify(extra)
        persons.push(record)
        // å¤„理人脸信息
        if (person.face) {
            try {
                logger.info('[mqttService] å¼€å§‹å¤„理人脸信息:', person.userId)
                let faceFilePath = person.face
                // æ£€æŸ¥æ˜¯å¦æ˜¯base64编码的图片数据
                if (person.face.startsWith('data:image/')) {
                    logger.info('[mqttService] æ£€æµ‹åˆ°base64编码的图片数据')
                    // æå–base64数据
                    let base64Data = person.face.split(',')[1]
                    // åˆ›å»ºä¸´æ—¶æ–‡ä»¶
                    faceFilePath = '/app/data/user/temp_face_' + person.userId + '.jpg'
                    std.ensurePathExists(faceFilePath)
                    // å°†base64数据转换为文件
                    common.base64_2binfile(faceFilePath, base64Data)
                    logger.info('[mqttService] å·²å°†base64数据保存为文件:', faceFilePath)
                } else {
                    return "数据格式错误,face字段必须是base64编码的图片数据"
                }
                // æ£€æŸ¥æ–‡ä»¶æ˜¯å¦å­˜åœ¨
                let fileExists = common.systemWithRes(`test -e "${faceFilePath}" && echo "OK" || echo "NO"`, 2)
                logger.info('[mqttService] äººè„¸å›¾ç‰‡æ–‡ä»¶å­˜åœ¨:', fileExists.includes('OK'))
                if (fileExists.includes('OK')) {
                    // æ³¨å†Œäººè„¸
                    logger.info('[mqttService] å¼€å§‹æ³¨å†Œäººè„¸:', person.userId)
                    let ret = driver.face.registerFaceByPicFile(person.userId, faceFilePath)
                    logger.info('[mqttService] æ³¨å†Œäººè„¸ç»“æžœ:', ret)
                    if (ret == 0) {
                        // æ³¨å†ŒæˆåŠŸåŽç§»åŠ¨å›¾ç‰‡åˆ°ç”¨æˆ·ç›®å½•
                        let src = "/app/data/user/" + person.userId + "/register.jpg"
                        std.ensurePathExists(src)
                        logger.info('[mqttService] ç§»åŠ¨äººè„¸å›¾ç‰‡åˆ°ç”¨æˆ·ç›®å½•:', faceFilePath, '->', src)
                        common.systemBrief('mv ' + faceFilePath + " " + src)
                        // ä¿å­˜äººè„¸å‡­è¯
                        logger.info('[mqttService] ä¿å­˜äººè„¸å‡­è¯:', person.userId)
                        let voucherRet = sqliteService.d1_voucher.save({
                            keyId: std.genRandomStr(32),
                            type: "300",
                            code: src,
                            userId: person.userId,
                            extra: JSON.stringify({ faceType: 0 })
                        });
                        logger.info('[mqttService] ä¿å­˜äººè„¸å‡­è¯ç»“æžœ:', voucherRet)
                    } else {
                        logger.error('[mqttService] æ³¨å†Œäººè„¸å¤±è´¥ï¼Œè¿”回码:', ret)
                    }
                } else {
                    logger.error('[mqttService] äººè„¸å›¾ç‰‡æ–‡ä»¶ä¸å­˜åœ¨:', faceFilePath)
                }
            } catch (error) {
                logger.error('[mqttService] å¤„理人脸信息错误:', error)
                return "处理人脸信息错误: " + error.message
            } finally {
                logger.info('[mqttService] äººè„¸ä¿¡æ¯å¤„理完成:', person.userId)
            }
        }
    }
    let ret = sqliteService.d1_person.saveAll(persons)
    if (ret != 0) {
        //失败了 æŠŠè¿™äº›äººå…¨éƒ½åˆ é™¤åŽåœ¨æ–°å¢žä¸€ä¸‹
        let userIds = persons.map(obj => obj.userId);
        sqliteService.d1_person.deleteByUserIdInBatch(userIds)
        //重新新增
        let ret = sqliteService.d1_person.saveAll(persons)
        if (ret != 0) {
            return "sql error ret:" + ret
        }
    }
    // ä¸ºç”¨æˆ·æ·»åŠ å¯¹åº”æƒé™
    for (let i = 0; i < data.length; i++) {
        const person = data[i];
        let userId = person.userId
        let userType = person.type
        // åªæœ‰ä¿ç®¡å‘˜ï¼ˆ0)和科长(1)需要添加权限
        if (userType == 0 || userType == 1) {
            try {
                // æ£€æŸ¥æ˜¯å¦å·²å­˜åœ¨æƒé™è®°å½•
                let existingPermissions = sqliteService.d1_permission.findByUserId(userId)
                if (existingPermissions && existingPermissions.length == 0) {
                    // æ·»åŠ æ°¸ä¹…æƒé™
                    let permissionRet = sqliteService.d1_permission.save({
                        permissionId: std.genRandomStr(32),
                        userId: userId,
                        timeType: 0, // æ°¸ä¹…权限
                        beginTime: 0,
                        endTime: 0,
                        repeatBeginTime: 0,
                        repeatEndTime: 0,
                        period: ""
                    });
                    logger.info('[mqttService] ä¸ºç”¨æˆ·æ·»åŠ æƒé™ç»“æžœ:', permissionRet)
                } else {
                    logger.info('[mqttService] ç”¨æˆ·å·²å­˜åœ¨æƒé™è®°å½•,跳过权限添加:', userId)
                }
            } catch (error) {
                logger.error('[mqttService] æ·»åŠ æƒé™æ—¶å‡ºé”™:', error)
            }
        }
    }
    return true
}
/**
 * æŸ¥è¯¢äººå‘˜
 * @param {object} event - MQTT事件对象
 */
mqttService.getUser = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°getUser命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        let res = this.getUserAgreement(data)
        logger.info('[mqttService] æŸ¥è¯¢ç»“æžœ:', JSON.stringify(res))
        return reply(event, res)
    } catch (error) {
        logger.error('[mqttService] getUser error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æŸ¥è¯¢äººå‘˜é€šç”¨åè®®æ ¼å¼
 * @param {object} data - æŸ¥è¯¢å‚æ•°
 * @returns {object} æŸ¥è¯¢ç»“æžœ
 */
mqttService.getUserAgreement = function (data) {
    try {
        data.page = isEmpty(data.page) ? 0 : data.page
        data.size = isEmpty(data.size) ? 10 : data.size
        let totalCount = sqliteService.d1_person.count(data)
        let persons = sqliteService.d1_person.findAll(data)
        // è§£æž extra å­—段,JSON字符串转化为JSON对象,消除转义字符
        persons.forEach(person => {
            try {
                if (person.extra) {
                    person.extra = JSON.parse(person.extra)
                }
            } catch (error) {
                logger.error('[mqttService] è§£æž extra å­—段错误:', error)
            }
        })
        let result = {
            content: persons,
            page: data.page,
            size: data.size,
            total: totalCount,
            totalPage: Math.ceil(totalCount / data.size),
            count: persons.length
        }
        return result
    } catch (error) {
        logger.error('[mqttService] getUserAgreement error:', error)
        throw error
    }
}
/**
 * åˆ é™¤äººå‘˜
 * @param {object} event - MQTT事件对象
 */
mqttService.delUser = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°delUser命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : []
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.delUserAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] delUser失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] delUser成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] delUser error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * åˆ é™¤äººå‘˜é€šç”¨åè®®æ ¼å¼
 * @param {array} data - äººå‘˜ID数组
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.delUserAgreement = function (data) {
    if (data && data.length > 0) {
        sqliteService.transaction()
        let ret1 = sqliteService.d1_person.deleteByUserIdInBatch(data)
        let ret2 = sqliteService.d1_permission.deleteByUserIdInBatch(data)
        let ret3 = sqliteService.d1_voucher.deleteByUserIdInBatch(data)
        if (ret1 != 0 || ret2 != 0 || ret3 != 0) {
            sqliteService.rollback()
            return "sql error"
        }
        sqliteService.commit()
        // åˆ é™¤äººå‘˜çš„人脸数据
        data.forEach(element => {
            driver.face.delete(element)
        });
    }
    return true
}
/**
 * æ¸…空人员
 * @param {object} event - MQTT事件对象
 */
mqttService.clearUser = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°clearUser命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let persons = sqliteService.d1_person.findAll()
        // åˆ é™¤æ‰€æœ‰äººå‘˜çš„人脸数据
        logger.info('[mqttService] å¼€å§‹åˆ é™¤äººè„¸æ•°æ®ï¼Œå…±', persons.length, '条')
        persons.forEach(element => {
            driver.face.delete(element.userId)
        });
        let ret1 = sqliteService.d1_person.deleteAll()
        let ret2 = sqliteService.d1_permission.deleteAll()
        let ret3 = sqliteService.d1_voucher.deleteAll()
        if (ret1 == 0 && ret2 == 0 && ret3 == 0) {
            logger.info('[mqttService] clearUser成功')
            return reply(event)
        } else {
            let errorMsg = "sql error ret: " + ret1 + ", " + ret2 + ", " + ret3
            logger.error('[mqttService] clearUser失败:', errorMsg)
            return reply(event, errorMsg, CODE.E_100)
        }
    } catch (error) {
        logger.error('[mqttService] clearUser error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
// =================================凭证增删改查=================================
/**
 * æ·»åŠ å‡­è¯
 * @param {object} event - MQTT事件对象
 */
mqttService.insertKey = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°insertKey命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.insertKeyAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] insertKey失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] insertKey成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] insertKey error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æ·»åŠ å‡­è¯é€šç”¨åè®®æ ¼å¼
 * @param {array} data - å‡­è¯æ•°æ®æ•°ç»„
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.insertKeyAgreement = function (data) {
    let vouchers = []
    for (let i = 0; i < data.length; i++) {
        const voucher = data[i];
        if (!voucher.keyId || !voucher.type || !voucher.code || !voucher.userId) {
            return "keyId or type or code  or userId cannot be empty"
        }
        // å‡­è¯é‡å¤
        let ret = sqliteService.d1_voucher.findAllBycode(voucher.code)
        if (ret.length != 0) {
            return "Duplicate vouchers"
        }
        if (voucher.type == 300) {
            if (voucher.extra) {
                if (voucher.extra.faceType != 0 && voucher.extra.faceType != 1) {
                    return "faceType Incorrect format"
                }
            } else {
                return "faceType is required"
            }
        }
        let record = {}
        record.keyId = voucher.keyId
        record.type = voucher.type
        if (voucher.type == "400") {
            if (voucher.code.length > 6) {
                return "Password length cannot exceed 6 digits"
            }
        }
        if (voucher.type == "300") {
            if (voucher.extra.faceType == 0) {
                record.code = `/app/data/user/${voucher.userId}/register.jpg`
                // ä¿å­˜base64图片
                std.ensurePathExists(record.code)
                common.base64_2binfile(record.code, voucher.code)
                // æ³¨å†Œäººè„¸
                let weq = driver.face.registerFaceByPicFile(voucher.userId, record.code)
                if (weq == 0) {
                    logger.info("注册人脸成功")
                } else {
                    logger.info("第一次人脸注册失败")
                    //删除重新注册
                    driver.face.delete(voucher.userId)
                    let res = driver.face.registerFaceByPicFile(voucher.userId, record.code)
                    if (res == 0) {
                        logger.info("第二次注册人脸成功")
                        sqliteService.d1_voucher.deleteByKeyId(record.keyId)
                    } else {
                        return "Face registration failed"
                    }
                }
            } else {
                record.code = voucher.code
                //特征值注册
                let res = driver.face.reg(voucher.userId, voucher.code)
                if (res != 0) {
                    return "Face registration failed"
                }
            }
        } else {
            record.code = voucher.code
            let ret = sqliteService.d1_voucher.findAllByCodeAndType(voucher.code, voucher.type)
            if (ret.length != 0) {
                return "Duplicate vouchers"
            }
        }
        record.userId = voucher.userId
        record.extra = isEmpty(voucher.extra) ? JSON.stringify({ type: 0 }) : JSON.stringify(voucher.extra)
        vouchers.push(record)
    }
    let ret = sqliteService.d1_voucher.saveAll(vouchers)
    if (ret == 0) {
        return true
    } else {
        return "sql error ret:" + ret
    }
}
/**
 * æŸ¥è¯¢å‡­è¯
 * @param {object} event - MQTT事件对象
 */
mqttService.getKey = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°getKey命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„æŸ¥è¯¢å‚æ•°:', JSON.stringify(data))
        let res = this.getKeyAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] getKey失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] æŸ¥è¯¢ç»“æžœ:', JSON.stringify(res))
        return reply(event, res)
    } catch (error) {
        logger.error('[mqttService] getKey error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æŸ¥è¯¢å‡­è¯é€šç”¨åè®®æ ¼å¼
 * @param {object} data - æŸ¥è¯¢å‚æ•°
 * @returns {object|string} æŸ¥è¯¢ç»“果或错误信息
 */
mqttService.getKeyAgreement = function (data) {
    if (!data.type) {
        return "type is required"
    }
    if (data.type == 300) {
        data.size = 1
    } else {
        data.page = isEmpty(data.page) ? 0 : data.page
        data.size = isEmpty(data.size) ? 10 : data.size
    }
    let totalCount = sqliteService.d1_voucher.count(data)
    let vouchers = sqliteService.d1_voucher.findAll(data)
    vouchers.forEach(element => {
        if (element.type == 300 && element.extra && JSON.parse(element.extra).faceType == 0) {
            //人脸特殊处理一下
            element.code = driver.face.fileToBase64(element.code)
        }
    });
    return {
        content: vouchers,
        page: data.page,
        size: data.size,
        total: totalCount,
        totalPage: Math.ceil(totalCount / data.size),
        count: vouchers.length
    }
}
/**
 * åˆ é™¤å‡­è¯
 * @param {object} event - MQTT事件对象
 */
mqttService.delKey = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°delKey命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.delKeyAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] delKey失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] delKey成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] delKey error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * åˆ é™¤å‡­è¯é€šç”¨åè®®æ ¼å¼
 * @param {object} data - åˆ é™¤å‚æ•°
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.delKeyAgreement = function (data) {
    if (data.keyIds && data.keyIds.length > 0) {
        let userIds = []
        for (let i = 0; i < data.keyIds.length; i++) {
            const element = data.keyIds[i];
            let res = sqliteService.d1_voucher.findAllByKeyId(element)
            if (res.length <= 0) {
                continue
            }
            if (res[0].type == 300) {
                userIds.push(res[0].userId)
            }
        }
        let ret = sqliteService.d1_voucher.deleteByKeyIdInBatch(data.keyIds)
        if (ret != 0) {
            return "sql error ret:" + ret
        }
        // åˆ é™¤äººè„¸æ•°æ®
        userIds.forEach(element => {
            driver.face.delete(element)
        });
    }
    if (data.userIds && data.userIds.length > 0) {
        let ret = sqliteService.d1_voucher.deleteByUserIdInBatch(data.userIds)
        if (ret != 0) {
            return "sql error ret:" + ret
        }
        // åˆ é™¤äººè„¸æ•°æ®
        data.userIds.forEach(element => {
            driver.face.delete(element)
        });
    }
    return true
}
/**
 * æ¸…空凭证
 * @param {object} event - MQTT事件对象
 */
mqttService.clearKey = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°clearKey命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let res = sqliteService.d1_voucher.findAll()
        let userIds = []
        res.forEach(element => {
            if (element.type == 300) {
                userIds.push(element.userId)
            }
        });
        logger.info('[mqttService] æ‰¾åˆ°éœ€è¦åˆ é™¤çš„人脸数据,共', userIds.length, '条')
        let ret = sqliteService.d1_voucher.deleteAll()
        if (ret == 0) {
            // åˆ é™¤äººè„¸æ•°æ®
            logger.info('[mqttService] å¼€å§‹åˆ é™¤äººè„¸æ•°æ®')
            userIds.forEach((element, index) => {
                driver.face.delete(element)
            });
            logger.info('[mqttService] clearKey成功')
            reply(event)
        } else {
            logger.error('[mqttService] clearKey失败:', "sql error ret:" + ret)
            reply(event, "sql error ret:" + ret, CODE.E_100)
        }
    } catch (error) {
        logger.error('[mqttService] clearKey error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
// =================================应急开仓密码增删改查=================================
/**
 * æ·»åŠ åº”æ€¥å¼€ä»“å¯†ç 
 * @param {object} event - MQTT事件对象
 */
mqttService.insertEmergencyPassword = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°insertEmergencyPassword命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.insertEmergencyPasswordAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] insertEmergencyPassword失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] insertEmergencyPassword成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] insertEmergencyPassword error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æ·»åŠ åº”æ€¥å¼€ä»“å¯†ç é€šç”¨åè®®æ ¼å¼
 * @param {object} data - åº”急开仓密码数据对象
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.insertEmergencyPasswordAgreement = function (data) {
    // åº”急开仓密码在设备中仅有唯一的1个,所以先清空表
    let deleteRet = sqliteService.d1_emergency_password.deleteAll()
    if (deleteRet != 0) {
        return "清空旧密码失败: " + deleteRet
    }
    // æ£€æŸ¥å¯†ç æ˜¯å¦æœ‰æ•ˆ
    if (!data.password) {
        return "password cannot be empty"
    }
    // æ£€æŸ¥å¯†ç é•¿åº¦æ˜¯å¦å¤§äºŽç­‰äºŽ8位
    if (data.password.length < 8) {
        return "Password length must be at least 8 digits"
    }
    // æž„建密码记录
    let record = {}
    record.id = data.id || 'emergency_' + Date.now() // å¦‚果没有id,自动生成
    record.password = data.password
    record.description = data.description || ""
    record.createTime = Date.now()
    record.updateTime = Date.now()
    record.status = data.status || 1
    // ä¿å­˜å¯†ç 
    let ret = sqliteService.d1_emergency_password.save(record)
    if (ret == 0) {
        return true
    } else {
        return "sql error ret:" + ret
    }
}
/**
 * æŸ¥è¯¢åº”急开仓密码
 * @param {object} event - MQTT事件对象
 */
mqttService.getEmergencyPassword = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°getEmergencyPassword命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let res = this.getEmergencyPasswordAgreement()
        logger.info('[mqttService] æŸ¥è¯¢ç»“æžœ:', JSON.stringify(res))
        return reply(event, res)
    } catch (error) {
        logger.error('[mqttService] getEmergencyPassword error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æ—¶é—´æˆ³è½¬æ—¥æœŸå­—符串
 * @param {number} timestamp - æ—¶é—´æˆ³
 * @returns {string} æ—¥æœŸå­—符串,格式:YYYY-MM-DD HH:MM:SS
 */
function timestampToDateString(timestamp) {
    const date = new Date(timestamp);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    const seconds = String(date.getSeconds()).padStart(2, '0');
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
/**
 * æŸ¥è¯¢åº”急开仓密码通用协议格式
 * @returns {object} æŸ¥è¯¢ç»“æžœ
 */
mqttService.getEmergencyPasswordAgreement = function () {
    let passwords = sqliteService.d1_emergency_password.findAll()
    if (passwords && passwords.length > 0) {
        let password = passwords[0];
        // è½¬æ¢æ—¶é—´æˆ³ä¸ºå­—符串格式
        password.createTime = timestampToDateString(password.createTime);
        password.updateTime = timestampToDateString(password.updateTime);
        return password;
    }
    return {};
}
/**
 * æ¸…空应急开仓密码
 * @param {object} event - MQTT事件对象
 */
mqttService.clearEmergencyPassword = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°clearEmergencyPassword命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let ret = sqliteService.d1_emergency_password.deleteAll()
        if (ret == 0) {
            logger.info('[mqttService] clearEmergencyPassword成功')
            return reply(event)
        } else {
            logger.error('[mqttService] clearEmergencyPassword失败:', "sql error ret:" + ret)
            return reply(event, "sql error ret:" + ret, CODE.E_100)
        }
    } catch (error) {
        logger.error('[mqttService] clearEmergencyPassword error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
// =================================密钥增删改查=================================
/**
 * æ·»åР坆钥
 * @param {object} event - MQTT事件对象
 */
mqttService.insertSecurity = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°insertSecurity命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.insertSecurityAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] insertSecurity失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] insertSecurity成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] insertSecurity error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æ·»åŠ å¯†é’¥é€šç”¨åè®®æ ¼å¼
 * @param {array} data - å¯†é’¥æ•°æ®æ•°ç»„
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.insertSecurityAgreement = function (data) {
    let securities = []
    for (let i = 0; i < data.length; i++) {
        const security = data[i];
        let record = []
        record.securityId = security.securityId
        record.type = security.type
        record.key = security.key
        record.value = security.value
        record.startTime = security.startTime
        record.endTime = security.endTime
        securities.push(record)
    }
    let ret = sqliteService.d1_security.saveAll(securities)
    if (ret == 0) {
        return true
    } else {
        return "sql error ret:" + ret
    }
}
/**
 * æŸ¥è¯¢å¯†é’¥
 * @param {object} event - MQTT事件对象
 */
mqttService.getKey = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°getKey命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„æŸ¥è¯¢å‚æ•°:', JSON.stringify(data))
        let res = this.getKeyAgreement(data)
        logger.info('[mqttService] æŸ¥è¯¢ç»“æžœ:', JSON.stringify(res))
        return reply(event, res)
    } catch (error) {
        logger.error('[mqttService] getKey error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æŸ¥è¯¢å¯†é’¥é€šç”¨åè®®æ ¼å¼
 * @param {object} data - æŸ¥è¯¢å‚æ•°
 * @returns {object} æŸ¥è¯¢ç»“æžœ
 */
mqttService.getSecurityAgreement = function (data) {
    data.page = isEmpty(data.page) ? 0 : data.page
    data.size = isEmpty(data.size) ? 10 : data.size
    let totalCount = sqliteService.d1_security.count(data)
    let securities = sqliteService.d1_security.findAll(data)
    return {
        content: securities,
        page: data.page,
        size: data.size,
        total: totalCount,
        totalPage: Math.ceil(totalCount / data.size),
        count: securities.length
    }
}
/**
 * åˆ é™¤å¯†é’¥
 * @param {object} event - MQTT事件对象
 */
mqttService.delSecurity = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°delSecurity命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.delSecurityAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] delSecurity失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] delSecurity成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] delSecurity error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * åˆ é™¤å¯†é’¥é€šç”¨åè®®æ ¼å¼
 * @param {array} data - å¯†é’¥ID数组
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.delSecurityAgreement = function (data) {
    if (data.length > 0) {
        let ret = sqliteService.d1_security.deleteBySecurityIdInBatch(data)
        if (ret != 0) {
            return "sql error ret:" + ret
        }
    }
    return true
}
/**
 * æ¸…空密钥
 * @param {object} event - MQTT事件对象
 */
mqttService.clearSecurity = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°clearSecurity命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let ret = sqliteService.d1_key.deleteAll()
        if (ret == 0) {
            logger.info('[mqttService] clearSecurity成功')
            return reply(event)
        } else {
            logger.error('[mqttService] clearSecurity失败:', "sql error ret:" + ret)
            return reply(event, "sql error ret:" + ret, CODE.E_100)
        }
    } catch (error) {
        logger.error('[mqttService] clearSecurity error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * è¿œç¨‹æŽ§åˆ¶
 * @param {object} event - MQTT事件对象
 */
mqttService.control = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°control命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload.data || {}
        switch (data.command) {
            case 0:
                //重启
                logger.info('[mqttService] æ‰§è¡Œé‡å¯å‘½ä»¤')
                reply(event)
                common.asyncReboot(2)
                return
            case 1:
                //远程开门
                logger.info('[mqttService] æ‰§è¡Œè¿œç¨‹å¼€é—¨å‘½ä»¤')
                driver.gpio.open()
                break
            case 4:
                //重置
                logger.info('[mqttService] æ‰§è¡Œé‡ç½®å‘½ä»¤')
                common.systemBrief("rm -rf /app/data/config/*")
                common.systemBrief("rm -rf /app/data/db/*")
                common.systemBrief("rm -rf /app/data/user/*")
                common.systemBrief("rm -rf /app/data/user/*")
                common.systemBrief("rm -rf /vgmj.db")
                reply(event)
                common.asyncReboot(2)
                return
            case 5:
                //播放语音
                logger.info('[mqttService] æ‰§è¡Œæ’­æ”¾è¯­éŸ³å‘½ä»¤')
                if (data.extra) {
                    let res = common.systemWithRes(`test -e "/app/code/resource/wav/${data.extra.wav}.wav" && echo "OK" || echo "NO"`, 2)
                    if (res.includes('OK')) {
                        driver.alsa.play(`/app/code/resource/wav/${data.extra.wav}.wav`)
                    }
                }
                break
            case 6:
                // 6:屏幕展示图片
                // TODO
                logger.info('[mqttService] æ‰§è¡Œå±å¹•展示图片命令')
                break
            case 7:
                // 7:屏幕展示文字
                // TODO
                logger.info('[mqttService] æ‰§è¡Œå±å¹•展示文字命令')
                break
            case 10:
                logger.info('[mqttService] æ‰§è¡ŒäºŒç»´ç å±•示命令')
                if (!isEmpty(data.extra.qrCodeBase64) && typeof data.extra.qrCodeBase64 == 'string') {
                    //base64转图片保存
                    let src = `/app/code/resource/image/app_qrcode.png`
                    std.ensurePathExists(src)
                    common.base64_2binfile(src, data.extra.qrCodeBase64)
                    logger.info('[mqttService] äºŒç»´ç ä¿å­˜æˆåŠŸ')
                    return reply(event)
                }
                break
            default:
                logger.info('[mqttService] æœªçŸ¥å‘½ä»¤:', data.command)
                break
        }
        logger.info('[mqttService] control命令执行完成')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] control error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æŸ¥è¯¢é…ç½®
 * @param {object} event - MQTT事件对象
 */
mqttService.getConfig = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°getConfig命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let configAll = config.getAll()
        let res = {}
        // é…ç½®åˆ†ç»„
        for (const key in configAll) {
            const value = configAll[key];
            const keys = key.split(".")
            if (keys.length == 2) {
                if (!res[keys[0]]) {
                    res[keys[0]] = {}
                }
                res[keys[0]][keys[1]] = value
            } else {
                res[keys[0]] = value
            }
        }
        res.sys = {
            // ä¿ç•™åŽŸæœ‰çš„ sysInfo ä¸­çš„其他值
            ...res.sys,
            totalmem: common.getTotalmem(),
            freemem: common.getFreemem(),
            totaldisk: common.getTotaldisk(),
            freedisk: common.getFreedisk(),
            freecpu: common.getFreecpu()
        };
        if (isEmpty(data) || typeof data != "string" || data == "") {
            // æŸ¥è¯¢å…¨éƒ¨
            logger.info('[mqttService] getConfig成功,返回全部配置,配置数量:', Object.keys(res).length)
            return reply(event, res)
        }
        // å•条件查询"data": "mqttInfo.clientId"
        let keys = data.split(".")
        let search = {}
        if (keys.length == 2) {
            if (res[keys[0]]) {
                search[keys[0]] = {}
                search[keys[0]][keys[1]] = res[keys[0]][keys[1]]
            }
        } else {
            search[keys[0]] = res[keys[0]]
        }
        logger.info('[mqttService] getConfig成功,返回指定配置:', JSON.stringify(search))
        return reply(event, search)
    } catch (error) {
        logger.error('[mqttService] getConfig error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * ä¿®æ”¹é…ç½®
 * @param {object} event - MQTT事件对象
 */
mqttService.setConfig = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°setConfig命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        if (!data || typeof data != 'object') {
            logger.error('[mqttService] setConfig失败: data should not be empty')
            return reply(event, "data should not be empty", CODE.E_100)
        }
        let res = configService.configVerifyAndSave(data)
        if (typeof res != 'boolean') {
            // è¿”回错误信息
            logger.error('[mqttService] setConfig失败:', res)
            return reply(event, res, CODE.E_100)
        }
        if (res) {
            logger.info('[mqttService] setConfig成功')
            return reply(event)
        } else {
            logger.error('[mqttService] setConfig失败: unknown failure')
            return reply(event, "unknown failure", CODE.E_100)
        }
    } catch (error) {
        logger.error('[mqttService] setConfig error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * å‡çº§å›ºä»¶
 * @param {object} event - MQTT事件对象
 */
mqttService.upgradeFirmware = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°upgradeFirmware命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        if (!data || typeof data != 'object' || typeof data.type != 'number' || typeof data.url != 'string' || typeof data.md5 != 'string') {
            logger.error('[mqttService] upgradeFirmware失败: data\'s params error')
            return reply(event, "data's params error", CODE.E_100)
        }
        if (data.type == 0) {
            try {
                logger.info('[mqttService] å¼€å§‹å›ºä»¶å‡çº§ï¼Œurl:', data.url, 'md5:', data.md5)
                driver.screen.upgrade({ title: "confirm.upgrade", content: "confirm.upgrading" })
                ota.updateHttp(data.url, data.md5, 300)
                driver.screen.upgrade({ title: "confirm.upgrade", content: "confirm.upgradeSuccess" })
                logger.info('[mqttService] å›ºä»¶å‡çº§æˆåŠŸ')
                reply(event)
                common.asyncReboot(3)
                return
            } catch (error) {
                logger.error('[mqttService] å›ºä»¶å‡çº§å¤±è´¥:', error)
                driver.screen.upgrade({ title: "confirm.upgrade", content: "confirm.upgradeFail" })
                return reply(event, "upgrade failure", CODE.E_100)
            }
        }
        logger.error('[mqttService] upgradeFirmware失败: ä¸æ”¯æŒçš„升级类型')
        return reply(event, "upgrade failure", CODE.E_100)
    } catch (error) {
        logger.error('[mqttService] upgradeFirmware error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * æŸ¥è¯¢è¯†åˆ«è®°å½•
 * @param {object} event - MQTT事件对象
 */
mqttService.getRecords = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°getRecords命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„æŸ¥è¯¢å‚æ•°:', JSON.stringify(data))
        let res = this.getRecordsAgreement(data)
        logger.info('[mqttService] æŸ¥è¯¢ç»“æžœ:', JSON.stringify(res))
        return reply(event, res)
    } catch (error) {
        logger.error('[mqttService] getRecords error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * å°†æ—¥æœŸå­—符串转换为时间戳(秒)
 * @param {string} dateString - æ—¥æœŸå­—符串,格式:YYYY-MM-DD HH:MM:SS
 * @returns {number} æ—¶é—´æˆ³ï¼ˆç§’)
 */
function dateStringToTimestamp(dateString) {
    if (!dateString) return null
    // å°†YYYY-MM-DD HH:MM:SS格式转换为YYYY-MM-DDTHH:MM:SS格式,以便new Date()正确解析
    const formattedDateString = dateString.replace(' ', 'T')
    const date = new Date(formattedDateString)
    return Math.floor(date.getTime() / 1000)
}
/**
 * æŸ¥è¯¢è¯†åˆ«è®°å½•通用协议格式
 * @param {object} data - æŸ¥è¯¢å‚æ•°
 * @returns {object} æŸ¥è¯¢ç»“æžœ
 */
mqttService.getRecordsAgreement = function (data) {
    data.page = isEmpty(data.page) ? 0 : data.page
    data.size = isEmpty(data.size) ? 10 : data.size
    // å¤„理时间参数,将字符串格式转换为时间戳
    let startTime = null
    let endTime = null
    if (data.startTime) {
        if (typeof data.startTime === 'string') {
            startTime = dateStringToTimestamp(data.startTime)
        } else {
            startTime = Math.floor(data.startTime / 1000) // è½¬æ¢ä¸ºç§’级时间戳
        }
    }
    if (data.endTime) {
        if (typeof data.endTime === 'string') {
            endTime = dateStringToTimestamp(data.endTime)
        } else {
            endTime = Math.floor(data.endTime / 1000) // è½¬æ¢ä¸ºç§’级时间戳
        }
    }
    // æž„建查询条件
    let queryData = {}
    let nameFilter = null
    // å¤åˆ¶å…¶ä»–查询参数
    for (const key in data) {
        if (key !== 'startTime' && key !== 'endTime' && key !== 'name') {
            queryData[key] = data[key]
        } else if (key === 'name') {
            nameFilter = data[key]
        }
    }
    // æž„建SQL条件
    let whereClause = ''
    if (startTime) {
        whereClause += `time >= ${startTime} `
    }
    if (endTime) {
        if (whereClause) {
            whereClause += `AND `
        }
        whereClause += `time <= ${endTime} `
    }
    // å¤åˆ¶å…¶ä»–条件
    for (const key in queryData) {
        if (key !== 'page' && key !== 'size') {
            if (whereClause) {
                whereClause += `AND `
            }
            if (typeof queryData[key] === 'string') {
                whereClause += `${key} = '${queryData[key]}' `
            } else {
                whereClause += `${key} = ${queryData[key]} `
            }
        }
    }
    // æ‰§è¡ŒæŸ¥è¯¢
    let totalCount = 0
    let securities = []
    try {
        // æž„建count SQL
        let countSql = `SELECT COUNT(*) FROM d1_pass_record `
        if (whereClause) {
            countSql += `WHERE ${whereClause} `
        }
        countSql += `;`
        let countResult = sqlite.select(countSql)
        if (countResult && countResult[0] && countResult[0]['COUNT(*)']) {
            totalCount = countResult[0]['COUNT(*)']
        }
        // æž„建findAll SQL
        let findSql = `SELECT * FROM d1_pass_record `
        if (whereClause) {
            findSql += `WHERE ${whereClause} `
        }
        findSql += `ORDER BY time DESC `
        if (queryData.page !== undefined && queryData.size !== undefined) {
            findSql += `LIMIT ${queryData.size} OFFSET ${queryData.page * queryData.size} `
        }
        findSql += `;`
        securities = sqlite.select(findSql)
    } catch (error) {
        logger.error('[mqttService] æŸ¥è¯¢è®°å½•失败:', error)
    }
    // å¤„理每条记录
    let processedSecurities = securities.map(record => {
        // è§£æžextra字段
        let extraData = {}
        try {
            if (record.extra && record.extra !== '') {
                extraData = JSON.parse(record.extra)
            }
        } catch (error) {
            logger.error('[mqttService] è§£æžextra失败:', error)
        }
        // è§£æžextra2字段
        let extra2Data = {}
        try {
            if (record.extra2 && record.extra2 !== '') {
                extra2Data = JSON.parse(record.extra2)
            }
        } catch (error) {
            logger.error('[mqttService] è§£æžextra2失败:', error)
        }
        // æž„建新记录
        return {
            id: record.id,
            keyId: record.keyId,
            permissionId: record.permissionId,
            permissionId2: record.permissionId2,
            userId: record.userId,
            userId2: record.userId2,
            type: record.type,
            code: record.code,
            door: record.door,
            time: timestampToDateString(record.time * 1000), // å°†ç§’级时间戳转换为毫秒级,再转换为日期字符串
            result: record.result,
            name: extraData.name || '',
            idCard: extraData.idCard || '',
            name2: extra2Data.name || '',
            idCard2: extra2Data.idCard || '',
            message: record.message
        }
    })
    // åº”用name过滤
    if (nameFilter) {
        processedSecurities = processedSecurities.filter(record =>
            record.name.toLowerCase().includes(nameFilter.toLowerCase())
        )
        // æ›´æ–°æ€»æ•°å’Œæ€»é¡µæ•°
        totalCount = processedSecurities.length
    }
    return {
        content: processedSecurities,
        page: data.page,
        size: data.size,
        total: totalCount,
        totalPage: Math.ceil(totalCount / data.size),
        count: processedSecurities.length
    }
}
/**
 * åˆ é™¤è®°å½•
 * @param {object} event - MQTT事件对象
 */
mqttService.delRecords = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°delRecords命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        let data = payload.payload && payload.payload.data ? payload.payload.data : {}
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(data))
        let res = this.delRecordsAgreement(data)
        if (typeof res == 'string') {
            logger.error('[mqttService] delRecords失败:', res)
            return reply(event, res, CODE.E_100)
        }
        logger.info('[mqttService] delRecords成功')
        return reply(event)
    } catch (error) {
        logger.error('[mqttService] delRecords error:', error)
        return reply(event, { error: error.message }, CODE.E_100)
    }
}
/**
 * åˆ é™¤è®°å½•通用协议格式
 * @param {object} data - åˆ é™¤å‚æ•°
 * @returns {boolean|string} true表示成功,string表示错误信息
 */
mqttService.delRecordsAgreement = function (data) {
    // æ ¹æ®æ—¶é—´èŒƒå›´åˆ é™¤è®°å½•
    if (data.startTime || data.endTime) {
        logger.info('[mqttService] æ—¶é—´èŒƒå›´: startTime=' + data.startTime + ', endTime=' + data.endTime)
        try {
            // æž„建查询条件
            let query = {};
            if (data.startTime) {
                query.startTime = data.startTime;
            }
            if (data.endTime) {
                query.endTime = data.endTime;
            }
            // ä½¿ç”¨getRecordsAgreement函数的查询逻辑来获取符合条件的记录
            let result = mqttService.getRecordsAgreement(query);
            let records = result.content || [];
            // é€ä¸ªåˆ é™¤è®°å½•
            let deletedCount = 0;
            for (let record of records) {
                // å¦‚果是人脸记录,删除对应的图片文件
                if (record.type == 300 && record.code) {
                    try {
                        common.systemBrief(`rm -rf ${record.code}`);
                    } catch (error) {
                        logger.error('[mqttService] åˆ é™¤å›¾ç‰‡æ–‡ä»¶å‡ºé”™: ' + error.message);
                    }
                }
                // åˆ é™¤è®°å½•
                sqliteService.d1_pass_record.delete({ id: record.id });
                deletedCount++;
            }
            logger.info('[mqttService] æˆåŠŸåˆ é™¤ ' + deletedCount + ' æ¡è®°å½•');
        } catch (error) {
            logger.error('[mqttService] åˆ é™¤è®°å½•出错: ' + error.message);
            // å¿½ç•¥é”™è¯¯ï¼Œè¿”回成功
        }
    }
    return true
}
/**
 * é€šè¡Œä¸ŠæŠ¥å›žå¤
 * @param {object} event - MQTT事件对象
 */
mqttService.access_reply = function (event) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°access_reply命令:', JSON.stringify(event.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', event.payload)
        let payload = JSON.parse(event.payload)
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(payload))
        let serialNo = map.get(payload.serialNo)
        if (serialNo) {
            logger.info('[mqttService] æ¸…理临时文件:', serialNo)
            common.systemBrief(`rm -rf ${serialNo}`)
            map.del(payload.serialNo)
        }
        logger.info('[mqttService] æ¸…空通行记录')
        sqliteService.d1_pass_record.deleteAll()
        logger.info('[mqttService] access_reply处理完成')
    } catch (error) {
        logger.error('[mqttService] access_reply error:', error)
    }
}
/**
 * åœ¨çº¿éªŒè¯å›žå¤
 * @param {object} raw - MQTT事件对象
 */
mqttService.access_online_reply = function (raw) {
    try {
        logger.info('[mqttService] æŽ¥æ”¶åˆ°access_online_reply命令:', JSON.stringify(raw.topic))
        logger.info('[mqttService] å‘½ä»¤payload:', raw.payload)
        let payload = JSON.parse(raw.payload)
        logger.info('[mqttService] è§£æžåŽçš„参数:', JSON.stringify(payload))
        let map = dxMap.get("VERIFY")
        let data = map.get(payload.serialNo)
        if (data) {
            logger.info('[mqttService] å¤„理在线验证回复,serialNo:', payload.serialNo)
            map.del(payload.serialNo)
            driver.mqtt.getOnlinecheckReply(payload)
        }
        logger.info('[mqttService] access_online_reply处理完成')
    } catch (error) {
        logger.error('[mqttService] access_online_reply error:', error)
    }
}
/**
 * é”™è¯¯ä»£ç å®šä¹‰
 */
const CODE = {
    // æˆåŠŸ
    S_000: "000000",
    // æœªçŸ¥é”™è¯¯
    E_100: "100000",
    // è®¾å¤‡å·²è¢«ç¦ç”¨
    E_101: "100001",
    // è®¾å¤‡æ­£å¿™ï¼Œè¯·ç¨åŽå†è¯•
    E_102: "100002",
    // ç­¾åæ£€éªŒå¤±è´¥
    E_103: "100003",
    // è¶…时错误
    E_104: "100004",
    // è®¾å¤‡ç¦»çº¿
    E_105: "100005",
}
mqttService.CODE = CODE
/**
 * ä¸ŠæŠ¥è®¾å¤‡ä¿¡æ¯å’Œé€šè¡Œè®°å½•
 */
mqttService.report = function () {
    // åœ¨çº¿ä¸ŠæŠ¥
    let payloadReply = mqttReply(std.genRandomStr(10), {
        mac: config.get("sys.mac") || '',
        version: config.get("sys.version"),
        appVersion: config.get("sys.version"),
        releaseTime: config.get("sys.createTime"),
        type: config.get("net.type"),
    }, CODE.S_000)
    driver.mqtt.send("access_device/v2/event/connect", JSON.stringify(payloadReply))
    //通行记录上报 - å·²å…³é—­
    // let res = sqliteService.d1_pass_record.findAll()
    // if (res.length <= 0) {
    //     return
    // }
    // // ç­›é€‰å‡º type === 300 çš„对象(人脸记录)
    // let faceArray = res.filter(item => item.type == 300);
    // // ç­›é€‰å‡º type !== 300 çš„对象(其他记录)
    // let recordArray = res.filter(item => item.type != 300);
    // if (recordArray.length > 0) {
    //     driver.mqtt.send("access_device/v2/event/access", JSON.stringify(mqttReply(std.genRandomStr(10), recordArray, CODE.S_000)))
    // }
    // if (faceArray.length > 0) {
    //     let index = 0
    //     let timer = std.setInterval(() => {
    //         let serialNo = std.genRandomStr(10)
    //         //缓存放入要删除的人脸照片 src
    //         map.del(serialNo)
    //         map.put(serialNo, faceArray[index].code)
    //
    //         // æ£€æŸ¥faceArray[index].code是否有效
    //         if (faceArray[index].code) {
    //             faceArray[index].code = driver.face.fileToBase64(faceArray[index].code)
    //         } else {
    //             faceArray[index].code = ""
    //             logger.info("人脸记录中code字段为空,跳过Base64转换")
    //         }
    //
    //         driver.mqtt.send("access_device/v2/event/access", JSON.stringify(mqttReply(serialNo, [faceArray[index]], CODE.S_000)))
    //         index++
    //         if (!faceArray[index]) {
    //             std.clearInterval(saveTimer)
    //             std.clearInterval(timer)
    //         }
    //     }, 1000)
    //     // æ¯éš”500ms检查一次mqtt连接状态,如果断开,则停止上报
    //     let saveTimer = std.setInterval(() => {
    //         if (!driver.mqtt.getStatus()) {
    //             std.clearInterval(saveTimer)
    //             std.clearInterval(timer)
    //         }
    //     }, 500)
    // }
}
/**
 * mqtt请求统一回复
 * @param {object} event - MQTT事件对象
 * @param {any} data - å›žå¤æ•°æ®
 * @param {string} code - é”™è¯¯ä»£ç 
 */
function reply(event, data, code) {
    try {
        let topic = getReplyTopic(event)
        let payload = JSON.parse(event.payload)
        let serialNo = payload.serialNo || std.genRandomStr(10)
        let reply = JSON.stringify(mqttReply(serialNo, data, isEmpty(code) ? CODE.S_000 : code))
        driver.mqtt.send(topic, reply)
    } catch (error) {
        logger.error('[mqttService] reply error:', error)
    }
}
/**
 * èŽ·å–å›žå¤ä¸»é¢˜
 * @param {object} data - MQTT事件对象
 * @returns {string} å›žå¤ä¸»é¢˜
 */
function getReplyTopic(data) {
    //    return data.topic.replace("/" + config.get("sys.sn"), '') + "_reply";
    try {
        let sn = config.get("mqtt.clientId")
        return data.topic.replace("/" + sn, '') + "_reply";
    } catch (error) {
        logger.error('[mqttService] getReplyTopic error:', error)
        // å›žé€€åˆ°ä½¿ç”¨å›ºå®šæ ¼å¼
        return data.topic + "_reply"
    }
}
/**
 * mqtt回复格式构建
 * @param {string} serialNo - åºåˆ—号
 * @param {any} data - å›žå¤æ•°æ®
 * @param {string} code - é”™è¯¯ä»£ç 
 * @returns {object} å›žå¤æ ¼å¼å¯¹è±¡
 */
function mqttReply(serialNo, data, code) {
    // ç”Ÿæˆå½“前时间的字符串格式
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const hours = String(now.getHours()).padStart(2, '0');
    const minutes = String(now.getMinutes()).padStart(2, '0');
    const seconds = String(now.getSeconds()).padStart(2, '0');
    const timeString = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    return {
        serialNo: serialNo,
        uuid: config.get("sys.uuid"),
        sign: '',
        code: code,
        data: data,
        time: timeString
    }
}
mqttService.mqttReply = mqttReply
/**
 * èŽ·å–æ‰€æœ‰è®¢é˜…çš„topic
 * @returns {array} è®¢é˜…çš„topic列表
 */
mqttService.getTopics = function () {
    // èŽ·å–æ‰€æœ‰è®¢é˜…çš„topic
    let sn = config.get("mqtt.clientId")
    const topics = [
        "control", "getConfig", "setConfig", "upgradeFirmware", "test",
        "getPermission", "insertPermission", "delPermission", "clearPermission",
        "getKey", "insertKey", "delKey", "clearKey",
        "getUser", "insertUser", "delUser", "clearUser",
        "getSecurity", "insertSecurity", "delSecurity", "clearSecurity", "getRecords", "delRecords",
        "insertEmergencyPassword", "delEmergencyPassword", "clearEmergencyPassword", "getEmergencyPassword"
    ]
    const eventReplies = ["connect_reply", "alarm_reply", "access_reply", "access_online_reply"]
    let flag = 'access_device/v2/cmd/' + sn + "/"
    let eventFlag = 'access_device/v2/event/' + sn + "/"
    return topics.map(item => flag + item).concat(eventReplies.map(item => eventFlag + item));
}
/**
 * åˆ¤ç©ºå‡½æ•°
 * @param {any} value - è¦åˆ¤æ–­çš„值
 * @returns {boolean} æ˜¯å¦ä¸ºç©º
 */
function isEmpty(value) {
    return value === undefined || value === null || value === ""
}
export default mqttService
/*
`mqttService.getTopics()` å‡½æ•°è¿”回的所有 topic å¦‚下:
### å‘½ä»¤ topic(用于接收服务器下发的命令):
- `access_device/v2/cmd/{sn}/control` - æŽ§åˆ¶å‘½ä»¤
- `access_device/v2/cmd/{sn}/getConfig` - èŽ·å–é…ç½®
- `access_device/v2/cmd/{sn}/setConfig` - è®¾ç½®é…ç½®
- `access_device/v2/cmd/{sn}/upgradeFirmware` - å›ºä»¶å‡çº§
- `access_device/v2/cmd/{sn}/test` - æµ‹è¯•命令
- `access_device/v2/cmd/{sn}/getPermission` - èŽ·å–æƒé™
- `access_device/v2/cmd/{sn}/insertPermission` - æ’入权限
- `access_device/v2/cmd/{sn}/delPermission` - åˆ é™¤æƒé™
- `access_device/v2/cmd/{sn}/clearPermission` - æ¸…除权限
- `access_device/v2/cmd/{sn}/getKey` - èŽ·å–å¯†é’¥
- `access_device/v2/cmd/{sn}/insertKey` - æ’入密钥
- `access_device/v2/cmd/{sn}/delKey` - åˆ é™¤å¯†é’¥
- `access_device/v2/cmd/{sn}/clearKey` - æ¸…除密钥
- `access_device/v2/cmd/{sn}/getUser` - èŽ·å–ç”¨æˆ·
- `access_device/v2/cmd/{sn}/insertUser` - æ’入用户
- `access_device/v2/cmd/{sn}/delUser` - åˆ é™¤ç”¨æˆ·
- `access_device/v2/cmd/{sn}/clearUser` - æ¸…除用户
- `access_device/v2/cmd/{sn}/getSecurity` - èŽ·å–å®‰å…¨ä¿¡æ¯
- `access_device/v2/cmd/{sn}/insertSecurity` - æ’入安全信息
- `access_device/v2/cmd/{sn}/delSecurity` - åˆ é™¤å®‰å…¨ä¿¡æ¯
- `access_device/v2/cmd/{sn}/clearSecurity` - æ¸…除安全信息
- `access_device/v2/cmd/{sn}/getRecords` - èŽ·å–è®°å½•
- `access_device/v2/cmd/{sn}/delRecords` - åˆ é™¤è®°å½•
### äº‹ä»¶å›žå¤ topic(用于接收服务器对事件的回复):
- `access_device/v2/event/{sn}/connect_reply` - è¿žæŽ¥å›žå¤
- `access_device/v2/event/{sn}/alarm_reply` - å‘Šè­¦å›žå¤
- `access_device/v2/event/{sn}/access_reply` - é€šè¡Œå›žå¤
- `access_device/v2/event/{sn}/access_online_reply` - åœ¨çº¿éªŒè¯å›žå¤
其中 `{sn}` æ˜¯è®¾å¤‡çš„序列号,会被替换为实际的设备序列号。
*/
vf205_access/src/service/nfcService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
/**
 * NFC服务模块
 * å¤„理NFC卡片相关的业务逻辑,包括身份证卡和云证的处理
 */
import log from '../../dxmodules/dxLogger.js'
import dxMap from '../../dxmodules/dxMap.js'
import accessService from '../service/accessService.js'
import config from '../../dxmodules/dxConfig.js'
import driver from '../driver.js';
const nfcService = {}
/**
 * æŽ¥æ”¶NFC卡片消息并处理
 * @param {object} data - NFC卡片数据
 * @param {string} [data.card_type] - å¡ç‰‡ç±»åž‹
 * @param {string} [data.id] - èº«ä»½è¯ç‰©ç†å¡å·
 * @param {string} [data.name] - å§“名(云证)
 * @param {string} [data.sex] - æ€§åˆ«ï¼ˆäº‘证)
 * @param {string} [data.idCardNo] - èº«ä»½è¯å·ç ï¼ˆäº‘证)
 */
nfcService.receiveMsg = function (data) {
    // log.info('[nfcService] receiveMsg :' + JSON.stringify(data))
    // é¦–先判断是否是身份证卡
    if (data.card_type && data.id) {
        if (dxMap.get("UI").get("getCardStart")) {
            driver.screen.getCard(data.id)
            return
        }
        // èº«ä»½è¯ç‰©ç†å¡å·/普通卡
        accessService.access({ type: "200", code: data.id })
    } else if (data.name && data.sex && data.idCardNo) {
        if (dxMap.get("UI").get("getCardStart")) {
            driver.screen.getCard(data.idCardNo)
            return
        }
        // äº‘证
        accessService.access({ type: "200", code: data.idCardNo });
    }
}
export default nfcService
vf205_access/src/service/platService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
/**
 * å¹³å°æœåŠ¡é€šä¿¡æ¨¡å—
 * å¤„理平台服务相关的业务逻辑,包括凭证下发和云证的处理
 */
vf205_access/src/service/sqliteService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,616 @@
/**
 * SQLite服务模块
 * æä¾›æ•°æ®åº“初始化、表结构管理、JPA风格的增删改查方法和事务管理
 */
import log from '../../dxmodules/dxLogger.js'
import sqlite from '../../dxmodules/dxSqlite.js'
//-------------------------variable-------------------------
const sqliteService = {}
//-------------------------public-------------------------
/**
 * åˆå§‹åŒ–数据库
 * @param {string} path - æ•°æ®åº“文件路径
 * @throws {Error} å¦‚果路径为空则抛出错误
 */
sqliteService.init = function (path) {
    if (!path) {
        throw new Error("path should not be null or empty")
    }
    // åˆ›å»ºæ•°æ®åº“
    sqlite.init(path)
    // åˆ›å»ºè¡¨
    createTables()
}
/**
 * æ•°æ®åº“表结构定义
 */
let entities = {
    d1_pass_record: {
        id: "VARCHAR(128) PRIMARY KEY",
        keyId: "VARCHAR(128)",
        permissionId: "VARCHAR(128)",
        permissionId2: "VARCHAR(128)",
        userId: "VARCHAR(128)",
        userId2: "VARCHAR(128)",
        type: "VARCHAR(128)",
        code: "VARCHAR(128)",
        door: "VARCHAR(128)",
        time: "INTEGER",
        result: "INTEGER",
        extra: "TEXT",
        extra2: "TEXT",
        message: "TEXT",
    },
    d1_permission: {
        permissionId: "VARCHAR(128) PRIMARY KEY",
        userId: "VARCHAR(128)",
        door: "VARCHAR(128)",
        extra: "TEXT",
        timeType: "INTEGER",
        beginTime: "INTEGER",
        endTime: "INTEGER",
        repeatBeginTime: "INTEGER",
        repeatEndTime: "INTEGER",
        period: "TEXT",
    },
    d1_security: {
        securityId: "VARCHAR(128) PRIMARY KEY",
        type: "VARCHAR(128)",
        key: "VARCHAR(128)",
        value: "TEXT",
        startTime: "INTEGER",
        endTime: "INTEGER",
    },
    d1_voucher: {
        keyId: "VARCHAR(128) PRIMARY KEY",
        type: "VARCHAR(128)",
        code: "TEXT",
        userId: "VARCHAR(128)",
        extra: "TEXT",
    },
    d1_person: {
        userId: "VARCHAR(128) PRIMARY KEY",
        name: "VARCHAR(128)",
        extra: "TEXT",
    },
    d1_emergency_password: {
        id: "VARCHAR(128) PRIMARY KEY",
        password: "VARCHAR(128)",
        description: "VARCHAR(256)",
        createTime: "INTEGER",
        updateTime: "INTEGER",
        status: "INTEGER"
    }
}
/**
 * SQL类型转JS类型
 * @param {string} sqlType - SQL数据类型
 * @returns {string} JavaScript数据类型
 */
let sqlType2jsType = (sqlType) => {
    if (sqlType.indexOf("INTEGER") > -1) {
        return 'number'
    } else {
        return 'string'
    }
}
/**
 * åˆ›å»ºè¡¨ç»“æž„
 * @throws {Error} å¦‚果表创建失败则抛出错误
 */
function createTables() {
    for (const tableName in entities) {
        const table = entities[tableName];
        let sql = `CREATE TABLE IF NOT EXISTS ${tableName} (`
        for (const column in table) {
            const type = table[column];
            sql += ` ${column} ${type},`
        }
        sql = sql.slice(0, -1);
        sql += ")"
        let ret = sqlite.exec(sql)
        if (ret != 0) {
            throw new Error(`table ${tableName} create exception: ${ret}`)
        }
    }
}
/**
 * åˆ›å»ºJPA风格的代理处理器
 */
let handler = {
    get: function (target, prop, receiver) {
        return (...args) => {
            return createJPA(prop, target.tableName, ...args)
        }
    }
}
// ä¸ºæ¯ä¸ªè¡¨åˆ›å»ºJPA风格的增删改查方法
sqliteService.d1_pass_record = new Proxy({ tableName: "d1_pass_record" }, handler);
// æ·»åŠ æ ¹æ®æ—¶é—´èŒƒå›´åˆ é™¤è®°å½•çš„æ–¹æ³•
sqliteService.d1_pass_record.deleteByTimeRange = function (startTime, endTime) {
    try {
        // æž„建查询条件
        let conditions = {};
        if (startTime) {
            conditions.time = {
                $gte: startTime
            };
        }
        if (endTime) {
            if (!conditions.time) {
                conditions.time = {};
            }
            conditions.time.$lte = endTime;
        }
        // ä½¿ç”¨JPA风格的delete方法
        let ret = sqliteService.d1_pass_record.delete(conditions);
        log.info('[sqliteService] deleteByTimeRange返回值: ' + ret);
        return ret;
    } catch (error) {
        log.error('deleteByTimeRange error:', error);
        return -1;
    }
};
sqliteService.d1_permission = new Proxy({ tableName: "d1_permission" }, handler);
sqliteService.d1_security = new Proxy({ tableName: "d1_security" }, handler);
sqliteService.d1_voucher = new Proxy({ tableName: "d1_voucher" }, handler);
sqliteService.d1_person = new Proxy({ tableName: "d1_person" }, handler);
sqliteService.d1_emergency_password = new Proxy({ tableName: "d1_emergency_password" }, handler);
/**
 * å¼€å§‹äº‹åŠ¡
 * äº‹åŠ¡ä¸æäº¤æ•°æ®åº“é‡å¯åŽï¼Œæ•°æ®ä¼šè¿˜åŽŸï¼Œæ‰€ä»¥transaction后一定要commit
 * å¦‚果在一个事务尚未提交或回滚的情况下执行另一个 BEGIN TRANSACTION,SQLite ä¼šè‡ªåŠ¨å°†æ–°çš„äº‹åŠ¡åµŒå¥—åœ¨ä¹‹å‰çš„äº‹åŠ¡å†…éƒ¨ï¼Œè€Œä¸æ˜¯è¦†ç›–ä¹‹å‰çš„äº‹åŠ¡ã€‚
 */
sqliteService.transaction = function () {
    sqlite.exec("BEGIN TRANSACTION;")
}
/**
 * å›žæ»šäº‹åŠ¡
 */
sqliteService.rollback = function () {
    sqlite.exec("ROLLBACK;")
}
/**
 * æäº¤äº‹åŠ¡
 * æäº¤åŽæ— æ³•回滚,数据无法还原
 */
sqliteService.commit = function () {
    sqlite.exec("COMMIT;")
}
/**
 * è‡ªåŠ¨åˆ›å»ºjpa常用增删改查sql方法,
 * æ”¯æŒçš„规则:findByAAndBAndC,findAll,findAllOrderByADescBAsc,deleteByAAndBAndC,deleteAll,deleteInBatch,deleteByIdInBatch,updateAByBAndCAndD,save,saveAll,count,countBy
 * æ¡ä»¶åˆ†é¡µæŸ¥è¯¢ï¼Œeg:findByAAndBAndC(x,x,x,{ page: 0, size: 200, å…¶ä»–条件, id:"123456" })
 * æ‰¹é‡åˆ é™¤ï¼Œeg:deleteInBatch([{ a: 1, b: 2, c: "3" }, { a: 2 }])
 * æ¡ä»¶åˆ é™¤ï¼Œeg:deleteAll({ a: 1, b: 2, c: "3" })
 * å•条件批量删除,eg:deleteByIdInBatch([1,2,3,4,5,6])
 * æ›´å¤šç¤ºä¾‹å¯å‚考下面测试方法
 * @param {string} methodName æ–¹æ³•名
 * @param {string} tableName è¡¨å
 * @param  {...any} nums æ–¹æ³•参数
 * @returns {any} sqlite执行结果
 */
function createJPA(methodName, tableName, ...nums) {
    let sql
    let isFind = false
    let isCount = false
    let noPageable = false
    let hasOrderBy = false
    if (methodName.startsWith("save")) {
        // å¢ž
        if (methodName.startsWith("saveAll")) {
            // æ‰¹é‡
            nums = nums[0]
            sql = `INSERT INTO ${tableName} VALUES `
            for (let i = 0; i < nums.length; i++) {
                const record = nums[i];
                sql += `(`
                for (const column in entities[tableName]) {
                    const item = record[column];
                    if (sqlType2jsType(entities[tableName][column]) == 'string') {
                        sql += `'${isEmpty(item) ? "" : item}',`
                    } else {
                        sql += `${isEmpty(item) ? 0 : item},`
                    }
                }
                sql = sql.slice(0, -1);
                sql += `)`
                if (i != nums.length - 1) {
                    sql += `, `
                }
            }
        } else {
            // å•条
            let record = nums[0]
            sql = `INSERT INTO ${tableName} VALUES (`
            for (const column in entities[tableName]) {
                const item = record[column];
                if (sqlType2jsType(entities[tableName][column]) == 'string') {
                    sql += `'${isEmpty(item) ? "" : item}',`
                } else {
                    sql += `${isEmpty(item) ? 0 : item},`
                }
            }
            sql = sql.slice(0, -1);
            sql += `)`
        }
        methodName = ""
        noPageable = true
    } else if (methodName.startsWith("delete")) {
        // åˆ 
        if (methodName.startsWith("deleteAll")) {
            // æ¸…空表
            sql = `DELETE FROM ${tableName} `
            methodName = ""
        } else if (methodName.endsWith("InBatch")) {
            if (nums.length != 1) {
                log.error("[JPA]:", "缺少参数")
                return
            }
            sql = `DELETE FROM ${tableName} WHERE `
            if (methodName.indexOf("By") > -1) {
                methodName = methodName.split("By")[1].split("InBatch")[0]
                sql += `${firstLower(methodName)} IN `
                let whereClauses = ""
                for (let i = 0; i < nums[0].length; i++) {
                    const value = nums[0][i];
                    if (typeof value == 'string') {
                        whereClauses += `'${value}'`
                    } else {
                        whereClauses += `${value} `
                    }
                    if (i != nums[0].length - 1) {
                        whereClauses += ","
                    }
                }
                sql += `(${whereClauses})`
            } else {
                for (let i = 0; i < nums[0].length; i++) {
                    let whereClauses = ""
                    const record = nums[0][i];
                    for (const column in record) {
                        const value = record[column];
                        if (typeof value == 'string') {
                            whereClauses += `${column} = '${value}'`
                        } else {
                            whereClauses += `${column} = ${value}`
                        }
                        whereClauses += ` AND `
                    }
                    whereClauses = whereClauses.slice(0, " AND ".length * (-1))
                    sql += `(${whereClauses})`
                    if (i != nums[0].length - 1) {
                        sql += ` OR `
                    }
                }
            }
            methodName = ""
            noPageable = true
        } else {
            sql = `DELETE FROM ${tableName} `
            methodName = methodName.substring("delete".length)
        }
    } else if (methodName.startsWith("update")) {
        // æ”¹
        sql = `UPDATE ${tableName} SET`
        methodName = methodName.substring("update".length)
    } else if (methodName.startsWith("find")) {
        // æŸ¥
        isFind = true
        sql = `SELECT * FROM ${tableName} `
        if (methodName.startsWith("findAll")) {
            methodName = methodName.substring("findAll".length)
        } else {
            methodName = methodName.substring("find".length)
        }
        let index = methodName.indexOf("OrderBy")
        if (index > -1) {
            hasOrderBy = methodName.substring(index + "OrderBy".length).match(/\w+?(Desc|Asc)/g)
            methodName = methodName.substring(0, index)
        }
    } else if (methodName.startsWith("count")) {
        // ç»Ÿè®¡
        isFind = true
        isCount = true
        sql = `SELECT COUNT(*) FROM ${tableName} `
        methodName = methodName.substring("count".length)
    } else {
        log.error("[JPA]:", "不支持的方法")
        return
    }
    // where条件构建
    let index = methodName.indexOf("By")
    let whereClauses = ""
    if (index > -1) {
        let count = 0
        let conditionsPart = methodName.substring(index + 2)
        if (conditionsPart.indexOf("And") > -1) {
            conditionsPart = conditionsPart.split("And")
            if (nums.length < conditionsPart.length) {
                log.error("[JPA]:", "缺少参数")
                return
            }
            for (let i = 0; i < conditionsPart.length; i++) {
                const field = conditionsPart[i];
                if (typeof nums[i] == 'string') {
                    whereClauses += `${firstLower(field)} = '${nums[i]}'`
                } else {
                    whereClauses += `${firstLower(field)} = ${nums[i]}`
                }
                if (i != conditionsPart.length - 1) {
                    whereClauses += ` AND `
                }
                count = i
            }
        } else if (conditionsPart.indexOf("Or") > -1) {
            conditionsPart = conditionsPart.split("Or")
            if (nums.length < conditionsPart.length) {
                log.error("[JPA]:", "缺少参数")
                return
            }
            for (let i = 0; i < conditionsPart.length; i++) {
                const field = conditionsPart[i];
                if (typeof nums[i] == 'string') {
                    whereClauses += `${firstLower(field)} = '${nums[i]}'`
                } else {
                    whereClauses += `${firstLower(field)} = ${nums[i]}`
                }
                if (i != conditionsPart.length - 1) {
                    whereClauses += ` OR `
                }
                count = i
            }
        } else {
            if (nums.length < 1) {
                log.error("[JPA]:", "缺少参数")
                return
            }
            if (typeof nums[0] == 'string') {
                whereClauses = `${firstLower(conditionsPart)} = '${nums[0]}' `
            } else {
                whereClauses = `${firstLower(conditionsPart)} = ${nums[0]} `
            }
        }
        count++
        // update的set项构建
        let setClauses = ""
        let prefix = methodName.substring(0, index);
        if (prefix.length > 0) {
            prefix = prefix.split("And")
            if ((nums.length - count) < prefix.length) {
                log.error("[JPA]:", "缺少参数")
                return
            }
            for (let i = 0; i < prefix.length; i++) {
                const field = prefix[i];
                if (typeof nums[i + count] == 'string') {
                    setClauses += `${firstLower(field)} = '${nums[i + count]}',`
                } else {
                    setClauses += `${firstLower(field)} = ${nums[i + count]},`
                }
            }
            setClauses = setClauses.slice(0, -1)
            sql += ` ${setClauses} `
        }
        sql += `WHERE ${whereClauses} `
    }
    // order排序
    let orderByClauses = ""
    if (hasOrderBy) {
        orderByClauses = "ORDER BY "
        let conditionsPart = hasOrderBy
        for (let i = 0; i < conditionsPart.length; i++) {
            const orderItem = conditionsPart[i];
            let isDesc = orderItem.indexOf("Desc")
            let isAsc = orderItem.indexOf("Asc")
            if (isDesc > -1) {
                orderByClauses += `${firstLower(orderItem.substring(0, isDesc))} DESC,`
            }
            if (isAsc > -1) {
                orderByClauses += `${firstLower(orderItem.substring(0, isAsc))} ASC,`
            }
        }
        orderByClauses = orderByClauses.slice(0, -1)
    }
    // åˆ¤æ–­åˆ†é¡µæ¡ä»¶æŸ¥è¯¢
    let pageable = nums[nums.length - 1]
    if (typeof pageable == 'object' && !noPageable) {
        let clauses = ""
        for (const key in pageable) {
            const condition = pageable[key];
            if (key == "page" || key == "size") {
                continue
            }
            if (typeof condition == 'string') {
                clauses += `${firstLower(key)} = '${condition}'`
            } else {
                clauses += `${firstLower(key)} = ${condition}`
            }
            clauses += ` AND `
        }
        if (clauses.length > 0) {
            clauses = clauses.slice(0, " AND ".length * (-1))
            if (sql.indexOf("WHERE") > -1) {
                sql += `AND ${clauses} `
            } else {
                sql += `WHERE ${clauses} `
            }
        }
        sql += `${orderByClauses} `
        if (isFind && !isCount && !isEmpty(pageable.page) && !isEmpty(pageable.size)) {
            sql += `LIMIT ${pageable.size} OFFSET ${pageable.page * pageable.size} `
        }
    } else {
        sql += `${orderByClauses} `
    }
    sql += `;`;
    // log.info("[JPA]:", sql)
    let ret
    if (isFind) {
        ret = sqlite.select(sql)
        if (isCount) {
            if (ret[0] && ret[0]["COUNT(*)"]) {
                return ret[0]["COUNT(*)"]
            } else {
                return 0
            }
        }
    } else {
        ret = sqlite.exec(sql)
    }
    return ret
}
/**
 * åˆ¤ç©ºå‡½æ•°
 * @param {any} value - è¦åˆ¤æ–­çš„值
 * @returns {boolean} æ˜¯å¦ä¸ºç©º
 */
function isEmpty(value) {
    return value === undefined || value === null
}
/**
 * é¦–字母小写
 * @param {string} str - å­—符串
 * @returns {string} é¦–字母小写的字符串
 */
function firstLower(str) {
    return str.charAt(0).toLowerCase() + str.slice(1);
}
/**
 * JPA测试方法
 * æä¾›å„种JPA方法的使用示例
 */
sqliteService.testJPA = function () {
    // æŸ¥è¯¢
    // SELECT * FROM d1_pass_record ;
    sqliteService.d1_pass_record.find()
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.find({ a: 1, b: 2 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.find({ a: 1, b: 2, page: 1 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 LIMIT 1 OFFSET 1 ;
    sqliteService.d1_pass_record.find({ a: 1, b: 2, page: 1, size: 1 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 ;
    sqliteService.d1_pass_record.findByAAndBAndC(1, 2, 3)
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 AND a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.findByAAndBAndC(1, 2, 3, { a: 1, b: 2 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 AND a = 1 AND b = 2 LIMIT 1 OFFSET 1 ;
    sqliteService.d1_pass_record.findByAAndBAndC(1, 2, 3, { a: 1, b: 2, page: 1, size: 1 })
    // SELECT * FROM d1_pass_record ;
    sqliteService.d1_pass_record.findAll()
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.findAll({ a: 1, b: 2 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.findAll({ a: 1, b: 2, page: 1 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 LIMIT 1 OFFSET 1 ;
    sqliteService.d1_pass_record.findAll({ a: 1, b: 2, page: 1, size: 1 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 ;
    sqliteService.d1_pass_record.findAllByAAndBAndC(1, 2, 3)
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 AND a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.findAllByAAndBAndC(1, 2, 3, { a: 1, b: 2 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 AND a = 1 AND b = 2 LIMIT 1 OFFSET 1 ;
    sqliteService.d1_pass_record.findAllByAAndBAndC(1, 2, 3, { a: 1, b: 2, page: 1, size: 1 })
    // SELECT * FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 AND a = 1 AND b = 2 ORDER BY a DESC,b ASC,c ASC LIMIT 1 OFFSET 1 ;
    sqliteService.d1_pass_record.findAllByAAndBAndCOrderByADescBAscCAsc(1, 2, 3, { a: 1, b: 2, page: 1, size: 1 })
    // åˆ é™¤
    // DELETE FROM d1_pass_record ;
    sqliteService.d1_pass_record.delete()
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.delete({ a: 1, b: 2 })
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.delete({ a: 1, b: 2, page: 1 })
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.delete({ a: 1, b: 2, page: 1, size: 1 })
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 ;
    sqliteService.d1_pass_record.deleteByAAndBAndC(1, 2, 3)
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 AND a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.deleteByAAndBAndC(1, 2, 3, { a: 1, b: 2 })
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 AND a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.deleteByAAndBAndC(1, 2, 3, { a: 1, b: 2, page: 1, size: 1 })
    // DELETE FROM d1_pass_record ;
    sqliteService.d1_pass_record.deleteAll()
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.deleteAll({ a: 1, b: 2 })
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.deleteAll({ a: 1, b: 2, page: 1 })
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.deleteAll({ a: 1, b: 2, page: 1, size: 1 })
    // DELETE FROM d1_pass_record ;
    sqliteService.d1_pass_record.deleteAllByAAndBAndC(1, 2, 3)
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.deleteAllByAAndBAndC(1, 2, 3, { a: 1, b: 2 })
    // DELETE FROM d1_pass_record WHERE a = 1 AND b = 2 ;
    sqliteService.d1_pass_record.deleteAllByAAndBAndC(1, 2, 3, { a: 1, b: 2, page: 1, size: 1 })
    // DELETE FROM d1_pass_record WHERE (a = 1 AND b = 2) OR (a = 1 AND b = 2 AND page = 1) OR (a = 1 AND b = 2 AND page = 1 AND size = 1);
    sqliteService.d1_pass_record.deleteInBatch([{ a: 1, b: 2 }, { a: 1, b: 2, page: 1 }, { a: 1, b: 2, page: 1, size: 1 }])
    // DELETE FROM d1_pass_record WHERE id IN (1 ,2 ,3 );
    sqliteService.d1_pass_record.deleteByIdInBatch([1, 2, 3])
    // æ›´æ–°
    // UPDATE d1_pass_record SET a = 4 WHERE b = 1 AND c = 2 AND d = 3 ;
    sqliteService.d1_pass_record.updateAByBAndCAndD(1, 2, 3, 4)
    // UPDATE d1_pass_record SET a = 4,b = 5,c = 6 WHERE d = 1 AND e = 2 AND f = 3 ;
    sqliteService.d1_pass_record.updateAAndBAndCByDAndEAndF(1, 2, 3, 4, 5, 6)
    // æ·»åŠ 
    // INSERT INTO d1_pass_record VALUES (,,,,,,,0,0,,);
    sqliteService.d1_pass_record.save({ a: 1, b: 2 })
    // INSERT INTO d1_pass_record VALUES (,,,,,,,0,0,,), (,,,,,,,0,0,,);
    sqliteService.d1_pass_record.saveAll([{ a: 1, b: 2 }, { a: 1, b: 2 }])
    // èšåˆ
    // SELECT COUNT(*) FROM d1_pass_record ;
    sqliteService.d1_pass_record.count();
    // SELECT COUNT(*) FROM d1_pass_record WHERE a = 1 AND b = 2 AND c = 3 ;
    sqliteService.d1_pass_record.countByAAndBAndC(1, 2, 3);
}
/**
 * å®‰å…¨å¯†é’¥æŸ¥è¯¢
 * @param {string} code - ä»£ç 
 * @param {string} type - ç±»åž‹
 * @param {string} id - å®‰å…¨ID
 * @param {number} time - æ—¶é—´
 * @param {string} key - å¯†é’¥
 * @param {string} index - ç´¢å¼•
 * @returns {array} æŸ¥è¯¢ç»“æžœ
 */
sqliteService.securityFindAllByCodeAndTypeAndTimeAndkey = function (code, type, id, time, key, index) {
    var query = `SELECT * FROM d1_security WHERE 1=1`
    if (code) {
        query += ` AND code = '${code}'`
    }
    if (type) {
        query += ` AND type = '${type}'`
    }
    if (id) {
        query += ` AND securityId = '${id}'`
    }
    if (index) {
        query += ` AND door = '${index}'`
    }
    if (key) {
        query += ` AND key = '${key}'`
    }
    if (time) {
        query += ` AND endTime >= '${time}'`
    }
    return sqlite.select(query)
}
export default sqliteService
vf205_access/src/service/uart485Service.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,190 @@
/**
 * UART485服务模块
 * å¤„理UART485通信相关的业务逻辑,包括条码数据接收、设备配置管理等功能
 */
import bus from "../../dxmodules/dxEventBus.js"
import common from "../../dxmodules/dxCommon.js"
import log from "../../dxmodules/dxLogger.js"
import std from '../../dxmodules/dxStd.js'
import driver from '../driver.js'
import utils from '../common/utils/utils.js'
import dxMap from '../../dxmodules/dxMap.js'
import config from '../../dxmodules/dxConfig.js'
const uart485Service = {}
/**
 * å°†åè¿›åˆ¶æ•°è½¬æ¢ä¸ºå°ç«¯åºåå…­è¿›åˆ¶å­—符串
 * @param {number} decimalNumber - åè¿›åˆ¶æ•°å­—
 * @param {number} byteSize - å­—节大小
 * @returns {string} å°ç«¯åºåå…­è¿›åˆ¶å­—符串
 */
function decimalToLittleEndianHex (decimalNumber, byteSize) {
  const littleEndianBytes = [];
  for (let i = 0; i < byteSize; i++) {
    littleEndianBytes.push(decimalNumber & 0xFF);
    decimalNumber >>= 8; // ç›¸å½“于除以256
  }
  const littleEndianHex = littleEndianBytes
    .map((byte) => byte.toString(16).padStart(2, '0'))
    .join('');
  return littleEndianHex;
}
/**
 * å°†æ•°æ®åŒ…转换为字符串格式
 * @param {object} pack - æ•°æ®åŒ…对象
 * @param {string} pack.cmd - å‘½ä»¤ç 
 * @param {string} pack.result - ç»“果码
 * @param {string} [pack.data] - æ•°æ®
 * @returns {string} è½¬æ¢åŽçš„字符串
 */
function pack2str (pack) {
  pack.data = (!pack.data) ? [] : pack.data.match(/.{2}/g)
  let len = decimalToLittleEndianHex(pack.data.length, 2)
  let str = "55aa" + pack.cmd + pack.result + len + pack.data.join('')
  let crc = common.calculateBcc([0x55, 0xaa, parseInt(pack.cmd, 16), parseInt(pack.result, 16), pack.data.length % 256, pack.data.length / 256].concat(pack.data.map(v => parseInt(v, 16))))
  return str + crc.toString(16).padStart(2, '0')
}
/**
 * æŽ¥æ”¶UART485数据并处理
 * @param {object} data - æŽ¥æ”¶åˆ°çš„æ•°æ®
 * @param {string} type - æ•°æ®ç±»åž‹ï¼Œ'code'表示条码数据,'instruction'表示指令
 */
uart485Service.receive = function (data, type) {
  log.info("code:",JSON.stringify(data))
  if (type == 'code') {
    if(data.cmd == "30") {
      if(data.length > 0) {
        let code = common.hexToString(data.data)
        const now = new Date().getTime()
        let map = dxMap.get("CODETIME")
        let time = map.get("time") || 0
        let interval = Math.max(1000, config.get("sys.interval"))
        if(now -  time > interval) {
          bus.fire("getCode", code)
          map.put("time", new Date().getTime())
        }
      }
    }
  }
  if (type == 'instruction') {
    if (data.cmd == "0a") {
      // èŽ·å–è®¾å¤‡SN
      if (data.length > 0) {
        console.log('---0A写入--');
        let newSn = common.hexToString(data.data)
        //修改 sn å·æ”¹æˆä¼ å…¥å‚æ•°
        try {
          let wgetApp = common.systemWithRes(`test -e "/etc/.sn" && echo "OK" || echo "NO"`, 2)
          if (!wgetApp.includes('OK')) {
            //没有创建一下
            common.systemBrief("touch /etc/.sn")
          }
          std.saveFile('/etc/.sn', newSn)
          common.systemWithRes(`rm -rf /app/data/config/config.json`, 2)
        } catch (error) {
          log.info('0A写入 sn å¤±è´¥åŽŸå› :', error.stack)
          let pack1 = { "cmd": '0A', "result": '90', 'data': '' }
          driver.uart485.sendVg(pack2str(pack1))
          return
        }
        //返回串口
        let pack1 = { "cmd": '0A', "result": '00', 'data': common.stringToHex(newSn) }
        driver.uart485.sendVg(pack2str(pack1))
        common.asyncReboot(2)
      } else {
        log.info('-----0A查询-----', common.getSn());
        let pack1 = { "cmd": '0A', "result": '00', "data": common.stringToHex(common.getSn()) }
        // log.info(pack2str(pack1));
        driver.uart485.sendVg(pack2str(pack1))
      }
    } else if (data.cmd == "b0") {
        log.info("----b0---")
      // æŸ¥è¯¢/修改设备配置
      let str = data.data
      if (!str) {
        return
      }
      //数据域第一个字节表示修改还是查询   00 æŸ¥è¯¢ 01 ä¿®æ”¹
      if (parseInt(str.substring(0, 2)) == 0) {
        //查询配置
        let pack1 = { "cmd": 'B0', "result": '00', "data": common.stringToHex(common.getSn()) }
        driver.uart485.sendVg(pack2str(pack1))
      } else {
        //修改配置
        if (data.dlen <= 1) {
          return
        }
        // ___VBAR_CONFIG_V1.1.0___{ble_name="11127S"}--lLqHBRnE2bU8D2HJ5RTioQ==
        let toString = common.hexToString(str.substring(2))
        let content = parseString(toString)
        if (content.sn) {
          //修改 sn å·æ”¹æˆä¼ å…¥å‚æ•°
          try {
            let wgetApp = common.systemWithRes(`test -e "/etc/.sn" && echo "OK" || echo "NO"`, 2)
            if (!wgetApp.includes('OK')) {
              //没有创建一下
              common.systemBrief("touch /etc/.sn")
            }
            std.saveFile('/etc/.sn', content.sn)
            common.systemWithRes(`rm -rf /app/data/config/config.json`, 2)
          } catch (error) {
            log.info('写入/etc/.sn文件失败,原因:', error.stack)
            let pack1 = { "cmd": 'B0', "result": '90', "data": common.stringToHex(common.getSn()) }
            driver.uart485.sendVg(pack2str(pack1))
            return
          }
          //返回串口
          let pack1 = { "cmd": 'B0', "result": '00', "data": common.stringToHex(content.sn) }
          driver.uart485.sendVg(pack2str(pack1))
          common.asyncReboot(2)
        }
      }
    } else if (data.cmd == "0c") {
        log.info("----0c--")
      // èŽ·å–ä¸»æŽ§chipID
      let pack = { "cmd": '0C', "result": '00', "data": common.stringToHex(common.getUuid()) }
      driver.uart485.sendVg(pack2str(pack))
    }
  }
}
/**
 * è§£æžå­—符串为JSON对象,注意value内不能有"号
 * @param {string} inputString - è¾“入字符串
 * @returns {object} è§£æžåŽçš„JSON对象
 */
utils.parseString = function (inputString) {
    // èŽ·å–{}及其之间的内容
    inputString = inputString.slice(inputString.indexOf("{"), inputString.lastIndexOf("}") + 1)
    // key=value正则,key是\w+(字母数字下划线,区别大小写),=两边可有空格,value是\w+或相邻两个"之间的内容(包含")
    const keyValueRegex = /(\w+)\s*=\s*("[^"]*"|\w+)/g;
    let jsonObject = {};
    let match;
    while ((match = keyValueRegex.exec(inputString)) !== null) {
        let key = match[1];
        let value = match[2]
        if (/^\d+$/.test(value)) {
            // æ•°å­—
            value = parseInt(value)
        } else if (/^\d+\.\d+$/.test(value)) {
            // å°æ•°
            value = parseFloat(value)
        } else if (value == 'true') {
            value = true
        } else if (value == 'false') {
            value = false
        } else {
            // å­—符串
            value = value.replace(/"/g, '').trim()
        }
        jsonObject[key] = value;
    }
    return jsonObject;
}
export default uart485Service
vf205_access/src/services.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
/**
 * æœåŠ¡æ± é…ç½®æ–‡ä»¶
 * æ³¨å†Œç³»ç»Ÿä¸­æ‰€æœ‰æœåŠ¡çš„äº‹ä»¶å¤„ç†å›žè°ƒï¼Œå¤„ç†æ¥è‡ªå„æ¨¡å—çš„äº‹ä»¶
 */
import pool from '../dxmodules/dxWorkerPool.js'
import face from '../dxmodules/dxFace.js'
import driver from './driver.js'
import bus from '../dxmodules/dxEventBus.js'
import faceService from './service/faceService.js'
import net from '../dxmodules/dxNet.js'
import config from '../dxmodules/dxConfig.js'
import nfc from '../dxmodules/dxNfc.js'
import mqtt from '../dxmodules/dxMqtt.js'
import map from '../dxmodules/dxMap.js'
import mqttService from './service/mqttService.js'
import accessService from './service/accessService.js'
import nfcService from './service/nfcService.js'
import common from '../dxmodules/dxCommon.js'
import log from '../dxmodules/dxLogger.js'
import dxGpioKey from '../dxmodules/dxGpioKey.js'
import uart from '../dxmodules/dxUart.js'
import uart485Service from './service/uart485Service.js'
import configService from './service/configService.js'
import grainService from './service/grainService.js'
import gpiokeyService from './service/gpiokeyService.js'
import codeService from './service/codeService.js'
/**
 * æœåŠ¡æ± å›žè°ƒå‡½æ•°
 * å¤„理来自各模块的事件消息,根据事件主题分发到相应的服务处理
 * @param {object} data - äº‹ä»¶æ•°æ®
 * @param {string} data.topic - äº‹ä»¶ä¸»é¢˜
 * @param {any} data.data - äº‹ä»¶æ•°æ®
 */
pool.callback((data) => {
    let topic = data.topic
    let msg = data.data
    switch (topic) {
        case face.RECEIVE_MSG:
            // å¤„理人脸识别消息
            faceService.receiveMsg(msg)
            break;
        case dxGpioKey.RECEIVE_MSG:
            // å¤„理GPIO按键消息
            gpiokeyService.receiveMsg(msg)
            break;
        case "netGetWifiSsidList":
            // èŽ·å–WiFi列表
            let wifiList = driver.net.netGetWifiSsidList()
            bus.fire("netWifiSsidList", wifiList)
            break;
        case "switchNetworkType":
            // åˆ‡æ¢ç½‘络类型
            config.setAndSave("net.type", msg)
            console.log("切换网络", msg);
            driver.net.changeNetType()
            break;
        case "setConfig":
            // é…ç½®éªŒè¯å’Œä¿å­˜
            configService.configVerifyAndSave(msg)
            break;
        case "access":
            // å¤„理通行验证
            accessService.access(msg.data, msg.fileName, msg.similarity)
            break;
        case nfc.RECEIVE_MSG:
            // å¤„理NFC卡片消息
            nfcService.receiveMsg(msg)
            break;
        case net.STATUS_CHANGE:
            // ç½‘络状态变化
            map.get("NET").put("status", msg.status)
            bus.fire("netStatus", msg)
            break;
        case mqtt.CONNECTED_CHANGED:
            // MQTT连接状态变化
            bus.fire("mqttStatus", msg)
            // mqtt连接上报
            if (msg == "connected") {
                mqttService.report()
            }
            break;
        case mqtt.RECEIVE_MSG:
            // å¤„理MQTT消息
            mqttService.receiveMsg(msg)
            break;
        case uart.VG.RECEIVE_MSG + driver.uart485.id:
            // å¤„理UART485指令消息
            uart485Service.receive(msg, 'instruction')
            break;
        case uart.VG.RECEIVE_MSG + driver.uartCode.id:
            // å¤„理UART码消息
            uart485Service.receive(msg, 'code')
            break;
        case "getCode":
            // å¤„理条码数据
            codeService.code(msg)
            break;
        case "trackResult":
            // å¤„理人脸识别结果事件(由mainView.js处理)
            break;
        default:
            // æœªçŸ¥ä¸»é¢˜
            log.error("No such topic ", topic)
            break;
    }
})
vf205_access/src/ui.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
import ui from "../dxmodules/dxUi.js";
import std from "../dxmodules/dxStd.js"
// ui上下文
let context = {}
// ui初始化
ui.init({ orientation: 0 }, context);
const screenMain = ui.View.build('mainView', ui.Utils.LAYER.MAIN)
const bottomSnBtn = ui.Button.build('bottomSnBtn', screenMain)
bottomSnBtn.bgColor(0xff0000)
bottomSnBtn.bgOpa(20)
bottomSnBtn.setSize(200, 100)
bottomSnBtn.setPos(100, 700)
bottomSnBtn.on(ui.Utils.EVENT.CLICK, () => {
    print("passwordView")
})
// åŠ è½½å±å¹•
ui.loadMain(screenMain)
// åˆ·æ–°ui
let timer = std.setInterval(() => {
    if (ui.handler() < 0) {
        std.clearInterval(timer)
    }
}, 1)
vf205_access/src/view/appView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
import dxui from '../../dxmodules/dxUi.js'
import std from '../../dxmodules/dxStd.js'
import viewUtils from "./viewUtils.js"
import topView from './topView.js'
import mainView from './mainView.js'
import i18n from './i18n.js'
const appView = {}
appView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('appView', dxui.Utils.LAYER.MAIN)
    appView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        appQrcode.source('/app/code/resource/image/app_qrcode.png')
        // æ— æ“ä½œ10秒自动返回
        if (appView.timer) {
            std.clearInterval(appView.timer)
        }
        appView.timer = std.setInterval(() => {
            let count = dxui.Utils.GG.NativeDisp.lvDispGetInactiveTime()
            if (count > 10 * 1000) {
                std.clearInterval(appView.timer)
                appView.timer = null
                dxui.loadMain(mainView.screenMain)
            }
        }, 1000)
    })
    const appQrcode = dxui.Image.build('appQrcode', screenMain)
    appQrcode.source('/app/code/resource/image/app_qrcode.png')
    appQrcode.align(dxui.Utils.ALIGN.TOP_MID, 0, 206)
    const knowedBtn = viewUtils.bottomBtn(screenMain, 'knowedBtn', 'appView.knowed', () => {
        dxui.loadMain(mainView.screenMain)
    })
    knowedBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -124)
    const appQrcodeLbl = dxui.Label.build('appQrcodeLbl', screenMain)
    appQrcodeLbl.text('使用小程序便捷管理')
    appQrcodeLbl.textFont(viewUtils.font(30))
    appQrcodeLbl.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -403)
    appQrcodeLbl.dataI18n = 'appView.appQrcodeLbl'
}
export default appView
vf205_access/src/view/config/configView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
import dxui from "../../../dxmodules/dxUi.js"
import config from "../../../dxmodules/dxConfig.js"
import viewUtils from "../viewUtils.js"
import topView from "../topView.js"
import mainView from "../mainView.js"
import cloudCertView from "./menu/cloudCertView.js"
import doorControlView from "./menu/doorControlView.js"
import helpView from "./menu/helpView.js"
import networkSettingView from "./menu/networkSettingView.js"
import systemSettingView from "./menu/systemSettingView.js"
import deviceInfoView from "./menu/deviceInfoView.js"
import factoryTestView from "./menu/factoryTestView.js"
import localUserView from "./menu/localUserView.js"
import recordQueryView from "./menu/recordQueryView.js"
import voiceBroadcastView from "./menu/voiceBroadcastView.js"
import screen from '../../screen.js'
const configView = {}
configView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('configView', dxui.Utils.LAYER.MAIN)
    configView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
    })
    // const confirm = viewUtils.confirmWin('configViewConfirm', 'configView.confirmExit', () => {
    //     dxui.loadMain(mainView.screenMain)
    // })
    const titleBox = viewUtils.title(screenMain, undefined, 'configViewTitle', 'configView.title', () => {
        viewUtils.confirmOpen('configView.confirmExit', 'configView.confirmExitContent', () => {
            dxui.loadMain(mainView.screenMain)
        }, () => { })
    })
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const menuBox = dxui.View.build('menuBox', screenMain)
    viewUtils._clearStyle(menuBox)
    menuBox.setSize(screen.screenSize.width, 800)
    menuBox.bgOpa(0)
    menuBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 178)
    menuBox.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    menuBox.flexAlign(dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START)
    menuBox.obj.lvObjSetStylePadGap(0, dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME)
    configView.menuBtn('localUser', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/localUser.png', 'configView.localUser', () => {
        dxui.loadMain(localUserView.screenMain)
    })
    configView.menuBtn('networkSetting', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/networkSetting.png', 'configView.networkSetting', () => {
        dxui.loadMain(networkSettingView.screenMain)
    })
    configView.menuBtn('doorControl', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/doorControl.png', 'configView.doorControl', () => {
        dxui.loadMain(doorControlView.screenMain)
    })
    configView.menuBtn('systemSetting', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/systemSetting.png', 'configView.systemSetting', () => {
        dxui.loadMain(systemSettingView.screenMain)
    })
    configView.menuBtn('deviceInfo', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/deviceInfo.png', 'configView.deviceInfo', () => {
        dxui.loadMain(deviceInfoView.screenMain)
    })
    configView.menuBtn('recordQuery', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/recordQuery.png', 'configView.recordQuery', () => {
        dxui.loadMain(recordQueryView.screenMain)
    })
    configView.menuBtn('voiceBroadcast', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/voiceBroadcast.png', 'configView.voiceBroadcast', () => {
        dxui.loadMain(voiceBroadcastView.screenMain)
    })
    if (config.get("base.showIdentityCard") == 1) {
        configView.menuBtn('cloudCert', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/cloudCert.png', 'configView.cloudCert', () => {
            dxui.loadMain(cloudCertView.screenMain)
        })
    }
    configView.menuBtn('help', menuBox, screen.screenSize.width / 4, screen.screenSize.width / 4, '/app/code/resource/image/help.png', 'configView.help', () => {
        dxui.loadMain(helpView.screenMain)
    })
}
configView.menuBtn = function (id, parent, width, height, src, dataI18n, callback = () => { }) {
    const box = dxui.View.build(id, parent)
    viewUtils._clearStyle(box)
    box.setSize(width, height)
    box.bgOpa(0)
    const zoom = 1.02
    const bg = dxui.View.build(id + 'bg', box)
    viewUtils._clearStyle(bg)
    bg.setSize(140, 140)
    bg.bgColor(0xf6f6f6)
    bg.radius(30)
    bg.align(dxui.Utils.ALIGN.TOP_MID, 0, 10)
    const image = dxui.Image.build(id + 'image', bg)
    image.source(src)
    image.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    bg.on(dxui.Utils.ENUM.LV_EVENT_PRESSED, () => {
        bg.setSize(140 * zoom, 140 * zoom)
        image.obj.lvImgSetZoom(256 * zoom)
    })
    bg.on(dxui.Utils.ENUM.LV_EVENT_RELEASED, () => {
        bg.setSize(140, 140)
        image.obj.lvImgSetZoom(256)
    })
    bg.on(dxui.Utils.EVENT.CLICK, () => {
        callback()
    })
    const textLbl = dxui.Label.build(id + 'text', box)
    textLbl.textFont(viewUtils.font(22))
    textLbl.textColor(0x767676)
    textLbl.dataI18n = dataI18n
    textLbl.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -10)
    textLbl.width(width)
    textLbl.textAlign(dxui.Utils.TEXT_ALIGN.CENTER)
    textLbl.longMode(dxui.Utils.LABEL_LONG_MODE.SCROLL_CIRCULAR)
}
export default configView
vf205_access/src/view/config/identityVerificationView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,185 @@
import dxui from '../../../dxmodules/dxUi.js'
import std from '../../../dxmodules/dxStd.js'
import viewUtils from "../viewUtils.js"
import topView from '../topView.js'
import mainView from '../mainView.js'
import configView from './configView.js'
import i18n from '../i18n.js'
import screen from '../../screen.js'
const identityVerificationView = {}
identityVerificationView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('identityVerificationView', dxui.Utils.LAYER.MAIN)
    identityVerificationView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        toggleTab(0)
        // æ— æ“ä½œ15秒自动返回
        if (identityVerificationView.timer) {
            std.clearInterval(identityVerificationView.timer)
        }
        identityVerificationView.timer = std.setInterval(() => {
            let count = dxui.Utils.GG.NativeDisp.lvDispGetInactiveTime()
            if (count > 15 * 1000) {
                std.clearInterval(identityVerificationView.timer)
                identityVerificationView.timer = null
                dxui.loadMain(mainView.screenMain)
            }
        }, 1000)
    })
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_UNLOADED, () => {
        // äººè„¸è®¤è¯ç»“束
        if (identityVerificationView.timer) {
            std.clearInterval(identityVerificationView.timer)
        }
        if (!faceRec.isHide()) {
            screen.faceAuthEnd()
        }
    })
    const titleBoxBg = dxui.View.build('titleBoxBg', screenMain)
    viewUtils._clearStyle(titleBoxBg)
    titleBoxBg.setSize(screen.screenSize.width, 70)
    titleBoxBg.align(dxui.Utils.ALIGN.TOP_MID, 0, 0)
    titleBoxBg.bgColor(0xffffff)
    const titleBox = viewUtils.title(screenMain, mainView.screenMain, 'identityVerificationViewTitle', 'identityVerificationView.title')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const tab = dxui.View.build('tab', screenMain)
    viewUtils._clearStyle(tab)
    tab.setSize(screen.screenSize.width, 80)
    tab.alignTo(titleBox, dxui.Utils.ALIGN.OUT_BOTTOM_MID, 0, 0)
    tab.flexFlow(dxui.Utils.FLEX_FLOW.ROW)
    tab.flexAlign(dxui.Utils.FLEX_ALIGN.SPACE_AROUND, dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.CENTER)
    const pwdLogBox = dxui.View.build('pwdLogBox', tab)
    viewUtils._clearStyle(pwdLogBox)
    const pwdLogLbl = dxui.Label.build('pwdLogLbl', pwdLogBox)
    pwdLogLbl.textFont(viewUtils.font(28))
    pwdLogLbl.textColor(0x888888)
    pwdLogLbl.text('密码登录')
    pwdLogLbl.dataI18n = 'identityVerificationView.pwdLog'
    const pwdLogText = pwdLogLbl.text
    pwdLogLbl.text = (data) => {
        pwdLogText.call(pwdLogLbl, data)
        pwdLogLbl.update()
        pwdLogBox.setSize(pwdLogLbl.width() + 8, 80)
    }
    pwdLogLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    pwdLogLbl.update()
    pwdLogBox.setSize(pwdLogLbl.width() + 8, 80)
    pwdLogBox.borderWidth(4)
    pwdLogBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    pwdLogBox.setBorderColor(0x0836C)
    pwdLogBox.on(dxui.Utils.EVENT.CLICK, () => {
        toggleTab(0)
    })
    const faceLogBox = dxui.View.build('faceLogBox', tab)
    viewUtils._clearStyle(faceLogBox)
    const faceLogLbl = dxui.Label.build('faceLogLbl', faceLogBox)
    faceLogLbl.textFont(viewUtils.font(28))
    faceLogLbl.textColor(0x888888)
    faceLogLbl.text('人脸登录')
    faceLogLbl.dataI18n = 'identityVerificationView.faceLog'
    const faceLogText = faceLogLbl.text
    faceLogLbl.text = (data) => {
        faceLogText.call(faceLogLbl, data)
        faceLogLbl.update()
        faceLogBox.setSize(faceLogLbl.width() + 8, 80)
    }
    faceLogLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    faceLogLbl.update()
    faceLogBox.setSize(faceLogLbl.width() + 8, 80)
    faceLogBox.borderWidth(4)
    faceLogBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    faceLogBox.setBorderColor(0x0836C)
    faceLogBox.on(dxui.Utils.EVENT.CLICK, () => {
        toggleTab(1)
    })
    const pwdInput = viewUtils.input(screenMain, screenMain.id + 'pwdInput', undefined, undefined, 'identityVerificationView.pwd')
    pwdInput.align(dxui.Utils.ALIGN.TOP_MID, 0, 263)
    pwdInput.setPasswordMode(true)
    const eyeFill = viewUtils.imageBtn(screenMain, screenMain.id + 'eye_fill', '/app/code/resource/image/eye-fill.png')
    eyeFill.alignTo(pwdInput, dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    eyeFill.on(dxui.Utils.EVENT.CLICK, () => {
        pwdInput.setPasswordMode(true)
        eyeFill.hide()
        eyeOff.show()
    })
    eyeFill.hide()
    const eyeOff = viewUtils.imageBtn(screenMain, screenMain.id + 'eye_off', '/app/code/resource/image/eye-off.png')
    eyeOff.alignTo(pwdInput, dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    eyeOff.on(dxui.Utils.EVENT.CLICK, () => {
        pwdInput.setPasswordMode(false)
        eyeFill.show()
        eyeOff.hide()
    })
    const pwdAccessBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'pwdAccessBtn', 'identityVerificationView.pwdAccess', () => {
        if (screen.getConfig()['base.password'] === pwdInput.text()) {
            // è¿›å…¥è®¾ç½®èœå•
            std.clearInterval(identityVerificationView.timer)
            dxui.loadMain(configView.screenMain)
        } else {
            if (faceRec.isHide()) {
                // å¯†ç é”™è¯¯
                identityVerificationView.statusPanel.fail('identityVerificationView.pwdFail')
            } else {
                // äººè„¸è®¤è¯å¤±è´¥
                identityVerificationView.statusPanel.fail('identityVerificationView.fail')
            }
        }
    })
    pwdAccessBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
    const faceRec = dxui.Image.build('faceRec', screenMain)
    faceRec.source('/app/code/resource/image/faceRec.png')
    faceRec.alignTo(tab, dxui.Utils.ALIGN.OUT_BOTTOM_MID, 0, 70)
    identityVerificationView.statusPanel = viewUtils.statusPanel(screenMain, 'identityVerificationView.success', 'identityVerificationView.fail')
    function toggleTab(index) {
        screenMain.send(dxui.Utils.EVENT.CLICK)
        if (index == 0) {
            pwdLogLbl.textColor(0x0836C)
            faceLogLbl.textColor(0x888888)
            pwdLogBox.setBorderColor(0x0836C)
            faceLogBox.setBorderColor(0xffffff)
            pwdInput.show()
            eyeFill.show()
            eyeOff.show()
            pwdAccessBtn.show()
            screenMain.bgOpa(100)
            faceRec.hide()
            // äººè„¸è®¤è¯ç»“束
            screen.faceAuthEnd()
        } else {
            pwdLogLbl.textColor(0x888888)
            faceLogLbl.textColor(0x0836C)
            pwdLogBox.setBorderColor(0xffffff)
            faceLogBox.setBorderColor(0x0836C)
            pwdInput.hide()
            eyeFill.hide()
            eyeOff.hide()
            pwdAccessBtn.hide()
            screenMain.bgOpa(0)
            faceRec.show()
            // äººè„¸è®¤è¯å¼€å§‹
            screen.faceAuthStart()
        }
    }
    toggleTab(0)
}
export default identityVerificationView
vf205_access/src/view/config/menu/cloudCertView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
import dxui from '../../../../dxmodules/dxUi.js'
import viewUtils from "../../viewUtils.js"
import topView from "../../topView.js"
import configView from '../configView.js'
const cloudCertView = {}
cloudCertView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('cloudCertView', dxui.Utils.LAYER.MAIN)
    cloudCertView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'cloudCertViewTitle', 'cloudCertView.cloudCertActive')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const inputBox = viewUtils.input(screenMain, 'cloudCertViewInput', undefined, () => {
        console.log('cloudCertViewInput')
    }, 'cloudCertView.inputKey')
    inputBox.align(dxui.Utils.ALIGN.TOP_LEFT, 109, 179)
    inputBox.width(654)
    const keyLbl = dxui.Label.build('cloudCertViewKey', screenMain)
    keyLbl.dataI18n = 'cloudCertView.key'
    keyLbl.textFont(viewUtils.font(26))
    keyLbl.align(dxui.Utils.ALIGN.TOP_LEFT, 43, 201)
    const tipLbl = dxui.Label.build('cloudCertViewTip', screenMain)
    tipLbl.dataI18n = 'cloudCertView.tip'
    tipLbl.textFont(viewUtils.font(22))
    tipLbl.textColor(0x888888)
    tipLbl.align(dxui.Utils.ALIGN.TOP_MID, 0, 650)
    const saveBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'saveBtn', 'cloudCertView.save', () => {
    })
    saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
}
export default cloudCertView
vf205_access/src/view/config/menu/deviceInfo/dataCapacityInfoView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,118 @@
import dxui from '../../../../../dxmodules/dxUi.js'
import dxCommon from '../../../../../dxmodules/dxCommon.js'
import viewUtils from "../../../viewUtils.js"
import topView from "../../../topView.js"
import deviceInfoView from '../deviceInfoView.js'
import i18n from "../../../i18n.js"
import sqliteService from '../../../../service/sqliteService.js'
import screen from '../../../../screen.js'
const dataCapacityInfoView = {}
dataCapacityInfoView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('dataCapacityInfoView', dxui.Utils.LAYER.MAIN)
    dataCapacityInfoView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        dataCapacityInfoView.info[0].label.text(Math.floor(dxCommon.getTotaldisk() / 1024 / 1024) + ' M')
        dataCapacityInfoView.info[1].label.text(Math.floor((dxCommon.getTotaldisk() - dxCommon.getFreedisk()) / 1024 / 1024) + ' M')
        dataCapacityInfoView.info[2].label.text(Math.floor(dxCommon.getFreedisk() / 1024 / 1024) + ' M')
        dataCapacityInfoView.info[3].label.text(sqliteService.d1_person.count() + '')
        dataCapacityInfoView.info[4].label.text(sqliteService.d1_voucher.countByType(300) + '')
        dataCapacityInfoView.info[5].label.text(sqliteService.d1_voucher.countByType(400) + '')
        dataCapacityInfoView.info[6].label.text(sqliteService.d1_voucher.countByType(200) + '')
        dataCapacityInfoView.info[7].label.text(sqliteService.d1_pass_record.count() + '')
    })
    const titleBox = viewUtils.title(screenMain, deviceInfoView.screenMain, 'dataCapacityInfoViewTitle', 'deviceInfoView.dataCapacityInfo')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    dataCapacityInfoView.info = [
        {
            title: "deviceInfoView.deviceTotalSpace",
            type: 'label',
            value: '5918 M',
        },
        {
            title: "deviceInfoView.deviceUsedSpace",
            type: 'label',
            value: '344 M',
        },
        // {
        //     title: "deviceInfoView.deviceFreeSpace",
        //     type: 'label',
        //     value: '5574 M',
        // },
        {
            title: "deviceInfoView.deviceRemainingSpace",
            type: 'label',
            value: '3',
        },
        {
            title: "deviceInfoView.registeredPersonNum",
            type: 'label',
            value: '3',
        },
        {
            title: "deviceInfoView.localFaceWhiteListNum",
            type: 'label',
            value: '3',
        },
        {
            title: "deviceInfoView.localPasswordWhiteListNum",
            type: 'label',
            value: '3',
        },
        {
            title: "deviceInfoView.localSwipeCardWhiteListNum",
            type: 'label',
            value: '3',
        },
        {
            title: "deviceInfoView.passLogTotalNum",
            type: 'label',
            value: '3',
        }
    ]
    const dataCapacityInfoBox = dxui.View.build('dataCapacityInfoBox', screenMain)
    viewUtils._clearStyle(dataCapacityInfoBox)
    dataCapacityInfoBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 140)
    dataCapacityInfoBox.setSize(screen.screenSize.width, 700)
    dataCapacityInfoBox.bgOpa(0)
    dataCapacityInfoBox.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    dataCapacityInfoBox.flexAlign(dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START)
    dataCapacityInfoBox.obj.lvObjSetStylePadGap(0, dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME)
    dataCapacityInfoBox.borderWidth(1)
    dataCapacityInfoBox.setBorderColor(0xDEDEDE)
    dataCapacityInfoBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_TOP, 0)
    dataCapacityInfoView.info.forEach(item => {
        const itemBox = dxui.View.build(item.title, dataCapacityInfoBox)
        viewUtils._clearStyle(itemBox)
        itemBox.setSize(760, 76)
        itemBox.borderWidth(1)
        itemBox.setBorderColor(0xDEDEDE)
        itemBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
        const itemLabel = dxui.Label.build(item.title + 'Label', itemBox)
        itemLabel.dataI18n = item.title
        itemLabel.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
        itemLabel.textFont(viewUtils.font(26))
        switch (item.type) {
            case 'label':
                const label = dxui.Label.build(item.title + 'label', itemBox)
                label.textFont(viewUtils.font(24))
                label.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
                label.text(item.value)
                label.textColor(0x767676)
                item.label = label
                break;
        }
    })
}
export default dataCapacityInfoView
vf205_access/src/view/config/menu/deviceInfo/systemInfoView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
import dxui from '../../../../../dxmodules/dxUi.js'
import viewUtils from "../../../viewUtils.js"
import topView from "../../../topView.js"
import deviceInfoView from '../deviceInfoView.js'
import i18n from "../../../i18n.js"
import screen from '../../../../screen.js'
const systemInfoView = {}
systemInfoView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('systemInfoView', dxui.Utils.LAYER.MAIN)
    systemInfoView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        const config = screen.getConfig()
        systemInfoView.info[0].label.text(config["sys.sn"])
        systemInfoView.info[1].label.text(config["sys.appVersion"])
        systemInfoView.info[2].label.text(config["sys.releaseTime"])
    })
    const titleBox = viewUtils.title(screenMain, deviceInfoView.screenMain, 'systemInfoViewTitle', 'deviceInfoView.systemInfo')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    systemInfoView.info = [
        {
            title: "deviceInfoView.deviceSN",
            type: 'label',
            value: 'G2440288881',
        },
        {
            title: "deviceInfoView.firmwareVersion",
            type: 'label',
            value: 'VF203-v1.1.36.3a885-240611',
        },
        {
            title: "deviceInfoView.firmwareReleaseDate",
            type: 'label',
            value: '2024-06-11 18:00:00',
        },
    ]
    const settingInfoBox = dxui.View.build('settingInfoBox', screenMain)
    viewUtils._clearStyle(settingInfoBox)
    settingInfoBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 140)
    settingInfoBox.setSize(screen.screenSize.width, 700)
    settingInfoBox.bgOpa(0)
    settingInfoBox.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    settingInfoBox.flexAlign(dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START)
    settingInfoBox.obj.lvObjSetStylePadGap(0, dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME)
    settingInfoBox.borderWidth(1)
    settingInfoBox.setBorderColor(0xDEDEDE)
    settingInfoBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_TOP, 0)
    systemInfoView.info.forEach(item => {
        const itemBox = dxui.View.build(item.title, settingInfoBox)
        viewUtils._clearStyle(itemBox)
        itemBox.setSize(760, 76)
        itemBox.borderWidth(1)
        itemBox.setBorderColor(0xDEDEDE)
        itemBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
        const itemLabel = dxui.Label.build(item.title + 'Label', itemBox)
        itemLabel.dataI18n = item.title
        itemLabel.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
        itemLabel.textFont(viewUtils.font(26))
        switch (item.type) {
            case 'label':
                const label = dxui.Label.build(item.title + 'label', itemBox)
                label.textFont(viewUtils.font(24))
                label.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
                label.text(item.value)
                label.textColor(0x767676)
                item.label = label
                break;
        }
    })
    const currentVersion = dxui.Label.build('deviceInfoView.currentVersion', screenMain)
    currentVersion.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -213)
    currentVersion.textFont(viewUtils.font(22))
    currentVersion.textColor(0x888888)
    currentVersion.dataI18n = 'deviceInfoView.currentVersion'
    currentVersion.textAlign(dxui.Utils.TEXT_ALIGN.CENTER, 0, 0)
    currentVersion.hide()
    const saveBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'saveBtn', 'deviceInfoView.updateDevice', () => {
    })
    saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
    saveBtn.hide()
}
export default systemInfoView
vf205_access/src/view/config/menu/deviceInfoView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
import dxui from '../../../../dxmodules/dxUi.js'
import viewUtils from "../../viewUtils.js"
import topView from "../../topView.js"
import configView from '../configView.js'
import systemInfoView from './deviceInfo/systemInfoView.js'
import dataCapacityInfoView from './deviceInfo/dataCapacityInfoView.js'
import i18n from "../../i18n.js"
import screen from '../../../screen.js'
const deviceInfoView = {}
deviceInfoView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('deviceInfoView', dxui.Utils.LAYER.MAIN)
    deviceInfoView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        let config = screen.getConfig()
        dxui.Utils.GG.NativeBasicComponent.lvQrcodeUpdate(deviceInfoView.sysInfo[2].qrcodeObj, config["sys.sn"])
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'deviceInfoViewTitle', 'deviceInfoView.title')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    deviceInfoView.sysInfo = [
        {
            title: 'deviceInfoView.systemInfo',
            type: 'menu',
            view: systemInfoView,
            obj: null,
        },
        {
            title: 'deviceInfoView.dataCapacityInfo',
            type: 'menu',
            view: dataCapacityInfoView,
            obj: null,
        },
        {
            title: 'deviceInfoView.deviceQrCode',
            value: '123',
            type: 'qrcode',
            obj: null,
        },
    ]
    const deviceInfoBox = dxui.View.build('deviceInfoBox', screenMain)
    viewUtils._clearStyle(deviceInfoBox)
    deviceInfoBox.setSize(screen.screenSize.width, screen.screenSize.height - 140)
    deviceInfoBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 140)
    deviceInfoBox.bgColor(0xf7f7f7)
    deviceInfoBox.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    deviceInfoBox.flexAlign(dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START)
    deviceInfoBox.obj.lvObjSetStylePadGap(10, dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME)
    deviceInfoBox.padTop(10)
    deviceInfoBox.padBottom(10)
    deviceInfoView.sysInfo.forEach(item => {
        item.obj = dxui.View.build(item.title, deviceInfoBox)
        viewUtils._clearStyle(item.obj)
        item.obj.setSize(760, 76)
        item.obj.bgColor(0xffffff)
        item.obj.radius(10)
        item.obj.on(dxui.Utils.ENUM.LV_EVENT_PRESSED, () => {
            item.obj.bgColor(0xEAEAEA)
        })
        item.obj.on(dxui.Utils.ENUM.LV_EVENT_RELEASED, () => {
            item.obj.bgColor(0xffffff)
        })
        const titleLbl = dxui.Label.build(item.title + 'Label', item.obj)
        titleLbl.dataI18n = item.title
        titleLbl.align(dxui.Utils.ALIGN.LEFT_MID, 20, 0)
        titleLbl.textFont(viewUtils.font(26))
        switch (item.type) {
            case 'menu':
                const image = dxui.Image.build(item.title + 'Image', item.obj)
                image.align(dxui.Utils.ALIGN.RIGHT_MID, -15, 0)
                image.source('/app/code/resource/image/right.png')
                item.obj.on(dxui.Utils.EVENT.CLICK, () => {
                    dxui.loadMain(item.view.screenMain)
                })
                break
            case 'qrcode':
                item.obj.height(350)
                if (item.title == "deviceInfoView.miniProgramCode") {
                    const qrcodeImage = dxui.Image.build(item.title + 'qrcodeImage', item.obj)
                    deviceInfoView.qrcodeImage = qrcodeImage
                    qrcodeImage.source('/app/code/resource/image/app_qrcode.png')
                    qrcodeImage.obj.lvImgSetZoom(256 * 0.6)
                    qrcodeImage.obj.lvImgSetSizeMode(dxui.Utils.ENUM.LV_IMG_SIZE_MODE_REAL)
                    qrcodeImage.align(dxui.Utils.ALIGN.RIGHT_MID, -20, 0)
                } else {
                    const qrcodeBox = dxui.View.build(item.title + 'QrCode', item.obj)
                    viewUtils._clearStyle(qrcodeBox)
                    qrcodeBox.setSize(220, 220)
                    qrcodeBox.align(dxui.Utils.ALIGN.RIGHT_MID, -20, 0)
                    const qrcodeObj = dxui.Utils.GG.NativeBasicComponent.lvQrcodeCreate(qrcodeBox.obj, 220, 0x000000, 0xffffff)
                    dxui.Utils.GG.NativeBasicComponent.lvQrcodeUpdate(qrcodeObj, item.value)
                    item.qrcodeObj = qrcodeObj
                }
                break
        }
    })
}
export default deviceInfoView
vf205_access/src/view/config/menu/dockingSetting.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,135 @@
import dxui from '../../../../dxmodules/dxUi.js'
import std from '../../../../dxmodules/dxStd.js'
import viewUtils from '../../viewUtils.js'
import topView from '../../topView.js'
import configView from '../configView.js'
import i18n from '../../i18n.js'
import screen from '../../../screen.js'
const dockingSettingView = {}
dockingSettingView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('dockingSettingView', dxui.Utils.LAYER.MAIN)
    dockingSettingView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        const configAll = screen.getConfig()
        httpGasSettingInput.text(configAll['http.gas'] || '')
        httpStatusSettingInput.text(configAll['http.status'] || '')
        gasUpdateTimeSettingInput.text((configAll['update.gasTime'] || 5) + '')
        statusUpdateTimeSettingInput.text((configAll['update.statusTime'] || 5) + '')
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'dockingSettingViewTitle', 'dockingSettingView.title')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const httpGasSettingBox = dxui.View.build('httpGasSettingBox', screenMain)
    viewUtils._clearStyle(httpGasSettingBox)
    httpGasSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 140)
    httpGasSettingBox.setSize(750, 76)
    httpGasSettingBox.borderWidth(1)
    httpGasSettingBox.setBorderColor(0xDEDEDE)
    httpGasSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const httpGasSettingLbl = dxui.Label.build('httpGasSettingLbl', httpGasSettingBox)
    httpGasSettingLbl.text('HTTP_气体')
    httpGasSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    httpGasSettingLbl.textFont(viewUtils.font(26))
    const httpGasSettingInput = viewUtils.input(httpGasSettingBox, 'httpGasSettingInput', undefined, undefined, 'dockingSettingView.input')
    httpGasSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    httpGasSettingInput.setSize(320, 60)
    const httpStatusSettingBox = dxui.View.build('httpStatusSettingBox', screenMain)
    viewUtils._clearStyle(httpStatusSettingBox)
    httpStatusSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 216)
    httpStatusSettingBox.setSize(750, 76)
    httpStatusSettingBox.borderWidth(1)
    httpStatusSettingBox.setBorderColor(0xDEDEDE)
    httpStatusSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const httpStatusSettingLbl = dxui.Label.build('httpStatusSettingLbl', httpStatusSettingBox)
    httpStatusSettingLbl.text('HTTP_状态')
    httpStatusSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    httpStatusSettingLbl.textFont(viewUtils.font(26))
    const httpStatusSettingInput = viewUtils.input(httpStatusSettingBox, 'httpStatusSettingInput', undefined, undefined, 'dockingSettingView.input')
    httpStatusSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    httpStatusSettingInput.setSize(320, 60)
    // æ°”体浓度更新时间设置
    const gasUpdateTimeSettingBox = dxui.View.build('gasUpdateTimeSettingBox', screenMain)
    viewUtils._clearStyle(gasUpdateTimeSettingBox)
    gasUpdateTimeSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 292)
    gasUpdateTimeSettingBox.setSize(750, 76)
    gasUpdateTimeSettingBox.borderWidth(1)
    gasUpdateTimeSettingBox.setBorderColor(0xDEDEDE)
    gasUpdateTimeSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const gasUpdateTimeSettingLbl = dxui.Label.build('gasUpdateTimeSettingLbl', gasUpdateTimeSettingBox)
    gasUpdateTimeSettingLbl.text('气体浓度更新时间')
    gasUpdateTimeSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    gasUpdateTimeSettingLbl.textFont(viewUtils.font(26))
    const gasUpdateTimeSettingUnitLbl = dxui.Label.build('gasUpdateTimeSettingUnitLbl', gasUpdateTimeSettingBox)
    gasUpdateTimeSettingUnitLbl.text('秒')
    gasUpdateTimeSettingUnitLbl.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    gasUpdateTimeSettingUnitLbl.textFont(viewUtils.font(26))
    const gasUpdateTimeSettingInput = viewUtils.input(gasUpdateTimeSettingBox, 'gasUpdateTimeSettingInput', 2, undefined, 'dockingSettingView.input')
    gasUpdateTimeSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, -45, 0)
    gasUpdateTimeSettingInput.setSize(150, 60)
    // çŠ¶æ€ä¿¡æ¯æ›´æ–°æ—¶é—´è®¾ç½®
    const statusUpdateTimeSettingBox = dxui.View.build('statusUpdateTimeSettingBox', screenMain)
    viewUtils._clearStyle(statusUpdateTimeSettingBox)
    statusUpdateTimeSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 368)
    statusUpdateTimeSettingBox.setSize(750, 76)
    statusUpdateTimeSettingBox.borderWidth(1)
    statusUpdateTimeSettingBox.setBorderColor(0xDEDEDE)
    statusUpdateTimeSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const statusUpdateTimeSettingLbl = dxui.Label.build('statusUpdateTimeSettingLbl', statusUpdateTimeSettingBox)
    statusUpdateTimeSettingLbl.text('状态信息更新时间')
    statusUpdateTimeSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    statusUpdateTimeSettingLbl.textFont(viewUtils.font(26))
    const statusUpdateTimeSettingUnitLbl = dxui.Label.build('statusUpdateTimeSettingUnitLbl', statusUpdateTimeSettingBox)
    statusUpdateTimeSettingUnitLbl.text('秒')
    statusUpdateTimeSettingUnitLbl.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    statusUpdateTimeSettingUnitLbl.textFont(viewUtils.font(26))
    const statusUpdateTimeSettingInput = viewUtils.input(statusUpdateTimeSettingBox, 'statusUpdateTimeSettingInput', 2, undefined, 'dockingSettingView.input')
    statusUpdateTimeSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, -45, 0)
    statusUpdateTimeSettingInput.setSize(150, 60)
    const saveBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'saveBtn', 'dockingSettingView.save', () => {
        const saveConfigData = {
            http: {
                gas: httpGasSettingInput.text(),
                status: httpStatusSettingInput.text()
            },
            update: {
                gasTime: parseInt(gasUpdateTimeSettingInput.text()) || 5,
                statusTime: parseInt(statusUpdateTimeSettingInput.text()) || 5
            }
        }
        const res = screen.saveConfig(saveConfigData)
        if (res === true) {
            dockingSettingView.statusPanel.success()
            std.setTimeout(() => {
                // æˆåŠŸè¿”å›žä¸Šä¸€å±‚ç•Œé¢
                dxui.loadMain(configView.screenMain)
            }, 500)
        } else {
            dockingSettingView.statusPanel.fail()
        }
    })
    saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
    dockingSettingView.statusPanel = viewUtils.statusPanel(screenMain, 'dockingSettingView.success', 'dockingSettingView.fail')
}
export default dockingSettingView
vf205_access/src/view/config/menu/doorControlView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,258 @@
import dxui from '../../../../dxmodules/dxUi.js'
import std from '../../../../dxmodules/dxStd.js'
import viewUtils from "../../viewUtils.js"
import topView from "../../topView.js"
import configView from '../configView.js'
import i18n from "../../i18n.js"
import screen from '../../../screen.js'
import bus from '../../../../dxmodules/dxEventBus.js'
const doorControlView = {}
doorControlView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('doorControlView', dxui.Utils.LAYER.MAIN)
    doorControlView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        const configAll = screen.getConfig()
        delaySettingInput.text(configAll['access.relayTime'] + '')
        alarmSettingSwitch.select(configAll['access.tamperAlarm'] == 1)
        mqttSettingInput.text(configAll['mqtt.addr'])
        mqttUserSettingInput.text(configAll['mqtt.username'])
        mqttPwdSettingInput.text(configAll['mqtt.password'])
        onlineCheckingSettingSwitch.select(configAll['mqtt.onlinecheck'] == 1)
        onlineCheckingTimeoutSettingInput.text(configAll['mqtt.timeout'] + '')
        GranarySettingInput.text(configAll['GranaryName'] || '中央储备粮某某直属库')
        houseNameSettingInput.text(configAll['houseName'] || '01号仓')
        doorHttpGasSettingInput.text(configAll['http.safeInputAccess'] || "http://192.168.1.199:80/cgi-bin/safeInputAccess")
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'doorControlViewTitle', 'doorControlView.title')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const delaySettingBox = dxui.View.build('delaySettingBox', screenMain)
    viewUtils._clearStyle(delaySettingBox)
    delaySettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 140)
    delaySettingBox.setSize(750, 76)
    delaySettingBox.borderWidth(1)
    delaySettingBox.setBorderColor(0xDEDEDE)
    delaySettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const delaySettingLbl = dxui.Label.build('delaySettingLbl', delaySettingBox)
    delaySettingLbl.dataI18n = 'doorControlView.openDoorRelayDelay'
    delaySettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    delaySettingLbl.textFont(viewUtils.font(26))
    const delaySettingUnitLbl = dxui.Label.build('delaySettingUnitLbl', delaySettingBox)
    delaySettingUnitLbl.dataI18n = "doorControlView.ms"
    delaySettingUnitLbl.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    delaySettingUnitLbl.textFont(viewUtils.font(26))
    const delaySettingInput = viewUtils.input(delaySettingBox, 'delaySettingInput', 2, undefined, 'doorControlView.input')
    delaySettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, -60, 0)
    delaySettingInput.setSize(150, 60)
    const alarmSettingBox = dxui.View.build('alarmSettingBox', screenMain)
    viewUtils._clearStyle(alarmSettingBox)
    alarmSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 216)
    alarmSettingBox.setSize(750, 76)
    alarmSettingBox.borderWidth(1)
    alarmSettingBox.setBorderColor(0xDEDEDE)
    alarmSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const alarmSettingLbl = dxui.Label.build('alarmSettingLbl', alarmSettingBox)
    alarmSettingLbl.dataI18n = 'doorControlView.antiTamperAlarm'
    alarmSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    alarmSettingLbl.textFont(viewUtils.font(26))
    const alarmSettingSwitch = dxui.Switch.build('alarmSettingSwitch', alarmSettingBox)
    alarmSettingSwitch.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    alarmSettingSwitch.setSize(70, 35)
    const mqttSettingBox = dxui.View.build('mqttSettingBox', screenMain)
    viewUtils._clearStyle(mqttSettingBox)
    mqttSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 292)
    mqttSettingBox.setSize(750, 76)
    mqttSettingBox.borderWidth(1)
    mqttSettingBox.setBorderColor(0xDEDEDE)
    mqttSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const mqttSettingLbl = dxui.Label.build('mqttSettingLbl', mqttSettingBox)
    mqttSettingLbl.dataI18n = 'doorControlView.mqttAddr'
    mqttSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    mqttSettingLbl.textFont(viewUtils.font(26))
    const mqttSettingInput = viewUtils.input(mqttSettingBox, 'mqttSettingInput', undefined, undefined, 'doorControlView.input')
    mqttSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    mqttSettingInput.setSize(320, 60)
    const mqttUserSettingBox = dxui.View.build('mqttUserSettingBox', screenMain)
    viewUtils._clearStyle(mqttUserSettingBox)
    mqttUserSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 368)
    mqttUserSettingBox.setSize(750, 76)
    mqttUserSettingBox.borderWidth(1)
    mqttUserSettingBox.setBorderColor(0xDEDEDE)
    mqttUserSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const mqttUserSettingLbl = dxui.Label.build('mqttUserSettingLbl', mqttUserSettingBox)
    mqttUserSettingLbl.dataI18n = 'doorControlView.mqttUser'
    mqttUserSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    mqttUserSettingLbl.textFont(viewUtils.font(26))
    const mqttUserSettingInput = viewUtils.input(mqttUserSettingBox, 'mqttUserSettingInput', undefined, undefined, 'doorControlView.input')
    mqttUserSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    mqttUserSettingInput.setSize(320, 60)
    const mqttPwdSettingBox = dxui.View.build('mqttPwdSettingBox', screenMain)
    viewUtils._clearStyle(mqttPwdSettingBox)
    mqttPwdSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 444)
    mqttPwdSettingBox.setSize(750, 76)
    mqttPwdSettingBox.borderWidth(1)
    mqttPwdSettingBox.setBorderColor(0xDEDEDE)
    mqttPwdSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const mqttPwdSettingLbl = dxui.Label.build('mqttPwdSettingLbl', mqttPwdSettingBox)
    mqttPwdSettingLbl.dataI18n = 'doorControlView.mqttPwd'
    mqttPwdSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    mqttPwdSettingLbl.textFont(viewUtils.font(26))
    const mqttPwdSettingInput = viewUtils.input(mqttPwdSettingBox, 'mqttPwdSettingInput', undefined, undefined, 'doorControlView.input')
    mqttPwdSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    mqttPwdSettingInput.setSize(320, 60)
    const onlineCheckingSettingBox = dxui.View.build('onlineCheckingSettingBox', screenMain)
    viewUtils._clearStyle(onlineCheckingSettingBox)
    onlineCheckingSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 540)
    onlineCheckingSettingBox.setSize(750, 76)
    onlineCheckingSettingBox.borderWidth(1)
    onlineCheckingSettingBox.setBorderColor(0xDEDEDE)
    onlineCheckingSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const onlineCheckingSettingLbl = dxui.Label.build('onlineCheckingSettingLbl', onlineCheckingSettingBox)
    onlineCheckingSettingLbl.dataI18n = 'doorControlView.onlineChecking'
    onlineCheckingSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    onlineCheckingSettingLbl.textFont(viewUtils.font(26))
    const onlineCheckingSettingSwitch = dxui.Switch.build('onlineCheckingSettingSwitch', onlineCheckingSettingBox)
    onlineCheckingSettingSwitch.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    onlineCheckingSettingSwitch.setSize(70, 35)
    const onlineCheckingTimeoutSettingBox = dxui.View.build('onlineCheckingTimeoutSettingBox', screenMain)
    viewUtils._clearStyle(onlineCheckingTimeoutSettingBox)
    onlineCheckingTimeoutSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 616)
    onlineCheckingTimeoutSettingBox.setSize(750, 76)
    onlineCheckingTimeoutSettingBox.borderWidth(1)
    onlineCheckingTimeoutSettingBox.setBorderColor(0xDEDEDE)
    onlineCheckingTimeoutSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const onlineCheckingTimeoutSettingLbl = dxui.Label.build('onlineCheckingTimeoutSettingLbl', onlineCheckingTimeoutSettingBox)
    onlineCheckingTimeoutSettingLbl.dataI18n = 'doorControlView.onlineCheckingTimeout'
    onlineCheckingTimeoutSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    onlineCheckingTimeoutSettingLbl.textFont(viewUtils.font(26))
    const onlineCheckingTimeoutSettingUnitLbl = dxui.Label.build('onlineCheckingTimeoutSettingUnitLbl', onlineCheckingTimeoutSettingBox)
    onlineCheckingTimeoutSettingUnitLbl.text('ms')
    onlineCheckingTimeoutSettingUnitLbl.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    onlineCheckingTimeoutSettingUnitLbl.textFont(viewUtils.font(26))
    const onlineCheckingTimeoutSettingInput = viewUtils.input(onlineCheckingTimeoutSettingBox, 'onlineCheckingTimeoutSettingInput', 2, undefined, 'doorControlView.input')
    onlineCheckingTimeoutSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, -45, 0)
    onlineCheckingTimeoutSettingInput.setSize(150, 60)
    // HTTP接口路径设置
    // åº“区名称设置
    const GranarySettingBox = dxui.View.build('GranarySettingBox', screenMain)
    viewUtils._clearStyle(GranarySettingBox)
    GranarySettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 692)
    GranarySettingBox.setSize(750, 76)
    GranarySettingBox.borderWidth(1)
    GranarySettingBox.setBorderColor(0xDEDEDE)
    GranarySettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const GranarySettingLbl = dxui.Label.build('GranarySettingLbl', GranarySettingBox)
    GranarySettingLbl.text('库区名称')
    GranarySettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    GranarySettingLbl.textFont(viewUtils.font(26))
    const GranarySettingInput = viewUtils.input(GranarySettingBox, 'GranarySettingInput', undefined, undefined, 'doorControlView.input')
    GranarySettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    GranarySettingInput.setSize(320, 60)
    // ä»“廒名称设置
    const houseNameSettingBox = dxui.View.build('houseNameSettingBox', screenMain)
    viewUtils._clearStyle(houseNameSettingBox)
    houseNameSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 768)
    houseNameSettingBox.setSize(750, 76)
    houseNameSettingBox.borderWidth(1)
    houseNameSettingBox.setBorderColor(0xDEDEDE)
    houseNameSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const houseNameSettingLbl = dxui.Label.build('houseNameSettingLbl', houseNameSettingBox)
    houseNameSettingLbl.text('仓廒名称')
    houseNameSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    houseNameSettingLbl.textFont(viewUtils.font(26))
    const houseNameSettingInput = viewUtils.input(houseNameSettingBox, 'houseNameSettingInput', undefined, undefined, 'doorControlView.input')
    houseNameSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    houseNameSettingInput.setSize(320, 60)
    // HTTP接口路径设置
    const doorHttpGasSettingBox = dxui.View.build('doorHttpGasSettingBox', screenMain)
    viewUtils._clearStyle(doorHttpGasSettingBox)
    doorHttpGasSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 844)
    doorHttpGasSettingBox.setSize(750, 76)
    doorHttpGasSettingBox.borderWidth(1)
    doorHttpGasSettingBox.setBorderColor(0xDEDEDE)
    doorHttpGasSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const doorHttpGasSettingLbl = dxui.Label.build('doorHttpGasSettingLbl', doorHttpGasSettingBox)
    doorHttpGasSettingLbl.text('HTTP接口路径')
    doorHttpGasSettingLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    doorHttpGasSettingLbl.textFont(viewUtils.font(26))
    const doorHttpGasSettingInput = viewUtils.input(doorHttpGasSettingBox, 'doorHttpGasSettingInput', undefined, undefined, 'doorControlView.input')
    doorHttpGasSettingInput.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    doorHttpGasSettingInput.setSize(320, 60)
    const saveBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'saveBtn', 'doorControlView.save', () => {
        const saveConfigData = {
            access: {
                relayTime: parseInt(delaySettingInput.text()),
                tamperAlarm: alarmSettingSwitch.isSelect() ? 1 : 0
            },
            mqtt: {
                addr: mqttSettingInput.text(),
                username: mqttUserSettingInput.text(),
                password: mqttPwdSettingInput.text(),
                onlinecheck: onlineCheckingSettingSwitch.isSelect() ? 1 : 0,
                timeout: parseInt(onlineCheckingTimeoutSettingInput.text())
            },
            GranaryName: GranarySettingInput.text(),
            houseName: houseNameSettingInput.text(),
            http: {
                safeInputAccess: doorHttpGasSettingInput.text()
            }
        }
        const res = screen.saveConfig(saveConfigData)
        if (res === true) {
            doorControlView.statusPanel.success()
            // é€šçŸ¥mainView更新库区名称和仓号
            bus.fire('warehouseInfoUpdated')
            std.setTimeout(() => {
                // æˆåŠŸè¿”å›žä¸Šä¸€å±‚ç•Œé¢
                dxui.loadMain(configView.screenMain)
            }, 500)
        } else {
            doorControlView.statusPanel.fail()
        }
    })
    saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
    doorControlView.statusPanel = viewUtils.statusPanel(screenMain, 'doorControlView.success', 'doorControlView.fail')
}
export default doorControlView
vf205_access/src/view/config/menu/factoryTestView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
import dxui from "../../../../dxmodules/dxUi.js";
import viewUtils from "../../viewUtils.js";
import topView from "../../topView.js";
import configView from "../configView.js";
import i18n from "../../i18n.js";
import screen from "../../../screen.js";
const factoryTestView = {};
factoryTestView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build("factoryTestView", dxui.Utils.LAYER.MAIN);
    factoryTestView.screenMain = screenMain;
    screenMain.scroll(false);
    screenMain.bgColor(0xffffff);
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true);
    });
    const titleBox = viewUtils.title(
        screenMain,
        configView.screenMain,
        "factoryTestViewTitle",
        "factoryTestView.title"
    );
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70);
    const factoryTestBox = dxui.View.build("factoryTestBox", screenMain);
    viewUtils._clearStyle(factoryTestBox);
    factoryTestBox.setSize(
        screen.screenSize.width,
        screen.screenSize.height - 140
    );
    factoryTestBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 140);
    factoryTestBox.bgColor(0xf7f7f7);
    factoryTestBox.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP);
    factoryTestBox.flexAlign(
        dxui.Utils.FLEX_ALIGN.CENTER,
        dxui.Utils.FLEX_ALIGN.START,
        dxui.Utils.FLEX_ALIGN.START
    );
    factoryTestBox.obj.lvObjSetStylePadGap(
        10,
        dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME
    );
    factoryTestBox.padTop(10);
    factoryTestBox.padBottom(10);
    const calibrationBox = dxui.View.build("calibrationBox", factoryTestBox);
    viewUtils._clearStyle(calibrationBox);
    // calibrationBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 150);
    calibrationBox.setSize(560, 76);
    // calibrationBox.bgColor(0xf7f7f7);
    calibrationBox.bgColor(0xffffff);
    calibrationBox.radius(10);
    calibrationBox.on(dxui.Utils.ENUM.LV_EVENT_PRESSED, () => {
        calibrationBox.bgColor(0xeaeaea);
    });
    calibrationBox.on(dxui.Utils.ENUM.LV_EVENT_RELEASED, () => {
        calibrationBox.bgColor(0xffffff);
    });
    const titleLbl = dxui.Label.build("calibrationBox" + "Label", calibrationBox);
    titleLbl.dataI18n = "factoryTestView.calibration";
    titleLbl.align(dxui.Utils.ALIGN.LEFT_MID, 20, 0);
    titleLbl.textFont(viewUtils.font(26));
    const image = dxui.Image.build(calibrationBox.id + "Image", calibrationBox);
    image.align(dxui.Utils.ALIGN.RIGHT_MID, -15, 0);
    image.source("/app/code/resource/image/right.png");
    calibrationBox.on(dxui.Utils.EVENT.CLICK, () => {
        // dxui.loadMain(item.view.screenMain);
        console.log(123);
    });
};
export default factoryTestView;
vf205_access/src/view/config/menu/helpView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
import dxui from '../../../../dxmodules/dxUi.js'
import viewUtils from "../../viewUtils.js"
import topView from "../../topView.js"
import configView from '../configView.js'
import i18n from "../../i18n.js"
const helpView = {}
helpView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('helpView', dxui.Utils.LAYER.MAIN)
    helpView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'helpViewTitle', 'helpView.title')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    // äºŒç»´ç 
    const helpQrcode = dxui.View.build('helpQrcode', screenMain)
    viewUtils._clearStyle(helpQrcode)
    helpQrcode.setSize(344, 344)
    helpQrcode.align(dxui.Utils.ALIGN.TOP_MID, 0, 170)
    helpQrcode.bgOpa(0)
    const qrcode = dxui.View.build(helpQrcode.id + 'qrcode', helpQrcode)
    viewUtils._clearStyle(qrcode)
    qrcode.setSize(320, 320)
    qrcode.align(dxui.Utils.ALIGN.CENTER, 0, 0);
    const qrcodeObj = dxui.Utils.GG.NativeBasicComponent.lvQrcodeCreate(qrcode.obj, 320, 0x000000, 0xffffff)
    dxui.Utils.GG.NativeBasicComponent.lvQrcodeUpdate(qrcodeObj, '微信暂不支持展示二维码中的文本内容')
    const helpLabel = dxui.Label.build('helpLabel', screenMain)
    helpLabel.dataI18n='helpView.scanCode'
    helpLabel.align(dxui.Utils.ALIGN.TOP_MID, 0, 541)
    helpLabel.textFont(viewUtils.font(26))
}
export default helpView
vf205_access/src/view/config/menu/localUser/faceEnterView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,117 @@
import dxui from '../../../../../dxmodules/dxUi.js'
import std from '../../../../../dxmodules/dxStd.js'
import viewUtils from "../../../viewUtils.js"
import topView from "../../../topView.js"
import i18n from "../../../i18n.js"
import localUserAddView from './localUserAddView.js'
import screen from '../../../../screen.js'
const faceEnterView = {}
faceEnterView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('faceEnterView', dxui.Utils.LAYER.MAIN)
    faceEnterView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgOpa(0)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(false)
        screen.faceEnterStart(localUserAddView.nowUser.id)
        faceEnterView.statusPanel.success("faceEnterView.faceAdd")
        // faceEnterView.faceAdd.show()
        // faceEnterView.faceError.hide()
        // æ³¨å†Œ10秒超时
        faceEnterView.backTimer = std.setTimeout(() => {
            if (!faceEnterView.successFlag) {
                faceEnterView.statusPanel.fail("faceEnterView.faceError")
                std.setTimeout(() => {
                    faceEnterView.backCb()
                    dxui.loadMain(localUserAddView.screenMain)
                }, 500);
            }
        }, 10000);
    })
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_UNLOADED, () => {
        faceEnterView.successFlag = false
        screen.faceEnterEnd()
        if (faceEnterView.backTimer) {
            std.clearTimeout(faceEnterView.backTimer)
            faceEnterView.backTimer = null
        }
    })
    const titleBoxBg = dxui.View.build(screenMain.id + 'titleBoxBg', screenMain)
    viewUtils._clearStyle(titleBoxBg)
    titleBoxBg.setSize(screen.screenSize.width, 70)
    titleBoxBg.align(dxui.Utils.ALIGN.TOP_MID, 0, 0)
    titleBoxBg.bgColor(0xffffff)
    const titleBox = viewUtils.title(screenMain, localUserAddView.screenMain, 'faceEnterViewTitle', 'faceEnterView.title', faceEnterView.backCb)
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const faceRec2 = dxui.Image.build('faceRec2', screenMain)
    faceRec2.align(dxui.Utils.ALIGN.TOP_MID, 0, -111)
    faceRec2.source('/app/code/resource/image/faceRec2.png')
    // const faceAdd = dxui.Image.build('faceAdd', screenMain)
    // faceEnterView.faceAdd = faceAdd
    // faceAdd.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -90)
    // faceAdd.source('/app/code/resource/image/faceAdd.png')
    // const faceAddLbl = dxui.Label.build('faceAddLbl', faceAdd)
    // faceAddLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    // faceAddLbl.textFont(viewUtils.font(30))
    // faceAddLbl.textColor(0xffffff)
    // faceAddLbl.dataI18n = 'faceEnterView.faceAdd'
    // faceAddLbl.textAlign(dxui.Utils.TEXT_ALIGN.CENTER)
    faceEnterView.statusPanel = viewUtils.statusPanel(screenMain)
    // const faceError = dxui.Image.build('faceError', screenMain)
    // faceEnterView.faceError = faceError
    // faceError.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -90)
    // faceError.source('/app/code/resource/image/faceError.png')
    // faceError.hide()
    // const faceErrorLbl = dxui.Label.build('faceErrorLbl', faceError)
    // faceErrorLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    // faceErrorLbl.textFont(viewUtils.font(30))
    // faceErrorLbl.textColor(0xffffff)
    // faceErrorLbl.dataI18n = 'faceEnterView.faceError'
    // faceErrorLbl.textAlign(dxui.Utils.TEXT_ALIGN.CENTER)
    // faceEnterView.timeout()
}
faceEnterView.timeout = function () {
     // faceEnterView.statusPanel.fail("faceEnterView.faceError")
    // faceEnterView.faceAdd.hide()
    // faceEnterView.faceError.show()
}
faceEnterView.backCb = function () {
    if (!localUserAddView.nowUser) {
        return
    }
    if (localUserAddView.nowUser.id) {
        localUserAddView.addID(localUserAddView.nowUser.id)
    }
    if (localUserAddView.nowUser.name) {
        localUserAddView.addName(localUserAddView.nowUser.name)
    }
    if (localUserAddView.nowUser.idCard) {
        localUserAddView.addIDCard(localUserAddView.nowUser.idCard)
    }
    if (localUserAddView.nowUser.face) {
        localUserAddView.addFace(localUserAddView.nowUser.face)
    }
    if (localUserAddView.nowUser.pwd) {
        localUserAddView.addPwd(localUserAddView.nowUser.pwd)
    }
    if (localUserAddView.nowUser.card) {
        localUserAddView.addCard(localUserAddView.nowUser.card)
    }
    localUserAddView.addType(localUserAddView.nowUser.type)
}
export default faceEnterView
vf205_access/src/view/config/menu/localUser/localUserAddView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,682 @@
import dxui from '../../../../../dxmodules/dxUi.js'
import std from '../../../../../dxmodules/dxStd.js'
import viewUtils from "../../../viewUtils.js"
import topView from "../../../topView.js"
import localUserView from '../localUserView.js'
import faceEnterView from './faceEnterView.js'
import i18n from "../../../i18n.js"
import pinyin from '../../../pinyin/pinyin.js'
import screen from '../../../../screen.js'
const localUserAddView = {}
const dropdownData = ['保管员', '科长']
const dropdownData2 = ['User', 'Administrator']
localUserAddView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('localUserAddView', dxui.Utils.LAYER.MAIN)
    localUserAddView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        refreshType()
        if (!localUserAddView.deleteBtn.isHide()) {
            //修改用户不允许改id
            localUserAddView.userInfo[0].input.disable(true)
        } else {
            localUserAddView.userInfo[0].input.disable(false)
        }
    })
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_UNLOADED, () => {
    })
    const titleBox = viewUtils.title(screenMain, localUserView.screenMain, 'localUserAddViewTitle', 'localUserAddView.title', undefined)
    localUserAddView.titleBox = titleBox
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const titleBox2 = viewUtils.title(screenMain, localUserView.screenMain, 'localUserAddViewTitle2', 'localUserAddView.title2', undefined)
    localUserAddView.titleBox2 = titleBox2
    titleBox2.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    titleBox2.hide()
    const addUserBox = dxui.View.build('addUserBox', screenMain)
    viewUtils._clearStyle(addUserBox)
    addUserBox.setSize(screen.screenSize.width, 700)
    addUserBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 142)
    addUserBox.borderWidth(1)
    addUserBox.setBorderColor(0xDEDEDE)
    addUserBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_TOP, 0)
    addUserBox.bgOpa(0)
    addUserBox.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    addUserBox.flexAlign(dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.START)
    addUserBox.obj.lvObjSetStylePadGap(0, dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME)
    localUserAddView.userInfo = [
        {
            title: 'localUserAddView.id',
            value: null,
            required: true,
            type: 'input',
            input: null
        },
        {
            title: 'localUserAddView.name',
            value: null,
            required: true,
            type: 'input',
            input: null
        },
        {
            title: 'localUserAddView.idCard',
            value: null,
            type: 'input',
            input: null
        },
        {
            title: 'localUserAddView.face',
            value: null,
            type: 'button',
            btn: null,
            btnEdit: null,
            faceImg: null,
            deleteBtn: null
        },
        {
            title: 'localUserAddView.pwd',
            value: null,
            type: 'button',
            btn: null,
            btnEdit: null,
            pwdLbl: null,
            deleteBtn: null
        },
        {
            title: 'localUserAddView.card',
            value: null,
            type: 'button',
            btn: null,
            btnEdit: null,
            cardLbl: null,
            deleteBtn: null
        },
        {
            title: 'localUserAddView.type',
            value: null,
            type: 'dropdown'
        }
    ]
    localUserAddView.userInfo.forEach((item, index) => {
        const userBox = dxui.View.build('userInfo' + index, addUserBox)
        viewUtils._clearStyle(userBox)
        userBox.setSize(700, 65)
        userBox.borderWidth(1)
        userBox.setBorderColor(0xDEDEDE)
        userBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
        userBox.bgOpa(0)
        if (item.required) {
            const titleLbl = dxui.Label.build('titleLblRequired' + index, userBox)
            titleLbl.textFont(viewUtils.font(22))
            titleLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
            titleLbl.text('*')
            titleLbl.textColor(0xFD5353)
        }
        const titleLbl = dxui.Label.build('titleLbl' + index, userBox)
        titleLbl.textFont(viewUtils.font(22))
        titleLbl.align(dxui.Utils.ALIGN.LEFT_MID, 10, 0)
        titleLbl.dataI18n = item.title
        if (item.type === 'input') {
            const input = viewUtils.input(userBox, item.title, item.mode, undefined, "localUserAddView.input")
            input.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            input.textFont(viewUtils.font(22))
            input.setSize(260, 50)
            item.input = input
            input.on(dxui.Utils.EVENT.VALUE_CHANGED, () => {
                if (input.text() === "") {
                    return
                }
                switch (item.title) {
                    case 'localUserAddView.id':
                        localUserAddView.nowUser.id = input.text()
                        localUserAddView.nowUser.userId = input.text()
                        break;
                    case 'localUserAddView.name':
                        localUserAddView.nowUser.name = input.text()
                        break;
                    case 'localUserAddView.idCard':
                        localUserAddView.nowUser.idCard = input.text()
                        break;
                    default:
                        break;
                }
            })
        } else if (item.type === 'button') {
            const btn = dxui.Button.build(item.title, userBox)
            item.btn = btn
            btn.setSize(150, 50)
            btn.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            btn.bgColor(0xEEEEEE)
            btn.radius(10)
            const btnLbl = dxui.Label.build(item.title + 'btnLbl', btn)
            btnLbl.textFont(viewUtils.font(22))
            btnLbl.textColor(0x05AA8D)
            btnLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
            const btnEdit = dxui.Button.build(item.title + 'edit', userBox)
            item.btnEdit = btnEdit
            btnEdit.setSize(150, 50)
            btnEdit.align(dxui.Utils.ALIGN.RIGHT_MID, -60, 0)
            btnEdit.bgColor(0xEEEEEE)
            btnEdit.radius(10)
            btnEdit.hide()
            const btnEditLbl = dxui.Label.build(item.title + 'btnEditLbl', btnEdit)
            btnEditLbl.textFont(viewUtils.font(22))
            btnEditLbl.textColor(0x05AA8D)
            btnEditLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
            const deleteBtn = viewUtils.imageBtn(userBox, item.title + 'deleteBtn', '/app/code/resource/image/delete.png')
            item.deleteBtn = deleteBtn
            deleteBtn.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            deleteBtn.hide()
            if (item.title === 'localUserAddView.pwd') {
                btnLbl.dataI18n = 'localUserAddView.generate'
                btnEditLbl.dataI18n = 'localUserAddView.reset'
                // å¯†ç 
                const pwdLbl = dxui.Label.build(userBox.id + 'pwdLbl', userBox)
                item.pwdLbl = pwdLbl
                pwdLbl.align(dxui.Utils.ALIGN.LEFT_MID, 180, 0)
                pwdLbl.textColor(0x767676)
                pwdLbl.textFont(viewUtils.font(26))
                pwdLbl.hide()
                btn.on(dxui.Utils.EVENT.CLICK, () => {
                    pwdBoxBg.show()
                    pwdBoxBg.moveForeground()
                    topView.changeTheme(false)
                    localUserAddView.changePwd()
                })
                btnEdit.on(dxui.Utils.EVENT.CLICK, () => {
                    btn.send(dxui.Utils.EVENT.CLICK)
                })
                deleteBtn.on(dxui.Utils.EVENT.CLICK, () => {
                    viewUtils.confirmOpen('localUserAddView.confirm', 'localUserAddView.confirmPwd', () => {
                        localUserAddView.removePwd()
                    }, () => { })
                })
            } else {
                btnLbl.dataI18n = 'localUserAddView.enter'
                btnEditLbl.dataI18n = 'localUserAddView.edit'
            }
            if (item.title === 'localUserAddView.card') {
                // å¡
                const cardLbl = dxui.Label.build(userBox.id + 'cardLbl', userBox)
                item.cardLbl = cardLbl
                cardLbl.align(dxui.Utils.ALIGN.LEFT_MID, 180, 0)
                cardLbl.textColor(0x767676)
                cardLbl.textFont(viewUtils.font(26))
                cardLbl.hide()
                cardLbl.longMode(dxui.Utils.LABEL_LONG_MODE.SCROLL_CIRCULAR)
                cardLbl.width(150)
                btn.on(dxui.Utils.EVENT.CLICK, () => {
                    cardBoxBg.show()
                    cardBoxBg.moveForeground()
                    topView.changeTheme(false)
                    // å¼€å¯åˆ·å¡è¯†åˆ«
                    screen.getCardStart()
                })
                btnEdit.on(dxui.Utils.EVENT.CLICK, () => {
                    btn.send(dxui.Utils.EVENT.CLICK)
                })
                deleteBtn.on(dxui.Utils.EVENT.CLICK, () => {
                    viewUtils.confirmOpen('localUserAddView.confirm', 'localUserAddView.confirmCard', () => {
                        localUserAddView.removeCard()
                    }, () => { })
                })
            }
            if (item.title === 'localUserAddView.face') {
                // userBox.height(220)
                btn.on(dxui.Utils.EVENT.CLICK, () => {
                    if (!checkRequired()) {
                        return
                    }
                    dxui.loadMain(faceEnterView.screenMain)
                })
                btnEdit.on(dxui.Utils.EVENT.CLICK, () => {
                    if (!checkRequired()) {
                        return
                    }
                    dxui.loadMain(faceEnterView.screenMain)
                })
                // äººè„¸å›¾ç‰‡
                const facePreview = dxui.Button.build('facePreview', userBox)
                item.facePreview = facePreview
                facePreview.bgColor(0x000000)
                facePreview.align(dxui.Utils.ALIGN.LEFT_MID, 180, 0)
                const facePreviewLbl = dxui.Label.build('facePreviewLbl', facePreview)
                facePreviewLbl.textFont(viewUtils.font(22))
                facePreviewLbl.dataI18n = "localUserAddView.preview"
                facePreview.on(dxui.Utils.EVENT.CLICK, () => {
                    facePreviewBox.show()
                    facePreviewBox.moveForeground()
                })
                const facePreviewBox = dxui.View.build('facePreviewBox', screenMain)
                viewUtils._clearStyle(facePreviewBox)
                facePreviewBox.hide()
                facePreviewBox.setSize(screenMain.width(), screenMain.height())
                facePreviewBox.on(dxui.Utils.EVENT.CLICK, () => {
                    facePreviewBox.hide()
                })
                const faceImg = dxui.Image.build('faceImg', facePreviewBox)
                faceImg.align(dxui.Utils.ALIGN.CENTER, 0, 0)
                item.faceImg = faceImg
                deleteBtn.on(dxui.Utils.EVENT.CLICK, () => {
                    if (!checkRequired()) {
                        return
                    }
                    viewUtils.confirmOpen('localUserAddView.confirm', 'localUserAddView.confirmFace', () => {
                        localUserAddView.removeFace()
                    }, () => { })
                })
            }
        } else if (item.type === 'dropdown') {
            const dropdown = dxui.Dropdown.build(item.title, userBox)
            item.dropdown = dropdown
            dropdown.setSize(260, 50)
            dropdown.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            dropdown.textFont(viewUtils.font(22))
            dropdown.getList().textFont(viewUtils.font(22))
            dropdown.setSymbol('/app/code/resource/image/down.png')
            dropdown.on(dxui.Utils.EVENT.VALUE_CHANGED, () => {
                localUserAddView.nowUser.type = dropdown.getSelected()
            })
        }
    })
    // å¯†ç ç”Ÿæˆé¡µ
    const pwdBoxBg = dxui.View.build('pwdBoxBg', screenMain)
    viewUtils._clearStyle(pwdBoxBg)
    pwdBoxBg.bgColor(0x000000)
    pwdBoxBg.bgOpa(50)
    pwdBoxBg.setSize(screen.screenSize.width, screen.screenSize.height)
    pwdBoxBg.scroll(false)
    pwdBoxBg.hide()
    pwdBoxBg.on(dxui.Utils.EVENT.CLICK, () => {
        pwdBoxCloseBtn.send(dxui.Utils.EVENT.CLICK)
    })
    const pwdBox = dxui.View.build('pwdBox', pwdBoxBg)
    viewUtils._clearStyle(pwdBox)
    pwdBox.setSize(screen.screenSize.width, 694)
    pwdBox.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, 50)
    pwdBox.bgColor(0xffffff)
    pwdBox.radius(50)
    const pwdBoxLbl = dxui.Label.build('pwdBoxLbl', pwdBox)
    pwdBoxLbl.dataI18n = 'localUserAddView.pwdBoxLbl'
    pwdBoxLbl.textFont(viewUtils.font(36))
    pwdBoxLbl.align(dxui.Utils.ALIGN.TOP_MID, 0, 39)
    const pwdBoxCloseBtn = viewUtils.imageBtn(pwdBox, 'pwdBoxCloseBtn', '/app/code/resource/image/close_small.png')
    pwdBoxCloseBtn.align(dxui.Utils.ALIGN.TOP_RIGHT, -55, 18)
    pwdBoxCloseBtn.on(dxui.Utils.EVENT.CLICK, () => {
        pwdBoxBg.hide()
        topView.changeTheme(true)
    })
    const pwdBoxContent = dxui.View.build('pwdBoxContent', pwdBox)
    viewUtils._clearStyle(pwdBoxContent)
    pwdBoxContent.setSize(650, 100)
    pwdBoxContent.align(dxui.Utils.ALIGN.TOP_MID, 0, 172)
    pwdBoxContent.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    pwdBoxContent.flexAlign(dxui.Utils.FLEX_ALIGN.SPACE_AROUND, dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.CENTER)
    localUserAddView.pwdBoxContentItem = []
    for (let i = 0; i < 6; i++) {
        const pwdBoxContentItem = dxui.View.build('pwdBoxContentItem' + i, pwdBoxContent)
        pwdBoxContentItem.setSize(78, 90)
        pwdBoxContentItem.radius(13)
        pwdBoxContentItem.borderWidth(1)
        pwdBoxContentItem.setBorderColor(0xEAEAEA)
        const pwdBoxContentItemLbl = dxui.Label.build('pwdBoxContentItemLbl' + i, pwdBoxContentItem)
        pwdBoxContentItemLbl.textFont(viewUtils.font(30))
        pwdBoxContentItemLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
        pwdBoxContentItemLbl.text('0')
        localUserAddView.pwdBoxContentItem.push(pwdBoxContentItemLbl)
    }
    const pwdBoxSaveBtn = dxui.Button.build('pwdBoxSaveBtn', pwdBox)
    pwdBoxSaveBtn.setSize(210, 60)
    pwdBoxSaveBtn.align(dxui.Utils.ALIGN.TOP_LEFT, 87, 340)
    pwdBoxSaveBtn.bgColor(0xEAEAEA)
    pwdBoxSaveBtn.radius(10)
    pwdBoxSaveBtn.on(dxui.Utils.EVENT.CLICK, () => {
        localUserAddView.changePwd()
    })
    const pwdBoxSaveBtnLbl = dxui.Label.build('pwdBoxSaveBtnLbl', pwdBoxSaveBtn)
    pwdBoxSaveBtnLbl.dataI18n = 'localUserAddView.pwdBoxSaveBtnLbl'
    pwdBoxSaveBtnLbl.textFont(viewUtils.font(24))
    pwdBoxSaveBtnLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    pwdBoxSaveBtnLbl.textColor(0x000000)
    const pwdBoxConfirmBtn = dxui.Button.build('pwdBoxConfirmBtn', pwdBox)
    pwdBoxConfirmBtn.setSize(210, 60)
    pwdBoxConfirmBtn.align(dxui.Utils.ALIGN.TOP_RIGHT, -76, 340)
    pwdBoxConfirmBtn.bgColor(0x000000)
    pwdBoxConfirmBtn.radius(10)
    pwdBoxConfirmBtn.on(dxui.Utils.EVENT.CLICK, () => {
        localUserAddView.addPwd(localUserAddView.pwdBoxContentFin)
        pwdBoxCloseBtn.send(dxui.Utils.EVENT.CLICK)
    })
    const pwdBoxConfirmBtnLbl = dxui.Label.build('pwdBoxConfirmBtnLbl', pwdBoxConfirmBtn)
    pwdBoxConfirmBtnLbl.dataI18n = 'localUserAddView.pwdBoxConfirmBtnLbl'
    pwdBoxConfirmBtnLbl.textFont(viewUtils.font(24))
    pwdBoxConfirmBtnLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    // è¯»å–卡片中
    const cardBoxBg = dxui.View.build('cardBoxBg', screenMain)
    viewUtils._clearStyle(cardBoxBg)
    cardBoxBg.setSize(screen.screenSize.width, screen.screenSize.height)
    cardBoxBg.align(dxui.Utils.ALIGN.TOP_MID, 0, 0)
    cardBoxBg.bgColor(0x000000)
    cardBoxBg.bgOpa(50)
    cardBoxBg.scroll(false)
    cardBoxBg.hide()
    cardBoxBg.on(dxui.Utils.EVENT.CLICK, () => {
        cardBoxCloseBtn.send(dxui.Utils.EVENT.CLICK)
    })
    const cardBox = dxui.View.build('cardBox', cardBoxBg)
    viewUtils._clearStyle(cardBox)
    cardBox.setSize(screen.screenSize.width, 694)
    cardBox.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, 50)
    cardBox.bgColor(0xffffff)
    cardBox.radius(50)
    cardBox.on(dxui.Utils.EVENT.CLICK, () => {
    })
    const cardBoxLbl = dxui.Label.build('cardBoxLbl', cardBox)
    cardBoxLbl.dataI18n = 'localUserAddView.cardBoxLbl'
    cardBoxLbl.textFont(viewUtils.font(36))
    cardBoxLbl.align(dxui.Utils.ALIGN.TOP_MID, 0, 39)
    const cardBoxCloseBtn = viewUtils.imageBtn(cardBox, 'cardBoxCloseBtn', '/app/code/resource/image/close_small.png')
    cardBoxCloseBtn.align(dxui.Utils.ALIGN.TOP_RIGHT, -55, 18)
    cardBoxCloseBtn.on(dxui.Utils.EVENT.CLICK, () => {
        cardBoxBg.hide()
        topView.changeTheme(true)
        // å…³é—­åˆ·å¡è¯†åˆ«
        screen.endCardEnd()
    })
    const cardBoxInput = viewUtils.input(cardBox, 'localUserAddView.cardBoxInput', undefined, undefined, 'localUserAddView.cardBoxInput')
    localUserAddView.cardBoxInput = cardBoxInput
    cardBoxInput.align(dxui.Utils.ALIGN.TOP_MID, 0, 183)
    cardBoxInput.setSize(630, 75)
    cardBoxInput.on(dxui.Utils.EVENT.CLICK, () => {
        cardBoxInput.align(dxui.Utils.ALIGN.TOP_MID, 0, 90)
        pinyin.hideCb(() => {
            cardBoxInput.align(dxui.Utils.ALIGN.TOP_MID, 0, 183)
        })
    })
    const cardBoxResetBtn = dxui.Button.build('cardBoxResetBtn', cardBox)
    cardBoxResetBtn.setSize(210, 60)
    cardBoxResetBtn.align(dxui.Utils.ALIGN.TOP_LEFT, 87, 340)
    cardBoxResetBtn.bgColor(0xEAEAEA)
    cardBoxResetBtn.radius(10)
    cardBoxResetBtn.on(dxui.Utils.EVENT.CLICK, () => {
        cardBoxInput.text('')
    })
    const cardBoxResetBtnLbl = dxui.Label.build('cardBoxResetBtnLbl', cardBoxResetBtn)
    cardBoxResetBtnLbl.dataI18n = 'localUserAddView.cardBoxResetBtnLbl'
    cardBoxResetBtnLbl.textFont(viewUtils.font(24))
    cardBoxResetBtnLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    cardBoxResetBtnLbl.textColor(0x000000)
    const cardBoxSaveBtn = dxui.Button.build('cardBoxSaveBtn', cardBox)
    cardBoxSaveBtn.setSize(210, 60)
    cardBoxSaveBtn.align(dxui.Utils.ALIGN.TOP_RIGHT, -76, 340)
    cardBoxSaveBtn.bgColor(0x000000)
    cardBoxSaveBtn.radius(10)
    cardBoxSaveBtn.on(dxui.Utils.EVENT.CLICK, () => {
        cardBoxCloseBtn.send(dxui.Utils.EVENT.CLICK)
        if (cardBoxInput.text()) {
            localUserAddView.addCard(cardBoxInput.text())
        }
    })
    const cardBoxSaveBtnLbl = dxui.Label.build('cardBoxSaveBtnLbl', cardBoxSaveBtn)
    cardBoxSaveBtnLbl.dataI18n = 'localUserAddView.cardBoxSaveBtnLbl'
    cardBoxSaveBtnLbl.textFont(viewUtils.font(24))
    cardBoxSaveBtnLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    const deleteBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'deleteBtn', 'localUserAddView.delete', () => {
        if (!checkRequired()) {
            return
        }
        viewUtils.confirmOpen('localUserAddView.confirmDelete', 'localUserAddView.confirmDeleteContent', () => {
            // åˆ é™¤ç”¨æˆ·
            const res = screen.deleteUser(localUserAddView.nowUser)
            if (res) {
                dxui.loadMain(localUserView.screenMain)
            } else {
                localUserAddView.statusPanel.fail()
            }
        }, () => { })
    }, 0xEAEAEA, 0xEA0000)
    deleteBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -200)
    localUserAddView.deleteBtn = deleteBtn
    deleteBtn.hide()
    const saveBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'saveBtn', 'localUserAddView.save', async () => {
        if (!checkRequired()) {
            return
        }
        let res = false
        if (localUserAddView.deleteBtn.isHide()) {
            // æ–°å¢žç”¨æˆ·
            res = await screen.insertUser(localUserAddView.nowUser)
        } else {
            // ä¿®æ”¹ç”¨æˆ·
            res = screen.updateUser(localUserAddView.nowUser)
        }
        if (res === true) {
            localUserAddView.statusPanel.success()
            std.setTimeout(() => {
                // æˆåŠŸè¿”å›žä¸Šä¸€å±‚ç•Œé¢
                dxui.loadMain(localUserView.screenMain)
            }, 500)
        } else {
            if (typeof res === "string") {
                localUserAddView.statusPanel.fail(res)
            } else {
                localUserAddView.statusPanel.fail()
            }
        }
    })
    saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
    localUserAddView.saveBtn = saveBtn
    localUserAddView.statusPanel = viewUtils.statusPanel(screenMain, 'localUserAddView.success', 'localUserAddView.fail')
}
localUserAddView.addID = function (id) {
    localUserAddView.userInfo[0].input.text(id)
    localUserAddView.nowUser.id = id
}
localUserAddView.removeID = function () {
    localUserAddView.userInfo[0].input.text('')
}
localUserAddView.addName = function (name) {
    localUserAddView.userInfo[1].input.text(name)
    localUserAddView.nowUser.name = name
}
localUserAddView.removeName = function () {
    localUserAddView.userInfo[1].input.text('')
}
localUserAddView.addIDCard = function (idCard) {
    localUserAddView.userInfo[2].input.text(idCard)
    localUserAddView.nowUser.idCard = idCard
}
localUserAddView.removeIDCard = function () {
    localUserAddView.userInfo[2].input.text('')
}
localUserAddView.addFace = function (face) {
    localUserAddView.userInfo[3].btnEdit.show()
    localUserAddView.userInfo[3].btn.hide()
    const faceImg = localUserAddView.userInfo[3].faceImg
    faceImg.source(face)
    faceImg.show()
    // let header = dxui.Utils.GG.NativeDraw.lvImgDecoderGetInfo(face)
    // let zoom = 60 / header.h * 256
    // faceImg.obj.lvImgSetZoom(zoom)
    // faceImg.obj.lvImgSetSizeMode(dxui.Utils.ENUM.LV_IMG_SIZE_MODE_REAL)
    // faceImg.setSize(Math.ceil(zoom / 256 * header.w), 60)
    localUserAddView.userInfo[3].deleteBtn.show()
    localUserAddView.nowUser.face = face
}
localUserAddView.removeFace = function () {
    localUserAddView.userInfo[3].btn.show()
    localUserAddView.userInfo[3].btnEdit.hide()
    localUserAddView.userInfo[3].deleteBtn.hide()
    localUserAddView.userInfo[3].faceImg.hide()
    if (localUserAddView.nowUser && localUserAddView.nowUser.face) {
        delete localUserAddView.nowUser.face
    }
}
localUserAddView.addPwd = function (pwd) {
    localUserAddView.userInfo[4].btn.hide()
    localUserAddView.userInfo[4].btnEdit.show()
    localUserAddView.userInfo[4].deleteBtn.show()
    localUserAddView.userInfo[4].pwdLbl.show()
    localUserAddView.userInfo[4].pwdLbl.text(pwd)
    localUserAddView.nowUser.pwd = pwd
}
localUserAddView.removePwd = function () {
    localUserAddView.userInfo[4].btn.show()
    localUserAddView.userInfo[4].btnEdit.hide()
    localUserAddView.userInfo[4].deleteBtn.hide()
    localUserAddView.userInfo[4].pwdLbl.hide()
    if (localUserAddView.nowUser && localUserAddView.nowUser.pwd) {
        delete localUserAddView.nowUser.pwd
    }
}
localUserAddView.addCard = function (card) {
    localUserAddView.userInfo[5].btn.hide()
    localUserAddView.userInfo[5].btnEdit.show()
    localUserAddView.userInfo[5].deleteBtn.show()
    localUserAddView.userInfo[5].cardLbl.show()
    localUserAddView.userInfo[5].cardLbl.text(card)
    localUserAddView.nowUser.card = card
}
localUserAddView.removeCard = function () {
    localUserAddView.userInfo[5].btn.show()
    localUserAddView.userInfo[5].btnEdit.hide()
    localUserAddView.userInfo[5].deleteBtn.hide()
    localUserAddView.userInfo[5].cardLbl.hide()
    if (localUserAddView.nowUser && localUserAddView.nowUser.card) {
        delete localUserAddView.nowUser.card
    }
}
localUserAddView.addType = function (type) {
    localUserAddView.userInfo[6].dropdown.setSelected(type)
}
localUserAddView.changePwd = function () {
    const randomPwd = Math.floor(Math.random() * 900000 + 100000).toString()
    localUserAddView.pwdBoxContentFin = randomPwd
    localUserAddView.pwdBoxContentItem.forEach((item, index) => {
        item.text(randomPwd[index])
    })
}
localUserAddView.isEdit = function (flag) {
    localUserAddView.removeFace()
    localUserAddView.removePwd()
    localUserAddView.removeCard()
    localUserAddView.removeID()
    localUserAddView.removeName()
    localUserAddView.removeIDCard()
    if (flag) {
        localUserAddView.saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -53)
        localUserAddView.deleteBtn.show()
        localUserAddView.titleBox2.show()
    } else {
        localUserAddView.saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
        localUserAddView.deleteBtn.hide()
        localUserAddView.titleBox2.hide()
        localUserAddView.nowUser = {}
    }
}
// æ£€æŸ¥å¿…填项
function checkRequired() {
    if (!localUserAddView.userInfo[0].input.text()) {
        localUserAddView.statusPanel.fail("localUserAddView.requiredInfo")
        return false
    }
    if (!localUserAddView.userInfo[1].input.text()) {
        localUserAddView.statusPanel.fail("localUserAddView.requiredInfo")
        return false
    }
    return true
}
function refreshType() {
    switch (screen.getConfig()['base.language']) {
        case 'CN':
            localUserAddView.userInfo[6].dropdown.setOptions(dropdownData)
            break;
        case 'EN':
            localUserAddView.userInfo[6].dropdown.setOptions(dropdownData2)
            break;
        default:
            break;
    }
}
export default localUserAddView
vf205_access/src/view/config/menu/localUserView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,315 @@
import dxui from '../../../../dxmodules/dxUi.js'
import viewUtils from "../../viewUtils.js"
import topView from "../../topView.js"
import configView from '../configView.js'
import pinyin from '../../pinyin/pinyin.js'
import localUserAddView from './localUser/localUserAddView.js'
import faceEnterView from './localUser/faceEnterView.js'
import screen from '../../../screen.js'
const localUserView = {}
localUserView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('localUserView', dxui.Utils.LAYER.MAIN)
    localUserView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        localUserView.nowPage = localUserView.nowPage ? localUserView.nowPage : 0
        let users = screen.getUsers(localUserView.nowPage, 6)
        while (users.data.length == 0 && localUserView.nowPage > 0) {
            localUserView.nowPage -= 1
            users = screen.getUsers(localUserView.nowPage, 6)
        }
        if (users.data.length > 0) {
            // localUserView.initData([{ id: "1", name: '张三' }, { id: "2", name: '李四' }, { id: "3", name: '王五' }, { id: "4", name: '赵六' }, { id: "5", name: '孙七' }, { id: "6", name: '周八' }, { id: "7", name: '吴九' }, { id: "8", name: '郑十' }, { id: "9", name: '陈十一' }, { id: "10", name: '赵十二' }, { id: "11", name: '孙十三' }, { id: "12", name: '周十四' }, { id: "13", name: '吴十五' }, { id: "14", name: '郑十六' }, { id: "15", name: '陈十七' }, { id: "16", name: '赵十八' }, { id: "17", name: '孙十九' }, { id: "20", name: '周二十' },])
            localUserView.initData(users.data)
        } else {
            localUserView.initData()
        }
        // åˆ·æ–°åˆ†é¡µä¿¡æ¯
        refreshPageInfo(users)
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'localUserViewTitle', 'localUserView.title', () => { localUserView.nowPage = 0 })
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const empty = dxui.Image.build('empty', screenMain)
    localUserView.empty = empty
    empty.align(dxui.Utils.ALIGN.TOP_MID, 0, 218)
    empty.source('/app/code/resource/image/empty.png')
    const emptyLbl = dxui.Label.build('emptyLbl', screenMain)
    localUserView.emptyLbl = emptyLbl
    emptyLbl.textFont(viewUtils.font(26))
    emptyLbl.align(dxui.Utils.ALIGN.TOP_MID, 0, 479)
    emptyLbl.dataI18n = 'localUserView.empty'
    emptyLbl.textColor(0x888888)
    const userList = dxui.View.build('userList', screenMain)
    viewUtils._clearStyle(userList)
    localUserView.userList = userList
    userList.setSize(screen.screenSize.width, 570)
    userList.align(dxui.Utils.ALIGN.TOP_MID, 0, 142)
    userList.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    userList.flexAlign(dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START)
    userList.obj.lvObjSetStylePadGap(5, dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME)
    userList.hide()
    const searchBox = dxui.View.build('searchBox', userList)
    viewUtils._clearStyle(searchBox)
    searchBox.setSize(screen.screenSize.width, 76)
    searchBox.bgOpa(0)
    searchBox.borderWidth(1)
    searchBox.setBorderColor(0xDEDEDE)
    searchBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const searchInput = viewUtils.input(searchBox, 'searchBoxInput', undefined, () => {
    }, 'localUserView.search')
    searchInput.setSize(screen.screenSize.width / 2, 60)
    searchInput.align(dxui.Utils.ALIGN.LEFT_MID, 28, 0)
    const searchBtn = dxui.Button.build('searchBtn', searchBox)
    searchBtn.setSize(126, 44)
    searchBtn.align(dxui.Utils.ALIGN.RIGHT_MID, -29, 0)
    searchBtn.bgColor(0xF6FAFA)
    searchBtn.radius(10)
    searchBtn.on(dxui.Utils.EVENT.CLICK, () => {
        const users = screen.getUsers(0, 6, searchInput.text(), searchInput.text())
        if (users.data) {
            localUserView.initData(users.data)
        } else {
            localUserView.initData([])
        }
        // pinyin.hide()
    })
    const searchBtnLbl = dxui.Label.build('searchBtnLbl', searchBtn)
    searchBtnLbl.dataI18n = 'localUserView.searchBtn'
    searchBtnLbl.textFont(viewUtils.font(26))
    searchBtnLbl.textColor(0x05AA8D)
    searchBtnLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    localUserView.userItemList = []
    for (let i = 0; i < 6; i++) {
        const userItem = dxui.View.build('userItem' + i, userList)
        viewUtils._clearStyle(userItem)
        userItem.setSize(screen.screenSize.width, 76)
        userItem.align(dxui.Utils.ALIGN.TOP_MID, 0, 0)
        userItem.bgOpa(0)
        userItem.borderWidth(1)
        userItem.setBorderColor(0xDEDEDE)
        userItem.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
        userItem.hide()
        const userItemId0 = dxui.Label.build('userItemId0' + i, userItem)
        userItemId0.text('ID:')
        userItemId0.textFont(viewUtils.font(26))
        userItemId0.align(dxui.Utils.ALIGN.LEFT_MID, 28, 0)
        const userItemId = dxui.Label.build('userItemId' + i, userItem)
        userItemId.text(i + '')
        userItemId.textFont(viewUtils.font(26))
        userItemId.align(dxui.Utils.ALIGN.LEFT_MID, 80, 0)
        userItemId.width(100)
        userItemId.longMode(dxui.Utils.LABEL_LONG_MODE.SCROLL_CIRCULAR)
        const userItemName = dxui.Label.build('userItemName' + i, userItem)
        userItemName.text('')
        userItemName.textFont(viewUtils.font(26))
        userItemName.align(dxui.Utils.ALIGN.LEFT_MID, 220, 0)
        userItemName.width(200)
        userItemName.longMode(dxui.Utils.LABEL_LONG_MODE.SCROLL_CIRCULAR)
        const userItemEdit = dxui.Button.build('userItemEdit' + i, userItem)
        userItemEdit.setSize(126, 44)
        userItemEdit.align(dxui.Utils.ALIGN.RIGHT_MID, -29, 0)
        userItemEdit.bgColor(0xF6FAFA)
        userItemEdit.radius(10)
        userItemEdit.on(dxui.Utils.EVENT.CLICK, () => {
            localUserAddView.isEdit(true)
            dxui.loadMain(localUserAddView.screenMain)
            let item = localUserView.userData.filter(item => {
                return item.id === userItemId.text().replace('ID:', '')
            })
            if (item) {
                item = item[0]
                const voucher = screen.getVoucher(item.id)
                Object.assign(item, voucher);
                localUserAddView.nowUser = item
                if (item.id) {
                    localUserAddView.addID(item.id)
                }
                if (item.name) {
                    localUserAddView.addName(item.name)
                }
                if (item.idCard) {
                    localUserAddView.addIDCard(item.idCard)
                }
                if (item.face) {
                    localUserAddView.addFace(item.face)
                }
                if (item.pwd) {
                    localUserAddView.addPwd(item.pwd)
                }
                if (item.card) {
                    localUserAddView.addCard(item.card)
                }
                localUserAddView.addType(item.type)
            }
        })
        const userItemEditLbl = dxui.Label.build('userItemEditLbl' + i, userItemEdit)
        userItemEditLbl.dataI18n = 'localUserView.edit'
        userItemEditLbl.textFont(viewUtils.font(26))
        userItemEditLbl.textColor(0x05AA8D)
        userItemEditLbl.align(dxui.Utils.ALIGN.CENTER, 0, 0)
        localUserView.userItemList.push({ userItem, userItemId, userItemName })
    }
    const pageNextBtn = dxui.Button.build('pageNextBtn', screenMain)
    pageNextBtn.bgColor(0x000000)
    localUserView.pageNextBtn = pageNextBtn
    const pageNextLbl = dxui.Label.build('pageNextLbl', pageNextBtn)
    pageNextLbl.text("→")
    pageNextBtn.align(dxui.Utils.ALIGN.BOTTOM_RIGHT, -20, -372)
    pageNextBtn.textFont(viewUtils.font(20))
    const pagePrevBtn = dxui.Button.build('pagePrevBtn', screenMain)
    pagePrevBtn.bgColor(0x000000)
    localUserView.pagePrevBtn = pagePrevBtn
    const pagePrevLbl = dxui.Label.build('pagePrevLbl', pagePrevBtn)
    pagePrevLbl.text("←")
    pagePrevBtn.align(dxui.Utils.ALIGN.BOTTOM_LEFT, 20, -372)
    pagePrevBtn.textFont(viewUtils.font(20))
    pageNextBtn.on(dxui.Utils.EVENT.CLICK, () => {
        if (!localUserView.nowPage) {
            localUserView.nowPage = 0
        }
        localUserView.pageNum += 1
        const users = screen.getUsers(localUserView.pageNum, 6)
        if (users.data) {
            localUserView.initData(users.data)
        } else {
            localUserView.initData([])
        }
        refreshPageInfo(users)
    })
    pagePrevBtn.on(dxui.Utils.EVENT.CLICK, () => {
        if (!localUserView.nowPage) {
            localUserView.nowPage = 0
        }
        localUserView.pageNum -= 1
        const users = screen.getUsers(localUserView.pageNum, 6)
        if (users.data) {
            localUserView.initData(users.data)
        } else {
            localUserView.initData([])
        }
        refreshPageInfo(users)
    })
    const pageSelect = dxui.Dropdown.build('pageSelect', screenMain)
    localUserView.pageSelect = pageSelect
    pageSelect.textFont(viewUtils.font(22))
    pageSelect.getList().textFont(viewUtils.font(22))
    pageSelect.setSize(150, 55)
    pageSelect.setSymbol('/app/code/resource/image/down.png')
    pageSelect.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -370)
    pageSelect.on(dxui.Utils.EVENT.VALUE_CHANGED, () => {
        localUserView.pageNum = pageSelect.getSelected()
        const users = screen.getUsers(localUserView.pageNum, 6)
        if (users.data) {
            localUserView.initData(users.data)
        } else {
            localUserView.initData([])
        }
        refreshPageInfo(users)
    })
    const syncBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'syncBtn', 'localUserView.sync', () => {
    }, 0xEAEAEA, 0x000000)
    syncBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -204)
    localUserView.syncBtn = syncBtn
    syncBtn.hide()
    syncBtn.on(dxui.Utils.EVENT.CLICK, () => {
        viewUtils.confirmOpen('localUserView.attention', 'localUserView.attentionContent', () => {
            viewUtils.confirmOpen('localUserView.tip', 'localUserView.tipContent')
        })
    })
    const addBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'addBtn', 'localUserView.add', () => {
        localUserAddView.isEdit(false)
        dxui.loadMain(localUserAddView.screenMain)
    })
    addBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
}
localUserView.initData = function (data) {
    // å½“前页的人员信息
    localUserView.userData = data
    localUserView.refresh(data)
}
localUserView.refresh = function (data) {
    if (data === undefined || data === null) {
        localUserView.empty.show()
        localUserView.emptyLbl.show()
        localUserView.syncBtn.hide()
        localUserView.userList.hide()
        return
    }
    localUserView.userItemList.forEach(item => {
        item.userItem.hide()
    })
    // æ¸²æŸ“人员列表
    data.forEach((item, index) => {
        if (index >= localUserView.userItemList.length) {
            return
        }
        localUserView.userItemList[index].userItemId.text(item.id)
        // æ˜¾ç¤ºå§“名和人员类型
        let typeText = item.type == 1 ? '(保管员)' : item.type == 2 ? '(科长)' : ''
        localUserView.userItemList[index].userItemName.text(item.name + typeText)
        localUserView.userItemList[index].userItem.show()
    })
    localUserView.empty.hide()
    localUserView.emptyLbl.hide()
    // localUserView.syncBtn.show()
    localUserView.userList.show()
}
function refreshPageInfo(users) {
    if (users.currentPage == 1) {
        localUserView.pagePrevBtn.disable(true)
        localUserView.pagePrevBtn.hide()
    } else {
        localUserView.pagePrevBtn.disable(false)
        localUserView.pagePrevBtn.show()
    }
    if (users.currentPage == users.totalPage || users.totalPage == 0) {
        localUserView.pageNextBtn.disable(true)
        localUserView.pageNextBtn.hide()
    } else {
        localUserView.pageNextBtn.disable(false)
        localUserView.pageNextBtn.show()
    }
    if (users.totalPage == 0 || users.totalPage == 1) {
        localUserView.pageSelect.hide()
    } else {
        localUserView.pageSelect.show()
    }
    localUserView.pageSelect.setOptions(Array.from({ length: users.totalPage }, (_, index) => String(index + 1)))
    localUserView.pageSelect.setSelected(users.currentPage - 1)
}
export default localUserView
vf205_access/src/view/config/menu/networkSettingView.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,441 @@
import dxui from '../../../../dxmodules/dxUi.js'
import std from '../../../../dxmodules/dxStd.js'
import viewUtils from "../../viewUtils.js"
import topView from "../../topView.js"
import configView from '../configView.js'
import i18n from "../../i18n.js"
import screen from '../../../screen.js'
const networkSettingView = {}
networkSettingView.init = function () {
    /**************************************************创建屏幕*****************************************************/
    const screenMain = dxui.View.build('networkSettingView', dxui.Utils.LAYER.MAIN)
    networkSettingView.screenMain = screenMain
    screenMain.scroll(false)
    screenMain.bgColor(0xffffff)
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_LOADED, () => {
        topView.changeTheme(true)
        let devType = screen.getConfig()["sys.devType"]
        let netTypes = []
        netTypes = devType == 1 ? [i18n.t('networkSettingView.ethernet'), i18n.t('networkSettingView._4G')] : [i18n.t('networkSettingView.ethernet'), i18n.t('networkSettingView.wifi'), i18n.t('networkSettingView._4G')]
        networkSettingView.netInfo[0].value = netTypes
        networkSettingView.netInfo[0].dropdown.setOptions(networkSettingView.netInfo[0].value)
        const configAll = screen.getConfig()
        networkSettingView.changeNetType(configAll["net.type"] - 1)
        networkSettingView.netInfo[0].dropdown.setSelected(configAll["net.type"] - 1)
        networkSettingView.netInfo[1].dropdown.setOptions([configAll["net.ssid"]])
        networkSettingView.netInfo[1].dropdown.setSelected(0)
        networkSettingView.netInfo[2].input.text(configAll["net.psk"])
        networkSettingView.netInfo[3].switch.select(configAll["net.dhcp"] == 2)
        networkSettingView.netInfo[4].input.text(configAll["net.ip"])
        networkSettingView.netInfo[5].input.text(configAll["net.mask"])
        networkSettingView.netInfo[6].input.text(configAll["net.gateway"])
        networkSettingView.netInfo[7].input.text(configAll["net.dns"].split(",")[0])
        networkSettingView.netInfo[8].input.text(configAll["net.dns"].split(",")[1])
        networkSettingView.netInfo[9].label.text(configAll["net.mac"])
    })
    screenMain.on(dxui.Utils.ENUM.LV_EVENT_SCREEN_UNLOADED, () => {
        wifiListBoxClose.send(dxui.Utils.EVENT.CLICK)
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'networkSettingViewTitle', 'networkSettingView.title')
    titleBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 70)
    const networkSettingBox = dxui.View.build('networkSettingBox', screenMain)
    viewUtils._clearStyle(networkSettingBox)
    networkSettingBox.setSize(screen.screenSize.width, 830)
    networkSettingBox.bgColor(0xeeeeee)
    networkSettingBox.align(dxui.Utils.ALIGN.TOP_MID, 0, 142)
    networkSettingBox.borderWidth(1)
    networkSettingBox.setBorderColor(0xDEDEDE)
    networkSettingBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_TOP, 0)
    networkSettingBox.bgOpa(0)
    networkSettingBox.flexFlow(dxui.Utils.FLEX_FLOW.ROW_WRAP)
    networkSettingBox.flexAlign(dxui.Utils.FLEX_ALIGN.CENTER, dxui.Utils.FLEX_ALIGN.START, dxui.Utils.FLEX_ALIGN.START)
    networkSettingBox.obj.lvObjSetStylePadGap(0, dxui.Utils.ENUM._LV_STYLE_STATE_CMP_SAME)
    networkSettingView.netInfo = [
        {
            title: 'networkSettingView.type',
            value: [i18n.t('networkSettingView.ethernet'), i18n.t('networkSettingView.wifi'), i18n.t('networkSettingView._4G')],
            type: 'dropdown',
            obj: null,
            dropdown: null
        },
        {
            title: 'networkSettingView.wifiName',
            value: [],
            type: 'dropdown',
            obj: null,
            dropdown: null
        },
        {
            title: 'networkSettingView.wifiPwd',
            value: null,
            type: 'input',
            obj: null
        },
        {
            title: 'networkSettingView.dhcp',
            value: null,
            type: 'switch',
            obj: null
        },
        {
            title: 'networkSettingView.ip',
            value: null,
            type: 'input',
            obj: null
        },
        {
            title: 'networkSettingView.mask',
            value: null,
            type: 'input',
            obj: null
        },
        {
            title: 'networkSettingView.gateway',
            value: null,
            type: 'input',
            obj: null
        },
        {
            title: 'networkSettingView.dns',
            value: null,
            type: 'input',
            obj: null
        },
        {
            title: 'networkSettingView.dns2',
            value: null,
            type: 'input',
            obj: null
        },
        {
            title: 'networkSettingView.mac',
            value: 'DC-87-F2-97-3B-26',
            type: 'label',
            obj: null,
            label: null
        },
        {
            title: 'networkSettingView.status',
            value: i18n.t('networkSettingView.networkUnconnected'),
            type: 'label',
            obj: null,
            label: null
        }
    ]
    networkSettingView.netInfo.forEach((item, index) => {
        const networkSettingItem = dxui.View.build(networkSettingBox.id + item.title, networkSettingBox)
        viewUtils._clearStyle(networkSettingItem)
        item.obj = networkSettingItem
        networkSettingItem.setSize(760, 76)
        networkSettingItem.bgColor(0xffffff)
        networkSettingItem.borderWidth(1)
        networkSettingItem.setBorderColor(0xDEDEDE)
        networkSettingItem.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
        const title = dxui.Label.build(item.title, networkSettingItem)
        title.textFont(viewUtils.font(26))
        title.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
        title.dataI18n = item.title
        if (item.type === 'input') {
            const input = viewUtils.input(networkSettingItem, item.title + 'input', undefined, undefined, 'networkSettingView.input')
            input.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            input.setSize(400, 60)
            item.input = input
        }
        if (item.type === 'switch') {
            const __switch = dxui.Switch.build(item.title + 'switch', networkSettingItem)
            __switch.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            __switch.setSize(70, 35)
            item.switch = __switch
        }
        if (item.type === 'dropdown') {
            const dropdown = dxui.Dropdown.build(item.title + 'dropdown', networkSettingItem)
            dropdown.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            dropdown.setSize(200, 60)
            dropdown.textFont(viewUtils.font(26))
            dropdown.getList().textFont(viewUtils.font(26))
            dropdown.setSymbol('/app/code/resource/image/down.png')
            dropdown.setOptions(item.value)
            item.dropdown = dropdown
            if (item.title === 'networkSettingView.type') {
                dropdown.on(dxui.Utils.EVENT.VALUE_CHANGED, () => {
                    networkSettingView.changeNetType(dropdown.getSelected())
                    if(dropdown.getSelected() == 2) {
                        // screen.switchNetworkType(dropdown.getSelected() + 2)
                    } else {
                        screen.switchNetworkType(dropdown.getSelected() + 1)
                    }
                })
            }
            if (item.title === 'networkSettingView.wifiName') {
                dropdown.on(dxui.Utils.EVENT.CLICK, () => {
                    screen.netGetWifiSsidList()
                    wifiList.refresh()
                    wifiListBoxbg.moveForeground()
                    wifiListBoxbg.show()
                })
            }
        }
        if (item.type === 'label') {
            const label = dxui.Label.build(item.title + 'label', networkSettingItem)
            label.textFont(viewUtils.font(26))
            label.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
            label.text(item.value)
            label.textColor(0x333333)
            item.label = label
        }
    })
    // wifi列表
    const wifiListBoxbg = dxui.View.build('wifiListBoxbg', screenMain)
    viewUtils._clearStyle(wifiListBoxbg)
    wifiListBoxbg.setSize(screen.screenSize.width, screen.screenSize.height)
    wifiListBoxbg.bgColor(0x000000)
    wifiListBoxbg.bgOpa(50)
    wifiListBoxbg.hide()
    wifiListBoxbg.on(dxui.Utils.EVENT.CLICK, () => {
        wifiListBoxClose.send(dxui.Utils.EVENT.CLICK)
    })
    const wifiListBox = dxui.View.build('wifiListBox', wifiListBoxbg)
    viewUtils._clearStyle(wifiListBox)
    wifiListBox.setSize(520, 560)
    wifiListBox.bgColor(0xffffff)
    wifiListBox.radius(20)
    wifiListBox.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    const wifiListBoxTitle = dxui.Label.build('wifiListBoxTitle', wifiListBox)
    wifiListBoxTitle.textFont(viewUtils.font(28))
    wifiListBoxTitle.align(dxui.Utils.ALIGN.TOP_MID, 0, 32)
    wifiListBoxTitle.dataI18n = 'networkSettingView.wifiList'
    const wifiListBoxClose = viewUtils.imageBtn(wifiListBox, 'wifiListBoxClose', '/app/code/resource/image/close_small.png')
    wifiListBoxClose.align(dxui.Utils.ALIGN.TOP_RIGHT, -36, 30)
    wifiListBoxClose.on(dxui.Utils.EVENT.CLICK, () => {
        wifiListBoxbg.hide()
    })
    const closeBtn = dxui.Button.build('closeBtn', wifiListBox)
    closeBtn.setSize(172, 50)
    closeBtn.radius(10)
    closeBtn.bgColor(0xEAEAEA)
    closeBtn.align(dxui.Utils.ALIGN.BOTTOM_LEFT, 69, -53)
    closeBtn.on(dxui.Utils.EVENT.CLICK, () => {
        wifiListBoxClose.send(dxui.Utils.EVENT.CLICK)
    })
    const closeBtnText = dxui.Label.build('closeBtnText', closeBtn)
    closeBtnText.textFont(viewUtils.font(24))
    closeBtnText.dataI18n = 'networkSettingView.close'
    closeBtnText.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    closeBtnText.textColor(0x000000)
    const confirmBtn = dxui.Button.build('confirmBtn', wifiListBox)
    confirmBtn.setSize(172, 50)
    confirmBtn.radius(10)
    confirmBtn.bgColor(0x000000)
    confirmBtn.align(dxui.Utils.ALIGN.BOTTOM_RIGHT, -69, -53)
    confirmBtn.on(dxui.Utils.EVENT.CLICK, () => {
        wifiListBoxClose.send(dxui.Utils.EVENT.CLICK)
        networkSettingView.netInfo[1].dropdown.setOptions([networkSettingView.selectedValue])
    })
    const confirmBtnText = dxui.Label.build('confirmBtnText', confirmBtn)
    confirmBtnText.textFont(viewUtils.font(24))
    confirmBtnText.dataI18n = 'networkSettingView.confirm'
    confirmBtnText.align(dxui.Utils.ALIGN.CENTER, 0, 0)
    confirmBtnText.textColor(0xffffff)
    networkSettingView.wifiListData = []
    networkSettingView.selectedValue = ''
    networkSettingView.selectedItem = 0
    const wifiList = viewUtils.cycleList(wifiListBox, 'wifiList', [440, 300], 5, (item) => {
        const wifiItemLbl = dxui.Label.build(item.id + 'wifiItemLbl', item)
        wifiItemLbl.align(dxui.Utils.ALIGN.LEFT_MID, 25, 0)
        wifiItemLbl.textFont(viewUtils.font(26))
        wifiItemLbl.textColor(0x888888)
        wifiItemLbl.width(300)
        wifiItemLbl.longMode(dxui.Utils.LABEL_LONG_MODE.SCROLL_CIRCULAR)
        item.radius(10)
        item.on(dxui.Utils.EVENT.CLICK, () => {
            if (networkSettingView.selectedItem) {
                networkSettingView.selectedItem.bgOpa(0)
            }
            networkSettingView.selectedItem = item
            networkSettingView.selectedValue = wifiItemLbl.text()
            item.bgColor(0xEAEAEA)
            item.bgOpa(100)
        })
        const wifiItemImg = dxui.Image.build(item.id + 'wifi', item)
        wifiItemImg.align(dxui.Utils.ALIGN.RIGHT_MID, -24, 0)
        wifiItemImg.source('/app/code/resource/image/wifi.png')
        const lockItemImg = dxui.Image.build(item.id + 'lock', item)
        lockItemImg.align(dxui.Utils.ALIGN.RIGHT_MID, -55, 0)
        lockItemImg.source('/app/code/resource/image/lock.png')
        return { wifiItemLbl, wifiItemImg, lockItemImg }
    }, (userdata, index) => {
        const txt = networkSettingView.wifiListData[Math.abs((index % 5))]
        if (txt) {
            userdata.wifiItemLbl.text(txt)
            userdata.wifiItemImg.show()
            userdata.lockItemImg.show()
        } else {
            userdata.wifiItemLbl.text(' ')
            userdata.wifiItemImg.hide()
            userdata.lockItemImg.hide()
        }
    })
    wifiList.align(dxui.Utils.ALIGN.TOP_MID, 0, 110)
    wifiList.bgOpa(0)
    networkSettingView.wifiList = wifiList
    const saveBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'saveBtn', 'networkSettingView.save', () => {
        // èŽ·å–WiFi SSID,优先使用下拉选择的值,如果没有则使用配置中的值
        let wifiSsid = networkSettingView.selectedValue || ""
        let saveConfigData = {
            "net": {
                "type": networkSettingView.netInfo[0].dropdown.getSelected() + 1,
                "dhcp": networkSettingView.netInfo[3].switch.isSelect() ? 2 : 1,
                "ip": networkSettingView.netInfo[4].input.text(),
                "mask": networkSettingView.netInfo[5].input.text(),
                "gateway": networkSettingView.netInfo[6].input.text(),
                "dns": networkSettingView.netInfo[7].input.text() + ',' + networkSettingView.netInfo[8].input.text(),
                "psk": networkSettingView.netInfo[2].input.text(),
                "ssid": wifiSsid
            }
        }
        if (!networkSettingView.netInfo[3].switch.isSelect()) {
            // é™æ€ip
            if (networkSettingView.netInfo[0].dropdown.getSelected() + 1 == 1) {
                // ä»¥å¤ªç½‘
                saveConfigData = {
                    "net": {
                        "type": networkSettingView.netInfo[0].dropdown.getSelected() + 1,
                        "dhcp": networkSettingView.netInfo[3].switch.isSelect() ? 2 : 1,
                        "ip": networkSettingView.netInfo[4].input.text(),
                        "mask": networkSettingView.netInfo[5].input.text(),
                        "gateway": networkSettingView.netInfo[6].input.text(),
                        "dns": networkSettingView.netInfo[7].input.text() + ',' + networkSettingView.netInfo[8].input.text(),
                    }
                }
            } else if (networkSettingView.netInfo[0].dropdown.getSelected() + 1 == 2) {
                // WIFI
                saveConfigData = {
                    "net": {
                        "type": networkSettingView.netInfo[0].dropdown.getSelected() + 1,
                        "dhcp": networkSettingView.netInfo[3].switch.isSelect() ? 2 : 1,
                        "ip": networkSettingView.netInfo[4].input.text(),
                        "mask": networkSettingView.netInfo[5].input.text(),
                        "gateway": networkSettingView.netInfo[6].input.text(),
                        "dns": networkSettingView.netInfo[7].input.text() + ',' + networkSettingView.netInfo[8].input.text(),
                        "psk": networkSettingView.netInfo[2].input.text(),
                        "ssid": wifiSsid
                    }
                }
            } else {
                //4G
                saveConfigData = {
                    "net": {
                        "type": 4,
                    }
                }
            }
        } else {
            // åŠ¨æ€ip
            if (networkSettingView.netInfo[0].dropdown.getSelected() + 1 == 1) {
                // ä»¥å¤ªç½‘
                saveConfigData = {
                    "net": {
                        "type": networkSettingView.netInfo[0].dropdown.getSelected() + 1,
                        "dhcp": networkSettingView.netInfo[3].switch.isSelect() ? 2 : 1,
                    }
                }
            } else if (networkSettingView.netInfo[0].dropdown.getSelected() + 1 == 2) {
                // WIFI
                saveConfigData = {
                    "net": {
                        "type": networkSettingView.netInfo[0].dropdown.getSelected() + 1,
                        "dhcp": networkSettingView.netInfo[3].switch.isSelect() ? 2 : 1,
                        "psk": networkSettingView.netInfo[2].input.text(),
                        "ssid": wifiSsid
                    }
                }
            } else {
                //4G
                saveConfigData = {
                    "net": {
                        "type": 4,
                    }
                }
            }
        }
        const res = screen.saveConfig(saveConfigData)
        if (res === true) {
            networkSettingView.statusPanel.success()
            std.setTimeout(() => {
                // æˆåŠŸè¿”å›žä¸Šä¸€å±‚ç•Œé¢
                dxui.loadMain(configView.screenMain)
            }, 500)
        } else {
            networkSettingView.statusPanel.fail()
        }
    })
    saveBtn.align(dxui.Utils.ALIGN.BOTTOM_MID, 0, -83)
    networkSettingView.changeNetType(0)
    networkSettingView.statusPanel = viewUtils.statusPanel(screenMain, 'networkSettingView.success', 'networkSettingView.fail')
}
// 0:以太网 1:wifi 3:4G
networkSettingView.changeNetType = function (type) {
    if (type === 0) {
        networkSettingView.netInfo[1].obj.hide()
        networkSettingView.netInfo[2].obj.hide()
        networkSettingView.netInfo[3].obj.show()
        networkSettingView.netInfo[4].obj.show()
        networkSettingView.netInfo[5].obj.show()
        networkSettingView.netInfo[6].obj.show()
        networkSettingView.netInfo[7].obj.show()
        networkSettingView.netInfo[8].obj.show()
    } else if (type === 1) {
        networkSettingView.netInfo[1].obj.show()
        networkSettingView.netInfo[2].obj.show()
        networkSettingView.netInfo[3].obj.show()
        networkSettingView.netInfo[4].obj.show()
        networkSettingView.netInfo[5].obj.show()
        networkSettingView.netInfo[6].obj.show()
        networkSettingView.netInfo[7].obj.show()
        networkSettingView.netInfo[8].obj.show()
    } else {
        networkSettingView.netInfo[1].obj.hide()
        networkSettingView.netInfo[2].obj.hide()
        networkSettingView.netInfo[3].obj.hide()
        networkSettingView.netInfo[4].obj.hide()
        networkSettingView.netInfo[5].obj.hide()
        networkSettingView.netInfo[6].obj.hide()
        networkSettingView.netInfo[7].obj.hide()
        networkSettingView.netInfo[8].obj.hide()
    }
}
export default networkSettingView
在上述文件截断后对比
vf205_access/src/view/config/menu/recordQuery/recordQueryDetailView.js vf205_access/src/view/config/menu/recordQueryView.js vf205_access/src/view/config/menu/systemSetting/displaySettingView.js vf205_access/src/view/config/menu/systemSetting/faceRecognitionSettingView.js vf205_access/src/view/config/menu/systemSetting/passLogSettingView.js vf205_access/src/view/config/menu/systemSetting/passwordManagementView.js vf205_access/src/view/config/menu/systemSetting/passwordOpenDoorSettingView.js vf205_access/src/view/config/menu/systemSetting/swipeCardRecognitionSettingView.js vf205_access/src/view/config/menu/systemSetting/timeSettingView.js vf205_access/src/view/config/menu/systemSettingView.js vf205_access/src/view/config/menu/voiceBroadcastView.js vf205_access/src/view/config/newPwdView.js vf205_access/src/view/emergencyPwdView.js vf205_access/src/view/i18n.js vf205_access/src/view/idleView.js vf205_access/src/view/mainView.js vf205_access/src/view/pinyin/dict.js vf205_access/src/view/pinyin/pinyin.js vf205_access/src/view/pwdView.js vf205_access/src/view/topView.js vf205_access/src/view/viewUtils.js