First draft of non-monolithic state tree updater thing. Needs realworld testing

pull/1098/head
Rick Carlino 2019-01-21 14:54:18 -06:00
parent e4b40a23ae
commit 46711ba87e
6 changed files with 86 additions and 50 deletions

View File

@ -51,7 +51,7 @@
"css-loader": "2.1.0",
"enzyme": "3.8.0",
"enzyme-adapter-react-16": "1.7.1",
"farmbot": "7.0.0-rc1",
"farmbot": "7.0.0-rc2",
"farmbot-toastr": "1.0.3",
"fastclick": "1.0.6",
"file-loader": "3.0.1",

View File

@ -20,7 +20,7 @@ jest.mock("../../../config_storage/actions", () => {
import { HardwareState } from "../../../devices/interfaces";
import {
incomingStatus,
incomingLegacyStatus,
actOnChannelName,
showLogOnScreen,
TITLE,
@ -56,8 +56,8 @@ describe("readStatus()", () => {
describe("incomingStatus", () => {
it("creates an action", () => {
const stub = {} as HardwareState;
const result = incomingStatus(stub);
expect(result.type).toEqual(Actions.BOT_CHANGE);
const result = incomingLegacyStatus(stub);
expect(result.type).toEqual(Actions.LEGACY_BOT_CHANGE);
expect(result.payload).toEqual(stub);
});
});

View File

@ -25,6 +25,7 @@ import { BooleanSetting } from "../session_keys";
import { versionOK } from "../util";
import { onLogs } from "./log_handlers";
import { ChannelName } from "../sequences/interfaces";
import { DeepPartial } from "redux";
export const TITLE = "New message from bot";
/** TODO: This ought to be stored in Redux. It is here because of historical
@ -36,8 +37,11 @@ export const HACKY_FLAGS = {
/** Action creator that is called when FarmBot OS emits a status update.
* Coordinate updates, movement, etc.*/
export let incomingStatus = (statusMessage: HardwareState) =>
({ type: Actions.BOT_CHANGE, payload: statusMessage });
export const incomingLegacyStatus = (statusMessage: HardwareState) =>
({ type: Actions.LEGACY_BOT_CHANGE, payload: statusMessage });
export const incomingStatus =
(payload: DeepPartial<HardwareState>) => ({ type: Actions.STATUS_UPDATE, payload });
/** Determine if an incoming log has a certain channel. If it is, execute the
* supplied callback. */
@ -106,20 +110,40 @@ export const changeLastClientConnected = (bot: Farmbot) => () => {
"LAST_CLIENT_CONNECTED": JSON.stringify(new Date())
}).catch(() => { }); // This is internal stuff, don't alert user.
};
const onStatus = (dispatch: Function, getState: GetState) =>
(throttle(function (msg: BotStateTree) {
bothUp("Got a status message");
/** Too many status updates === too many screen redraws. */
const slowDown = (fn: (...args: unknown[]) => unknown) =>
throttle(fn, 600, { leading: false, trailing: true });
const setBothUp = () => bothUp("Got a status message");
const legacyChecks = (getState: GetState) => {
if (HACKY_FLAGS.needVersionCheck) {
const IS_OK = versionOK(getState()
.bot
.hardware
.informational_settings
.controller_version, EXPECTED_MAJOR, EXPECTED_MINOR);
if (!IS_OK) { badVersion(); }
HACKY_FLAGS.needVersionCheck = false;
}
};
/** Legacy handler for bots that have not upgraded to FBOS v7 yet.
* - RC 21 JAN 18 */
const onLegacyStatus =
(dispatch: Function, getState: GetState) => slowDown((msg: BotStateTree) => {
setBothUp();
dispatch(incomingLegacyStatus(msg));
legacyChecks(getState);
});
const onStatus =
(dispatch: Function, getState: GetState) => slowDown((msg: DeepPartial<BotStateTree>) => {
setBothUp();
dispatch(incomingStatus(msg));
if (HACKY_FLAGS.needVersionCheck) {
const IS_OK = versionOK(getState()
.bot
.hardware
.informational_settings
.controller_version, EXPECTED_MAJOR, EXPECTED_MINOR);
if (!IS_OK) { badVersion(); }
HACKY_FLAGS.needVersionCheck = false;
}
}, 600, { leading: false, trailing: true }));
legacyChecks(getState);
});
type Client = { connected?: boolean };
@ -152,6 +176,7 @@ const attachEventListeners =
bot.on("offline", onOffline);
bot.on("sent", onSent(bot.client));
bot.on("logs", onLogs(dispatch, getState));
bot.on("legacy_status", onLegacyStatus(dispatch, getState));
bot.on("status", onStatus(dispatch, getState));
bot.on("malformed", onMalformed);
bot.client.on("message", autoSync(dispatch, getState));

View File

@ -704,7 +704,9 @@ export enum Actions {
CHANGE_STEP_SIZE = "CHANGE_STEP_SIZE",
SETTING_UPDATE_START = "SETTING_UPDATE_START",
SETTING_UPDATE_END = "SETTING_UPDATE_END",
BOT_CHANGE = "BOT_CHANGE",
/** Used in FBOS < v7. Remove ASAP. RC 21 JAN 18 */
LEGACY_BOT_CHANGE = "LEGACY_BOT_CHANGE",
STATUS_UPDATE = "STATUS_UPDATE",
FETCH_OS_UPDATE_INFO_OK = "FETCH_OS_UPDATE_INFO_OK",
FETCH_OS_UPDATE_INFO_ERROR = "FETCH_OS_UPDATE_INFO_ERROR",
FETCH_BETA_OS_UPDATE_INFO_OK = "FETCH_BETA_OS_UPDATE_INFO_OK",

View File

@ -84,7 +84,7 @@ describe("botReducer", () => {
payload.location_data.position.x = -1;
payload.location_data.position.y = -1;
payload.location_data.position.z = -1;
const action = { type: Actions.BOT_CHANGE, payload };
const action = { type: Actions.LEGACY_BOT_CHANGE, payload };
// Make the starting state different than initialState();
const result = botReducer(state, action);
// Resets .hardware to initialState()

View File

@ -10,6 +10,7 @@ import { ReduxAction } from "../redux/interfaces";
import { connectivityReducer } from "../connectivity/reducer";
import { versionOK } from "../util";
import { EXPECTED_MAJOR, EXPECTED_MINOR } from "./actions";
import { DeepPartial } from "redux";
const afterEach = (state: BotState, a: ReduxAction<{}>) => {
state.connectivity = connectivityReducer(state.connectivity, a);
@ -126,36 +127,11 @@ export let botReducer = generateReducer<BotState>(initialState(), afterEach)
s.minOsFeatureData = payload;
return s;
})
.add<HardwareState>(Actions.BOT_CHANGE, (state, { payload }) => {
state.hardware = payload;
const { informational_settings } = state.hardware;
const syncStatus = informational_settings.sync_status;
/** USE CASE: You reboot the bot. The old state values are still hanging
* around. You think the bot is broke, but it isn't. The FE is holding on
* to stale data. */
if (syncStatus === "maintenance") {
const emptyState = initialState();
state.hardware = emptyState.hardware;
state.hardware.informational_settings.sync_status = "maintenance";
return state;
}
const info = {
consistent: state.consistent,
syncStatus,
fbosVersion: informational_settings.controller_version,
autoSync: !!state.hardware.configuration.auto_sync
};
state.consistent = info.consistent;
info.consistent = state.consistent;
const nextSyncStatus = maybeNegateStatus(info);
versionOK(informational_settings.controller_version,
EXPECTED_MAJOR, EXPECTED_MINOR);
state.hardware.informational_settings.sync_status = nextSyncStatus;
return state;
.add<DeepPartial<HardwareState>>(Actions.STATUS_UPDATE, (s, { payload }) => {
const nextState = { ...s, ...payload };
return nextState;
})
.add<HardwareState>(Actions.LEGACY_BOT_CHANGE, legacyStatusHandler)
.add<void>(Actions.STASH_STATUS, (s) => {
stash(s);
return s;
@ -189,3 +165,36 @@ const stash = (s: BotState) => {
/** Put the old syncStatus back where it was after bot becomes consistent. */
const unstash = (s: BotState) =>
s.hardware.informational_settings.sync_status = s.statusStash;
function legacyStatusHandler(state: BotState,
action: ReduxAction<HardwareState>): BotState {
const { payload } = action;
state.hardware = payload;
const { informational_settings } = state.hardware;
const syncStatus = informational_settings.sync_status;
/** USE CASE: You reboot the bot. The old state values are still hanging
* around. You think the bot is broke, but it isn't. The FE is holding on
* to stale data. */
if (syncStatus === "maintenance") {
const emptyState = initialState();
state.hardware = emptyState.hardware;
state.hardware.informational_settings.sync_status = "maintenance";
return state;
}
const info = {
consistent: state.consistent,
syncStatus,
fbosVersion: informational_settings.controller_version,
autoSync: !!state.hardware.configuration.auto_sync
};
state.consistent = info.consistent;
info.consistent = state.consistent;
const nextSyncStatus = maybeNegateStatus(info);
versionOK(informational_settings.controller_version,
EXPECTED_MAJOR, EXPECTED_MINOR);
state.hardware.informational_settings.sync_status = nextSyncStatus;
return state;
}