Files
rodrigonet/src/fiberhome/manager_tl1.js
T
rodrigo 0a9ce75800 fiberhome: add prelimunary add ONU support
Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo.sousa.577@gmail.com>
2026-02-10 19:30:53 -03:00

676 lines
26 KiB
JavaScript

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;