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,
|
2017-08-21 08:31:47 -06:00
|
|
|
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";
|
2018-11-08 09:41:09 -07:00
|
|
|
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";
|
2018-03-07 08:59:52 -07:00
|
|
|
|
|
|
|
export * from "./selectors_by_id";
|
2018-03-07 09:39:16 -07:00
|
|
|
export * from "./selectors_by_kind";
|
2018-03-07 08:59:52 -07:00
|
|
|
export * from "./selectors_for_indexing";
|
|
|
|
|
2017-10-29 10:08:35 -06:00
|
|
|
/** 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. */
|
2019-12-10 12:53:20 -07:00
|
|
|
export const maybeDetermineUuid =
|
2017-10-29 10:08:35 -06:00
|
|
|
(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
|
|
|
|
2019-12-10 12:53:20 -07:00
|
|
|
export const findId = (index: ResourceIndex, kind: ResourceName, id: number): UUID => {
|
2017-10-29 10:08:35 -06:00
|
|
|
const uuid = maybeDetermineUuid(index, kind, id);
|
2017-06-29 12:54:02 -06:00
|
|
|
if (uuid) {
|
|
|
|
return uuid;
|
|
|
|
} else {
|
2017-06-30 14:24:09 -06:00
|
|
|
throw new Error("UUID not found for id " + id);
|
2017-06-29 12:54:02 -06:00
|
|
|
}
|
2017-06-30 14:24:09 -06:00
|
|
|
};
|
2017-06-29 12:54:02 -06:00
|
|
|
|
2019-12-10 12:53:20 -07: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) */
|
2019-12-10 12:53:20 -07:00
|
|
|
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);
|
2017-07-02 18:43:12 -06:00
|
|
|
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
|
|
|
|
2018-05-22 12:41:28 -06:00
|
|
|
export const getDeviceAccountSettings =
|
2018-05-22 12:44:37 -06:00
|
|
|
(index: ResourceIndex): TaggedDevice => {
|
2018-05-22 12:41:28 -06:00
|
|
|
const device = maybeGetDevice(index);
|
2018-11-01 07:47:56 -06:00
|
|
|
switch (Object.keys(index.byKind.Device).length) {
|
2018-05-22 12:41:28 -06:00
|
|
|
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!");
|
2018-05-22 12:41:28 -06:00
|
|
|
default: return bail("Found more than 1 device");
|
|
|
|
}
|
|
|
|
};
|
2017-06-29 12:54:02 -06:00
|
|
|
|
|
|
|
export function maybeFetchUser(index: ResourceIndex):
|
2017-06-30 14:24:09 -06:00
|
|
|
TaggedUser | undefined {
|
2018-11-01 07:47:56 -06:00
|
|
|
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);
|
2017-07-02 18:43:12 -06:00
|
|
|
}
|
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) {
|
2018-11-01 08:42:40 -06:00
|
|
|
return betterCompact(Object.keys(index.all).map(uuid => index.references[uuid]));
|
2017-06-29 12:54:02 -06:00
|
|
|
}
|