UNPKG

@neurosity/sdk

Version:
44 lines (43 loc) 2.31 kB
import { combineLatest, timer } from "rxjs"; import { map, startWith, switchMap } from "rxjs/operators"; import { withLatestFrom, distinctUntilChanged } from "rxjs/operators"; import isEqual from "fast-deep-equal"; import { STATUS } from "../types/status"; const HEARTBEAT_UPDATE_INTERVAL = 30000; // 30 seconds - set by the OS const LOST_LOCAL_HEARTBEAT_AFTER = HEARTBEAT_UPDATE_INTERVAL * 2.5; // 75 seconds const LOST_REMOTE_HEARTBEAT_AFTER = 8.64e7; // 24 hours export function heartbeatAwareStatus(status$) { const lastLocalHeartbeat$ = status$.pipe(map(({ lastHeartbeat }) => lastHeartbeat), distinctUntilChanged(), map(() => Date.now())); const lostHeartbeat$ = lastLocalHeartbeat$.pipe(switchMap(() => timer(LOST_LOCAL_HEARTBEAT_AFTER)), map(() => null), startWith(null)); return combineLatest({ status: status$, lostHeartbeat: lostHeartbeat$ // @important - do not remove, adeed for state synchronization, value not used }).pipe(withLatestFrom(lastLocalHeartbeat$), map(([{ status }, lastLocalHeartbeat]) => { if (!lastLocalHeartbeat) { return status; } const lostHeartbeat = deviceHasLostHeartbeat(status, lastLocalHeartbeat); return lostHeartbeat ? Object.assign(Object.assign({}, status), { state: STATUS.OFFLINE }) : status; }), distinctUntilChanged((a, b) => isEqual(a, b))); } export function deviceHasLostHeartbeat(status, lastLocalHeartbeat) { if (!("lastHeartbeat" in status)) { return false; } // We are converting the heartbeat to the local time because the previous // implementation that used the server timestamp had bug where SDK clients // running on hardware with drifted/out-of-sync clocks (cough cough Android) // would override the state to offline when the heartbeat was active. const lostLocalHeartbeat = Date.now() - lastLocalHeartbeat > LOST_LOCAL_HEARTBEAT_AFTER; if (lostLocalHeartbeat) { return true; } // Addresses devices with wrongful "online" state. This rarely happens, the // OS would have to crash without updating the state to "offline". const lostRemoteHeartbeat = Date.now() - status.lastHeartbeat > LOST_REMOTE_HEARTBEAT_AFTER; if (lostRemoteHeartbeat) { return true; } return false; }