const net = require('net'); class FiberhomeManager { constructor() { this.tl1Connection = null; this.commandId = 1; // Configuração apenas para UNM2000 via TL1 this.config = { unm2000: { host: '131.0.192.9', port: 3337, username: '1', password: '1', timeout: 15000 }, olt: { name: '131.0.192.9' // OLT configurada no TL1 } }; this.logger = require('../utils/logger'); } /** * Testar conectividade de rede com um host */ async testarConectividade(host, port, timeout = 5000) { return new Promise((resolve) => { const socket = new net.Socket(); socket.setTimeout(timeout); socket.connect(port, host, () => { this.logger.debug(`Conectividade OK: ${host}:${port}`); socket.destroy(); resolve(true); }); socket.on('error', (error) => { this.logger.debug(`Erro de conectividade ${host}:${port}:`, error.message); resolve(false); }); socket.on('timeout', () => { this.logger.debug(`Timeout de conectividade ${host}:${port}`); socket.destroy(); resolve(false); }); }); } /** * Conectar ao UNM2000 via TL1 */ async conectarUNM2000() { try { if (this.tl1Connection && !this.tl1Connection.destroyed) { console.log('✅ Conexão TL1 já existe e está ativa'); this.logger.debug('Conexão TL1 já existe e está ativa'); return true; } console.log('🔌 Conectando ao UNM2000 via TL1:', this.config.unm2000); this.logger.debug('Conectando ao UNM2000 via TL1', this.config.unm2000); return new Promise((resolve, reject) => { this.tl1Connection = new net.Socket(); let authenticated = false; let dataBuffer = ''; this.tl1Connection.setTimeout(this.config.unm2000.timeout); // Handler para dados recebidos this.tl1Connection.on('data', (data) => { dataBuffer += data.toString(); console.log('📥 TL1 dados recebidos:', dataBuffer); this.logger.debug('TL1 dados recebidos:', dataBuffer); // Verificar se login foi bem-sucedido if (!authenticated && dataBuffer.includes('COMPLD')) { authenticated = true; console.log('✅ Login TL1 bem-sucedido!'); this.logger.info('Login TL1 bem-sucedido'); resolve(true); } else if (!authenticated && dataBuffer.includes('DENY')) { console.log('❌ Login TL1 negado!'); this.logger.error('Login TL1 negado'); reject(new Error('Login TL1 negado')); } }); this.tl1Connection.connect(this.config.unm2000.port, this.config.unm2000.host, () => { console.log('🔗 Socket TL1 conectado, enviando comando de login'); this.logger.debug('Socket TL1 conectado, enviando comando de login'); // Comando de login TL1 - formato correto: LOGIN:::CTAG::UN=username,PWD=password; const loginCommand = `LOGIN:::${this.commandId++}::UN=${this.config.unm2000.username},PWD=${this.config.unm2000.password};`; console.log('📤 Enviando login:', loginCommand); this.tl1Connection.write(loginCommand + '\r\n'); // Timeout para autenticação setTimeout(() => { if (!authenticated) { console.log('⏱️ Timeout na autenticação TL1 (10s). DataBuffer:', dataBuffer); this.logger.error('Timeout na autenticação TL1', { dataBuffer }); reject(new Error('Timeout na autenticação TL1')); } }, 10000); }); this.tl1Connection.on('error', (error) => { console.log('❌ Erro na conexão TL1:', error.message); this.logger.error('Erro na conexão TL1', error); reject(error); }); this.tl1Connection.on('timeout', () => { console.log('⏱️ Timeout do socket TL1'); this.logger.error('Timeout na conexão TL1'); reject(new Error('Timeout na conexão TL1')); }); this.tl1Connection.on('close', () => { console.log('🔌 Conexão TL1 fechada'); this.logger.debug('Conexão TL1 fechada'); this.tl1Connection = null; }); }); } catch (error) { this.logger.error('Erro ao conectar UNM2000', error); return false; } } /** * Executar comando TL1 */ async executarComandoTL1(comando) { try { this.logger.debug('Executando comando TL1', { comando }); if (!this.tl1Connection || this.tl1Connection.destroyed) { const conectado = await this.conectarUNM2000(); if (!conectado) { throw new Error('Não foi possível conectar ao UNM2000'); } } return new Promise((resolve, reject) => { let dataBuffer = ''; let responseReceived = false; // Handler temporário para esta consulta const dataHandler = (data) => { dataBuffer += data.toString(); this.logger.debug('Resposta TL1 recebida:', dataBuffer); // Verificar se a resposta está completa (termina com ;) if (dataBuffer.includes(';') && !responseReceived) { responseReceived = true; this.tl1Connection.removeListener('data', dataHandler); if (dataBuffer.includes('COMPLD')) { resolve({ success: true, data: dataBuffer }); } else if (dataBuffer.includes('DENY')) { resolve({ success: false, error: 'Comando negado pelo equipamento' }); } else { resolve({ success: false, error: 'Resposta inválida do equipamento' }); } } }; this.tl1Connection.on('data', dataHandler); // Enviar comando TL1 com ID único const comandoComId = comando.replace(':1;', `:${this.commandId++};`); this.tl1Connection.write(comandoComId + '\r\n'); // Timeout para resposta setTimeout(() => { if (!responseReceived) { this.tl1Connection.removeListener('data', dataHandler); reject(new Error('Timeout aguardando resposta TL1')); } }, 10000); }); } catch (error) { this.logger.error('Erro ao executar comando TL1', { comando, error: error.message }); return { success: false, error: error.message }; } } /** * Obter status de ONU via TL1 */ async obterStatusONU(serial) { try { this.logger.debug('Obtendo status da ONU via TL1', { serial }); // Conectar ao UNM2000 const conectado = await this.conectarUNM2000(); if (!conectado) { throw new Error('Não foi possível conectar ao UNM2000'); } // Comando TL1 para buscar informações da ONU const comando = `RTRV-ONU::${serial}:1;`; this.logger.debug('Executando comando TL1', { comando }); const resultado = await this.executarComandoTL1(comando); if (!resultado.success) { throw new Error(resultado.error || 'Falha na consulta TL1'); } // Processar resposta TL1 da ONU const statusONU = this.processarStatusONUTL1(resultado.data, serial); this.logger.debug('Status ONU processado via TL1', statusONU); return { success: true, ...statusONU }; } catch (error) { this.logger.error('Erro ao obter status da ONU via TL1', { serial, error: error.message }); return { success: false, error: error.message, serial }; } } /** * Obter status da ONU por device name via TL1 */ async obterStatusONUPorNome(deviceName) { try { this.logger.debug('Obtendo status da ONU por nome via TL1', { deviceName }); // Conectar ao UNM2000 const conectado = await this.conectarUNM2000(); if (!conectado) { throw new Error('Não foi possível conectar ao UNM2000'); } // Comando TL1 para buscar ONU por nome const comando = `RTRV-ONU::${deviceName}:1;`; this.logger.debug('Executando comando TL1', { comando }); const resultado = await this.executarComandoTL1(comando); if (!resultado.success) { throw new Error(resultado.error || 'Falha na consulta TL1'); } // Processar resposta TL1 da ONU const statusONU = this.processarStatusONUTL1(resultado.data, deviceName); this.logger.debug('Status ONU processado via TL1', statusONU); return { success: true, ...statusONU }; } catch (error) { this.logger.error('Erro ao obter status da ONU por nome via TL1', { deviceName, error: error.message }); return { success: false, error: error.message, deviceName }; } } /** * Processar resposta TL1 para status de ONU */ processarStatusONUTL1(dadosTL1, identificador) { try { // Processar dados TL1 - formato típico Fiberhome // Exemplo de resposta TL1: "COMPLD RTRV-ONU:OLT:ONU-1:1:SERIAL=FHTT12345678,..." const resultado = { serial: identificador, deviceName: identificador, slot: 'N/A', pon: 'N/A', onuId: 'N/A', online: false, rxPower: 'N/A', txPower: 'N/A', distance: 'N/A', portasEthernet: [], servicosWAN: [] }; if (!dadosTL1 || typeof dadosTL1 !== 'string') { return resultado; } // Extrair informações básicas da resposta TL1 if (dadosTL1.includes('COMPLD')) { resultado.online = true; // Extrair serial se disponível const serialMatch = dadosTL1.match(/SERIAL=([A-Z0-9]+)/i); if (serialMatch) { resultado.serial = serialMatch[1]; } // Extrair device name se disponível const deviceMatch = dadosTL1.match(/DEVICENAME=([^,\s]+)/i); if (deviceMatch) { resultado.deviceName = deviceMatch[1]; } // Extrair posição (slot/pon/onu) const posicaoMatch = dadosTL1.match(/(\d+)-(\d+)-(\d+)/); if (posicaoMatch) { resultado.slot = posicaoMatch[1]; resultado.pon = posicaoMatch[2]; resultado.onuId = posicaoMatch[3]; } // Extrair informações de sinal const rxMatch = dadosTL1.match(/RXPOWER=([^,\s]+)/i); if (rxMatch) { resultado.rxPower = `${rxMatch[1]} dBm`; } const txMatch = dadosTL1.match(/TXPOWER=([^,\s]+)/i); if (txMatch) { resultado.txPower = `${txMatch[1]} dBm`; } const distMatch = dadosTL1.match(/DISTANCE=([^,\s]+)/i); if (distMatch) { resultado.distance = `${distMatch[1]} m`; } // Simular algumas portas Ethernet (dados típicos de ONU) resultado.portasEthernet = [ { porta: 1, status: 'up' }, { porta: 2, status: 'down' }, { porta: 3, status: 'down' }, { porta: 4, status: 'down' } ]; // Simular serviço WAN if (resultado.online) { resultado.servicosWAN = [ { id: 1, status: 'active', ip: '192.168.1.100' } ]; } } this.logger.debug('Dados TL1 processados', resultado); return resultado; } catch (error) { this.logger.error('Erro ao processar dados TL1', { error: error.message }); return { serial: identificador, deviceName: identificador, slot: 'N/A', pon: 'N/A', onuId: 'N/A', online: false, rxPower: 'N/A', txPower: 'N/A', distance: 'N/A', portasEthernet: [], servicosWAN: [] }; } } /** * Descobrir porta PON onde a ONU está descoberta * @param {string} serial - Serial da ONU (MAC address) */ async descobrirPortaPON(serial) { try { console.log('🔍 Descobrindo porta PON para ONU:', serial); this.logger.debug('Descobrindo porta PON para ONU', { serial }); // Conectar ao UNM2000 const conectado = await this.conectarUNM2000(); if (!conectado) { throw new Error('Não foi possível conectar ao UNM2000'); } let tentativas = 0; const maxTentativas = 320; // Máximo de tentativas (20 slots x 16 portas) // Primeiro, tentar listar dispositivos para descobrir o nome correto da OLT console.log('📋 Listando dispositivos OLT...'); const listarCmd = `LST-DEVICE:::${this.commandId++}::;`; const dispositivos = await this.executarComandoTL1(listarCmd); let oltName = this.config.olt.name; // Fallback para o IP configurado if (dispositivos.success && dispositivos.data) { console.log('📋 Resposta LST-DEVICE:', dispositivos.data); // Extrair DEVNAME da resposta (formato: AN5516-06_172.16.10.6) const match = dispositivos.data.match(/^([^\s]+)\s+[\d\.]+\s+AN5516/m); if (match && match[1]) { oltName = match[1]; console.log(`✅ Nome da OLT descoberto: ${oltName}`); // Atualizar configuração para uso futuro this.config.olt.name = oltName; } else { console.log('⚠️ Não foi possível extrair DEVNAME, usando IP como fallback'); } } else { console.log('⚠️ Não foi possível listar dispositivos:', dispositivos.error); } // Listar placas PON instaladas para saber quais slots existem console.log('📋 Listando placas PON instaladas...'); const listarBoardsCmd = `LST-BOARD::OLTID=${oltName}:${this.commandId++}::;`; const boards = await this.executarComandoTL1(listarBoardsCmd); let slotsDisponiveis = []; if (boards.success && boards.data) { console.log('📋 Placas encontradas:', boards.data); // Extrair slots das placas GPON (formato BOARDID: 1-1-slot, tipo: GCOB ou GC8B) // Exemplo: "1-1-12 GCOB GPON 16" const ponLines = boards.data.split('\n'); for (const line of ponLines) { // Verificar se a linha contém placa GPON if (line.includes('GPON')) { // Extrair BOARDID no formato rack-shelf-slot (ex: 1-1-12) const boardMatch = line.match(/(\d+)-(\d+)-(\d+)\s+(GCOB|GC8B)\s+GPON/); if (boardMatch) { const slot = parseInt(boardMatch[3]); // Pegar o terceiro número (slot) const numPortas = parseInt(line.match(/GPON\s+(\d+)/)[1]); // Número de portas if (!slotsDisponiveis.find(s => s.slot === slot)) { slotsDisponiveis.push({ slot, portas: numPortas }); } } } } slotsDisponiveis.sort((a, b) => a.slot - b.slot); console.log(`✅ Slots PON disponíveis:`, slotsDisponiveis.map(s => `${s.slot} (${s.portas} portas)`).join(', ')); } if (slotsDisponiveis.length === 0) { console.log('⚠️ Nenhuma placa GPON encontrada, tentando slots 1-20'); // Fallback: tentar todos os slots de 1 a 20 com 16 portas slotsDisponiveis = Array.from({length: 20}, (_, i) => ({ slot: i + 1, portas: 16 })); } // Buscar em todas as portas PON dos slots disponíveis for (const slotInfo of slotsDisponiveis) { if (tentativas >= maxTentativas) break; const { slot, portas } = slotInfo; for (let porta = 1; porta <= portas && tentativas < maxTentativas; porta++) { tentativas++; const ponId = `NA-NA-${slot}-${porta}`; const comando = `LST-UNREGONU::OLTID=${oltName},PONID=${ponId}:${this.commandId++}::;`; console.log(`🔎 Consultando porta PON ${ponId} (slot ${slot}, porta ${porta}/${portas})`); this.logger.debug('Consultando porta PON', { ponId, tentativa: tentativas, oltName }); const resultado = await this.executarComandoTL1(comando); if (resultado.success && resultado.data) { // Verificar se a ONU está na resposta if (resultado.data.toUpperCase().includes(serial.toUpperCase())) { console.log(`✅ Porta PON descoberta: ${ponId}`); this.logger.info('Porta PON descoberta', { serial, ponId }); return { success: true, portAlias: `${slot}-${porta}`, slot, porta }; } } else if (!resultado.success) { console.log(`⚠️ Erro ao consultar porta ${ponId}: ${resultado.error}`); // Se o erro for de formato, provavelmente todas as portas vão falhar if (resultado.error && resultado.error.includes('invalid parameter format')) { console.log('❌ Formato de comando inválido, interrompendo busca'); throw new Error('Formato de comando LST-UNREGONU inválido. Verifique configuração da OLT.'); } } // Pequeno delay entre consultas para não sobrecarregar await new Promise(resolve => setTimeout(resolve, 200)); } } // ONU não encontrada console.log(`❌ ONU ${serial} não encontrada após ${tentativas} tentativas`); this.logger.warn('ONU não encontrada em nenhuma porta PON', { serial, tentativas }); return { success: false, error: 'ONU não encontrada em nenhuma porta PON. Verifique se a ONU está ligada e descoberta pela OLT.' }; } catch (error) { console.log('❌ Erro ao descobrir porta PON:', error.message); this.logger.error('Erro ao descobrir porta PON', { serial, error: error.message }); return { success: false, error: error.message }; } } /** * Adicionar ONU Fiberhome via TL1 * @param {string} serial - Serial da ONU (MAC address) * @param {string} usuario - Nome/alias do usuário * @param {number} vlan - VLAN ID (padrão: 1002) * @param {string} onuType - Tipo da ONU (AN5506-02-B ou AN5506-01-A) */ async adicionarOnuFiberhome(serial, usuario, vlan = 1002, onuType = 'AN5506-02-B') { try { this.logger.info('Iniciando adição de ONU Fiberhome via TL1', { serial, usuario, vlan, onuType }); // Conectar ao UNM2000 const conectado = await this.conectarUNM2000(); if (!conectado) { throw new Error('Não foi possível conectar ao UNM2000'); } // Descobrir porta PON automaticamente const descoberta = await this.descobrirPortaPON(serial); if (!descoberta.success) { throw new Error(descoberta.error); } const portAlias = descoberta.portAlias; const nameOlt = this.config.olt.name; this.logger.info('Porta PON descoberta', { portAlias, nameOlt }); // 1. ADD-ONU: Autenticar a ONU const addOnuCmd = `ADD-ONU::OLTID=${nameOlt},PONID=NA-NA-${portAlias}:${this.commandId++}::AUTHTYPE=MAC,ONUTYPE=${onuType},NAME=${usuario},ONUID=${serial};`; this.logger.debug('Executando ADD-ONU', { comando: addOnuCmd }); const resultadoAdd = await this.executarComandoTL1(addOnuCmd); if (!resultadoAdd.success) { throw new Error(`Falha ao adicionar ONU: ${resultadoAdd.error}`); } // Aguardar um pouco para a ONU ser registrada await new Promise(resolve => setTimeout(resolve, 2000)); // 2. SET-WANSERVICE: Configurar WAN services // Determina quantas portas configurar baseado no tipo da ONU const numPortas = onuType === 'AN5506-02-B' ? 2 : 1; for (let porta = 1; porta <= numPortas; porta++) { const setWanCmd = `SET-WANSERVICE::OLTID=${nameOlt},PONID=NA-NA-${portAlias},ONUIDTYPE=MAC,ONUID=${serial}:${this.commandId++}::STATUS=1,MODE=2,CONNTYPE=1,VLAN=${vlan},COS=0,QOS=2,UPORT=${porta};`; this.logger.debug(`Configurando WAN Service porta ${porta}`, { comando: setWanCmd }); const resultadoWan = await this.executarComandoTL1(setWanCmd); if (!resultadoWan.success) { this.logger.warn(`Aviso ao configurar WAN porta ${porta}: ${resultadoWan.error}`); // Não lança erro, apenas registra o aviso } // Pequeno delay entre comandos await new Promise(resolve => setTimeout(resolve, 1000)); } // 3. CFG-WEBADMINISTRATOR: Configurar autenticação web const cfgWebCmd = `CFG-WEBADMINISTRATOR::OLTID=${nameOlt},PONID=NA-NA-${portAlias},ONUIDTYPE=MAC,ONUID=${serial}:${this.commandId++}::WEBUSERNAME=admin,WEBPASSWORD=clicknet10;`; this.logger.debug('Configurando autenticação web', { comando: cfgWebCmd }); const resultadoWeb = await this.executarComandoTL1(cfgWebCmd); if (!resultadoWeb.success) { this.logger.warn(`Aviso ao configurar autenticação web: ${resultadoWeb.error}`); // Não lança erro, apenas registra o aviso } // Aguardar antes de reiniciar await new Promise(resolve => setTimeout(resolve, 2000)); // 4. RESET-ONU: Reiniciar a ONU const resetCmd = `RESET-ONU::OLTID=${nameOlt},PONID=NA-NA-${portAlias},ONUIDTYPE=MAC,ONUID=${serial}:${this.commandId++}::;`; this.logger.debug('Reiniciando ONU', { comando: resetCmd }); const resultadoReset = await this.executarComandoTL1(resetCmd); if (!resultadoReset.success) { this.logger.warn(`Aviso ao reiniciar ONU: ${resultadoReset.error}`); // Não lança erro, apenas registra o aviso } this.logger.info('ONU Fiberhome adicionada com sucesso', { nameOlt, portAlias, serial, usuario, vlan }); return { success: true, message: 'ONU adicionada com sucesso', details: { olt: nameOlt, porta: portAlias, slot: descoberta.slot, ponPort: descoberta.porta, serial: serial, usuario: usuario, vlan: vlan, tipo: onuType, portas: numPortas } }; } catch (error) { this.logger.error('Erro ao adicionar ONU Fiberhome', { serial, error: error.message }); return { success: false, error: error.message }; } } /** * Buscar ONU registrada no sistema */ async buscarOnuRegistrada(serial) { try { this.logger.debug('Buscando ONU registrada no sistema', { serial }); // 1. Descobrir nome da OLT const lstDeviceCmd = `LST-DEVICE:::${this.commandId++}::;`; const deviceResult = await this.executarComandoTL1(lstDeviceCmd); if (!deviceResult.success) { throw new Error('Falha ao listar dispositivos OLT'); } console.log('📋 Resposta LST-DEVICE:', deviceResult.data); // Extrair nome da OLT da resposta const deviceLines = deviceResult.data.split('\n'); let nameOlt = null; for (const line of deviceLines) { if (line.includes('AN5516') || line.includes('AN5116')) { const parts = line.split(/\s+/).filter(p => p.trim()); if (parts.length > 0) { nameOlt = parts[0]; // Primeiro campo é o DEVNAME break; } } } if (!nameOlt) { throw new Error('Nome da OLT não encontrado'); } console.log(`✅ Nome da OLT descoberto: ${nameOlt}`); // 2. Listar todas as ONUs registradas const lstOnuCmd = `LST-ONU::OLTID=${nameOlt}:${this.commandId++}::;`; console.log(`📋 Listando ONUs registradas...`); const onuResult = await this.executarComandoTL1(lstOnuCmd); if (!onuResult.success) { throw new Error('Falha ao listar ONUs registradas'); } console.log('📋 ONUs registradas:', onuResult.data); // 3. Procurar a ONU pelo MAC/Serial const onuLines = onuResult.data.split('\n'); let onuInfo = null; for (const line of onuLines) { // Procurar linha que contenha o serial (case-insensitive) if (line.toUpperCase().includes(serial.toUpperCase())) { console.log(`🔍 Linha encontrada: ${line}`); // Extrair informações da linha // Formato esperado: campos separados por tabs ou espaços const parts = line.split(/\s+/).filter(p => p.trim() && p !== '--'); console.log(`📋 Parts extraídos:`, parts); if (parts.length >= 4) { // Tentar extrair PONID que pode estar em vários formatos: // - 1-1-12-14 (frameId-slotId-portId-onuId) // - NA-NA-12-14 // - 12-14 for (const part of parts) { // Regex mais flexível: aceita N-N-N-N ou NA-NA-N-N const ponidMatch = part.match(/^(?:\d+|\w+)-(?:\d+|\w+)-(\d+)-(\d+)$/); if (ponidMatch) { console.log(`✅ PONID identificado: ${part}`); onuInfo = { nameOlt: nameOlt, portAlias: part, slot: ponidMatch[1], porta: ponidMatch[2] }; break; } } } if (onuInfo) { console.log(`✅ ONU info completa:`, onuInfo); break; } else { console.log(`❌ Não conseguiu extrair PONID da linha`); } } } if (!onuInfo) { throw new Error('ONU não encontrada entre as ONUs registradas'); } console.log(`✅ ONU encontrada:`, onuInfo); return onuInfo; } catch (error) { this.logger.error('Erro ao buscar ONU registrada', { serial, error: error.message }); throw error; } } /** * Remover ONU Fiberhome via TL1 */ async removerOnuFiberhome(serial) { try { this.logger.debug('Removendo ONU Fiberhome via TL1', { serial }); // Conectar ao UNM2000 const conectado = await this.conectarUNM2000(); if (!conectado) { throw new Error('Não foi possível conectar ao UNM2000'); } // Normalizar serial para maiúsculas serial = serial.toUpperCase(); // Buscar a ONU registrada no sistema this.logger.debug('Buscando ONU registrada', { serial }); const onuInfo = await this.buscarOnuRegistrada(serial); if (!onuInfo) { throw new Error('ONU não encontrada no sistema'); } const { nameOlt, portAlias } = onuInfo; this.logger.debug('ONU encontrada', { nameOlt, portAlias }); // Executar comando DEL-ONU const delOnuCmd = `DEL-ONU::OLTID=${nameOlt},PONID=${portAlias}:${this.commandId++}::ONUIDTYPE=MAC,ONUID=${serial};`; this.logger.debug('Executando comando DEL-ONU', { comando: delOnuCmd }); const delResult = await this.executarComandoTL1(delOnuCmd); if (!delResult.success) { throw new Error(`Falha ao remover ONU: ${delResult.error}`); } this.logger.info('ONU Fiberhome removida com sucesso', { serial, olt: nameOlt, porta: portAlias }); return { success: true, message: 'ONU removida com sucesso', details: { olt: nameOlt, porta: portAlias, serial: serial, slot: onuInfo.slot, ponPort: onuInfo.porta } }; } catch (error) { this.logger.error('Erro ao remover ONU Fiberhome', { serial, error: error.message }); return { success: false, error: error.message }; } } /** * Desconectar TL1 */ async desconectar() { try { if (this.tl1Connection && !this.tl1Connection.destroyed) { this.logger.debug('Desconectando TL1'); this.tl1Connection.end(); this.tl1Connection = null; } } catch (error) { this.logger.error('Erro ao desconectar TL1', error); } } /** * Verificar se as conexões estão ativas */ verificarConexoes() { return { olt: false, // Não usamos mais OLT diretamente unm2000: this.tl1Connection && !this.tl1Connection.destroyed }; } /** * Diagnóstico completo de conectividade */ async diagnosticarConectividade() { const diagnostico = { olt: { host: 'N/A', port: 'N/A', conectividade: false, telnet: false, erro: 'Não utilizado - consultas via TL1' }, unm2000: { host: this.config.unm2000.host, port: this.config.unm2000.port, conectividade: false, tl1: false, erro: null } }; // Testar UNM2000 try { this.logger.debug('Testando conectividade UNM2000...'); diagnostico.unm2000.conectividade = await this.testarConectividade( this.config.unm2000.host, this.config.unm2000.port ); if (diagnostico.unm2000.conectividade) { this.logger.debug('Testando login TL1 UNM2000...'); diagnostico.unm2000.tl1 = await this.conectarUNM2000(); } } catch (error) { diagnostico.unm2000.erro = error.message; this.logger.error('Erro no diagnóstico UNM2000:', error); } return diagnostico; } /** * Configura IPoE em ONU FiberHome: deleta WAN services existentes e recria com VLAN 977 * 1. Busca ONU registrada pelo serial * 2. Consulta WAN services existentes (LST-ONUWANSERVICECFG) * 3. Deleta cada WAN service (SET-WANSERVICE STATUS=2) * 4. Recria com VLAN 977 (SET-WANSERVICE STATUS=1) * 5. Reinicia ONU * @param {string} serial - MAC/Serial da ONU * @param {string} onuType - Tipo da ONU (para determinar número de portas) * @returns {Promise} - Promise com resultado */ async configurarIPoEFiberhome(serial, onuType = 'AN5506-02-B') { try { const VLAN_IPOE = 977; this.logger.info('Iniciando configuração IPoE FiberHome via TL1', { serial, vlan: VLAN_IPOE }); const conectado = await this.conectarUNM2000(); if (!conectado) { throw new Error('Não foi possível conectar ao UNM2000'); } // Buscar ONU registrada pelo serial const onuInfo = await this.buscarOnuRegistrada(serial); const nameOlt = onuInfo.nameOlt || this.config.olt.name; const portAlias = onuInfo.portAlias; this.logger.info('ONU encontrada, consultando WAN Services existentes', { nameOlt, portAlias, serial }); // 1. Consultar WAN services existentes const lstCmd = `LST-ONUWANSERVICECFG::OLTID=${nameOlt},PONID=NA-NA-${portAlias},ONUIDTYPE=MAC,ONUID=${serial}:${this.commandId++}::;`; this.logger.debug('Consultando WAN Services', { comando: lstCmd }); const lstResult = await this.executarComandoTL1(lstCmd); let wanServices = []; if (lstResult.success && lstResult.data) { wanServices = this._parseWanServices(lstResult.data); this.logger.info(`WAN Services encontrados: ${wanServices.length}`, { wanServices }); } // 2. Deletar WAN services existentes for (const ws of wanServices) { const delCmd = `SET-WANSERVICE::OLTID=${nameOlt},PONID=NA-NA-${portAlias},ONUIDTYPE=MAC,ONUID=${serial}:${this.commandId++}::STATUS=2,MODE=${ws.mode},CONNTYPE=${ws.connType},VLAN=${ws.vlan},COS=${ws.cos},UPORT=${ws.uport};`; this.logger.debug(`Deletando WAN Service porta ${ws.uport}`, { comando: delCmd }); const delResult = await this.executarComandoTL1(delCmd); if (!delResult.success) { this.logger.warn(`Aviso ao deletar WAN porta ${ws.uport}: ${delResult.error}`); } await new Promise(resolve => setTimeout(resolve, 1000)); } // 3. Recriar WAN services com VLAN 977 const numPortas = onuType === 'AN5506-02-B' ? 2 : 1; // Usar as portas dos services existentes, ou fallback para 1..numPortas const portas = wanServices.length > 0 ? wanServices.map(ws => ws.uport) : Array.from({ length: numPortas }, (_, i) => i + 1); for (const porta of portas) { const addCmd = `SET-WANSERVICE::OLTID=${nameOlt},PONID=NA-NA-${portAlias},ONUIDTYPE=MAC,ONUID=${serial}:${this.commandId++}::STATUS=1,MODE=2,CONNTYPE=1,VLAN=${VLAN_IPOE},COS=0,QOS=2,UPORT=${porta};`; this.logger.debug(`Criando WAN Service IPoE porta ${porta}`, { comando: addCmd }); const addResult = await this.executarComandoTL1(addCmd); if (!addResult.success) { this.logger.warn(`Aviso ao criar WAN porta ${porta}: ${addResult.error}`); } await new Promise(resolve => setTimeout(resolve, 1000)); } // 4. Reiniciar ONU para aplicar await new Promise(resolve => setTimeout(resolve, 2000)); const resetCmd = `RESET-ONU::OLTID=${nameOlt},PONID=NA-NA-${portAlias},ONUIDTYPE=MAC,ONUID=${serial}:${this.commandId++}::;`; this.logger.debug('Reiniciando ONU após IPoE', { comando: resetCmd }); const resultadoReset = await this.executarComandoTL1(resetCmd); if (!resultadoReset.success) { this.logger.warn(`Aviso ao reiniciar ONU: ${resultadoReset.error}`); } this.logger.info('IPoE FiberHome configurado com sucesso', { serial, vlan: VLAN_IPOE }); return { success: true, message: `VLAN alterada para ${VLAN_IPOE} (IPoE)`, details: { olt: nameOlt, porta: portAlias, serial, vlan: VLAN_IPOE, portasConfiguradas: portas, wanServicesRemovidos: wanServices.length } }; } catch (error) { this.logger.error('Erro ao configurar IPoE FiberHome', { serial, error: error.message }); return { success: false, error: error.message }; } } /** * Parseia a resposta do LST-ONUWANSERVICECFG para extrair WAN services * Formato: SVCNAME CONNMODE CONNTYPE VLANID VLANCOS ... BINDPORTNO (tab-separated) */ _parseWanServices(data) { const services = []; const lines = data.split('\n'); // Encontrar a linha de cabeçalho e as linhas de dados let headerFound = false; let dashFound = false; for (const line of lines) { const trimmed = line.trim(); if (trimmed.startsWith('SVCNAME')) { headerFound = true; continue; } if (headerFound && trimmed.startsWith('---')) { if (!dashFound) { dashFound = true; // primeiro traço (após header) continue; } else { break; // segundo traço = fim dos dados } } if (dashFound && trimmed.length > 0 && !trimmed.startsWith('---')) { // Linha de dados: campos separados por tab ou espaços const parts = trimmed.split(/\t+|\s{2,}/).filter(p => p.trim()); // Formato: SVCNAME(0) CONNMODE(1) CONNTYPE(2) VLANID(3) VLANCOS(4) ... BINDPORTNO(18) if (parts.length >= 5) { const mode = parseInt(parts[1]) || 2; const connType = parseInt(parts[2]) || 1; const vlan = parseInt(parts[3]) || 0; const cos = parseInt(parts[4]) || 0; // BINDPORTNO é o último campo const uport = parseInt(parts[parts.length - 1]) || 1; services.push({ mode, connType, vlan, cos, uport, name: parts[0] }); console.log(`📋 [IPoE FH] WAN Service parseado: mode=${mode}, connType=${connType}, vlan=${vlan}, cos=${cos}, uport=${uport}`); } } } return services; } } module.exports = FiberhomeManager;