Skip to Content

Backend de Dados

O IronFlock provisiona um banco de dados privado para cada projeto, baseado no TimescaleDB. Sua aplicação define o esquema de dados; o IronFlock cria as tabelas e começa a coletar dados no momento em que um dispositivo é adicionado à aplicação.

Como Funciona

  1. Defina seu esquema de dados em .ironflock/data-template.yml.
  2. Use o SDK do IronFlock para publicar dados a partir do seu código de borda.
  3. O IronFlock configura automaticamente as tabelas do banco de dados em cada projeto onde a aplicação está instalada.
  4. Os dados fluem dos dispositivos pelo sistema de mensagens até o banco de dados do projeto.

Cada projeto recebe seu próprio banco de dados físico — não há compartilhamento de dados entre projetos.

O usuário tem controle total sobre os dados coletados pela sua aplicação no projeto dele. Como desenvolvedor, você não tem acesso a esses dados.

Definindo o Esquema de Dados

Crie um arquivo data-template.yml no diretório .ironflock/:

data: tables: - tablename: sensordata columns: - id: tsp name: Timestamp description: Timestamp of measurement path: args[0].timestamp dataType: timestamp - id: temperature name: Temperature description: Temperature reading in Celsius path: args[0].temperature dataType: numeric - id: humidity name: Humidity description: Relative humidity percentage path: args[0].humidity dataType: numeric - id: device_id name: Device ID description: Source device identifier path: args[0].device_id dataType: string

Opções de Coluna

CampoDescrição
idIdentificador interno da coluna (use tsp para colunas de timestamp)
nameNome legível da coluna exibido nos boards
descriptionDescrição opcional
pathCaminho até o valor no objeto de dados publicado (ex.: args[0].temperature)
dataTypeUm de: timestamp, numeric, string, boolean

Opções de Tabela

Além de columns, uma tabela aceita algumas chaves opcionais que controlam como ela é descrita e como seus dados envelhecem:

data: tables: - tablename: sensordata description: Leituras ambientais do chão de fábrica chunkTimeInterval: 1 hour dropAfter: 30 days columns: # ...
CampoDescrição
tablenameNome da tabela
descriptionDescrição opcional, exibida na interface e usada por agentes de IA para entender a tabela
chunkTimeIntervalTamanho das partições de tempo em que a tabela é dividida. Padrão: 7 days
dropAfterJanela de retenção — partições mais antigas que isso são descartadas automaticamente
downsampleMantém uma cópia pré-agregada para gráficos rápidos de janela longa — veja Downsampling Contínuo abaixo
maintainLatestFlagForColunas que identificam uma entidade única — veja Rastreando o Estado Mais Recente de uma Entidade abaixo
privateOculta esta tabela dos outros apps — veja Compartilhando Dados com Outros Apps abaixo

chunkTimeInterval controla como os dados de série temporal são particionados em disco. Escolha-o de modo que uma partição corresponda aproximadamente ao que você consulta de uma vez: dados de alta frequência coletados a cada segundo se beneficiam de chunks pequenos (de minutos a horas), e dados que mudam devagar, de chunks grandes (semanas). Este é apenas o padrão do app — o proprietário do projeto pode ajustá-lo depois em seu próprio data backend.

dropAfter transforma a tabela em uma janela deslizante. Partições inteiras mais antigas que o intervalo informado são descartadas, o que é bem mais barato do que excluir linhas individuais. A rotina de limpeza roda em um ciclo de dropAfter / 4, então um registro pode sobreviver ao seu vencimento por até um quarto do intervalo antes que sua partição seja removida. Omita dropAfter para manter os dados indefinidamente.

Ambos aceitam strings de intervalo do PostgreSQL — 30 minutes, 1 hour, 7 days, 6 months.

Downsampling Contínuo

Os dashboards podem pedir ao banco de dados que agregue os dados — médias horárias, totais diários, contagens por máquina. Calcular isso a partir dos registros brutos é tranquilo para um dia e caro para um ano. Adicione downsample a uma tabela e a plataforma manterá uma cópia pré-agregada dela, continuamente atualizada, e passará a responder às consultas de janela longa a partir dessa cópia:

data: tables: - tablename: sensordata dropAfter: 30 days downsample: bucket: 1 minute keepFor: 2 years paths: - payload.temperature columns: # ...
CampoDescrição
bucketGranularidade da cópia pré-agregada. Padrão: 1 minute
keepForPor quanto tempo manter o histórico reduzido. Omita para mantê-lo indefinidamente
pathsCaminhos de campos JSON a incluir, na mesma notação usada pelos dashboards

bucket é a resolução mais fina a partir da qual um gráfico pode ser servido — um gráfico que peça intervalos bem mais finos que isso lê a tabela bruta. Aceita intervalos de largura fixa de 1 second a 1 day que dividam o dia de forma exata (1 minute, 5 minutes, 1 hour). O padrão de 1 minute atende praticamente qualquer dashboard; um bucket mais grosso custa menos armazenamento e menos throughput de escrita.

keepFor é o que torna os históricos longos possíveis. Os registros brutos desaparecem com dropAfter, mas a cópia reduzida tem sua própria retenção: mantenha os dados brutos por 30 dias e os reduzidos por 2 anos, e um board ainda poderá plotar dois anos de médias horárias usando uma fração do armazenamento. Defina-o maior que dropAfter — a plataforma rejeita o contrário como configuração incorreta.

paths estende o downsampling a valores dentro de colunas JSON. As colunas numéricas são incluídas automaticamente; os campos JSON precisam ser nomeados explicitamente, já que uma coluna JSON não tem um conjunto fixo de chaves. Campos não declarados continuam funcionando nos dashboards — eles simplesmente são calculados a partir da tabela bruta.

Todo o resto é automático. Cada coluna numérica tem suas estatísticas mantidas (média, soma, mínimo, máximo, primeiro, último e uma contagem de registros), agrupadas pela chave de entidade da tabela (maintainLatestFlagFor, ou o dispositivo publicador). Os dashboards não precisam de configuração nem de qualquer consciência disso: um widget consulta como de costume, e a plataforma decide a cada consulta se a cópia pré-agregada consegue respondê-la — recorrendo à tabela bruta de forma transparente quando não consegue, por exemplo quando um filtro referencia uma coluna pela qual a cópia não agrupa.

Mudanças de esquema reconstroem a cópia. Adicionar, remover ou trocar o tipo de uma coluna de uma tabela com downsampling — ou editar o próprio bloco downsample — reconstrói a cópia pré-agregada a partir da tabela bruta. Tudo que for mais antigo que dropAfter não pode ser reconstruído e é perdido. Configure o bloco junto com a tabela sempre que possível, e trate mudanças posteriores de esquema em tabelas de vida longa como uma decisão deliberada.

Publicando Dados a partir do Código de Borda

Use o SDK do IronFlock para enviar dados da sua aplicação:

from ironflock import IronFlock flock = IronFlock() flock.publish_to_table("sensordata", { "timestamp": "2025-01-15T10:30:00Z", "temperature": 23.5, "humidity": 62.1, "device_id": "sensor-001" })

Para dados de alta frequência, envie várias linhas em uma única mensagem em vez de uma ida e volta por linha usando publish_rows_to_table / publishRowsToTable (fire-and-forget) ou append_rows_to_table / appendRowsToTable (retorna o resultado da inserção). Cada lote é inserido atomicamente — tudo ou nada. Consulte a referência do SDK para detalhes.

Tabelas de Transformação

Você pode definir transformações SQL que automaticamente agregam ou processam seus dados brutos:

data: tables: - tablename: sensordata columns: # ... raw data columns ... transforms: - tablename: hourly_averages materialize: true schedule: "0 * * * *" sql: > SELECT time_bucket('1 hour', tsp) AS hour, avg(temperature) AS avg_temp, avg(humidity) AS avg_humidity FROM sensordata GROUP BY hour columns: - id: hour name: Hour dataType: timestamp - id: avg_temp name: Average Temperature dataType: numeric - id: avg_humidity name: Average Humidity dataType: numeric
CampoDescrição
tablenameNome da tabela derivada
materializeSe true, os resultados são persistidos como uma tabela
scheduleExpressão cron que define quando a transformação é executada
sqlConsulta SQL que computa a transformação
columnsDefinições de coluna para a saída

As tabelas de transformação ficam acessíveis nos boards e via SDK, exatamente como as tabelas comuns.

Rastreando o Estado Mais Recente de uma Entidade

Para tabelas que representam o estado atual de entidades do mundo real — máquinas, ativos, ordens de produção — o IronFlock suporta um padrão chamado rastreamento do estado mais recente.

Em vez de sobrescrever uma linha quando algo muda, você sempre acrescenta uma nova linha. Você declara quais colunas identificam uma entidade única, e o IronFlock deriva a linha mais recente de cada entidade sempre que a tabela é lida. Isso lhe dá um histórico completo de cada alteração e, ao mesmo tempo, facilita consultar apenas o estado atual.

Habilite-o em uma tabela com maintainLatestFlagFor:

- tablename: machineform maintainLatestFlagFor: ['machinename'] columns: - id: tsp dataType: timestamp - id: machinename dataType: string - id: machinetype dataType: string - id: active dataType: boolean - id: description dataType: string

maintainLatestFlagFor recebe uma lista de colunas que, juntas, identificam uma entidade única. Nada é escrito na própria linha: o IronFlock indexa a tabela por essa chave de entidade mais o timestamp e escolhe a linha mais recente de cada entidade no momento da consulta. Assim, uma linha que chega atrasada ou fora de ordem nunca pode deixar para trás uma marcação desatualizada.

Para consultar apenas os estados atuais das máquinas:

SELECT DISTINCT ON (machinename) * FROM machineform ORDER BY machinename, tsp DESC

Para visualizar o histórico completo de uma máquina específica:

SELECT * FROM machineform WHERE machinename = 'Assembly-Line-01' ORDER BY tsp

Raramente você escreve essa consulta à mão. Widgets em um board que se conectam a esta tabela possuem um interruptor latest nas configurações de filtro, de modo que os usuários sempre vejam os valores atuais sem nenhum trabalho extra. A partir do SDK, solicite o mesmo modo adicionando {"latest": true} ao filterAnd — veja getHistory.

Migrando de latest_flag: versões anteriores do IronFlock armazenavam uma coluna booleana física chamada latest_flag. Essa coluna não existe mais — o estado atual passou a ser derivado em SQL, o que o mantém correto quando as linhas chegam fora de ordem. Boards e chamadas de SDK existentes que filtram por latest_flag = true continuam funcionando: o IronFlock os reconhece e aplica o modo de estado mais recente. Código novo deve usar o interruptor latest ou a entrada de filtro {"latest": true}.

Exclusão Lógica de Registros

O modelo append-only do IronFlock significa que os registros nunca são fisicamente excluídos. Em vez disso, use uma coluna booleana deleted para marcar um registro como removido. Isso preserva a trilha de auditoria completa ao mesmo tempo em que oculta os registros excluídos dos dashboards.

Adicione uma coluna deleted a qualquer tabela de entidade:

- id: deleted name: Deleted dataType: boolean

Quando um usuário exclui um registro (por exemplo, via um formulário no board), sua aplicação publica uma nova linha para aquela entidade com deleted: true. Combinado com maintainLatestFlagFor, essa nova linha torna-se o estado mais recente.

Para consultar apenas os registros atuais ativos (não excluídos):

SELECT * FROM ( SELECT DISTINCT ON (machinename) * FROM machineform ORDER BY machinename, tsp DESC ) latest WHERE deleted IS NULL OR deleted = false

A verificação de deleted é executada depois que a linha mais recente de cada máquina foi escolhida. Essa ordem importa: filtrar as linhas excluídas antes faria a linha anterior, não excluída, reaparecer como o estado atual.

Os widgets do board e o SDK aplicam a mesma ordem automaticamente — combine o interruptor latest (ou {"latest": true}) com um filtro de deleted e você obtém exatamente esse comportamento. Os registros excluídos desaparecem do dashboard imediatamente após o envio do formulário, mas permanecem no banco de dados para fins de histórico e auditoria.

Compartilhando Dados com Outros Apps

Seu data backend é privado do seu app: nenhum outro app instalado no projeto enxerga suas tabelas. Duas chaves opcionais em data-template.yml mudam isso.

Para ler os dados de outro app, liste os apps dos quais quer ler em uma seção consumes: de nível superior — ao lado de data:, não dentro dela:

consumes: - app: machine-monitor reason: "Calcula o OEE a partir dos fluxos de estado de máquina e contadores do monitor" data: tables: - tablename: oee_results columns: # ... as tabelas do seu próprio app, como sempre

app é o nome técnico do app fornecedor, ou "*" (as aspas são obrigatórias) para todos os apps do projeto. reason é exibido ao usuário no diálogo de consentimento — a declaração sozinha não concede nada até que ele aprove.

Para reservar tabelas específicas, marque-as com private: true. Tudo que você define é compartilhável por padrão; uma tabela ou transformação privada nunca aparece no catálogo que os outros apps veem.

data: tables: - tablename: measurements # compartilhada (padrão) columns: [ ... ] - tablename: calibration_state # interna — nunca visível para outros apps private: true columns: [ ... ]

O acesso é somente leitura, concedido pelo usuário por projeto e revogável a qualquer momento. Veja Consumindo Dados de Outros Apps para o modelo completo e as chamadas do SDK que leem o histórico e os fluxos ao vivo de um app fornecedor.

Last updated on