Aplicacao de Velocidade em Tempo Real
Quando uma regra de Policy municipal ativa, o Levy dispara comandos IoT especificos do OEM para cada veiculo atualmente dentro da geometria da regra. Este e o momento que prova a cidade que voce nao esta apenas relatando compliance -- voce esta aplicando. A meta de latencia e <10s p95 da ativacao da regra ao primeiro comando enviado.
Latencias-alvo
Roundtrip OKAI Cat-M e ~3-5s. Omni 4G e ~5-8s. Segway BLE-relay pode picar a 30s+ em veiculos frios -- para frotas Segway documentamos a lacuna com cidades em vez de prometer abaixo de 10s. Queclink e ZIMO ficam entre OKAI e Omni.
O servico de aplicacao
src/lib/compliance/enforcement.ts expoe dois pontos de entrada:
| Funcao | Chamador | O que faz |
|---|---|---|
fanOutEnforcementForRule(ruleId, reason) | Cron mds-policy-activate + motor de cruzamento de zona | Consulta veiculos na geometria da regra, pula GPS velho, despacha comando de velocidade especifico do OEM, registra em policy_enforcement_events. |
setSpeedLimit(vehicleUuid, kph, reason) | Acao "override manual" do dashboard | Mesmo caminho de despacho por OEM mas para um unico veiculo, chamado da UI do operador. |
Ambos passam pelo mesmo modulo src/lib/iot/dispatch.ts que lida com a formatacao de comando especifica do OEM.
Catalogo de comandos por OEM
Para cada veiculo na geometria afetada, o servico consulta iot_devices.iot_type e roteia para o comando correto:
| OEM | Comando | Formato |
|---|---|---|
| OKAI | •••••sign in | •••••sign in -- cap de velocidade ECU |
| Segway | •••••sign in | Parametro Iotrip S4 sobre protocolo HBCS -- define throttle maximo |
| Omni 4G (Levy Max, Acton, Feishen) | •••••sign in | Parametro S4 protocolo SCOS -- velocidade maxima em km/h |
| Queclink | •••••sign in | •••••sign in -- alarme de velocidade + cap ECU |
| ZIMO | MQTT •••••sign in | Payload JSON •••••sign in no topico de comando do dispositivo |
Para regras no_ride, o mesmo despachador emite o comando de bloqueio do OEM em vez (OKAI •••••sign in, Segway •••••sign in, Omni •••••sign in etc.). Para regras parking, nenhum comando IoT e enviado -- a aplicacao de estacionamento acontece no fim da viagem, nao no veiculo.
Idempotencia
Todo comando e marcado com uma chave de idempotencia sha256 derivada de:
sha256(rule_id + vehicle_uuid + action + rule_value + activation_timestamp)
A chave e gravada em policy_enforcement_events como uma coluna unica. Uma re-execucao (por exemplo, o cron de ativacao dispara duas vezes durante um deploy) tenta inserir a mesma chave, encontra a restricao unica e pula silenciosamente. A camada IoT nunca e pedida para enviar um comando duplicado.
Este e o mesmo padrao que usamos para filtragem de ruido Sentry e OTA de firmware OKAI. Significa que o loop de enforcement pode ser tentado de novo com seguranca.
O skip de GPS velho
Quando uma regra ativa, o servico consulta veiculos por sua ultima amostra GPS conhecida:
SELECT v.* FROM vehicles v
JOIN vehicle_events e ON e.vehicle_uuid = v.id
WHERE e.event_type = 'gps'
AND e.created_at > now() - interval '5 minutes'
AND ST_Contains(<rule.geometry>, ST_MakePoint(e.lng, e.lat))
Se o ultimo GPS de um veiculo for mais antigo que 5 minutos, ele e pulado e uma linha e registrada com error = 'stale_gps'. O raciocinio:
- Nao sabemos realmente onde o veiculo esta, entao nao sabemos se a regra se aplica.
- Wakeups Cat-M podem ser lentos em veiculos estacionados -- um comando enviado a um radio adormecido fica na fila e chega muito mais tarde, as vezes depois que o veiculo saiu da zona.
- Quando o veiculo acorda e envia um GPS fresco, o motor de cruzamento de zona existente verificara
policy_geofencese despachara o comando entao -- nenhum comando e perdido.
O limiar de 5 minutos e uma constante em enforcement.ts. Escolhemos para combinar com a janela de frescor de telemetria existente usada em outros lugares do codigo (cron auto-lock, verificacao de confianca de bateria SCOR).
Sequencia na ativacao de regra
cron mds-policy-activate
|
v
flipar mds_policies.status de 'pending' -> 'active'
|
v
para cada regra na politica:
|
v
fanOutEnforcementForRule(rule.id, "policy_activated")
|
+---> consultar veiculos na geometria, apenas GPS fresco
|
+---> para cada veiculo:
| consultar iot_devices.iot_type
| computar chave de idempotencia sha256
| INSERT INTO policy_enforcement_events (unique de idempotencia)
| despachar comando especifico do OEM via iot-proxy
| aguardar ack (best-effort, timeout 5s)
| UPDATE policy_enforcement_events SET command_ack_at = ...
|
v
registrar resumo do run no Sentry (contagens de sucesso, skip, erro)
Sequencia em veiculo entrando em zona existente
O cron de ativacao e o caminho "regra mudou". O outro caminho e "veiculo moveu para uma zona que ja estava ativa". Isso e lidado pelo motor de cruzamento de zona existente (src/lib/zones/enforcement.ts), que agora verifica policy_geofences alem das zonas de operador:
atualizacao de telemetria GPS chega durante viagem ativa
|
v
stackForPoint(point) // RPC PostGIS + scan policy_geofences
|
v
resolveStack(stack) // escada de prioridade
|
v
active.speed_limit_kph difere de current_speed_limit_zone_id?
|
v
sim -> despachar comando de limite de velocidade especifico do OEM (dedupado contra estado atual)
A mesma forma de chave de idempotencia se aplica, mas com rule.activation_timestamp substituido pelo timestamp da amostra GPS. Isso significa que um veiculo que pingue na fronteira de uma zona nao gera comandos duplicados a cada atualizacao GPS.
O que pilotos veem
No app, pilotos recebem uma notificacao de slow zone:
Titulo: "Slow zone da cidade"
Corpo: "Voce esta agora em uma slow zone municipal de Boulder. Velocidade maxima limitada a 8 km/h."
Para regras no-ride:
Titulo: "Zona no-ride da cidade"
Corpo: "Controles de pilotagem pausados enquanto voce esta nesta area. Mude para uma area aprovada para continuar."
Essas mensagens sao diferenciadas das notificacoes de zona do operador pelo prefixo "Cidade", entao pilotos sabem que o operador nao e quem esta colocando a regra. O texto da notificacao e configuravel em Configuracoes -> Notificacoes -> Compliance.
O que o dashboard mostra
A visao Eventos de Enforcement em /dashboard/compliance/{jurisdiction-id} (aba Enforcement) e o registro canonico. Cada linha carrega:
| Campo | Notas |
|---|---|
rule_id | FK para mds_policy_rules |
vehicle_uuid | O veiculo afetado |
action | speed_limit, lock, unlock_on_exit |
command_sent_at | Ts UTC no despacho |
command_ack_at | Ts UTC quando o OEM confirmou (null se nenhum ack ainda) |
command_response | JSON de resposta especifico do OEM |
error | stale_gps, offline, oem_rejected etc., ou null |
idempotency_key | sha256 hex |
Filtravel por jurisdicao, regra, veiculo e intervalo de datas. Util quando uma cidade pergunta "como sei que voces aplicaram a slow zone as 15h de terca?".
Limites e lacunas conhecidas
- Latencia Segway BLE-relay: veiculos que nao estiveram perto de um telefone por um tempo podem levar >30s para acordar. Para frotas com forte uso de Segway, documente isso por escrito com a cidade ao inves de prometer demais.
- Veiculos em estado
non_operational: pulados automaticamente. Nao tentamos enviar comandos para veiculos que voce colocou offline. - Veiculos sem linha
iot_devices: pulados comerror = 'no_iot_device'. Na maioria das vezes um veiculo importado sem ligacao IMEI. policy_geofences.geometryatualmente e NULL -- opointInPolygonem JS e o fallback. Mover paraST_GeomFromGeoJSONna ingestao esta no roadmap; reduzira o tempo de scan em JS para frotas com milhares de policy geofences.
Override manual do dashboard
Se voce precisar sobrepor uma decisao de aplicacao de politica para um unico veiculo (por exemplo, para demonstrar o sistema a um contato municipal ou para recuperar um veiculo de uma zona no-ride para um tecnico), use Detalhe do veiculo -> Acoes de compliance -> Definir limite de velocidade. Isso chama setSpeedLimit(vehicleUuid, kph, reason) diretamente. A acao e registrada em policy_enforcement_events com action = 'manual_override' e reason carregando a nota do usuario do dashboard.
Overrides manuais expiram no proximo evento de cruzamento de zona. Sao uma ferramenta tatica, nao um modo de isentar permanentemente um veiculo de uma politica.
Proximos passos
- Ingestao de Politica de Cidades -- de onde vem as regras.
- Prioridade de Geofences Empilhados -- como a regra ativa e escolhida.
- Solucao de Problemas -- quando enforcement nao dispara.