// --- 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 mdSelect = document.getElementById('cfgMacDomain'); const statusSpan = document.getElementById('fetchStatus'); mdSelect.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) { mdSelect.innerHTML = ''; result.data.forEach(md => { const option = document.createElement('option'); option.value = md; option.textContent = md; mdSelect.appendChild(option); }); statusSpan.textContent = "✅ 清單抓取成功!請選擇目標並點擊讀取配置。"; statusSpan.style.color = "#27ae60"; } else { mdSelect.innerHTML = ''; statusSpan.textContent = "無法解析清單,請確認設備狀態。"; statusSpan.style.color = "#e74c3c"; } } catch (error) { mdSelect.innerHTML = ''; statusSpan.textContent = "連線失敗:" + error; statusSpan.style.color = "#e74c3c"; } } 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 = ''; Object.keys(d.static_ds_bonding_groups).forEach(grp => dsSelect.innerHTML += ``); 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 = ''; Object.keys(d.static_us_bonding_groups).forEach(grp => usSelect.innerHTML += ``); 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 = `🚀 正在排隊並執行配置腳本,這可能需要幾秒鐘,請勿關閉視窗...\n\n${cliScript}`; 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}`; } }