fix: 优化断网处理

This commit is contained in:
2026-06-27 09:10:01 +08:00
parent 993b5ee4c0
commit 5d7ca83162
4 changed files with 88 additions and 21 deletions
+2 -2
View File
@@ -115,7 +115,7 @@
<article class="bento-control">
<span class="eyebrow">mobile data</span>
<h3 id="mobileDataState">--</h3>
<p id="mobileDataHint">启动时会自动关闭移动数据</p>
<p id="mobileDataHint">启动时会自动断开应用层拨号</p>
<div class="bento-control__actions">
<button class="button button--secondary button--compact" id="mobileDataToggle" type="button"
data-action="disable">强制关闭流量</button>
@@ -379,4 +379,4 @@
<script src="/assets/admin.js" type="module"></script>
</body>
</html>
</html>
+11 -10
View File
@@ -184,25 +184,26 @@ function renderMobileData(mobileData = {}) {
const enabled = Boolean(mobileData.enabled || mobileData.anyActive);
const cid = mobileData.cid || 1;
const mode = formatMobileDataMode(mobileData.mode);
const isMipcall = mobileData.mode === 'mipcall';
const activeContexts = formatMobileDataContexts(mobileData.contexts || []);
if (unknown) {
setText(elements.mobileDataSummary, '未知');
setText(elements.mobileDataState, '状态未知');
setText(elements.mobileDataHint, mobileData.error || '未能确认移动数据状态,可先强制关闭。');
elements.mobileDataToggle.textContent = '强制关闭流量';
setText(elements.mobileDataHint, mobileData.error || (isMipcall ? '未能确认应用层拨号状态,可先强制断开。' : '未能确认移动数据状态,可先强制关闭。'));
elements.mobileDataToggle.textContent = isMipcall ? '强制断开拨号' : '强制关闭流量';
elements.mobileDataToggle.dataset.action = 'disable';
} else if (enabled) {
setText(elements.mobileDataSummary, '已开启');
setText(elements.mobileDataState, '流量已开启');
setText(elements.mobileDataSummary, isMipcall ? '已连接' : '已开启');
setText(elements.mobileDataState, isMipcall ? '应用拨号已连接' : '流量已开启');
setText(elements.mobileDataHint, activeContexts ? `${mode} 检测到 ${activeContexts} 处于活动状态。` : `CID ${cid} 处于活动状态。`);
elements.mobileDataToggle.textContent = '关闭流量';
elements.mobileDataToggle.textContent = isMipcall ? '断开拨号' : '关闭流量';
elements.mobileDataToggle.dataset.action = 'disable';
} else {
setText(elements.mobileDataSummary, '已关闭');
setText(elements.mobileDataState, '流量已关闭');
setText(elements.mobileDataHint, `${mode} CID ${cid} 未建立移动数据连接,重启后也会先保持关闭。`);
elements.mobileDataToggle.textContent = '开启流量';
setText(elements.mobileDataSummary, isMipcall ? '已断开' : '已关闭');
setText(elements.mobileDataState, isMipcall ? '应用拨号已断开' : '流量已关闭');
setText(elements.mobileDataHint, isMipcall ? `${mode} CID ${cid} 未建立应用层拨号连接;已保护性尝试停用PDP。` : `${mode} CID ${cid} 未建立移动数据连接,重启后也会先保持关闭。`);
elements.mobileDataToggle.textContent = isMipcall ? '开启拨号' : '开启流量';
elements.mobileDataToggle.dataset.action = 'enable';
}
@@ -674,7 +675,7 @@ async function toggleMobileData() {
body: JSON.stringify({ enabled })
});
renderMobileData(result.data || {});
setStatusMessage(elements.mobileDataStatus, result.message || (enabled ? '移动数据已开启。' : '移动数据已关闭。'), 'success');
setStatusMessage(elements.mobileDataStatus, result.message || (enabled ? '已开启。' : '已关闭。'), 'success');
await refreshLogs();
} catch (err) {
setStatusMessage(elements.mobileDataStatus, err.message, 'error');
+5 -1
View File
@@ -496,9 +496,13 @@ class APIServer {
}
const mobileData = await this.modem.setMobileDataEnabled(req.body.enabled);
const message = mobileData.mode === 'mipcall'
? (req.body.enabled ? '应用层拨号已开启' : '应用层拨号已断开')
: (req.body.enabled ? '移动数据已开启' : '移动数据已关闭');
res.json({
success: true,
message: req.body.enabled ? '移动数据已开启' : '移动数据已关闭',
message,
data: mobileData
});
} catch (err) {
+70 -8
View File
@@ -544,19 +544,62 @@ class ModemManager extends EventEmitter {
async disableMobileData(options = {}) {
const mode = this.getMobileDataMode();
if (mode === 'mipcall') {
return await this.disableMipcallMobileData(options);
}
return await this.disableCgactMobileData(options);
}
async disableMipcallMobileData(options = {}) {
const before = await this.getMobileDataStatus();
const cids = this.getMobileDataTargetCids(before);
const label = mode === 'mipcall' ? '关闭移动数据拨号' : '停用PDP数据连接';
let commandSucceeded = false;
logger.info(`${options.reason || '手动操作'}: 关闭移动数据,避免流量消耗`);
logger.info(`${options.reason || '手动操作'}: 断开ML307应用层拨号连接,避免流量消耗`);
for (const cid of cids) {
const command = this.getMobileDataCommand(mode, false, cid);
const resp = await this.sendATWithRetry(command, {
try {
await this.sendATWithRetry(this.getMobileDataCommand('mipcall', false, cid), {
timeout: 8000,
retries: 1,
label: `断开应用层拨号(CID ${cid})`,
required: true
});
} catch (err) {
err.mobileDataCritical = true;
throw err;
}
}
const status = await this.waitForMobileDataState(false, options.timeout || 8000);
if (status.anyActive) {
const err = new Error(`MIPCALL断开后仍检测到活动连接: ${this.formatMobileDataContexts(status.contexts)}`);
err.mobileDataCritical = true;
throw err;
}
if (status.status === 'unknown') {
logger.warn('MIPCALL断开命令已成功发送,但状态未确认');
}
await this.tryDeactivatePdpContexts(cids);
logger.info('✓ 应用层拨号连接已断开');
return status;
}
async disableCgactMobileData(options = {}) {
const before = await this.getMobileDataStatus();
const cids = this.getMobileDataTargetCids(before);
let commandSucceeded = false;
logger.info(`${options.reason || '手动操作'}: 停用PDP数据连接,避免流量消耗`);
for (const cid of cids) {
const resp = await this.sendATWithRetry(this.getMobileDataCommand('cgact', false, cid), {
timeout: 8000,
retries: 1,
label: `${label}(CID ${cid})`,
label: `停用PDP数据连接(CID ${cid})`,
required: false
});
@@ -592,6 +635,21 @@ class ModemManager extends EventEmitter {
return status;
}
async tryDeactivatePdpContexts(cids) {
for (const cid of cids) {
const resp = await this.sendATWithRetry(this.getMobileDataCommand('cgact', false, cid), {
timeout: 8000,
retries: 1,
label: `保护性停用PDP(CID ${cid})`,
required: false
});
if (!resp) {
logger.warn(`保护性停用PDP(CID ${cid})未成功,已按MIPCALL断开结果继续`);
}
}
}
async forceMobileDataOff(stage) {
this.mobileData.desiredEnabled = false;
try {
@@ -601,6 +659,10 @@ class ModemManager extends EventEmitter {
timeout: 5000
});
} catch (err) {
if (err.mobileDataCritical) {
throw err;
}
logger.warn(`${stage}: 移动数据关闭未确认: ${err.message}`);
return this.getCachedMobileDataStatus();
}
@@ -792,7 +854,7 @@ class ModemManager extends EventEmitter {
await this.waitSIMReady();
await this.ensureFullFunctionality();
// 4. 启动保护:先关闭移动数据拨号,再等待短信所需的网络注册。
// 4. 启动保护:先断开应用层拨号,再等待短信所需的网络注册。
await this.forceMobileDataOff('启动保护(CFUN=1后)');
// 5. 等待网络注册
@@ -810,7 +872,7 @@ class ModemManager extends EventEmitter {
this.ready = false;
}
// 6. 驻网后再一次,防止模组自动拨号在驻网完成后重新拉起。
// 6. 驻网后再断开一次,防止模组自动拨号在驻网完成后重新拉起。
await this.forceMobileDataOff('启动保护(驻网后)');
// 7. 按文档配置短信功能,并启用本项目需要的PDU模式