Skip to content

Commit

Permalink
Merge pull request #100 from the-orange-alliance/aud-disp-manager
Browse files Browse the repository at this point in the history
Add audience display manager
  • Loading branch information
kyle-flynn authored Oct 6, 2023
2 parents 047bf76 + ae866f4 commit 651831f
Show file tree
Hide file tree
Showing 17 changed files with 625 additions and 8 deletions.
22 changes: 22 additions & 0 deletions back-end/api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions back-end/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@types/passport-jwt": "^3.0.9",
"@types/passport-local": "^1.0.35",
"@types/sqlite3": "^3.1.8",
"@types/uuid": "^9.0.4",
"@vercel/ncc": "^0.36.1",
"editorconfig": "^2.0.0",
"eslint": "^8.45.0",
Expand All @@ -44,6 +45,7 @@
"promised-sqlite3": "^2.1.0",
"socket.io": "^4.7.1",
"sqlite3": "^5.1.6",
"uuid": "^9.0.1",
"winston": "^3.10.0"
}
}
14 changes: 14 additions & 0 deletions back-end/api/sql/create_global.sql
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,17 @@ CREATE TABLE IF NOT EXISTS "event" (
PRIMARY KEY (eventKey),
UNIQUE (eventKey)
);

CREATE TABLE IF NOT EXISTS "socket_clients" (
"currentUrl" VARCHAR(255),
"ipAddress" VARCHAR(64) NOT NULL,
"fieldNumbers" VARCHAR(255),
"audienceDisplayChroma" VARCHAR(255),
"followerMode" INT NOT NULL,
"followerApiHost" VARCHAR(64),
"lastSocketId" VARCHAR(64),
"connected" INT NOT NULL,
"persistantClientId" VARCHAR(64) NOT NULL,
PRIMARY KEY (ipAddress, lastSocketId),
UNIQUE (ipAddress, lastSocketId)
);
2 changes: 2 additions & 0 deletions back-end/api/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import allianceController from './controllers/Alliance.js';
import tournamentController from './controllers/Tournament.js';
import frcFmsController from './controllers/FrcFms.js';
import resultsController from './controllers/Results.js';
import socketClientsController from './controllers/SocketClients.js';
import { handleCatchAll, handleErrors } from './middleware/ErrorHandler.js';
import logger from './util/Logger.js';
import { initGlobal } from './db/EventDatabase.js';
Expand Down Expand Up @@ -69,6 +70,7 @@ app.use('/alliance', allianceController);
app.use('/tournament', tournamentController);
app.use('/frc/fms', frcFmsController);
app.use('/results', resultsController);
app.use('/socketClients', socketClientsController);

// Define root/testing paths
app.get('/', requireAuth, (req, res) => {
Expand Down
108 changes: 108 additions & 0 deletions back-end/api/src/controllers/SocketClients.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { NextFunction, Request, Response, Router } from 'express';
import { getDB } from '../db/EventDatabase.js';
import { v4 as uuidv4 } from 'uuid';

const router = Router();

// Get all displays
router.get(
'/',
async (req: Request, res: Response, next: NextFunction) => {
try {
const db = await getDB('global');
const clients = await db.selectAll(
'socket_clients'
);
res.json(clients);
} catch (e) {
next(e);
}
}
);

// New client
router.post(
'/connect',
async (req: Request, res: Response, next: NextFunction) => {
try {
// Check if client already exists
const db = await getDB('global');
if (req.body.persistantClientId) {
req.body.connected = 1;
// Update
req.body.audienceDisplayChroma = req.body.audienceDisplayChroma.replaceAll('"', '')
await db.updateWhere(
'socket_clients',
req.body,
`persistantClientId = "${req.body.persistantClientId}"`
);
} else {
// Brand new client, create new UUID
req.body.persistantClientId = uuidv4();
await db.insertValue('socket_clients', [req.body]);
}

res.json(req.body);
} catch (e) {
next(e);
}
}
);

// Client Disconnected
router.post(
'/update/:persistantClientId',
async (req: Request, res: Response, next: NextFunction) => {
const { persistantClientId } = req.params;
try {
const db = await getDB('global');
await db.updateWhere(
'socket_clients',
req.body,
`persistantClientId = "${persistantClientId}"`
);
res.json({ success: true });
} catch (e) {
next(e);
}
}
);

// Client Deleted
router.post(
'/disconnect/:lastSocketId',
async (req: Request, res: Response, next: NextFunction) => {
const { lastSocketId } = req.params;
try {
const db = await getDB('global');
await db.updateWhere(
'socket_clients',
{ connected: 0 },
`lastSocketId = "${lastSocketId}"`
);
res.json({ success: true });
} catch (e) {
next(e);
}
}
);

// Client Deleted
router.delete(
'/remove/:persistantClientId',
async (req: Request, res: Response, next: NextFunction) => {
const { persistantClientId } = req.params;
try {
const db = await getDB('global');
await db.deleteWhere(
'socket_clients',
`persistantClientId = "${persistantClientId}"`
);
res.json({ success: true });
} catch (e) {
next(e);
}
}
);

export default router;
30 changes: 30 additions & 0 deletions back-end/realtime/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions back-end/realtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"typescript": "^5.1.6"
},
"dependencies": {
"@toa-lib/client": "file:../../lib/client",
"@toa-lib/models": "file:../../lib/models",
"@toa-lib/server": "file:../../lib/server",
"body-parser": "^1.20.2",
Expand Down
60 changes: 58 additions & 2 deletions back-end/realtime/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import jwt from "jsonwebtoken";
import { environment as env, getIPv4 } from "@toa-lib/server";
import logger from "./util/Logger.js";
import { assignRooms, initRooms, leaveRooms } from "./rooms/Rooms.js";
import { clientFetcher } from '@toa-lib/client';

// Setup our environment
env.loadAndSetDefaults(process.env);
Expand Down Expand Up @@ -46,6 +47,57 @@ io.on("connection", (socket) => {
`user '${user.username}' (${socket.handshake.address}) connected and verified`
);

socket.on("identify", async (data: any) => {
try {
// Add things
data.lastSocketId = socket.id;
data.connected = 1;
data.ipAddress = socket.handshake.address;

// Register socket to Database // TODO: Create model for this
const settings: any = await clientFetcher(`socketClients/connect`, 'POST', data);

// Send back the user's data
socket.emit("settings", settings);
} catch (e) {
console.log('Failed to negotiate sockets settings', e);
}
});

socket.on("update-socket-client", async (data: any) => {
// Update socket client
try {
// Update Server
await clientFetcher(`socketClients/connect`, 'POST', data);
// Locate socket by lastSocketId
const socketToUpdate = io.sockets.sockets.get(data.lastSocketId);
// Update socket
socketToUpdate?.emit("settings", data);

} catch (e) {
console.log('Failed to update socket client', e);
}
});

socket.on("identify-client", async (data: any) => {
// Find socket
const socketToIdentify = io.sockets.sockets.get(data.lastSocketId);
// Emit message
socketToIdentify?.emit("identify-client", data);
});

socket.on("identify-all-clients", async () => {
// Get all devices from api
const clients: any = await clientFetcher(`socketClients`, 'GET');
// Iterate over devices
clients.forEach((client: any) => {
// Find socket
const socketToIdentify = io.sockets.sockets.get(client.lastSocketId);
// Emit message
socketToIdentify?.emit("identify-client", client);
});
})

socket.on("rooms", (rooms: unknown) => {
if (Array.isArray(rooms) && rooms.every(room => typeof room === "string")) {
logger.info(
Expand All @@ -63,6 +115,11 @@ io.on("connection", (socket) => {
logger.info(
`user ${user.username} (${socket.handshake.address}) disconnected: ${reason}`
);
try {
clientFetcher(`socketClients/disconnect/${socket.id}`, 'POST');
} catch (e) {
console.log('Failed update socket db info', e);
}
leaveRooms(socket);
});

Expand All @@ -85,8 +142,7 @@ server.listen(
logger.info(
`[${env.get().nodeEnv.charAt(0).toUpperCase()}][${env
.get()
.serviceName.toUpperCase()}] Server started on ${host}:${
env.get().servicePort
.serviceName.toUpperCase()}] Server started on ${host}:${env.get().servicePort
}`
);
}
Expand Down
Loading

0 comments on commit 651831f

Please sign in to comment.