308 lines
15 KiB
JavaScript
308 lines
15 KiB
JavaScript
// --- static/mac-domain.js ---
|
||
|
||
import { apiGetMacDomainList, apiGetMacDomainConfig, apiExecuteConfig } from './api.js';
|
||
import { getGlobalConnectionInfo } from './utils.js';
|
||
|
||
// 🌟 將原本在 app.js 的全域變數移到這裡封裝
|
||
let currentMacDomainData = null;
|
||
|
||
export function hasMacDomainData() {
|
||
return currentMacDomainData !== null;
|
||
}
|
||
|
||
export function resetMacDomainData() {
|
||
currentMacDomainData = null;
|
||
}
|
||
|
||
export async function loadMacDomainList() {
|
||
const connInfo = getGlobalConnectionInfo();
|
||
if (!connInfo) return;
|
||
|
||
const mdInput = document.getElementById('cfgMacDomain');
|
||
const mdDatalist = document.getElementById('mac-domain-list');
|
||
const statusSpan = document.getElementById('fetchStatus');
|
||
|
||
// 載入前:鎖定輸入框並更改提示
|
||
mdInput.disabled = true;
|
||
mdInput.value = '';
|
||
mdInput.placeholder = '⏳ 查詢設備中...';
|
||
if (mdDatalist) mdDatalist.innerHTML = '';
|
||
|
||
statusSpan.textContent = "正在自動抓取現有 MAC Domain 清單...";
|
||
statusSpan.style.color = "#f39c12";
|
||
|
||
try {
|
||
const result = await apiGetMacDomainList(connInfo.host, connInfo.user, connInfo.pass);
|
||
if (result.status === 'success' && result.data.length > 0) {
|
||
// 載入成功:將資料塞入 datalist
|
||
if (mdDatalist) {
|
||
result.data.forEach(md => {
|
||
const option = document.createElement('option');
|
||
option.value = md;
|
||
mdDatalist.appendChild(option);
|
||
});
|
||
}
|
||
mdInput.placeholder = '例: 13:0/0.0 (可下拉或輸入)';
|
||
statusSpan.textContent = "✅ 清單抓取成功!請選擇目標並點擊讀取配置。";
|
||
statusSpan.style.color = "#27ae60";
|
||
} else {
|
||
mdInput.placeholder = '❌ 找不到資料';
|
||
statusSpan.textContent = "無法解析清單,請確認設備狀態。";
|
||
statusSpan.style.color = "#e74c3c";
|
||
}
|
||
} catch (error) {
|
||
mdInput.placeholder = '❌ 連線錯誤';
|
||
statusSpan.textContent = "連線失敗:" + error;
|
||
statusSpan.style.color = "#e74c3c";
|
||
} finally {
|
||
// 載入結束:解鎖輸入框
|
||
mdInput.disabled = false;
|
||
}
|
||
}
|
||
|
||
export async function fetchMacDomainConfig() {
|
||
const target = document.getElementById('cfgMacDomain').value.trim();
|
||
const connInfo = getGlobalConnectionInfo();
|
||
if (!connInfo || !target) return alert("請確認設備連線資訊與 MAC Domain!");
|
||
|
||
document.getElementById('fetchStatus').textContent = "⏳ 正在讀取並解析設定中...";
|
||
document.getElementById('fetchStatus').style.color = "#f39c12";
|
||
|
||
try {
|
||
const result = await apiGetMacDomainConfig(target, connInfo.host, connInfo.user, connInfo.pass);
|
||
if (result.status === 'success') {
|
||
currentMacDomainData = result.data;
|
||
populateFormFromData();
|
||
document.getElementById('macDomainConfigArea').style.display = 'block';
|
||
document.getElementById('fetchStatus').textContent = "✅ 讀取成功!";
|
||
document.getElementById('fetchStatus').style.color = "#27ae60";
|
||
} else {
|
||
alert("讀取失敗:" + result.detail);
|
||
document.getElementById('fetchStatus').textContent = "❌ 讀取失敗";
|
||
}
|
||
} catch (error) {
|
||
alert("連線錯誤:" + error);
|
||
}
|
||
}
|
||
|
||
export function populateFormFromData() {
|
||
const d = currentMacDomainData;
|
||
if (!d) return;
|
||
|
||
document.getElementById('g_ip_prov').value = d.common_settings['ip-provisioning-mode'] || 'dual-stack';
|
||
document.getElementById('g_diplexer').value = d.common_settings['diplexer-band-edge'] || 'enabled';
|
||
document.getElementById('g_bat31').value = d.common_settings['cm-battery-mode-31-support'] || 'disabled';
|
||
document.getElementById('g_bat30').value = d.common_settings['cm-battery-mode-30-support'] || 'disabled';
|
||
document.getElementById('g_docsis40').value = d.common_settings['docsis40'] || 'disabled';
|
||
document.getElementById('g_ds_dyn').value = d.common_settings['ds-dynamic-bonding-group'] || 'disabled';
|
||
document.getElementById('g_us_dyn').value = d.common_settings['us-dynamic-bonding-group'] || 'disabled';
|
||
|
||
document.getElementById('b_admin').value = d.basic_channel_sets['admin-state'] || 'down';
|
||
document.getElementById('b_ds_pri').value = d.basic_channel_sets['ds-primary-set'] || '';
|
||
document.getElementById('b_ds_non_pri').value = d.basic_channel_sets['ds-non-primary-set'] || '';
|
||
document.getElementById('b_us_phy').value = d.basic_channel_sets['us-phy-channel-set'] || '';
|
||
document.getElementById('b_ds_ofdm').value = d.basic_channel_sets['ds-ofdm-set'] || '';
|
||
document.getElementById('b_us_ofdma').value = d.basic_channel_sets['us-ofdma-set'] || '';
|
||
|
||
const dsSelect = document.getElementById('select_ds_group');
|
||
dsSelect.innerHTML = '<option value="_new_">[➕ 新增 DS Group]</option>';
|
||
Object.keys(d.static_ds_bonding_groups).forEach(grp => dsSelect.innerHTML += `<option value="${grp}">${grp}</option>`);
|
||
if (Object.keys(d.static_ds_bonding_groups).length > 0) dsSelect.value = Object.keys(d.static_ds_bonding_groups)[0];
|
||
|
||
const usSelect = document.getElementById('select_us_group');
|
||
usSelect.innerHTML = '<option value="_new_">[➕ 新增 US Group]</option>';
|
||
Object.keys(d.static_us_bonding_groups).forEach(grp => usSelect.innerHTML += `<option value="${grp}">${grp}</option>`);
|
||
if (Object.keys(d.static_us_bonding_groups).length > 0) usSelect.value = Object.keys(d.static_us_bonding_groups)[0];
|
||
|
||
toggleGroupVisibility();
|
||
loadDsGroupData();
|
||
loadUsGroupData();
|
||
}
|
||
|
||
export function revertFormChanges() {
|
||
if (!currentMacDomainData) return;
|
||
if (!confirm("確定要放棄目前的修改,還原回設備原始的設定值嗎?")) return;
|
||
|
||
populateFormFromData();
|
||
|
||
const previewEl = document.getElementById('cliPreviewContainer');
|
||
if (previewEl) {
|
||
previewEl.textContent = '';
|
||
previewEl.style.display = 'none';
|
||
}
|
||
|
||
const deployBtn = document.getElementById('btnDeployConfig');
|
||
if (deployBtn) {
|
||
deployBtn.style.display = 'none';
|
||
deployBtn.disabled = true;
|
||
}
|
||
|
||
const fetchStatus = document.getElementById('fetchStatus');
|
||
fetchStatus.textContent = "🔄 已還原為原始設定!";
|
||
fetchStatus.style.color = "#2980b9";
|
||
|
||
setTimeout(() => {
|
||
fetchStatus.textContent = "✅ 讀取成功!";
|
||
fetchStatus.style.color = "#27ae60";
|
||
}, 3000);
|
||
}
|
||
|
||
export function toggleGroupVisibility() {
|
||
const dsDyn = document.getElementById('g_ds_dyn').value;
|
||
const usDyn = document.getElementById('g_us_dyn').value;
|
||
document.getElementById('section_group_ds').style.display = (dsDyn === 'disabled') ? 'block' : 'none';
|
||
document.getElementById('section_group_us').style.display = (usDyn === 'disabled') ? 'block' : 'none';
|
||
}
|
||
|
||
export function loadDsGroupData() {
|
||
const grp = document.getElementById('select_ds_group').value;
|
||
const newGrpInput = document.getElementById('input_new_ds_group');
|
||
if (grp === '_new_') {
|
||
newGrpInput.style.display = 'inline-block';
|
||
document.getElementById('ds_g_admin').value = 'down';
|
||
document.getElementById('ds_g_down').value = '';
|
||
document.getElementById('ds_g_ofdm').value = '';
|
||
document.getElementById('ds_g_fdx').value = '';
|
||
} else {
|
||
newGrpInput.style.display = 'none';
|
||
const data = currentMacDomainData.static_ds_bonding_groups[grp];
|
||
document.getElementById('ds_g_admin').value = data['admin-state'] || 'down';
|
||
document.getElementById('ds_g_down').value = data['down-channel-set'] || '';
|
||
document.getElementById('ds_g_ofdm').value = data['ofdm-channel-set'] || '';
|
||
document.getElementById('ds_g_fdx').value = data['fdx-ofdm-channel-set'] || '';
|
||
}
|
||
}
|
||
|
||
export function loadUsGroupData() {
|
||
const grp = document.getElementById('select_us_group').value;
|
||
const newGrpInput = document.getElementById('input_new_us_group');
|
||
if (grp === '_new_') {
|
||
newGrpInput.style.display = 'inline-block';
|
||
document.getElementById('us_g_admin').value = 'down';
|
||
document.getElementById('us_g_us').value = '';
|
||
document.getElementById('us_g_ofdma').value = '';
|
||
document.getElementById('us_g_fdx').value = '';
|
||
} else {
|
||
newGrpInput.style.display = 'none';
|
||
const data = currentMacDomainData.static_us_bonding_groups[grp];
|
||
document.getElementById('us_g_admin').value = data['admin-state'] || 'down';
|
||
document.getElementById('us_g_us').value = data['us-channel-set'] || '';
|
||
document.getElementById('us_g_ofdma').value = data['ofdma-channel-set'] || '';
|
||
document.getElementById('us_g_fdx').value = data['fdx-ofdma-channel-set'] || '';
|
||
}
|
||
}
|
||
|
||
export function generateMacDomainCLI() {
|
||
const md = document.getElementById('cfgMacDomain').value.trim();
|
||
let cli = `! --- Harmonic MAC Domain Configuration SOP ---\n`;
|
||
|
||
cli += `! 1. [區塊] MAC Domain 全域與 Base 設定\n`;
|
||
cli += `cable mac-domain ${md} admin-state down\ncommit\n`;
|
||
|
||
const baseCmds = [
|
||
`ip-provisioning-mode ${document.getElementById('g_ip_prov').value}`,
|
||
`diplexer-band-edge control ${document.getElementById('g_diplexer').value}`,
|
||
`cm-battery-mode-31-support ${document.getElementById('g_bat31').value}`,
|
||
`cm-battery-mode-30-support ${document.getElementById('g_bat30').value}`,
|
||
`docsis40 ${document.getElementById('g_docsis40').value}`,
|
||
`ds-dynamic-bonding-group ${document.getElementById('g_ds_dyn').value}`,
|
||
`us-dynamic-bonding-group ${document.getElementById('g_us_dyn').value}`,
|
||
`ds-primary-set ${document.getElementById('b_ds_pri').value}`,
|
||
`ds-non-primary-set ${document.getElementById('b_ds_non_pri').value}`,
|
||
`us-phy-channel-set ${document.getElementById('b_us_phy').value}`,
|
||
`ds-ofdm-set ${document.getElementById('b_ds_ofdm').value}`,
|
||
`us-ofdma-set ${document.getElementById('b_us_ofdma').value}`
|
||
];
|
||
baseCmds.forEach(cmd => {
|
||
if (!cmd.endsWith(" ")) cli += `cable mac-domain ${md} ${cmd}\n`;
|
||
});
|
||
cli += `cable mac-domain ${md} admin-state ${document.getElementById('b_admin').value}\ncommit\n`;
|
||
|
||
if (document.getElementById('g_ds_dyn').value === 'disabled') {
|
||
const isNewDs = document.getElementById('select_ds_group').value === '_new_';
|
||
let dsName = isNewDs ? document.getElementById('input_new_ds_group').value.trim() : document.getElementById('select_ds_group').value;
|
||
|
||
if (dsName) {
|
||
cli += `\n! 2. [區塊] DS Bonding Group [${dsName}] 設定\n`;
|
||
if (!isNewDs) cli += `cable mac-domain ${md} ds-bonding-group ${dsName} admin-state down\ncommit\n`;
|
||
|
||
if (document.getElementById('ds_g_down').value) cli += `cable mac-domain ${md} ds-bonding-group ${dsName} down-channel-set ${document.getElementById('ds_g_down').value}\n`;
|
||
if (document.getElementById('ds_g_ofdm').value) cli += `cable mac-domain ${md} ds-bonding-group ${dsName} ofdm-channel-set ${document.getElementById('ds_g_ofdm').value}\n`;
|
||
if (document.getElementById('ds_g_fdx').value) cli += `cable mac-domain ${md} ds-bonding-group ${dsName} fdx-ofdm-channel-set ${document.getElementById('ds_g_fdx').value}\n`;
|
||
|
||
cli += `cable mac-domain ${md} ds-bonding-group ${dsName} admin-state ${document.getElementById('ds_g_admin').value}\ncommit\n`;
|
||
}
|
||
}
|
||
|
||
if (document.getElementById('g_us_dyn').value === 'disabled') {
|
||
const isNewUs = document.getElementById('select_us_group').value === '_new_';
|
||
let usName = isNewUs ? document.getElementById('input_new_us_group').value.trim() : document.getElementById('select_us_group').value;
|
||
|
||
if (usName) {
|
||
cli += `\n! 3. [區塊] US Bonding Group [${usName}] 設定\n`;
|
||
if (!isNewUs) cli += `cable mac-domain ${md} us-bonding-group ${usName} admin-state down\ncommit\n`;
|
||
|
||
if (document.getElementById('us_g_us').value) cli += `cable mac-domain ${md} us-bonding-group ${usName} us-channel-set ${document.getElementById('us_g_us').value}\n`;
|
||
if (document.getElementById('us_g_ofdma').value) cli += `cable mac-domain ${md} us-bonding-group ${usName} ofdma-channel-set ${document.getElementById('us_g_ofdma').value}\n`;
|
||
if (document.getElementById('us_g_fdx').value) cli += `cable mac-domain ${md} us-bonding-group ${usName} fdx-ofdma-channel-set ${document.getElementById('us_g_fdx').value}\n`;
|
||
|
||
cli += `cable mac-domain ${md} us-bonding-group ${usName} admin-state ${document.getElementById('us_g_admin').value}\ncommit\n`;
|
||
}
|
||
}
|
||
|
||
const previewEl = document.getElementById('cliPreviewContainer');
|
||
previewEl.textContent = cli;
|
||
previewEl.style.display = 'block';
|
||
|
||
const deployBtn = document.getElementById('btnDeployConfig');
|
||
deployBtn.style.display = 'inline-block';
|
||
deployBtn.disabled = false;
|
||
}
|
||
|
||
export async function executeBondingConfig() {
|
||
if (!confirm("⚠️ 警告:此操作將導致 MAC Domain 重新啟動,影響用戶服務。確定要執行嗎?")) return;
|
||
|
||
const cliScript = document.getElementById('cliPreviewContainer').textContent;
|
||
const connInfo = getGlobalConnectionInfo();
|
||
if (!connInfo) return;
|
||
|
||
// 開啟 Modal 顯示執行進度
|
||
document.getElementById('outputModal').classList.add('active');
|
||
document.getElementById('modalTargetInfo').textContent = connInfo.host ? `(${connInfo.host})` : '';
|
||
document.body.style.overflow = 'hidden';
|
||
|
||
const outputEl = document.getElementById('modalOutput');
|
||
outputEl.innerHTML = `<span style="color: #f39c12;">🚀 正在排隊並執行配置腳本,這可能需要幾秒鐘,請勿關閉視窗...</span>\n\n${cliScript}`;
|
||
|
||
// 鎖定關閉按鈕 (Execution Lock)
|
||
const closeBtn = document.getElementById('btn-modal-close');
|
||
if (closeBtn) {
|
||
closeBtn.disabled = true;
|
||
closeBtn.style.opacity = '0.5';
|
||
closeBtn.style.cursor = 'not-allowed';
|
||
closeBtn.style.pointerEvents = 'none';
|
||
}
|
||
|
||
try {
|
||
const result = await apiExecuteConfig(cliScript, connInfo.host, connInfo.user, connInfo.pass);
|
||
if (result.status === 'success') {
|
||
outputEl.style.color = "#e0e0e0";
|
||
outputEl.textContent = `✅ 設定執行完成!\n==================================================\n${result.data}`;
|
||
} else {
|
||
outputEl.style.color = "#e74c3c";
|
||
outputEl.textContent = `❌ 設定執行失敗:\n${result.message}`;
|
||
}
|
||
} catch (error) {
|
||
outputEl.style.color = "#e74c3c";
|
||
outputEl.textContent = `❌ 連線或伺服器錯誤: ${error}`;
|
||
} finally {
|
||
// 恢復關閉按鈕
|
||
if (closeBtn) {
|
||
closeBtn.disabled = false;
|
||
closeBtn.style.opacity = '1';
|
||
closeBtn.style.cursor = 'pointer';
|
||
closeBtn.style.pointerEvents = 'auto';
|
||
}
|
||
}
|
||
}
|