From 80ff81b1e0f71340896a4b5511fabe101c992d0f Mon Sep 17 00:00:00 2001 From: Kostas Drakontidis Date: Mon, 25 May 2026 05:57:35 +0300 Subject: [PATCH] room occupancy impl & race cond fix & local user roomID and psuedoID storage for cleanup and indexing --- src/officesense_pi/src/core/analyze.ts | 185 ++++++++++++++++--------- 1 file changed, 118 insertions(+), 67 deletions(-) diff --git a/src/officesense_pi/src/core/analyze.ts b/src/officesense_pi/src/core/analyze.ts index 4462be7..5159c1b 100644 --- a/src/officesense_pi/src/core/analyze.ts +++ b/src/officesense_pi/src/core/analyze.ts @@ -1,7 +1,8 @@ -import { getRedis } from "./redis.js"; +import { getRedis } from "../redis/redis.js"; import { prisma } from "../lib/prisma.js"; import { RoomTransition, transitions } from "./transition.js"; import config from "../config/config.js"; +import { type RedisUserData } from "../api/livedata/livedata.repository.js"; const adjectives = [ "Crazy", @@ -23,6 +24,31 @@ const nouns = [ "Phoenix" ]; +const running = new Set(); +const userRooms = new Map(); +const userPseudo = new Map(); + +export async function updateRoomOccupancyListener(message: string, channel: string) { + console.log(`[Redis] Key ${message} expired.`); + + const id = message.startsWith("user:") ? message.slice(5) : message; + const lastRoomID = userRooms.get(id); + const userPseudoID = userPseudo.get(id); + + const redis = getRedis(); + + if (!lastRoomID) { + console.log("[!] Key not found in local mapι") + } else { + await redis.decr(`room:${lastRoomID}`); + } + + await redis.del(`_user:${userPseudoID}`); + + userRooms.delete(id); + userPseudo.delete(id); +} + function generateNickname() { const adjective = adjectives[Math.floor(Math.random() * adjectives.length)]; @@ -46,77 +72,102 @@ export async function analyzeData(roomID: string, metrics: { tagID: string, rssi: number }) { - console.log(`\n=======================================================\n` + - `ROOM_ID: ${roomID}\nTAG_ID: ${metrics.tagID}\nRSSI: ${metrics.rssi}\n` + - `=======================================================\n`); - - const userID = ( - await prisma.tag.findUnique({ - where: { id: metrics.tagID }, - select: { userId: true }, - }) - )?.userId; - - console.log(`[SQLite] Resolved USER_ID: ${userID}`); - - if (!userID) - return; - - const redis = getRedis(); - const res = await redis.get(`user:${userID}`); - - if (!res) { - const user = newUser(); - console.log(`[Redis] Created USER: ${user.psuedoName} USER_ID: ${user.pseudoID}`); - - // trigger camera - - await redis.set(`user:${userID}`, JSON.stringify({ - userID: user.pseudoID, - name: user.psuedoName, - rssi: metrics.rssi, - room: roomID, - verified: false, - timestamp: Date.now() - }), { PX: config.core.userTTL }); - - console.log("[Redis] Stored new user in redis."); - + if (running.has(metrics.tagID)) { + console.log(`[!] Skipping TAG_ID: ${metrics.tagID} already in process.`); return; } - let resObj = JSON.parse(res); - console.log(`[Redis] User exists in redis in ROOM_ID: ${resObj["room"]}`); + running.add(metrics.tagID); - if (roomID == resObj["room"]) { - console.log("[Redis] Same room, updating redis..."); - resObj["rssi"] = metrics.rssi; - resObj["timestamp"] = Date.now() + try { + console.log(`\n=======================================================\n` + + `ROOM_ID: ${roomID}\nTAG_ID: ${metrics.tagID}\nRSSI: ${metrics.rssi}\n` + + `=======================================================\n`); - await redis.set(`user:${userID}`, JSON.stringify(resObj), { PX: config.core.userTTL }); + const userID = ( + await prisma.tag.findUnique({ + where: { id: metrics.tagID }, + select: { userId: true }, + }) + )?.userId; - return; + console.log(`[SQLite] Resolved USER_ID: ${userID}`); + + if (!userID) + return; + + const redis = getRedis(); + const res = await redis.get(`user:${userID}`); + + if (!res) { + const user = newUser(); + console.log(`[Redis] Created USER: ${user.psuedoName} USER_ID: ${user.pseudoID}`); + + // trigger camera + + await redis.set(`user:${userID}`, JSON.stringify({ + userID: user.pseudoID, + name: user.psuedoName, + rssi: metrics.rssi, + room: roomID, + verified: false, + timestamp: Date.now() + }), { PX: config.core.userTTL }); + + await redis.set(`_user:${user.pseudoID}`, userID); + + await redis.incr(`room:${roomID}`); + + userRooms.set(userID, roomID); + userPseudo.set(userID, user.pseudoID); + + console.log("[Redis] Stored new user in redis."); + + return; + } + + let resObj: RedisUserData = JSON.parse(res); + console.log(`[Redis] User exists in redis in ROOM_ID: ${resObj["room"]}`); + + if (roomID == resObj["room"]) { + console.log("[Redis] Same room, updating redis..."); + resObj["rssi"] = metrics.rssi; + resObj["timestamp"] = Date.now() + + await redis.set(`user:${userID}`, JSON.stringify(resObj), { PX: config.core.userTTL }); + + return; + } + + let transition = transitions.get(userID); + + // update room occupancy + + if (!transition) { + console.log("[!] Starting new transition"); + transition = new RoomTransition(); + transitions.set(userID, transition); + } + + if (transition.shouldTransitionTo(roomID, metrics.rssi, resObj["rssi"], resObj["timestamp"])) { + // trigger camera + + console.log(`[!] Transition done ${resObj["room"]} -> ${roomID}`); + + await redis.decr(`room:${resObj["room"]}`); + await redis.incr(`room:${roomID}`); + + userRooms.set(userID, roomID); + + resObj["rssi"] = metrics.rssi; + resObj["room"] = roomID; + resObj["timestamp"] = Date.now() + + + await redis.set(`user:${userID}`, JSON.stringify(resObj), { PX: config.core.userTTL }); + } else + console.log("[!] Transition declined"); + } finally { + running.delete(metrics.tagID); } - - let transition = transitions.get(userID); - - if (!transition) { - console.log("[!] Starting new transition"); - transition = new RoomTransition(); - transitions.set(userID, transition); - } - - if (transition.shouldTransitionTo(roomID, metrics.rssi, resObj["rssi"], resObj["timestamp"])) { - // trigger camera - - console.log(`[!] Transition done ${resObj["room"]} -> ${roomID}`); - - resObj["rssi"] = metrics.rssi; - resObj["room"] = roomID; - resObj["timestamp"] = Date.now() - - - await redis.set(`user:${userID}`, JSON.stringify(resObj), { PX: config.core.userTTL }); - } else - console.log("[!] Transition declined"); } \ No newline at end of file