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 = 20; // Limitar a 20 tentativas // 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); } // Buscar em todas as portas PON possíveis (slots 1-20, portas 1-16) for (let slot = 1; slot <= 20 && tentativas < maxTentativas; slot++) { for (let porta = 1; porta <= 16 && 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} (tentativa ${tentativas}/${maxTentativas})`); 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 }; } } /** * 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; } } module.exports = FiberhomeManager;