Handleiding

Sensor Beheer — werkwijze voor medewerkers en technische achtergrond

Deze handleiding beschrijft hoe je met Sensor Beheer de Xovis-sensoren van TRF beheert: heatmaps, sensorbeelden, logo's, configuratie-back-ups en health-controles. Elk hoofdstuk begint met de werkwijze; waar het nuttig is volgt een blok met de technische achtergrond voor ontwikkelaars.

01Wat de tool doet

Sensor Beheer bundelt de beheertaken die je anders per sensor in de Xovis-webinterface zou doen. Je werkt met alle toestellen tegelijk vanuit één scherm: een heatmap ophalen, het camerabeeld met tracking area en logic-geometrieën bekijken, het TRF-logo op tientallen sensoren pushen, configuratie-back-ups nemen en terugzetten, en een onafhankelijke health-controle draaien.

Jouw browserSensor BeheerServerroutes/api/* · geen secretsXovis HUBapi.xovis.cloudDe sensorvia de tunnelSupabasegedeeld token · 5 aanvragen/dag
Je browser praat nooit rechtstreeks met een sensor. Elke actie loopt over een serverroute die het gedeelde HUB-token gebruikt en via de tunnel bij het toestel komt. Supabase houdt je aanmelding, het versleutelde token en de back-uphistoriek bij.
Wat je hier doet

Beheertaken op de toestellen zelf: beeld, branding, configuratie en toestandscontrole — voor één sensor of voor de hele vloot in één keer.

Wat je hier niet doet

Bezoekersdata bekijken of gaten in tellingen herstellen. Dat is de Data Repair Tool (Herstelfile). Sensor Beheer raakt de tellingen niet aan.

Verhouding tot de HUB

De tool vervangt de Xovis HUB niet. Ze bundelt de handelingen die je vaak nodig hebt en voegt er bulkacties, planning en historiek aan toe.

02Toegang & aanmelden

De tool zit achter een aanmelding. Openen van de startpagina stuurt je door naar Sensor Beheer; zonder sessie beland je op het aanmeldscherm.

  • Magic link is de standaard: je vult je e-mailadres in en krijgt een aanmeldlink toegestuurd.
  • Wachtwoord kan ook — aanmelden of een account aanmaken, als back-up wanneer de mail niet aankomt.
  • Toegelaten domeinen: @theretailfactory.be, @theretailfactory.eu en @sentigrate.com.
Voor ontwikkelaars

useSession() levert de sessie; sensor-tools stuurt zelf door naar /auth wanneer die ontbreekt. Elke serverroute controleert de sessie opnieuw via verifySupabaseSession(request) en antwoordt 401 zonder geldig bearer-token. apiFetch/apiJson in src/lib/api-client.ts hangen dat token automatisch aan elke aanvraag.

Let op: de domeinbeperking staat enkel in de UI (ALLOWED_DOMAINS in src/routes/auth.tsx). Er is geen databasetrigger die ze afdwingt, in tegenstelling tot de Data Repair Tool. Zie hoofdstuk 11.

03Devices kiezen

Bovenaan staat Devicelijst vernieuwen. De lijst komt van de HUB en bevat enkel de toestellen die aan ons account gekoppeld zijn. In elk tabblad kies je vervolgens een toestel op een van twee manieren.

  • Doorzoekbare keuzelijst — typ een deel van het MAC-adres, de naam of de groep; er wordt op alle drie tegelijk gezocht.
  • Manueel MAC-adres — altijd beschikbaar, ook wanneer de devicelijst niet laadt. Formaat 80:1F:12:AB:CD:EF.
  • Bij Logo en Favicon werk je met aanvinkvakjes en Selecteer alles, zodat je een bulkactie op meerdere toestellen doet.
Devicelijst laadt niet, maar de rest werkt wel

Krijg je een foutmelding op de devicelijst terwijl acties op een manueel ingegeven MAC-adres gewoon lukken, dan is dat geen bug. Het HUB-endpoint voor de devicelijst vraagt een apart recht (DEVICE_API_ACCESS) dat losstaat van de tunnel-toegang. Dat is een accountinstelling bij Xovis; intussen kan je manueel verder werken.

04Heatmap

Een sensor bouwt zelf een heatmap op van waar mensen zich ophouden. Dit tabblad haalt dat beeld op als JPEG en kan de opgebouwde historiek op het toestel wissen.

  1. 1Kies een device — Via de lijst of manueel.
  2. 2Heatmap downloaden — Het beeld verschijnt in het scherm; met Bestand opslaan bewaar je het lokaal.
  3. 3Eventueel resetten — Heatmap resetten wist alle opgebouwde historiek op het toestel. Er volgt eerst een bevestigingsvenster.
Resetten is onomkeerbaar

De opgebouwde heatmap zit enkel op het toestel. Wissen betekent dat de opbouw weer bij nul begint — download het beeld dus eerst als je het nog nodig hebt.

Downloaden mag met leesrechten; resetten vraagt schrijfrechten (application_write). Lukt het downloaden wel en het resetten niet, dan gaat het om dat recht en niet om een storing.

05Sensorbeeld met overlays

Dit is het meest gebruikte tabblad bij controles op afstand: je ziet het achtergrondbeeld van de sensor met daarover de tracking area, de logic-geometrieën (de lijnen en zones waarop geteld wordt) en optioneel de start- en stoppunten van gedetecteerde bewegingen. Zo controleer je zonder ter plaatse te gaan of een sensor nog naar het juiste stuk kijkt en of de teltellijnen goed liggen.

  • Klik Sensorbeeld laden. Het beeld en alle overlays worden in één keer opgehaald.
  • Met de schakelaars zet je tracking area, geometrieën, punten en labels aan of uit. De punten staan standaard uit omdat ze het beeld snel vol maken.
  • Elke geometrie krijgt een eigen kleur en een label met de logic-nummers die ze gebruiken.
  • De badges bovenaan tonen het sensortype, het referentiesysteem en hoeveel start- en stoppunten er in het venster zaten.
Melding “referentiesysteem VIEW”

Bij een rechtgetrokken bovenaanzicht (SCENE) vallen de overlays exact op hun plaats. Bij een ruw camerabeeld (VIEW) is de positionering een benadering: de vorm klopt, maar de punten kunnen enkele pixels verschoven staan. De tool waarschuwt hiervoor. Gebruik zo'n beeld dus om te controleren of iets ongeveer goed staat, niet om millimeters af te meten.

Voor ontwikkelaars

/api/sensor-scene haalt achtergrond, tracking area, geometrieën, logics en start/stop-punten parallel op. De projectie komt uit de header X-Image-Metadata van de JPEG; let op de envelope — de transformatie zit in .image_metadata. De omzetting van scènecoördinaat naar pixel gebeurt client-side in SceneOverlay:

De route probeert bovendien beide sensortype-paden en kiest het pad dat effectief geometrieën teruggeeft — de logics-probe kiest soms het andere, waardoor de overlay leeg zou blijven. Bij de 3D start/stop-punten wordt de hoogte genegeerd. Ontbreekt een bron, dan blijft de rest werken: per bron wordt de status meegegeven in sources.

Projectieformule

pixel_x = linear[0][0] * x + linear[0][1] * y + translation[0]
pixel_y = linear[1][0] * x + linear[1][1] * y + translation[1]

06Logo & favicon

De webinterface van elke sensor kan het TRF-logo en -favicon dragen. Dat is puur cosmetisch, maar het maakt meteen duidelijk wie het toestel beheert. Beide tabbladen werken identiek en ondersteunen bulk.

  1. 1Selecteer toestellen — Zoek, vink aan, of gebruik Selecteer alles. Een manueel MAC-adres kan je er nog bij optellen.
  2. 2Kies een bestand — of niet — Laat je het vak leeg, dan wordt het standaard TRF-bestand gepusht. Dat is de gewone gang van zaken.
  3. 3Uploaden of verwijderen — Uploaden vervangt het bestaande bestand; Verwijderen haalt het weg zonder iets nieuws te plaatsen.
  4. 4Volg de resultatenlijst — Per toestel zie je of het gelukt is. Eén mislukking stopt de rest van de reeks niet.
WatToegelaten bestanden
LogoPNG, JPEG, GIF of SVG
FaviconEnkel .ico
  • In het Logo-tabblad staat een extra knop Standaard logo + favicon pushen: die zet in één beweging beide standaardbestanden op alle geselecteerde toestellen. Dat is de snelste weg bij een nieuwe installatie.
  • Zie je de melding over te weinig opslagruimte, dan is het geheugen van dat toestel vol. Kies een kleiner bestand of ruim eerst op — de andere toestellen zijn wel gelukt.
Voor ontwikkelaars

/api/sensor-logo en /api/sensor-favicon nemen { macs, fileBase64, mimeType, remove } en lopen de MAC's sequentieel af met 400 ms pauze, om de tunnel niet te overladen. Per toestel: eerst DELETE op de blob (404 wordt genegeerd), daarna PUT met de echte mime-type. HTTP 413 wordt expliciet vertaald naar “te weinig opslagruimte”. Het antwoord is altijd een lijst met per MAC succes of foutboodschap — de route faalt niet als geheel.

07Configuratie-back-ups

Van elke sensor wordt de volledige configuratie bewaard, zodat je na een defect of een verkeerde instelling kan terugvallen op een werkende versie. Dit gebeurt automatisch op de eerste van de maand om 03:00; de tabel toont per toestel de laatste back-up.

  • Nu back-uppen doet dezelfde ronde meteen, over alle toestellen. Dat duurt even: per toestel wordt de back-up aangevraagd, afgewacht en gedownload.
  • Download geeft je het back-upbestand via een tijdelijke link (vijf minuten geldig).
  • Terugzetten stuurt de bewaarde configuratie terug naar het toestel. Er volgt eerst een bevestiging.
  • De statuskolom toont per toestel of de laatste ronde lukte, met de foutboodschap als dat niet zo was.
Terugzetten overschrijft de configuratie

De huidige instellingen van het toestel worden vervangen door die uit de back-up, en het toestel kan daarbij herstarten — dus even geen tellingen. Doe dit bewust, en controleer eerst de datum van de back-up die je kiest.

Voor ontwikkelaars

runBackupAllDevices() doorloopt per toestel: POST device/backup (202 = al bezig, gewoon doorgaan), pollen op device/backup/state tot AVAILABLE (maximaal 20 keer met 3 s pauze), downloaden, en uploaden naar de private Supabase Storage-bucket device-backups als {mac}/{jaar}-{maand}.xbak. Elke poging wordt gelogd in device_backup_log. Omdat het pad de maand bevat en met upsert wordt geschreven, overschrijft een tweede ronde in dezelfde maand de eerste.

De planning loopt via pg_cron + pg_net, die maandelijks /api/public/backup-all-devices aanroept met de header X-Cron-Secret. Die publieke ingang bestaat omdat er bij een geplande aanroep geen ingelogde gebruiker is. De waarde van dat secret staat in Supabase Vault (backup_cron_secret) en wordt door de cron-job bij elke uitvoering opgehaald, zodat ze niet in de code of in een migratie staat. Terugzetten probeert eerst PUT op device/backup en valt bij 404/405 terug op POST device/backup/restore.

08Health (redundant)

Een tweede, onafhankelijke controle naast de monitoring die al in de HUB zit — geen vervanging. De bedoeling is problemen vinden voordat ze gaten in de tellingen veroorzaken, want zo'n gat moet je daarna handmatig herstellen.

Klik Nu controleren. Standaard zie je enkel de toestellen met een probleem; het aantal staat rechts van de knoppen. Je kan zoeken op MAC, naam, groep of firmware, en sorteren op aantal issues, tilt-afwijking, status of naam. Via Openen ga je naar de webinterface van het toestel zelf.

Wanneer krijgt een toestel een issue

IssueBetekenisWat je doet
Push-foutenHet toestel krijgt zijn data niet weggestuurd.Het meest dringende signaal: dit is precies wat later een gat in de tellingen wordt.
Tilt > 2°De gemeten hoek wijkt af van de ingestelde hoek — de sensor is vermoedelijk fysiek verschoven.Controleer het sensorbeeld (hoofdstuk 5). Klopt het beeld niet meer, dan is een interventie ter plaatse nodig.
Status niet ONLINEDe HUB ziet het toestel niet als online.Netwerk of voeding nakijken.
Config > 30 dagen oudDe configuratie is lang niet ververst.Meestal onschuldig, maar wel een aanwijzing dat het toestel weinig contact heeft.
ControlefoutDe controle zelf kon niet worden uitgevoerd voor dat toestel.Opnieuw proberen; blijft het duren, dan is het toestel of de tunnel het probleem.
Voor ontwikkelaars

runHealthCheck() combineert twee bronnen: het toestelrecord uit de HUB-devicelijst (status, firmware, tilt, laatste config-refresh — geen extra call nodig) en per toestel data/push/agents/status via de tunnel, met 300 ms pauze ertussen. Snapshots gaan naar device_health_snapshot, zodat je afwijkingen tegenover een vorige ronde kan tonen. De maandelijkse back-up draait deze controle mee, dus daarvoor is geen aparte planning nodig.

De push-foutdetectie is een heuristiek: het antwoord wordt naar tekst omgezet en er wordt gezocht naar “fail” of “error”. Dat kan zowel valse alarmen als gemiste fouten opleveren. Wie hierop wil bouwen, doet er goed aan het echte antwoordschema per firmwareversie uit te lezen. Zie hoofdstuk 11.

09Meldingen en wat ze betekenen

De meeste meldingen komen rechtstreeks van de sensor of van de HUB. Deze tabel helpt je onderscheiden wat je zelf kan oplossen en wat een rechtenkwestie is.

MeldingOorzaakWat je doet
Je sessie is verlopen (401)De aanmelding is niet meer geldig.Opnieuw aanmelden en de actie hernemen.
Geen toestemming (403)Het HUB-account mist een recht voor dit endpoint.Geen codefout. Vraag het recht aan bij Xovis: DEVICE_API_ACCESS voor de devicelijst, application_write om de heatmap te resetten.
Te weinig opslagruimte (413)Het geheugen van het toestel is vol.Kleiner bestand gebruiken, of eerst iets verwijderen op dat toestel.
Sensortype kon niet bepaald wordenHet toestel antwoordt niet op de twee bekende paden.MAC-adres controleren; daarna nakijken of het toestel online is in de Health-tab.
Back-up werd niet AVAILABLEHet toestel had de back-up niet klaar binnen ongeveer een minuut.Later opnieuw proberen; blijft het mislukken, controleer de toestand van het toestel.
Waarom je soms even moet wachten

Xovis staat maar vijf tokenaanvragen per dag toe voor ons hele account. Daarom hergebruikt de tool één gedeeld token voor alle acties en alle gebruikers, ook na een herstart. Je merkt dit niet in het gebruik — maar het is de reden waarom bulkacties bewust traag en na elkaar lopen in plaats van allemaal tegelijk.

10Technische opbouw

De browser praat enkel met eigen serverroutes; die houden de Xovis-credentials binnen de server en zetten elke aanvraag om naar een HUB- of tunnelcall. Alle routes volgen hetzelfde patroon: sessie controleren, credentials lezen, gedeeld token opvragen, sensortype detecteren, dan de eigenlijke call.

Stack

framework
TanStack Start (file-based routing) + React 19, Vite
styling
Tailwind v4 met de TRF-tokens uit styles.css, shadcn/ui op Radix
state
TanStack Query voor devicelijst en back-upstatus
data
Supabase: auth, Postgres (logs), Storage (back-upbestanden), pg_cron (planning)
herkomst
gegenereerd en onderhouden via Lovable

Waar wat staat

PadVerantwoordelijkheid
src/routes/sensor-tools.tsxDe volledige UI: de zes tabbladen met hun panelen. De plek waar je het vaakst zal zijn.
src/lib/xovis/config.tsTunnelhost, standaardheaders, tunnelUrl(), json(), sleep(). Leest process.env bewust nooit op moduleniveau.
src/lib/xovis/token.tsHet gedeelde token, met dubbele cache en één vlucht per keer.
src/lib/xovis/tokenStore.server.tsAES-256-GCM-versleutelde opslag van dat token in de database.
src/lib/xovis/sensorType.tsSingle- of multisensor detecteren, met cache per MAC.
src/lib/xovis/devices.tsDe HUB-devicelijst (state=MANAGED).
src/lib/xovis/backup.server.tsDe back-upronde over alle toestellen.
src/lib/xovis/health.server.tsDe health-controle en het wegschrijven van snapshots.
src/components/sensor/SceneOverlay.tsxDe SVG-overlay op het sensorbeeld, inclusief projectie.
src/lib/handleiding/Deze handleiding: content.json is de enige bron, ook voor de PDF's.
supabase/migrations/Tabellen, RLS, de storage-bucket en de cron-planning.

Serverroutes

RouteDoet
GET /api/list-devicesDe devicelijst uit de HUB, herleid tot mac/naam/groep/status/firmware/ip.
GET|DELETE /api/sensor-heatmapHeatmap ophalen als JPEG, of de historiek wissen.
GET /api/sensor-sceneBeeld, projectie, tracking area, geometrieën en start/stop-punten in één antwoord.
POST /api/sensor-logoLogo plaatsen of verwijderen, in bulk.
POST /api/sensor-faviconIdem voor het favicon van de device-web-UI.
POST /api/sensor-healthHealth-controle over alle of geselecteerde toestellen.
POST /api/backup-all-devicesBack-upronde; aanvaardt een gebruikerssessie óf het cron-secret.
POST /api/public/backup-all-devicesPublieke cron-ingang, enkel beveiligd met X-Cron-Secret.
GET /api/backup-statusLaatste back-uplogs en health-snapshots (elk maximaal 300 rijen).
GET|POST /api/backup-fileOndertekende downloadlink, of een back-up terugzetten naar het toestel.

Servervariabelen

XOVIS_CLIENT_ID
en XOVIS_CLIENT_SECRET — de HUB-API-gebruiker
XOVIS_TOKEN_ENC_KEY
sleutel waarmee het token in de database versleuteld wordt; zonder deze variabele werkt de persistente cache niet
XOVIS_TUNNEL_HOST
optioneel, standaard api.xovis.cloud
BACKUP_CRON_SECRET
beveiligt de geplande back-upaanroep; moet gelijk zijn aan het Vault-secret backup_cron_secret
SUPABASE_URL
en SUPABASE_PUBLISHABLE_KEY — sessiecontrole; client-side de VITE_-varianten

Lokaal draaien

npm install
npm run dev      # Vite dev server
npm run build    # productiebuild
npm run lint     # ESLint
npm run format   # Prettier

Deze handleiding aanpassen

src/lib/handleiding/content.json   # enige bron / single source
node scripts/build-handleiding-pdf.mjs   # → PDF (NL + EN)

De pagina in de app en de PDF's voor SharePoint lezen exact hetzelfde bestand. Pas je de tekst aan, draai dan het script opnieuw en vervang de PDF's op de drive. routeTree.gen.ts is gegenereerd — nooit met de hand aanpassen.

11Aandachtspunten voor wie verderbouwt

  • De domeinbeperking is enkel client-side. ALLOWED_DOMAINS in auth.tsx houdt niemand tegen die rechtstreeks tegen de auth-API registreert. De Data Repair Tool lost dit op met een trigger validate_email_domain() op auth.users; diezelfde aanpak is hier over te nemen.
  • De back-upronde kan tegen een uitvoeringslimiet lopen. Per toestel wordt tot 20 keer gepolld met 3 s pauze, sequentieel over alle toestellen. Bij een groeiende vloot loopt één aanvraag makkelijk minuten en riskeert ze afgekapt te worden. Overweeg de ronde op te splitsen in batches of per toestel te plannen, met voortgang in device_backup_log.
  • Push-foutdetectie is een tekstzoekactie. detectPushFailures() zet het antwoord om naar tekst en zoekt op “fail” of “error”. Dat is bewust ruim, maar het geeft zowel valse alarmen als gemiste fouten. Lees liever de echte velden per firmwareversie uit.
  • Het restore-endpoint is een vermoeden. Terugzetten probeert PUT device/backup en valt terug op POST device/backup/restore. Bevestig dit tegen de firmwaredocumentatie voor je erop vertrouwt bij een echte herstelactie.
  • Logs groeien onbeperkt. device_backup_log en device_health_snapshot worden nooit opgeruimd, en /api/backup-status haalt maximaal 300 rijen op. Bij veel toestellen verdwijnen oudere toestellen daardoor stil uit het overzicht “laatste back-up per device”. Een opruimtaak plus een query per toestel lost beide op.
  • Twee ingangen voor dezelfde back-upronde. /api/backup-all-devices (sessie óf secret) en /api/public/backup-all-devices (enkel secret) bevatten bijna dezelfde code. Samenvoegen tot één handler met twee auth-paden scheelt onderhoud.
  • Er zijn geen geautomatiseerde tests. De logische eerste zijn de pure functies: de projectie in SceneOverlay, findPoints()/normalizeType() in de scèneroute en issuesFor() voor de health-drempels.
  • .env staat mee in de repository en wordt niet door .gitignore uitgesloten. Nu zijn dat enkel de publieke Supabase-waarden, maar een toekomstig secret belandt er ongemerkt in.
  • Het token is een gedeelde bottleneck. Vijf aanvragen per dag voor het hele account: raak src/lib/xovis/token.ts niet aan zonder de dubbele cache te begrijpen, en voeg nooit een tweede tokenpad toe.

12Woordenlijst

TermBetekenis
HUBHet beheerplatform van Xovis waar onze sensoren aan hangen.
TunnelDe doorgang via de HUB waarmee je een sensor bevraagt zonder rechtstreekse netwerktoegang.
MAC-adresHet unieke hardware-adres dat hier als identificatie van een toestel dient.
Single- / multisensorEén toestel, of een groep toestellen die samen als één geheel tellen. De tool detecteert dit zelf.
LogicEen telconfiguratie op de sensor, gekoppeld aan een lijn of zone.
GeometrieDie lijn of zone zelf, uitgedrukt in coördinaten.
Tracking areaHet gebied waarbinnen de sensor mensen volgt.
SCENE / VIEWRechtgetrokken bovenaanzicht versus ruw camerabeeld — bepaalt hoe exact de overlays vallen.
TiltDe montagehoek. Wijkt de gemeten hoek af van de ingestelde, dan is de sensor mogelijk verschoven.
Push-agentHet onderdeel op de sensor dat tellingen naar buiten stuurt.
.xbakHet versleutelde back-upbestand met de configuratie van een toestel.