241 lines
7.4 KiB
TypeScript
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]));
|
|
}
|