- Visão geral do Jelina
- EMS Builder
- Exemplos de configuração
Exemplo: balanceamento de carga de um conjunto de carregadores
Este exemplo mostra como usar apenas recursos gerais do Edge-EMS Builder — varredura dinâmica de dados, cálculo de expressões, perfis de acionamento e limitação de frequência — para construir uma estratégia de balanceamento de carga que admite “quantidade variável de dispositivos sem alterar scripts”. As marcas e os prefixos usados são ilustrativos; substitua-os pelos nomes reais do projeto.
I. Contexto do negócio
Seção intitulada “I. Contexto do negócio”Um quadro de distribuição alimenta vários carregadores (sem restrição de protocolo: OCPP ou protocolo proprietário do fabricante) e outras cargas essenciais, como iluminação e climatização. A capacidade de entrada da rede é fixa. Se todos os carregadores operarem simultaneamente em potência máxima, a carga total poderá exceder a capacidade nominal da entrada, provocando desarme ou penalidades por ultrapassagem de capacidade.
Objetivo: garantir prioridade de alimentação às cargas essenciais e compartilhar a capacidade restante entre todos os carregadores online, ajustando dinamicamente a corrente de cada carregador. A entrada ou saída de qualquer unidade deve provocar um novo balanceamento em poucos segundos, e a inclusão de novos carregadores não deve exigir alterações nos scripts de estratégia.
II. Modelo de potência
Seção intitulada “II. Modelo de potência”Há dois pontos de medição principais no sistema:
Grid_total_power: potência total de entrada da rede em tempo real, incluindo todas as cargas do quadro: cargas essenciais + carregadores + outras;- As cargas essenciais já estão incluídas nessa medição. Portanto, basta controlar o consumo total dos carregadores para manter a potência total de entrada do quadro dentro do valor nominal, atendendo naturalmente às cargas essenciais primeiro.
-- Margem disponível (kW) = (potência nominal - potência total medida da rede) × fator de segurança-- A margem de 10% evita ultrapassagens de potência; a medição já inclui as cargas essenciais, sem necessidade de subtraí-las separadamentelocal avail_kw = (rated_kw - grid_kw) * 0.9local per_A = (avail_kw / n) * 1000 / voltage_V -- Divide igualmente e converte em corrente (A)III. Descoberta dinâmica: adicionar carregadores sem alterar scripts
Seção intitulada “III. Descoberta dinâmica: adicionar carregadores sem alterar scripts”A estratégia não pressupõe uma quantidade fixa de carregadores. A cada execução, identifica automaticamente todas as unidades online por uma varredura em lote por prefixo + sufixo:
local online = {}
-- Varre cada prefixo de marca; adicione ou remova conforme necessáriofor _, brandPrefix in ipairs({"VENDORA", "VENDORB"}) do local statusMap = getLatestDataValuesByPrefixAndSuffix("Collection", brandPrefix, "_status") if statusMap then -- O retorno é uma Table indexada por dataKey; percorra com pairs() e compare com a string sentinela "__Null__" para verificar ausência de valor for dataKey, dto in pairs(statusMap) do if dto.DataValue ~= "__Null__" then table.insert(online, dto) end end endendDesde que os pontos de aquisição de um novo carregador sigam a regra de nomenclatura definida (consulte “Regras de nomenclatura” abaixo), ele será incluído automaticamente no cálculo de divisão igualitária no próximo acionamento, sem alterar scripts nem reiniciar serviços. Esse recurso resulta da combinação do modelo de dados com o mecanismo Lua, e não de uma função exclusiva para esse caso.
IV. Modelo de acionamento: atualização dos dados + limitação de frequência no script
Seção intitulada “IV. Modelo de acionamento: atualização dos dados + limitação de frequência no script”Um polling de período fixo pode causar gravações redundantes mesmo sem alteração dos dados. Incluir também a limitação de frequência nas condições do gatilho torna a configuração mais complexa. Aqui, usa-se um modelo mais simples:
Acionamento por atualização dos dados + limitação por timestamp dentro do script: o gatilho apenas detecta a atualização dos dados, enquanto o script controla a frequência real de envio dos comandos.
Atualização dos dados → Condição satisfeita → Script acionado → Ler ponto agregado last_ctrl_ms (timestamp da última execução) → Tempo decorrido < 5000ms: retornar sem enviar comandos → Tempo decorrido ≥ 5000ms: executar controle → gravar comandos → atualizar timestampPré-requisito da implantação: crie manualmente um ponto agregado que servirá como “memória” do script:
{ "dataKey": "last_ctrl_ms", "dataName": "充电控制上次执行时间戳", "expression": "return 0", "collectionCycle": -1}V. Tabela de decisão por carregador
Seção intitulada “V. Tabela de decisão por carregador”Após cada acionamento, o script executa nesta ordem: ler parâmetros da rede → verificar a limitação de frequência → varrer carregadores online → calcular a corrente disponível → decidir por carregador → atualizar o timestamp → retornar a lista de comandos. Regras de decisão por carregador:
| Condição | Estado do carregador | Ação |
|---|---|---|
| Sobrecarga ou corrente por divisão igualitária < corrente mínima | Carregando | Parar o carregamento (stop_transaction) |
| Sobrecarga ou corrente por divisão igualitária < corrente mínima | Outro | Ignorar |
| Sem sobrecarga e corrente por divisão igualitária ≥ corrente mínima | Carregando | Enviar novo limite de corrente (set_charging_profile) |
| Sem sobrecarga e corrente por divisão igualitária ≥ corrente mínima | Não está carregando | Iniciar o carregamento (start_transaction, com Tag de autorização válida) |
Trecho principal do script correspondente (ação de gatilho que retorna um array de descrições de gravação):
local OffsetDateTime = require("OffsetDateTime")
local ratedKw = tonumber(${Grid_current} or 0) * tonumber(${Grid_voltage} or 0) * 0.001local gridKw = tonumber(${Grid_total_power} or 0)local overload = gridKw >= ratedKw
local voltage = tonumber(${Grid_voltage} or 1)local minChargePower = tonumber(${min_charge_power} or 4200)
local resultList = {}if #online > 0 then local availKw = (ratedKw - gridKw) * 0.9 local perA = math.floor((availKw / #online) * 1000 / voltage) local minA = math.floor(minChargePower / voltage)
for _, pile in ipairs(online) do local prefix = pile.DataKey:gsub("_status$", "") -- Recupera o prefixo do carregador a partir do dataKey, como VENDORA01 if overload or perA < minA then if pile.DataValue == "Charging" then table.insert(resultList, buildCommonWriteMap("Collection", prefix .. "_type", "stop_transaction", nil, nil, false)) end else if pile.DataValue == "Charging" then table.insert(resultList, buildCommonWriteMap("Collection", prefix .. "_periods", '[{"start":0,"limit":' .. perA .. '}]', nil, nil, false)) elseif ${id_tag} ~= "__Null__" then table.insert(resultList, buildCommonWriteMap("Collection", prefix .. "_type", "start_transaction", nil, nil, false)) end end endend
table.insert(resultList, buildCommonWriteMap("Aggregation", "last_ctrl_ms", OffsetDateTime.now():unixMilli(), nil, nil, false))return resultListVI. Regras de nomenclatura: pré-requisito para reconhecimento em lote
Seção intitulada “VI. Regras de nomenclatura: pré-requisito para reconhecimento em lote”O script varre em lote os pontos de aquisição dos carregadores no formato {prefixo_da_marca}{número_de_dois_dígitos}_{propriedade}, que deve ser seguido rigorosamente:
| Parte | Regra | Exemplo |
|---|---|---|
| Prefixo da marca | Letras maiúsculas, sem sublinhados | VENDORA, VENDORB (substitua pelos identificadores reais das marcas utilizadas) |
| Número | Dois dígitos com preenchimento de zero à esquerda | 01, 02…10 |
| Propriedade | Consulte a tabela abaixo | status, type |
Pontos de aquisição necessários em cada carregador (alguns exemplos):
| Propriedade | Exemplo de dataKey completo | Leitura/gravação | Descrição |
|---|---|---|---|
_status |
VENDORA01_status |
Leitura | Estado do carregador, usado na varredura; se estiver ausente, o carregador não será descoberto |
_transaction_id |
VENDORA01_transaction_id |
Leitura | ID da transação atual |
_type |
VENDORA01_type |
Gravação | Tipo de comando: start/stop/set_charging_profile |
_periods |
VENDORA01_periods |
Gravação | JSON do período de limitação, como [{"start":0,"limit":16}] |
Para adicionar o sexto carregador dessa marca, basta integrar seus pontos conforme a nomenclatura acima — VENDORA06_status, VENDORA06_type etc. — para incluí-lo automaticamente na lógica de divisão igualitária.
VII. Como lidar com mudanças futuras
Seção intitulada “VII. Como lidar com mudanças futuras”| Cenário | Exige alteração do script? | Operação |
|---|---|---|
| Adicionar carregador da mesma marca | Não | Adicione os pontos de aquisição conforme a regra de nomenclatura |
| Ajustar o limite de capacidade nominal | Não | Altere o valor do ponto agregado Grid_current |
| Ajustar a potência mínima de carregamento | Não | Altere o valor do ponto agregado min_charge_power |
| Carregador temporariamente offline | Não | Após a desconexão, _status fica vazio e o carregador é removido automaticamente da lista online |
| Adicionar uma marca de carregador | Sim | Acrescente um prefixo à lista de varredura, como {"VENDORA", "VENDORB", "VENDORC"} |
| Tornar configurável o fator de margem de segurança | Sim, uma única vez | Crie um ponto agregado para esse fator e altere o script para lê-lo |
Essa tabela também ilustra um princípio destacado na Filosofia de projeto: a maioria das mudanças de negócio exige apenas ajustes de configuração; poucas alterações estruturais exigem modificar scripts, com um escopo previsível e controlável.
VIII. Combinação com outros recursos
Seção intitulada “VIII. Combinação com outros recursos”- Se os carregadores estiverem distribuídos entre vários subquadros, use a colaboração entre dispositivos para compartilhar a potência medida da rede de cada subquadro com o controlador principal e realizar uma distribuição global unificada.
- Se novas marcas e modelos de carregadores forem incorporados continuamente e os projetos tiverem estruturas semelhantes, use a expansão dinâmica de unidades de dispositivo para transformar “prefixo de marca + regra de nomenclatura + pontos relacionados” em templates, reduzindo o tempo de implantação de novos projetos.
- Se for necessário receber limites de corrente da nuvem e consultar o estado geral do balanceamento, use a colaboração entre nuvem e borda para informar periodicamente a margem da rede e os registros de comandos enviados a cada carregador.
Este diagrama foi elaborado a partir da lógica de configuração desta página e mostra os fluxos de dados e controle. Para os campos específicos, consulte as configurações e os scripts no texto.