TODO: Insert QOS Ping ID into dispatchNetworkUp/Down()
parent
9599f14a98
commit
9858fa343b
|
@ -31,8 +31,6 @@ import { dispatchNetworkUp, dispatchNetworkDown } from "../connectivity";
|
|||
import { Session } from "../session";
|
||||
import { error } from "../toast/toast";
|
||||
|
||||
const A_STRING = expect.any(String);
|
||||
|
||||
interface FakeProps {
|
||||
uuid: string;
|
||||
method: Method;
|
||||
|
@ -67,7 +65,7 @@ describe("responseRejected", () => {
|
|||
it("undefined error", async () => {
|
||||
await expect(responseRejected(undefined)).rejects.toEqual(undefined);
|
||||
expect(dispatchNetworkUp).not.toHaveBeenCalled();
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("user.api", undefined, A_STRING);
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("user.api");
|
||||
});
|
||||
|
||||
it("safe error", async () => {
|
||||
|
@ -77,7 +75,7 @@ describe("responseRejected", () => {
|
|||
};
|
||||
await expect(responseRejected(safeError)).rejects.toEqual(safeError);
|
||||
expect(dispatchNetworkDown).not.toHaveBeenCalled();
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.api", undefined, A_STRING);
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.api");
|
||||
});
|
||||
|
||||
it("handles 500", async () => {
|
||||
|
|
|
@ -42,7 +42,6 @@ import { MessageType } from "../../../sequences/interfaces";
|
|||
import { FbjsEventName } from "farmbot/dist/constants";
|
||||
import { info, error, success, warning, fun, busy } from "../../../toast/toast";
|
||||
|
||||
const A_STRING = expect.any(String);
|
||||
describe("readStatus()", () => {
|
||||
it("forces a read_status request to FarmBot", () => {
|
||||
readStatus();
|
||||
|
@ -160,9 +159,9 @@ describe("initLog", () => {
|
|||
|
||||
describe("bothUp()", () => {
|
||||
it("marks MQTT and API as up", () => {
|
||||
bothUp("tests");
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.mqtt", undefined, A_STRING);
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("bot.mqtt", undefined, A_STRING);
|
||||
bothUp();
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.mqtt");
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("bot.mqtt");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -170,7 +169,7 @@ describe("onOffline", () => {
|
|||
it("tells the app MQTT is down", () => {
|
||||
jest.resetAllMocks();
|
||||
onOffline();
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("user.mqtt", undefined, A_STRING);
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("user.mqtt");
|
||||
expect(error).toHaveBeenCalledWith(Content.MQTT_DISCONNECTED);
|
||||
});
|
||||
});
|
||||
|
@ -179,7 +178,7 @@ describe("onOnline", () => {
|
|||
it("tells the app MQTT is up", () => {
|
||||
jest.resetAllMocks();
|
||||
onOnline();
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.mqtt", undefined, A_STRING);
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.mqtt");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -205,13 +204,13 @@ describe("onSent", () => {
|
|||
it("marks MQTT as up", () => {
|
||||
jest.resetAllMocks();
|
||||
onSent({ connected: true })();
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.mqtt", undefined, A_STRING);
|
||||
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.mqtt");
|
||||
});
|
||||
|
||||
it("marks MQTT as down", () => {
|
||||
jest.resetAllMocks();
|
||||
onSent({ connected: false })();
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("user.mqtt", undefined, A_STRING);
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("user.mqtt");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -235,7 +234,7 @@ describe("onLogs", () => {
|
|||
log.message = "bot xyz is offline";
|
||||
fn(log);
|
||||
globalQueue.maybeWork();
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("bot.mqtt", undefined, A_STRING);
|
||||
expect(dispatchNetworkDown).toHaveBeenCalledWith("bot.mqtt");
|
||||
});
|
||||
|
||||
it("handles log fields correctly", () => {
|
||||
|
|
|
@ -14,29 +14,29 @@ const SHORT_TIME_LATER = new Date(NOW.getTime() + 500).getTime();
|
|||
const LONGER_TIME_LATER = new Date(NOW.getTime() + 5000).getTime();
|
||||
|
||||
describe("dispatchNetworkUp", () => {
|
||||
const NOW_UP = networkUp("bot.mqtt", NOW.getTime(), "tests");
|
||||
const LATER_UP = networkUp("bot.mqtt", LONGER_TIME_LATER, "tests");
|
||||
const NOW_UP = networkUp("bot.mqtt", NOW.getTime());
|
||||
const LATER_UP = networkUp("bot.mqtt", LONGER_TIME_LATER);
|
||||
|
||||
it("calls redux directly", () => {
|
||||
dispatchNetworkUp("bot.mqtt", NOW.getTime(), "tests");
|
||||
dispatchNetworkUp("bot.mqtt", NOW.getTime());
|
||||
expect(mockRedux.store.dispatch).toHaveBeenLastCalledWith(NOW_UP);
|
||||
dispatchNetworkUp("bot.mqtt", SHORT_TIME_LATER, "tests");
|
||||
dispatchNetworkUp("bot.mqtt", SHORT_TIME_LATER);
|
||||
expect(mockRedux.store.dispatch).toHaveBeenLastCalledWith(NOW_UP);
|
||||
dispatchNetworkUp("bot.mqtt", LONGER_TIME_LATER, "tests");
|
||||
dispatchNetworkUp("bot.mqtt", LONGER_TIME_LATER);
|
||||
expect(mockRedux.store.dispatch).toHaveBeenLastCalledWith(LATER_UP);
|
||||
});
|
||||
});
|
||||
|
||||
describe("dispatchNetworkDown", () => {
|
||||
const NOW_DOWN = networkDown("user.api", NOW.getTime(), "tests");
|
||||
const LATER_DOWN = networkDown("user.api", LONGER_TIME_LATER, "tests");
|
||||
const NOW_DOWN = networkDown("user.api", NOW.getTime());
|
||||
const LATER_DOWN = networkDown("user.api", LONGER_TIME_LATER);
|
||||
|
||||
it("calls redux directly", () => {
|
||||
dispatchNetworkDown("user.api", NOW.getTime(), "tests");
|
||||
dispatchNetworkDown("user.api", NOW.getTime());
|
||||
expect(mockRedux.store.dispatch).toHaveBeenLastCalledWith(NOW_DOWN);
|
||||
dispatchNetworkDown("user.api", SHORT_TIME_LATER, "tests");
|
||||
dispatchNetworkDown("user.api", SHORT_TIME_LATER);
|
||||
expect(mockRedux.store.dispatch).toHaveBeenLastCalledWith(NOW_DOWN);
|
||||
dispatchNetworkDown("user.api", LONGER_TIME_LATER, "tests");
|
||||
dispatchNetworkDown("user.api", LONGER_TIME_LATER);
|
||||
expect(mockRedux.store.dispatch).toHaveBeenLastCalledWith(LATER_DOWN);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
jest.mock("../index", () => ({
|
||||
dispatchNetworkDown: jest.fn(),
|
||||
dispatchNetworkUp: jest.fn()
|
||||
dispatchNetworkUp: jest.fn(),
|
||||
dispatchQosStart: jest.fn()
|
||||
}));
|
||||
|
||||
const mockTimestamp = 0;
|
||||
|
@ -49,14 +50,14 @@ function fakeBot(): Farmbot {
|
|||
|
||||
function expectStale() {
|
||||
expect(dispatchNetworkDown)
|
||||
.toHaveBeenCalledWith("bot.mqtt", undefined, expect.any(String));
|
||||
.toHaveBeenCalledWith("bot.mqtt");
|
||||
}
|
||||
|
||||
function expectActive() {
|
||||
expect(dispatchNetworkUp)
|
||||
.toHaveBeenCalledWith("bot.mqtt", undefined, expect.any(String));
|
||||
.toHaveBeenCalledWith("bot.mqtt");
|
||||
expect(dispatchNetworkUp)
|
||||
.toHaveBeenCalledWith("user.mqtt", undefined, expect.any(String));
|
||||
.toHaveBeenCalledWith("user.mqtt");
|
||||
}
|
||||
|
||||
describe("ping util", () => {
|
||||
|
@ -67,12 +68,12 @@ describe("ping util", () => {
|
|||
});
|
||||
|
||||
it("marks the bot's connection to MQTT as 'stale'", () => {
|
||||
markStale();
|
||||
markStale("TESTS");
|
||||
expectStale();
|
||||
});
|
||||
|
||||
it("marks the bot's connection to MQTT as 'active'", () => {
|
||||
markActive();
|
||||
markActive("TESTS");
|
||||
expectActive();
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { networkUp, networkDown } from "../actions";
|
|||
describe("connectivityReducer", () => {
|
||||
it("goes up", () => {
|
||||
const state = connectivityReducer(DEFAULT_STATE,
|
||||
networkUp("user.mqtt", undefined, "tests"));
|
||||
networkUp("user.mqtt"));
|
||||
expect(state).toBeDefined();
|
||||
const x = state && state.uptime["user.mqtt"];
|
||||
if (x) {
|
||||
|
@ -17,7 +17,7 @@ describe("connectivityReducer", () => {
|
|||
|
||||
it("goes down", () => {
|
||||
const state = connectivityReducer(DEFAULT_STATE,
|
||||
networkDown("user.api", undefined, "tests"));
|
||||
networkDown("user.api"));
|
||||
const x = state && state.uptime["user.api"];
|
||||
if (x) {
|
||||
expect(x.state).toBe("down");
|
||||
|
|
|
@ -5,10 +5,10 @@ import { ReduxAction } from "../redux/interfaces";
|
|||
type NetChange = ReduxAction<EdgeStatus>;
|
||||
|
||||
const change = (state: "up" | "down") =>
|
||||
(name: Edge, at = (new Date()).getTime(), why: string): NetChange => {
|
||||
(name: Edge, at = (new Date()).getTime(), qosPingId?: string): NetChange => {
|
||||
return {
|
||||
type: Actions.NETWORK_EDGE_CHANGE,
|
||||
payload: { name, status: { state, at }, why }
|
||||
payload: { name, status: { state, at }, qosPingId }
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ export class BatchQueue {
|
|||
store.dispatch(batchInitResources(this.queue));
|
||||
}
|
||||
this.clear();
|
||||
bothUp("Inside BatchQueue instance");
|
||||
bothUp();
|
||||
}
|
||||
|
||||
push = (resource: TaggedLog) => {
|
||||
|
|
|
@ -95,9 +95,9 @@ export const batchInitResources =
|
|||
return { type: Actions.BATCH_INIT, payload };
|
||||
};
|
||||
|
||||
export const bothUp = (why: string) => {
|
||||
dispatchNetworkUp("user.mqtt", undefined, why);
|
||||
dispatchNetworkUp("bot.mqtt", undefined, why);
|
||||
export const bothUp = () => {
|
||||
dispatchNetworkUp("user.mqtt");
|
||||
dispatchNetworkUp("bot.mqtt");
|
||||
};
|
||||
|
||||
export function readStatus() {
|
||||
|
@ -108,7 +108,7 @@ export function readStatus() {
|
|||
}
|
||||
|
||||
export const onOffline = () => {
|
||||
dispatchNetworkDown("user.mqtt", undefined, "onOffline() callback");
|
||||
dispatchNetworkDown("user.mqtt");
|
||||
error(t(Content.MQTT_DISCONNECTED));
|
||||
};
|
||||
|
||||
|
@ -117,7 +117,7 @@ export const changeLastClientConnected = (bot: Farmbot) => () => {
|
|||
"LAST_CLIENT_CONNECTED": JSON.stringify(new Date())
|
||||
}).catch(noop); // This is internal stuff, don't alert user.
|
||||
};
|
||||
const setBothUp = () => bothUp("Got a status message");
|
||||
const setBothUp = () => bothUp();
|
||||
|
||||
const legacyChecks = (getState: GetState) => {
|
||||
const { controller_version } = getState().bot.hardware.informational_settings;
|
||||
|
@ -150,13 +150,12 @@ type Client = { connected?: boolean };
|
|||
|
||||
export const onSent = (client: Client) => () => {
|
||||
const connected = !!client.connected;
|
||||
const why = `Outbound mqtt.js. client.connected = ${connected}`;
|
||||
const cb = connected ? dispatchNetworkUp : dispatchNetworkDown;
|
||||
cb("user.mqtt", undefined, why);
|
||||
cb("user.mqtt");
|
||||
};
|
||||
|
||||
export function onMalformed() {
|
||||
bothUp("Got a malformed message");
|
||||
bothUp();
|
||||
if (!HACKY_FLAGS.alreadyToldUserAboutMalformedMsg) {
|
||||
warning(t(Content.MALFORMED_MESSAGE_REC_UPGRADE));
|
||||
HACKY_FLAGS.alreadyToldUserAboutMalformedMsg = true;
|
||||
|
@ -166,11 +165,11 @@ export function onMalformed() {
|
|||
export const onOnline =
|
||||
() => {
|
||||
success(t("Reconnected to the message broker."), t("Online"));
|
||||
dispatchNetworkUp("user.mqtt", undefined, "MQTT.js is online");
|
||||
dispatchNetworkUp("user.mqtt");
|
||||
};
|
||||
export const onReconnect =
|
||||
() => warning(t("Attempting to reconnect to the message broker"),
|
||||
t("Offline"), "yellow");
|
||||
t("Offline"), "yellow");
|
||||
|
||||
export function onPublicBroadcast(payl: unknown) {
|
||||
console.log(FbjsEventName.publicBroadcast, payl);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { store } from "../redux/store";
|
||||
import { networkUp, networkDown } from "./actions";
|
||||
import { Edge } from "./interfaces";
|
||||
import { Actions } from "../constants";
|
||||
|
||||
/* ABOUT THIS FILE: These functions allow us to mark the network as up or
|
||||
down from anywhere within the app (even outside of React-Redux). I usually avoid
|
||||
|
@ -24,14 +25,23 @@ function bumpThrottle(edge: Edge, now: number) {
|
|||
lastCalledAt[edge] = now;
|
||||
}
|
||||
|
||||
export let dispatchNetworkUp = (edge: Edge, at = (new Date()).getTime(), why: string) => {
|
||||
export const dispatchQosStart = (id: string) => {
|
||||
store.dispatch({
|
||||
type: Actions.START_QOS_PING,
|
||||
payload: { id }
|
||||
});
|
||||
};
|
||||
|
||||
export let dispatchNetworkUp = (edge: Edge, at = (new Date()).getTime()) => {
|
||||
console.log("TODO: Insert ID HERE");
|
||||
if (shouldThrottle(edge, at)) { return; }
|
||||
store.dispatch(networkUp(edge, at, why));
|
||||
store.dispatch(networkUp(edge, at));
|
||||
bumpThrottle(edge, at);
|
||||
};
|
||||
|
||||
export let dispatchNetworkDown = (edge: Edge, at = (new Date()).getTime(), why: string) => {
|
||||
export let dispatchNetworkDown = (edge: Edge, at = (new Date()).getTime()) => {
|
||||
console.log("TODO: Insert ID HERE");
|
||||
if (shouldThrottle(edge, at)) { return; }
|
||||
store.dispatch(networkDown(edge, at, why));
|
||||
store.dispatch(networkDown(edge, at));
|
||||
bumpThrottle(edge, at);
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface ConnectionStatus {
|
|||
export interface EdgeStatus {
|
||||
name: Edge;
|
||||
status: ConnectionStatus;
|
||||
why: string;
|
||||
qosPingId?: string;
|
||||
}
|
||||
|
||||
/** Name of a connection between two points. "." can be read as "to".
|
||||
|
|
|
@ -49,6 +49,6 @@ export const onLogs =
|
|||
// TODO: Make a `bot/device_123/offline` channel.
|
||||
const died =
|
||||
msg.message.includes("is offline") && msg.type === MessageType.error;
|
||||
died && dispatchNetworkDown("bot.mqtt", undefined, "Got offline message");
|
||||
died && dispatchNetworkDown("bot.mqtt");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { Farmbot } from "farmbot";
|
||||
import { dispatchNetworkDown, dispatchNetworkUp } from "./index";
|
||||
import { Farmbot, uuid } from "farmbot";
|
||||
import {
|
||||
dispatchNetworkDown,
|
||||
dispatchNetworkUp,
|
||||
dispatchQosStart
|
||||
} from "./index";
|
||||
import { isNumber } from "lodash";
|
||||
import axios from "axios";
|
||||
import { API } from "../api/index";
|
||||
|
@ -17,13 +21,15 @@ export function readPing(bot: Farmbot, direction: Direction): number | undefined
|
|||
return isNumber(val) ? val : undefined;
|
||||
}
|
||||
|
||||
export function markStale() {
|
||||
dispatchNetworkDown("bot.mqtt", undefined, "markStale()");
|
||||
export function markStale(_uuid: string) {
|
||||
// dispatch({ pings: failPing(this.pingState, id) })
|
||||
dispatchNetworkDown("bot.mqtt");
|
||||
}
|
||||
|
||||
export function markActive() {
|
||||
dispatchNetworkUp("user.mqtt", undefined, "markActive()");
|
||||
dispatchNetworkUp("bot.mqtt", undefined, "markActive()");
|
||||
export function markActive(_uuid: string) {
|
||||
// dispatch({ pings: completePing(this.pingState, id) })
|
||||
dispatchNetworkUp("user.mqtt");
|
||||
dispatchNetworkUp("bot.mqtt");
|
||||
}
|
||||
|
||||
export function isInactive(last: number, now: number): boolean {
|
||||
|
@ -31,8 +37,12 @@ export function isInactive(last: number, now: number): boolean {
|
|||
}
|
||||
|
||||
export function sendOutboundPing(bot: Farmbot) {
|
||||
console.log("TODO");
|
||||
bot.ping().then(markActive, markStale);
|
||||
const id = uuid();
|
||||
const ok = () => markActive(id);
|
||||
const no = () => markStale(id);
|
||||
dispatchQosStart(id);
|
||||
setTimeout(no, PING_INTERVAL);
|
||||
bot.ping().then(ok, no);
|
||||
}
|
||||
|
||||
export function startPinging(bot: Farmbot) {
|
||||
|
@ -41,7 +51,7 @@ export function startPinging(bot: Farmbot) {
|
|||
}
|
||||
|
||||
export function pingAPI() {
|
||||
const ok = () => dispatchNetworkUp("user.api", undefined, "pingApi OK");
|
||||
const no = () => dispatchNetworkDown("user.api", undefined, "pingApi NO");
|
||||
const ok = () => dispatchNetworkUp("user.api");
|
||||
const no = () => dispatchNetworkDown("user.api");
|
||||
return axios.get(API.current.devicePath).then(ok, no);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { computeBestTime } from "./reducer_support";
|
|||
import { TaggedDevice } from "farmbot";
|
||||
import { SyncBodyContents } from "../sync/actions";
|
||||
import { arrayUnwrap } from "../resources/util";
|
||||
import { startPing, completePing, failPing } from "../devices/connectivity/qos";
|
||||
|
||||
export const DEFAULT_STATE: ConnectionState = {
|
||||
uptime: {
|
||||
|
@ -18,8 +19,24 @@ export const DEFAULT_STATE: ConnectionState = {
|
|||
|
||||
export let connectivityReducer =
|
||||
generateReducer<ConnectionState>(DEFAULT_STATE)
|
||||
.add<{ id: string }>(Actions.START_QOS_PING, (s, { payload }) => {
|
||||
return {
|
||||
...s,
|
||||
pings: startPing(s.pings, payload.id)
|
||||
};
|
||||
})
|
||||
.add<EdgeStatus>(Actions.NETWORK_EDGE_CHANGE, (s, { payload }) => {
|
||||
s.uptime[payload.name] = payload.status;
|
||||
const { qosPingId, status } = payload;
|
||||
if (qosPingId) {
|
||||
if (status.state == "up") {
|
||||
console.log("OK!!!");
|
||||
s.pings = completePing(s.pings, qosPingId, status.at);
|
||||
} else {
|
||||
console.log("FAILED");
|
||||
s.pings = failPing(s.pings, qosPingId);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
})
|
||||
.add<SyncBodyContents<TaggedDevice>>(Actions.RESOURCE_READY, (s, a) => {
|
||||
|
@ -33,6 +50,7 @@ export let connectivityReducer =
|
|||
type Keys = (keyof ConnectionState["uptime"])[];
|
||||
const keys: Keys = ["bot.mqtt", "user.mqtt", "user.api"];
|
||||
keys.map(x => (s.uptime[x] = undefined));
|
||||
s.pings = {};
|
||||
|
||||
return s;
|
||||
});
|
||||
|
|
|
@ -994,5 +994,6 @@ export enum Actions {
|
|||
// Network
|
||||
NETWORK_EDGE_CHANGE = "NETWORK_EDGE_CHANGE",
|
||||
RESET_NETWORK = "RESET_NETWORK",
|
||||
SET_CONSISTENCY = "SET_CONSISTENCY"
|
||||
SET_CONSISTENCY = "SET_CONSISTENCY",
|
||||
START_QOS_PING = "START_QOS_PING"
|
||||
}
|
||||
|
|
|
@ -111,17 +111,17 @@ describe("botReducer", () => {
|
|||
step1.statusStash = "booting";
|
||||
step1.hardware.informational_settings.sync_status = "synced";
|
||||
|
||||
const step2 = botReducer(step1, networkDown("bot.mqtt", undefined, "tests"));
|
||||
const step2 = botReducer(step1, networkDown("bot.mqtt"));
|
||||
expect(step2.statusStash)
|
||||
.toBe(step1.hardware.informational_settings.sync_status);
|
||||
expect(step2.hardware.informational_settings.sync_status).toBeUndefined();
|
||||
|
||||
const step3 = botReducer(step1, networkDown("bot.mqtt", undefined, "tests"));
|
||||
const step3 = botReducer(step1, networkDown("bot.mqtt"));
|
||||
expect(step3.statusStash)
|
||||
.toBe(step1.hardware.informational_settings.sync_status);
|
||||
expect(step3.hardware.informational_settings.sync_status).toBeUndefined();
|
||||
|
||||
const step4 = botReducer(step3, networkUp("bot.mqtt", undefined, "tests"));
|
||||
const step4 = botReducer(step3, networkUp("bot.mqtt"));
|
||||
expect(step4.hardware.informational_settings.sync_status)
|
||||
.toBe(step3.statusStash);
|
||||
});
|
||||
|
|
|
@ -3,30 +3,32 @@ import { betterCompact } from "../../util";
|
|||
interface Pending {
|
||||
kind: "pending";
|
||||
id: string;
|
||||
start: Date;
|
||||
end?: undefined;
|
||||
start: number;
|
||||
end?: number;
|
||||
}
|
||||
|
||||
interface Timeout {
|
||||
kind: "timeout";
|
||||
id: string;
|
||||
start: Date;
|
||||
end?: undefined;
|
||||
start: number;
|
||||
end?: number;
|
||||
}
|
||||
|
||||
interface Complete {
|
||||
kind: "complete";
|
||||
id: string;
|
||||
start: Date;
|
||||
end: Date;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export type Ping = Complete | Pending | Timeout;
|
||||
export type PingDictionary = Record<string, Ping | undefined>;
|
||||
|
||||
const now = () => (new Date()).getTime();
|
||||
|
||||
export const startPing =
|
||||
(s: PingDictionary, id: string): PingDictionary => {
|
||||
return { ...s, [id]: { kind: "pending", id, start: new Date() } };
|
||||
(s: PingDictionary, id: string, start = now()): PingDictionary => {
|
||||
return { ...s, [id]: { kind: "pending", id, start } };
|
||||
};
|
||||
|
||||
export const failPing =
|
||||
|
@ -45,7 +47,7 @@ export const failPing =
|
|||
};
|
||||
|
||||
export const completePing =
|
||||
(s: PingDictionary, id: string): PingDictionary => {
|
||||
(s: PingDictionary, id: string, end = now()): PingDictionary => {
|
||||
const failure = s[id];
|
||||
if (failure && failure.kind == "pending") {
|
||||
return {
|
||||
|
@ -54,7 +56,7 @@ export const completePing =
|
|||
kind: "complete",
|
||||
id,
|
||||
start: failure.start,
|
||||
end: new Date()
|
||||
end
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -87,11 +89,8 @@ interface LatencyReport {
|
|||
total: number;
|
||||
}
|
||||
|
||||
const mapper = (p: Ping) => {
|
||||
if (p.kind === "complete") {
|
||||
return p.end.getTime() - p.start.getTime();
|
||||
}
|
||||
};
|
||||
const mapper = (p: Ping) => (p.kind === "complete") ?
|
||||
p.end - p.start : undefined;
|
||||
|
||||
export const calculateLatency =
|
||||
(s: PingDictionary): LatencyReport => {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
calculateLatency,
|
||||
calculatePingLoss,
|
||||
PingDictionary,
|
||||
} from "./qos";
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
pings: PingDictionary;
|
||||
}
|
||||
|
||||
export class QosRow extends React.Component<Props, {}> {
|
||||
get pingState(): PingDictionary { return this.props.pings; }
|
||||
|
||||
render() {
|
||||
const reportA = calculateLatency(this.pingState);
|
||||
const reportB = calculatePingLoss(this.pingState);
|
||||
const report = { ...reportA, ...reportB };
|
||||
const ber = ((report.complete || 0) / report.total) || 0;
|
||||
return <div>
|
||||
<br />
|
||||
<ul>
|
||||
<li>best: {report.best || 0}</li>
|
||||
<li>worst: {report.worst || 0}</li>
|
||||
<li>average: {(report.average || 0).toFixed(1)}</li>
|
||||
<li>Pings OK: {report.complete}</li>
|
||||
<li>Pings pending: {report.pending || 0}</li>
|
||||
<li>Pings failed: {report.timeout || 0}</li>
|
||||
<li>Total pings: {report.total || 0}</li>
|
||||
<li>Percent OK: {(100 * ber).toFixed(1)}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import { t } from "./i18next_wrapper";
|
|||
import { error } from "./toast/toast";
|
||||
|
||||
export function responseFulfilled(input: AxiosResponse): AxiosResponse {
|
||||
dispatchNetworkUp("user.api", undefined, "responseFulfilled()");
|
||||
dispatchNetworkUp("user.api");
|
||||
return input;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ export const isLocalRequest = (x: SafeError) =>
|
|||
let ONLY_ONCE = true;
|
||||
export function responseRejected(x: SafeError | undefined) {
|
||||
if (x && isSafeError(x)) {
|
||||
dispatchNetworkUp("user.api", undefined, "isSafeError() REST error");
|
||||
dispatchNetworkUp("user.api");
|
||||
const a = ![451, 401, 422].includes(x.response.status);
|
||||
const b = x.response.status > 399;
|
||||
// Openfarm API was sending too many 404's.
|
||||
|
@ -57,7 +57,7 @@ export function responseRejected(x: SafeError | undefined) {
|
|||
}
|
||||
return Promise.reject(x);
|
||||
} else {
|
||||
dispatchNetworkDown("user.api", undefined, "responseRejected");
|
||||
dispatchNetworkDown("user.api");
|
||||
return Promise.reject(x);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue