You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.7 KiB
188 lines
5.7 KiB
/* |
|
* @Author: 李皓 hao_li_work@163.com |
|
* @Date: 2026-07-28 16:31:45 |
|
* @LastEditors: 李皓 hao_li_work@163.com |
|
* @LastEditTime: 2026-07-28 16:44:07 |
|
* @FilePath: \pc\preload.js |
|
* @Description: |
|
* @Version: 1.0.0 |
|
*/ |
|
const { contextBridge, ipcRenderer } = require('electron') |
|
const nodeNet = require('net') |
|
const dns = require('dns') |
|
|
|
/** |
|
* 尝试通过 TCP 连接指定端口检测主机是否可达 |
|
*/ |
|
function tryConnect(host, port, timeout) { |
|
return new Promise((resolve) => { |
|
const socket = new nodeNet.Socket() |
|
socket.setTimeout(timeout) |
|
socket.on('connect', () => { socket.destroy(); resolve(true) }) |
|
socket.on('error', () => { socket.destroy(); resolve(false) }) |
|
socket.on('timeout', () => { socket.destroy(); resolve(false) }) |
|
socket.connect(port, host) |
|
}) |
|
} |
|
|
|
/** |
|
* 网络检测对象 |
|
*/ |
|
const networkUtil = { |
|
/** |
|
* 检测目标主机网络连通性 |
|
* @param {string} host - 目标主机地址 |
|
* @param {number} timeout - 超时时间(毫秒) |
|
* @returns {Promise<boolean>} - 是否可达 |
|
*/ |
|
async ping(host, timeout) { |
|
// 1. 先检查 DNS 能否解析 |
|
try { |
|
await dns.promises.resolve(host) |
|
} catch { |
|
return false |
|
} |
|
|
|
// 2. 依次尝试常见端口(443 优先,80 兜底) |
|
const ports = [443, 80] |
|
for (const port of ports) { |
|
const ok = await tryConnect(host, port, timeout) |
|
if (ok) return true |
|
} |
|
|
|
return false |
|
} |
|
} |
|
|
|
/** |
|
* 应用传感器对象 |
|
* 静态页面设置 appSensor.onValueCallback = function(obj) { ... } |
|
* obj 包含: { method, sensor, value } |
|
*/ |
|
const sensorCallbacks = { onValueCallback: null } |
|
|
|
const appSensor = { |
|
version: '1.0.0', |
|
name: 'appSensor', |
|
get onValueCallback() { |
|
return sensorCallbacks.onValueCallback |
|
}, |
|
set onValueCallback(fn) { |
|
sensorCallbacks.onValueCallback = fn |
|
} |
|
} |
|
|
|
// 监听主进程发送的传感器数据 |
|
ipcRenderer.on('sensor-data', (_event, data) => { |
|
if (sensorCallbacks.onValueCallback) { |
|
sensorCallbacks.onValueCallback(data) |
|
} |
|
}) |
|
|
|
/** |
|
* 应用缓存对象(数据持久化到本地文件) |
|
*/ |
|
const app = { |
|
/** |
|
* 获取缓存数据 |
|
* @param {string} key - 缓存键名 |
|
* @returns {Promise<any>} 缓存值 |
|
*/ |
|
getCache(key) { |
|
return ipcRenderer.invoke('get-cache', key) |
|
}, |
|
/** |
|
* 设置缓存数据 |
|
* @param {string} key - 缓存键名 |
|
* @param {any} value - 缓存值(建议传入 JSON.stringify 后的字符串) |
|
* @returns {Promise<boolean>} |
|
*/ |
|
setCache(key, value) { |
|
return ipcRenderer.invoke('set-cache', key, value) |
|
} |
|
} |
|
|
|
/** |
|
* USB Serial JS 桥接对象 |
|
* 静态页面可直接使用 usbSerial 或 window.usbSerial |
|
* 接口规范: docs/webview-usb-serial.md |
|
*/ |
|
const usbSerialCallbacks = { |
|
onDeviceAttached: null, |
|
onDeviceDetached: null, |
|
onDeviceDiscovered: null, |
|
onDeviceProbeResult: null, |
|
onDeviceOpened: null, |
|
onDeviceState: null, |
|
onDeviceData: null |
|
} |
|
|
|
// 监听主进程发送的 usb 事件,转换为回调调用 |
|
function setupUsbSerialListeners() { |
|
const events = [ |
|
['usb-callback-device-attached', 'onDeviceAttached'], |
|
['usb-callback-device-detached', 'onDeviceDetached'], |
|
['usb-callback-device-discovered', 'onDeviceDiscovered'], |
|
['usb-callback-device-probe-result', 'onDeviceProbeResult'], |
|
['usb-callback-device-opened', 'onDeviceOpened'], |
|
['usb-callback-device-state', 'onDeviceState'], |
|
['usb-callback-device-data', 'onDeviceData'] |
|
] |
|
|
|
for (const [eventName, cbName] of events) { |
|
ipcRenderer.on(eventName, (_event, jsonStr) => { |
|
const cb = usbSerialCallbacks[cbName] |
|
if (typeof cb === 'function') { |
|
cb(jsonStr) |
|
} |
|
}) |
|
} |
|
} |
|
setupUsbSerialListeners() |
|
|
|
const usbSerial = { |
|
// --- 回调属性(可读写)--- |
|
get onDeviceAttached() { return usbSerialCallbacks.onDeviceAttached }, |
|
set onDeviceAttached(fn) { usbSerialCallbacks.onDeviceAttached = fn }, |
|
get onDeviceDetached() { return usbSerialCallbacks.onDeviceDetached }, |
|
set onDeviceDetached(fn) { usbSerialCallbacks.onDeviceDetached = fn }, |
|
get onDeviceDiscovered() { return usbSerialCallbacks.onDeviceDiscovered }, |
|
set onDeviceDiscovered(fn) { usbSerialCallbacks.onDeviceDiscovered = fn }, |
|
get onDeviceProbeResult() { return usbSerialCallbacks.onDeviceProbeResult }, |
|
set onDeviceProbeResult(fn) { usbSerialCallbacks.onDeviceProbeResult = fn }, |
|
get onDeviceOpened() { return usbSerialCallbacks.onDeviceOpened }, |
|
set onDeviceOpened(fn) { usbSerialCallbacks.onDeviceOpened = fn }, |
|
get onDeviceState() { return usbSerialCallbacks.onDeviceState }, |
|
set onDeviceState(fn) { usbSerialCallbacks.onDeviceState = fn }, |
|
get onDeviceData() { return usbSerialCallbacks.onDeviceData }, |
|
set onDeviceData(fn) { usbSerialCallbacks.onDeviceData = fn }, |
|
|
|
// --- 方法 --- |
|
startDiscovery() { |
|
ipcRenderer.send('usb-start-discovery') |
|
}, |
|
stopDiscovery() { |
|
ipcRenderer.send('usb-stop-discovery') |
|
}, |
|
getDiscoveredDevices() { |
|
return ipcRenderer.invoke('usb-get-discovered') |
|
}, |
|
deviceProbe(deviceName, portNumber, baudRate, hexCmd, timeoutMs) { |
|
ipcRenderer.send('usb-device-probe', { deviceName, portNumber, baudRate, hexCmd, timeoutMs }) |
|
}, |
|
deviceOpen(deviceName, portNumber, baudRate) { |
|
ipcRenderer.send('usb-device-open', { deviceName, portNumber, baudRate }) |
|
}, |
|
deviceClose(deviceName, portNumber) { |
|
ipcRenderer.send('usb-device-close', { deviceName, portNumber }) |
|
}, |
|
deviceWrite(deviceName, portNumber, hexData) { |
|
ipcRenderer.send('usb-device-write', { deviceName, portNumber, hexData }) |
|
} |
|
} |
|
|
|
// 安全暴露 API 给渲染进程 |
|
contextBridge.exposeInMainWorld('net', networkUtil) |
|
contextBridge.exposeInMainWorld('appSensor', appSensor) |
|
contextBridge.exposeInMainWorld('app', app) |
|
contextBridge.exposeInMainWorld('usbSerial', usbSerial)
|
|
|