diff --git a/server/src/osc.ts b/server/src/osc.ts index b817602..c0abc09 100644 --- a/server/src/osc.ts +++ b/server/src/osc.ts @@ -21,6 +21,12 @@ export class OSC { static deviceStatus: { [deviceId: number]: number | false; } = {}; + static deviceResendStatus: { + [deviceId: number]: { + deviceNewStatus: number | false; + deviceSendStatusInterval: NodeJS.Timeout; + }; + } = {}; static devicePingChecks: { [deviceId: number]: { lastPingReceivedTimestamp: number; @@ -44,20 +50,28 @@ export class OSC { OSC.devicePingChecks[deviceId].lastPingReceivedTimestamp = Date.now(); if (OSC.deviceStatus[deviceId] !== msg[1]) { + // Device status has changed! Updated OSC.deviceStatus[deviceId] = msg[1]; - OSC.messageDevice( - deviceId, - "/cueb/outstationState", - OSC.deviceStatus[deviceId] - ); // Echo the state back to the device to confirm it was received + if ( + OSC.deviceResendStatus[deviceId]["deviceNewStatus"] == msg[1] + ) { + // Status has been set to what we wanted, and device replied to let us know, so we can stop pestering the device + OSC.deviceResendStatus[deviceId]["deviceNewStatus"] = false; + } eventEmitter.emit("trpc.deviceStatus"); } + OSC.messageDevice( + deviceId, + "/cueb/outstationState", + OSC.deviceStatus[deviceId] + ); // Echo the state back to the device to confirm it was received } } else if (msg[0] == "/cueb/pong" && msg.length === 2) { const deviceId = OSC.ipsToDevices[rinfo.address]; if (deviceId) { OSC.devicePingChecks[deviceId].lastPingReceivedTimestamp = Date.now(); + console.log("Pong from device", deviceId); } } else { console.log("Unknown message", msg, rinfo); @@ -77,28 +91,61 @@ export class OSC { OSC.ipsToDevices[ip] = deviceId; OSC.deviceStatus[deviceId] = false; OSC.devices[deviceId] = createOSCClient(ip, port); - + OSC.deviceResendStatus[deviceId] = { + deviceNewStatus: false, + deviceSendStatusInterval: setInterval(() => { + // Resend the device its status if it has not sent it back + if ( + OSC.deviceResendStatus[deviceId]["deviceNewStatus"] !== + OSC.deviceStatus[deviceId] && + OSC.deviceStatus[deviceId] !== false && + OSC.deviceResendStatus[deviceId]["deviceNewStatus"] !== false + ) { + console.log( + "Resending status to device", + OSC.deviceStatus[deviceId], + OSC.deviceResendStatus[deviceId]["deviceNewStatus"] + ); + OSC.messageDevice( + deviceId, + "/cueb/outstationState", + OSC.deviceResendStatus[deviceId]["deviceNewStatus"] + ); + } + }, 200), + }; OSC.devicePingChecks[deviceId] = { sendInterval: setInterval(() => { // Devices timeout after 5000ms of no pings received from a server if ( Date.now() - OSC.devicePingChecks[deviceId].lastPingSentTimestamp >= - 3500 + 3000 || + Date.now() - + OSC.devicePingChecks[deviceId].lastPingReceivedTimestamp >= + 3000 ) { OSC.messageDevice(deviceId, "/cueb/ping"); + console.log("Pinging device", deviceId); } - }, 500), + }, 200), checkInterval: setInterval(() => { - // We should mark a device as timed out if we haven't heard from it for 4 seconds - it should be transmitting its state every 2 seconds + // We should mark a device as timed out if we haven't heard from it for 5 seconds if ( Date.now() - OSC.devicePingChecks[deviceId].lastPingReceivedTimestamp > - 4000 + 5000 ) { - OSC.deviceStatus[deviceId] = false; - eventEmitter.emit("trpc.deviceStatus"); + if (OSC.deviceStatus[deviceId] != false) { + console.log("Device timed out", deviceId); + OSC.deviceStatus[deviceId] = false; + eventEmitter.emit("trpc.deviceStatus"); + } } - }, 500), + // Similarly, if we're not sure what a device's state is (it probably just joined the network) then we should ask it - it'll reply with it + if (OSC.deviceStatus[deviceId] === false) { + OSC.messageDevice(deviceId, "/cueb/outstationState"); + } + }, 200), lastPingReceivedTimestamp: 0, lastPingSentTimestamp: 0, }; @@ -116,6 +163,12 @@ export class OSC { } } } + static setStatus(deviceId: number, status: number) { + if (status !== OSC.deviceStatus[deviceId]) { + OSC.deviceResendStatus[deviceId]["deviceNewStatus"] = status; + OSC.messageDevice(deviceId, "/cueb/outstationState", status); + } + } static messageDevice( deviceId: number, message: string, diff --git a/server/src/server.ts b/server/src/server.ts index b4f25d5..40b8acd 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -124,7 +124,7 @@ const devicesRouter = router({ setState: publicProcedure .input(z.object({ id: z.number(), newState: z.number() })) .mutation(({ input }) => { - OSC.messageDevice(input.id, "/cueb/outstationState", input.newState); + OSC.setStatus(input.id, input.newState); return {}; }), scanForDevices: publicProcedure.mutation(() => {