Farmbot-Web-App/frontend/resources/selectors.ts

248 lines
7.6 KiB
TypeScript
Raw Normal View History

2019-11-06 10:01:05 -07:00
import { ResourceIndex, UUID } from "./interfaces";
2017-06-29 12:54:02 -06:00
import {
ResourceName,
TaggedGenericPointer,
TaggedPlantPointer,
TaggedRegimen,
TaggedResource,
TaggedSequence,
TaggedTool,
TaggedToolSlotPointer,
TaggedUser,
2018-01-10 13:08:56 -07:00
TaggedDevice,
2018-08-01 07:03:35 -06:00
} from "farmbot";
import {
isTaggedPlantPointer,
isTaggedGenericPointer,
isTaggedRegimen,
isTaggedSequence,
isTaggedTool,
isTaggedToolSlotPointer,
sanityCheck,
2017-06-29 12:54:02 -06:00
} from "./tagged_resources";
2018-03-07 09:39:16 -07:00
import { betterCompact, bail } from "../util";
import { findAllById } from "./selectors_by_id";
2019-04-09 23:17:03 -06:00
import {
findPoints, selectAllPoints, selectAllActivePoints
} from "./selectors_by_kind";
2018-04-25 11:45:22 -06:00
import { assertUuid } from "./util";
import { joinKindAndId } from "./reducer_support";
2019-02-04 07:32:26 -07:00
import { chain } from "lodash";
2019-04-09 23:17:03 -06:00
import { getWebAppConfig } from "./getters";
import { TimeSettings } from "../interfaces";
2019-06-14 17:00:42 -06:00
import { BooleanSetting } from "../session_keys";
export * from "./selectors_by_id";
2018-03-07 09:39:16 -07:00
export * from "./selectors_by_kind";
export * from "./selectors_for_indexing";
/** Similar to findId(), but does not throw exceptions. Do NOT use this method
* unless there is actually a reason for the resource to not have a UUID.
* `findId()` is more appropriate 99% of the time because it can spot
* referential integrity issues. */
export const maybeDetermineUuid =
(index: ResourceIndex, kind: ResourceName, id: number) => {
const kni = joinKindAndId(kind, id);
const uuid = index.byKindAndId[kni];
if (uuid) {
assertUuid(kind, uuid);
return uuid;
}
};
2017-10-26 14:49:23 -06:00
export const findId = (index: ResourceIndex, kind: ResourceName, id: number): UUID => {
const uuid = maybeDetermineUuid(index, kind, id);
2017-06-29 12:54:02 -06:00
if (uuid) {
return uuid;
} else {
throw new Error("UUID not found for id " + id);
2017-06-29 12:54:02 -06:00
}
};
2017-06-29 12:54:02 -06:00
export const isKind = (name: ResourceName) => (tr: TaggedResource) => tr.kind === name;
2017-06-29 12:54:02 -06:00
export function groupPointsByType(index: ResourceIndex) {
2019-02-04 07:32:26 -07:00
return chain(selectAllActivePoints(index))
2018-03-07 10:27:19 -07:00
// If this fails to compile....
2017-06-29 12:54:02 -06:00
.tap(x => x[0].body.pointer_type)
// ... this line must be updated:
.groupBy("body.pointer_type")
.value();
}
export function findPointerByTypeAndId(index: ResourceIndex,
2018-12-01 19:02:02 -07:00
pt: string,
2017-06-29 12:54:02 -06:00
id: number) {
2018-12-02 08:15:35 -07:00
const pni = joinKindAndId("Point", id);
const uuid = "" + index.byKindAndId[pni];
const resource = index.references[uuid];
2020-01-03 13:04:45 -07:00
if (resource?.kind === "Point") {
2018-12-02 08:15:35 -07:00
return resource;
2017-06-29 12:54:02 -06:00
} else {
// We might have a sequence dependency leak if this exception is ever
// thrown.
2018-12-01 19:02:02 -07:00
throw new Error(`Tried to fetch bad point ${pt} ${id}`);
2017-06-29 12:54:02 -06:00
}
}
export function selectAllGenericPointers(index: ResourceIndex):
TaggedGenericPointer[] {
2017-08-28 05:49:13 -06:00
const genericPointers = selectAllPoints(index)
2018-09-20 13:59:13 -06:00
.map(p => isTaggedGenericPointer(p) ? p : undefined);
2017-06-29 12:54:02 -06:00
return betterCompact(genericPointers);
}
export function selectAllPlantPointers(index: ResourceIndex): TaggedPlantPointer[] {
2018-09-20 13:59:13 -06:00
const plantPointers = selectAllActivePoints(index)
.map(p => isTaggedPlantPointer(p) ? p : undefined);
return betterCompact(plantPointers);
2017-06-29 12:54:02 -06:00
}
export function selectAllToolSlotPointers(index: ResourceIndex):
TaggedToolSlotPointer[] {
2018-09-20 13:59:13 -06:00
const toolSlotPointers = selectAllActivePoints(index)
.map(p => isTaggedToolSlotPointer(p) ? p : undefined);
return betterCompact(toolSlotPointers);
2017-06-29 12:54:02 -06:00
}
export function findPlant(i: ResourceIndex, uuid: string):
TaggedPlantPointer {
2017-08-28 05:49:13 -06:00
const point = findPoints(i, uuid);
2017-06-29 12:54:02 -06:00
if (point && sanityCheck(point) && point.body.pointer_type === "Plant") {
2018-03-07 10:27:19 -07:00
return point as TaggedPlantPointer;
2017-06-29 12:54:02 -06:00
} else {
throw new Error("That is not a true plant pointer");
}
}
export function selectCurrentToolSlot(index: ResourceIndex, uuid: string) {
2017-08-28 05:49:13 -06:00
const x = index.references[uuid];
2017-06-29 12:54:02 -06:00
if (x && isTaggedToolSlotPointer(x)) {
return x;
} else {
throw new Error("selectCurrentToolSlot: Not a tool slot: ");
}
}
export function getRegimenByUUID(index: ResourceIndex, uuid: string) {
2017-10-27 07:31:25 -06:00
assertUuid("Regimen", uuid);
2017-06-29 12:54:02 -06:00
return index.references[uuid];
}
export function getSequenceByUUID(index: ResourceIndex,
uuid: string): TaggedSequence {
2017-10-27 07:31:25 -06:00
assertUuid("Sequence", uuid);
2017-08-28 05:49:13 -06:00
const result = index.references[uuid];
2017-06-29 12:54:02 -06:00
if (result && isTaggedSequence(result)) {
return result;
} else {
throw new Error("BAD Sequence UUID;");
}
}
/** GIVEN: a slot UUID.
* FINDS: Tool in that slot (if any) */
export const currentToolInSlot = (index: ResourceIndex) =>
2017-06-29 12:54:02 -06:00
(toolSlotUUID: string): TaggedTool | undefined => {
2017-08-28 05:49:13 -06:00
const currentSlot = selectCurrentToolSlot(index, toolSlotUUID);
2017-06-29 12:54:02 -06:00
if (currentSlot
2017-10-27 07:31:25 -06:00
&& currentSlot.kind === "Point") {
2017-08-28 05:49:13 -06:00
const toolUUID = index
2017-10-27 07:31:25 -06:00
.byKindAndId[joinKindAndId("Tool", currentSlot.body.tool_id)];
2017-08-28 05:49:13 -06:00
const tool = index.references[toolUUID || "NOPE!"];
2017-06-29 12:54:02 -06:00
if (tool && isTaggedTool(tool)) {
return tool;
}
}
};
/** FINDS: All tools that are in use. */
export function toolsInUse(index: ResourceIndex): TaggedTool[] {
2019-04-09 23:17:03 -06:00
const ids = betterCompact(selectAllToolSlotPointers(index)
.map(ts => ts.body.tool_id));
2017-10-27 07:31:25 -06:00
return findAllById(index, ids, "Tool") as TaggedTool[];
2017-06-29 12:54:02 -06:00
}
export function maybeGetSequence(index: ResourceIndex,
uuid: string | undefined): TaggedSequence | undefined {
if (uuid) {
return getSequenceByUUID(index, uuid);
} else {
return undefined;
}
}
export function maybeGetRegimen(index: ResourceIndex,
uuid: string | undefined): TaggedRegimen | undefined {
2017-08-28 05:49:13 -06:00
const tr = uuid && getRegimenByUUID(index, uuid);
if (tr && isTaggedRegimen(tr)) { return tr; }
2017-06-29 12:54:02 -06:00
}
2019-12-30 09:08:48 -07:00
export function maybeGetToolSlot(index: ResourceIndex,
uuid: string | undefined): TaggedToolSlotPointer | undefined {
return selectAllToolSlotPointers(index).filter(x => x.uuid === uuid)[0];
}
2017-12-28 13:48:03 -07:00
/** Return the UTC offset of current bot if possible. If not, use UTC (0). */
export function maybeGetTimeOffset(index: ResourceIndex): number {
const dev = maybeGetDevice(index);
return dev ? dev.body.tz_offset_hrs : 0;
}
2018-03-07 09:39:16 -07:00
2019-04-09 23:17:03 -06:00
/** Return 12/24hr time format preference if possible. If not, use 12hr. */
export function maybeGet24HourTimeSetting(index: ResourceIndex): boolean {
const conf = getWebAppConfig(index);
2019-06-14 17:00:42 -06:00
return conf ? conf.body[BooleanSetting.time_format_24_hour] : false;
2019-04-09 23:17:03 -06:00
}
export function maybeGetTimeSettings(index: ResourceIndex): TimeSettings {
return {
utcOffset: maybeGetTimeOffset(index),
hour24: maybeGet24HourTimeSetting(index),
};
}
2017-12-28 12:33:11 -07:00
export function maybeGetDevice(index: ResourceIndex): TaggedDevice | undefined {
2018-11-01 07:59:51 -06:00
const dev = index.references[Object.keys(index.byKind.Device)[0] || "nope"];
2020-01-03 13:04:45 -07:00
return (dev?.kind === "Device") ?
2017-12-28 12:33:11 -07:00
dev : undefined;
}
2018-03-07 09:39:16 -07:00
export const getDeviceAccountSettings =
2018-05-22 12:44:37 -06:00
(index: ResourceIndex): TaggedDevice => {
const device = maybeGetDevice(index);
switch (Object.keys(index.byKind.Device).length) {
case 0: return bail(`Tried to load device before it was loaded.`);
2018-11-01 07:59:51 -06:00
case 1: return device ? device : bail("Malformed device!");
default: return bail("Found more than 1 device");
}
};
2017-06-29 12:54:02 -06:00
export function maybeFetchUser(index: ResourceIndex):
TaggedUser | undefined {
const list = Object.keys(index.byKind.User);
2017-08-28 05:49:13 -06:00
const uuid = list[0];
const user = index.references[uuid || -1];
2017-06-29 12:54:02 -06:00
if (user && sanityCheck(user) && list.length > 1) {
2018-01-29 08:46:38 -07:00
throw new Error("PROBLEM: Expected 1 user. Got: " + list.length);
}
2020-01-03 13:04:45 -07:00
if ((list.length === 1) && user?.kind === "User") {
2017-06-29 12:54:02 -06:00
return user;
} else {
return undefined;
}
}
export function getUserAccountSettings(index: ResourceIndex): TaggedUser {
2017-08-28 05:49:13 -06:00
const user = maybeFetchUser(index);
2017-06-29 12:54:02 -06:00
if (user) {
return user;
} else {
2018-01-29 08:46:38 -07:00
throw new Error(`PROBLEM: Tried to fetch user before it was available.`);
2017-06-29 12:54:02 -06:00
}
}
export function all(index: ResourceIndex) {
return betterCompact(Object.keys(index.all).map(uuid => index.references[uuid]));
2017-06-29 12:54:02 -06:00
}