No meu projeto de Supervisório, utilizando o scadaBR 1.2, contemplo um senário onde monitoro diferentes sistemas com operadores (pessoas) monitorando os respectivos sistemas.
Tenho um problema no qual a lista de alarmes apresenta todos os alarmes, independente do sistema e/ou operador, e isso não me parecia um problema, até um operador começar a reconhecer o alarme do sistema do outro operador.
A procura de uma solução, encontrei um tópico descontinuado que tratava este assunto, contudo está sem resposta: https://forum.scadabr.com.br/t/personalizacao-do-objeto-lista-de-alarmes-no-graphicview/1725
Alguns dos colegas mais avançados já trabalharam ou fazem ideia de como solucionar este “Problema”?
Pela lista de alarmes propriamente dita é mais difícil realizar personalizações. Uma alternativa é criar uma tabela de alarmes à parte com scripts para servidor.
Eu tenho um código antigo nesse sentido. Vou ver se consigo adaptá-lo para postar aqui.
Conforme prometido, segue o script de tabela de alarmes. Importante avisar que eu fiz esse script para um trabalho que tive com o Scada-LTS, então a aparência da tabela foi feita para atender às necessidades de um cliente específico, mas você pode editar o CSS e deixar o estilo da forma que preferir.
Código Javascript (insira como script para servidor)
// Configurar os títulos do cabeçalho da tabela
var titulo_nivel = "Nível de alarme";
var titulo_tempo = "Tempo de início";
var titulo_desc = "Descrição";
var titulo_status = "Status do evento";
var titulo_reconhecimento = "Reconhecimento";
var titulo_comando = "Comandos";
// Mostrar o texto do nível de alarme
var mostrar_nivel_alarme = true;
// Usar formatos mais longos de tempo (sempre exibir as datas)
var tempo_extenso = false;
// Mostrar comandos (reconhecer/silenciar)
var mostrar_comandos = true;
// MODIFICADO 03/02/2024
var uniqueClass = "evttable-" + pointComponent.id;
// FIM DA MODIFICAÇÃO
// Criação do HTML da tabela
var s = "";
s += "<div class='alarms-table " + uniqueClass + "'>";
s += "<table>";
// Cabeçalhos da tabela
s += "<thead>";
s += "<tr>";
s += "<th>" + titulo_nivel + "</th>";
s += "<th>" + titulo_tempo + "</th>";
s += "<th>" + titulo_desc + "</th>";
s += "<th>" + titulo_status + "</th>";
s += "<th>" + titulo_reconhecimento + "</th>";
// MODIFICADO 03/02/2024
if (mostrar_comandos) {
s += "<th>" + titulo_comando +
"<span style='margin-left: 4px;'><img class='cmd-btn' src='images/tick.png' title='Reconhecer todos' onclick='document.querySelectorAll(`." + uniqueClass + " td > .cmd-btn[src*=tick]:not([style*=hidden])`).forEach(elm => elm.click());'><img class='cmd-btn' src='images/sound_mute.png' title='Silenciar todos' onclick='MiscDwr.silenceAll();'>" +
"</span></th>";
}
// FIM DA MODIFICAÇÃO
s += "</tr>";
s += "</thead>";
// Corpo da tabela
s += "<tbody>";
var eventos = getActiveOrPendingEvents();
for (var i = 0; i < eventos.length; i++) {
var e = eventos[i];
var classe_linha = "";
classe_linha += e.isActive ? "evento-ativo " : "";
classe_linha += !e.isAcked ? "evento-pendente " : "";
s += "<tr class='" + classe_linha + "'>";
// Nível de alarme
var niveis = {0: "Nenhum", 1: "Informação", 2: "Urgente", 3: "Crítico", 4: "Risco de vida"};
var imagens = {
0: ['./images/flag_green_off.png', './images/flag_green.png'],
1: ['./images/flag_blue_off.png', './images/flag_blue.png'],
2: ['./images/flag_yellow_off.png', './images/flag_yellow.png'],
3: ['./images/flag_orange_off.png', './images/flag_orange.png'],
4: ['./images/flag_red_off.png', './images/flag_red.png']
};
s += "<td class='alarm-flag'>";
s += "<img src='" + imagens[e.alarmLevel][e.isActive] + "'>";
if (mostrar_nivel_alarme) {
s += niveis[e.alarmLevel];
}
"</td>";
// Tempo de início
s += "<td>" + e.activeTime + "</td>";
// Descrição e comentários
s += "<td><span>" + e.message + "</span>";
for (var j in e.comments) {
var c = e.comments[j];
s += "<span class='event-comment'>" + c.username + " - " + c.prettyTime + ": " + c.comment + "</span>";
}
s += "</td>";
// Status (tempo de inatividade)
s += "<td>" + e.statusMessage + "</td>";
// Reconhecimento (reconhecido/pendente)
if (e.isAcked) {
s += "<td> Reconhecido </td>";
} else {
s += "<td> Pendente </td>";
}
// Comandos
if (mostrar_comandos) {
s += "<td>";
var event_ref_code = com.serotonin.mango.vo.UserComment.TYPE_EVENT;
var sound_img = e.isSilenced ? "images/sound_mute.png" : "images/sound_none.png";
if (!e.isAcked) {
s += "<img class='cmd-btn' src='images/tick.png' title='Reconhecer' onclick='MiscDwr.acknowledgeEvent(" + e.id + ")'>";
} else {
s += "<img class='cmd-btn' style='visibility:hidden;' src='images/tick.png'>";
}
s += "<img class='cmd-btn' src='" + sound_img + "' title='Ativar/desativar som' onclick='MiscDwr.toggleSilence(" + e.id + ")'>";
s += "</td>";
}
s += "</tr>";
}
s += "<tbody>";
s += "</table>";
s += "</div>";
return s;
// Obter os eventos
function getActiveOrPendingEvents() {
// Carregar classes Java
var eventDao = include(org.scada_lts.mango.service.EventService, com.serotonin.mango.db.dao.EventDao);
var user = new com.serotonin.mango.Common().getUser();
// Consultar eventos pendentes no banco de dados
var pending = eventDao.getPendingEvents(user.getId());
// Consultar eventos ativos no banco de dados
var active = eventDao.getActiveEvents();
var evt1 = parseEvents(pending);
var evt2 = parseEvents(active);
if (evt1.length > 0) {
for (var i in evt2) {
var filter = evt1.filter(function(elm) { return elm.id == evt2[i].id; });
if (filter.length == 0) {
evt1.push(evt2[i]);
}
}
} else {
evt1 = evt2;
}
// Remover eventos com certas expressões
var list = ["entrou no sistema", "logged in"];
var modo_blacklist = true; // true: blacklist / false: whitelist
evt1 = evt1.filter(function(elm) {
for (var i in list) {
if (elm.message.toLowerCase().includes(list[i])) {
return !modo_blacklist;
}
}
return modo_blacklist;
});
evt1.sort(function(a, b) { return (b.id - a.id) });
return evt1;
}
function parseEvents(events) {
var eventArray = [];
var bundle = new com.serotonin.mango.Common().getBundle();
// Converter os dados para um objeto Javascript
for (var i = 0; i < events.size(); i++) {
var event = events.get(i);
var e = {};
// Dados básicos do evento
e.id = event.getId();
e.alarmLevel = event.getAlarmLevel();
e.isActive = Number(event.isActive());
e.isAcked = Number(event.isAcknowledged());
e.activeTime = tempo_extenso? event.getFullPrettyActiveTimestamp() : event.getPrettyActiveTimestamp();
e.message = sanitizeXss(event.getMessage().getLocalizedMessage(bundle)) || "";
e.comments = parseComments(event.getEventComments());
// Mensagem de status do evento (ativo/retornou/sem retorno)
e.statusMessage = "Ativo";
if (event.isRtnApplicable() && !event.isActive()) {
var rtnTime = tempo_extenso ? event.getFullPrettyRtnTimestamp() : event.getPrettyRtnTimestamp();
var rtnMessage = sanitizeXss(event.getRtnMessage().getLocalizedMessage(bundle));
e.statusMessage = rtnTime + " - " + rtnMessage;
} else if (!event.isRtnApplicable()) {
e.statusMessage = "\"Retornar ao normal\" desabilitado";
}
// Verificar se o evento está silenciado
if (event.isAlarm()) {
e.isSilenced = event.isSilenced();
}
eventArray.push(e);
}
// Retornar array com todos os eventos
return eventArray;
}
function parseComments(userComments) {
var comments = [];
if (!userComments) {
return [];
}
// Converter para um objeto Javascript
for (var i = 0; i < userComments.size(); i++) {
var uc = userComments.get(i);
var x = {};
x.comment = sanitizeXss(uc.getComment());
x.username = sanitizeXss(uc.getUsername());
x.timestamp = uc.getTs();
x.prettyTime = sanitizeXss(uc.getPrettyTime());
comments.push(x);
}
// Retornar um array com os comentários do evento
return comments;
}
function sanitizeXss(text) {
var sanitized = "";
// Prevents a loop at &'s replacement
String(text).split("&").forEach(function(value, index) {
if (index == 0) {
if (text.charAt(0) != "&") sanitized = value;
return;
}
if (value.search(";") > -1) {
sanitized += "&" + value;
} else {
sanitized += "&" + value;
}
});
return sanitized.replace(/</g, "<")
.replace(/>/g, ">").replace(/'/g, "'")
.replace(/"/g, '"').replace(/\//g, "/");
}
// Include Java classes conditionally (Scada-LTS compatibility feature)
function include(defaultClass, alternativeClass) {
try {
return defaultClass();
} catch (e) {
return alternativeClass();
}
}
Não posso prometer nada, mas tenho a intenção de em algum momento criar uma página no meu site com um repositório de modelos de scripts para servidor prontos. Se/quando eu fizer isso postarei no fórum o link.
Celso, ja testei um funcionou perfeitamente. Vou fuçar nas configurações e personalizar aqui do meu jeito. Cara, se vc conseguir um espaço no seu site dedicado a essas estripulias, com certeza será muito bem quisto, e vídeos né … na minha opinião, qualquer leigo assim como eu e com seus vídeos, consegue aprender scadaBR. Agradeço muito pelo seu empenho.
Parece que você está enfrentando um desafio interessante com o sistema SCADA, onde os alarmes não estão sendo devidamente filtrados por sistema ou operador, o que está causando confusão entre os operadores. Uma possível solução seria personalizar a visualização da lista de alarmes para permitir uma organização melhor, filtrando por sistema ou operador responsável pelo alarme. Vale a pena revisar o tópico descontinuado que você mencionou e tentar modificar as configurações ou o código para resolver esse problema.
Da mesma forma, se você precisar gerenciar as horas de trabalho dos operadores ou funcionários no seu projeto, pode ser interessante usar uma Calculadora de Horas. Essa ferramenta pode te ajudar a monitorar o tempo de cada operador em sistemas específicos, garantindo mais clareza e organização no seu projeto, assim como você gostaria de organizar os alarmes de acordo com a responsabilidade de cada operador.