光伏pc端应用
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

/*
* @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)