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

241 lines
7.4 KiB
TypeScript

import { ResourceIndex, UUID } from "./interfaces";
import {
ResourceName,
TaggedGenericPointer,
TaggedPlantPointer,
TaggedRegimen,
TaggedResource,
TaggedSequence,
TaggedTool,
TaggedToolSlotPointer,
TaggedUser,
TaggedDevice,
TaggedWeedPointer,
} from "farmbot";
import {
isTaggedPlantPointer,
isTaggedGenericPointer,
isTaggedRegimen,
isTaggedSequence,
isTaggedToolSlotPointer,
sanityCheck,
isTaggedWeedPointer,
} from "./tagged_resources";
import { betterCompact, bail } from "../util";
import { findAllById } from "./selectors_by_id";
import {
findPoints, selectAllPoints, selectAllActivePoints,
} from "./selectors_by_kind";
import { assertUuid } from "./util";
import { joinKindAndId } from "./reducer_support";
import { chain } from "lodash";
import { getWebAppConfig } from "./getters";
import { TimeSettings } from "../interfaces";
import { BooleanSetting } from "../session_keys";
export * from "./selectors_by_id";
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;
}
};
export const findId = (index: ResourceIndex, kind: ResourceName, id: number): UUID => {
const uuid = maybeDetermineUuid(index, kind, id);
if (uuid) {
return uuid;
} else {
throw new Error("UUID not found for id " + id);
}
};
export const isKind = (resourceName: ResourceName) => (tr: TaggedResource) =>
tr.kind === resourceName;
export function groupPointsByType(index: ResourceIndex) {
return chain(selectAllActivePoints(index))
// If this fails to compile....
.tap(x => x[0].body.pointer_type)
// ... this line must be updated:
.groupBy("body.pointer_type")
.value();
}
export function findPointerByTypeAndId(index: ResourceIndex,
pt: string,
id: number) {
const pni = joinKindAndId("Point", id);
const uuid = "" + index.byKindAndId[pni];
const resource = index.references[uuid];
if (resource?.kind === "Point") {
return resource;
} else {
// We might have a sequence dependency leak if this exception is ever
// thrown.
throw new Error(`Tried to fetch bad point ${pt} ${id}`);
}
}
export function selectAllGenericPointers(index: ResourceIndex):
TaggedGenericPointer[] {
const genericPointers = selectAllPoints(index)
.map(p => isTaggedGenericPointer(p) ? p : undefined);
return betterCompact(genericPointers);
}
export function selectAllWeedPointers(index: ResourceIndex):
TaggedWeedPointer[] {
const weedPointers = selectAllPoints(index)
.map(p => isTaggedWeedPointer(p) ? p : undefined);
return betterCompact(weedPointers);
}
export function selectAllPlantPointers(index: ResourceIndex): TaggedPlantPointer[] {
const plantPointers = selectAllActivePoints(index)
.map(p => isTaggedPlantPointer(p) ? p : undefined);
return betterCompact(plantPointers);
}
export function selectAllToolSlotPointers(index: ResourceIndex):
TaggedToolSlotPointer[] {
const toolSlotPointers = selectAllActivePoints(index)
.map(p => isTaggedToolSlotPointer(p) ? p : undefined);
return betterCompact(toolSlotPointers);
}
export function findPlant(i: ResourceIndex, uuid: string):
TaggedPlantPointer {
const point = findPoints(i, uuid);
if (point && sanityCheck(point) && point.body.pointer_type === "Plant") {
return point as TaggedPlantPointer;
} else {
throw new Error("That is not a true plant pointer");
}
}
export function selectCurrentToolSlot(index: ResourceIndex, uuid: string) {
const x = index.references[uuid];
if (x && isTaggedToolSlotPointer(x)) {
return x;
} else {
throw new Error("selectCurrentToolSlot: Not a tool slot: ");
}
}
export function getRegimenByUUID(index: ResourceIndex, uuid: string) {
assertUuid("Regimen", uuid);
return index.references[uuid];
}
export function getSequenceByUUID(index: ResourceIndex,
uuid: string): TaggedSequence {
assertUuid("Sequence", uuid);
const result = index.references[uuid];
if (result && isTaggedSequence(result)) {
return result;
} else {
throw new Error("BAD Sequence UUID;");
}
}
/** FINDS: All tools that are in use. */
export function toolsInUse(index: ResourceIndex): TaggedTool[] {
const ids = betterCompact(selectAllToolSlotPointers(index)
.map(ts => ts.body.tool_id));
return findAllById(index, ids, "Tool") as TaggedTool[];
}
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 {
const tr = uuid && getRegimenByUUID(index, uuid);
if (tr && isTaggedRegimen(tr)) { return tr; }
}
export function maybeGetToolSlot(index: ResourceIndex,
uuid: string | undefined): TaggedToolSlotPointer | undefined {
return selectAllToolSlotPointers(index).filter(x => x.uuid === uuid)[0];
}
/** 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;
}
/** Return 12/24hr time format preference if possible. If not, use 12hr. */
export function maybeGet24HourTimeSetting(index: ResourceIndex): boolean {
const conf = getWebAppConfig(index);
return conf ? conf.body[BooleanSetting.time_format_24_hour] : false;
}
export function maybeGetTimeSettings(index: ResourceIndex): TimeSettings {
return {
utcOffset: maybeGetTimeOffset(index),
hour24: maybeGet24HourTimeSetting(index),
};
}
export function maybeGetDevice(index: ResourceIndex): TaggedDevice | undefined {
const dev = index.references[Object.keys(index.byKind.Device)[0] || "nope"];
return (dev?.kind === "Device") ?
dev : undefined;
}
export const getDeviceAccountSettings =
(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.`);
case 1: return device ? device : bail("Malformed device!");
default: return bail("Found more than 1 device");
}
};
export function maybeFetchUser(index: ResourceIndex):
TaggedUser | undefined {
const list = Object.keys(index.byKind.User);
const uuid = list[0];
const user = index.references[uuid || -1];
if (user && sanityCheck(user) && list.length > 1) {
throw new Error("PROBLEM: Expected 1 user. Got: " + list.length);
}
if ((list.length === 1) && user?.kind === "User") {
return user;
} else {
return undefined;
}
}
export function getUserAccountSettings(index: ResourceIndex): TaggedUser {
const user = maybeFetchUser(index);
if (user) {
return user;
} else {
throw new Error(`PROBLEM: Tried to fetch user before it was available.`);
}
}
export function all(index: ResourceIndex) {
return betterCompact(Object.keys(index.all).map(uuid => index.references[uuid]));
}