From ba7b20ca0f76949b069d8af04555dfdf50defa57 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Thu, 28 Dec 2023 15:49:45 -0800 Subject: [PATCH 1/8] upgrade deps --- Gemfile.lock | 10 ++++----- frontend/connectivity/batch_queue.ts | 3 ++- frontend/connectivity/index.ts | 5 +++-- .../devices/connectivity/generate_data.ts | 2 +- frontend/farm_events/add_farm_event.tsx | 4 ++-- .../map_state_to_props_add_edit.ts | 4 ++-- frontend/folders/actions.ts | 3 ++- .../__tests__/image_filter_menu_test.tsx | 3 +-- .../image_filter_menu.tsx | 8 +++++-- frontend/photos/photo_filter_settings/util.ts | 2 +- .../__tests__/plant_inventory_item_test.tsx | 2 +- frontend/plants/edit_plant_status.tsx | 4 ++-- frontend/plants/plant_panel.tsx | 2 +- frontend/redux/store.ts | 5 ++--- frontend/redux/version_tracker_middleware.ts | 2 +- .../regimens/set_active_regimen_by_name.ts | 4 +++- frontend/resources/reducer_support.ts | 7 +++--- .../sequences/set_active_sequence_by_name.ts | 4 +++- frontend/wizard/actions.ts | 2 +- package.json | 22 +++++++++---------- 20 files changed, 54 insertions(+), 44 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 88e57cf222..4e0d566ee4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -114,7 +114,7 @@ GEM docile (1.4.0) e2mmap (0.1.0) erubi (1.12.0) - factory_bot (6.4.2) + factory_bot (6.4.4) activesupport (>= 5.0.0) factory_bot_rails (6.4.2) factory_bot (~> 6.4) @@ -222,7 +222,7 @@ GEM multipart-post (2.3.0) mutations (0.9.1) activesupport - net-imap (0.4.8) + net-imap (0.4.9) date net-protocol net-pop (0.1.2) @@ -232,7 +232,7 @@ GEM net-smtp (0.4.0) net-protocol nio4r (2.7.0) - nokogiri (1.15.5-x86_64-linux) + nokogiri (1.16.0-x86_64-linux) racc (~> 1.4) orm_adapter (0.5.0) os (1.1.4) @@ -336,7 +336,7 @@ GEM activerecord (>= 4.0.0) railties (>= 4.0.0) secure_headers (6.5.0) - set (1.0.4) + set (1.1.0) signet (0.18.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) @@ -433,4 +433,4 @@ RUBY VERSION ruby 3.1.4p223 BUNDLED WITH - 2.5.0 + 2.5.3 diff --git a/frontend/connectivity/batch_queue.ts b/frontend/connectivity/batch_queue.ts index 07a045da5f..b1fbb727b2 100644 --- a/frontend/connectivity/batch_queue.ts +++ b/frontend/connectivity/batch_queue.ts @@ -3,6 +3,7 @@ import { store } from "../redux/store"; import { batchInitResources, bothUp } from "./connect_device"; import { maybeGetDevice } from "../resources/selectors"; import { deviceIsThrottled } from "./device_is_throttled"; +import { UnknownAction } from "redux"; /** Performs resource initialization (Eg: a storm of incoming logs) in batches * at a regular interval. We only need one work queue for the whole app, @@ -25,7 +26,7 @@ export class BatchQueue { work = () => { const dev = maybeGetDevice(store.getState().resources.index); if (!deviceIsThrottled(dev ? dev.body : undefined)) { - store.dispatch(batchInitResources(this.queue)); + store.dispatch(batchInitResources(this.queue) as unknown as UnknownAction); } this.clear(); bothUp(); diff --git a/frontend/connectivity/index.ts b/frontend/connectivity/index.ts index 138366c50e..09b50abe8e 100644 --- a/frontend/connectivity/index.ts +++ b/frontend/connectivity/index.ts @@ -2,6 +2,7 @@ import { store } from "../redux/store"; import { networkUp, networkDown } from "./actions"; import { Edge } from "./interfaces"; import { Actions } from "../constants"; +import { UnknownAction } from "redux"; /* ABOUT THIS FILE: These functions allow us to mark the network as up or down from anywhere within the app (even outside of React-Redux). I usually avoid @@ -39,13 +40,13 @@ export const dispatchQosStart = (id: string) => { export const dispatchNetworkUp = (edge: Edge, at: number) => { if (shouldThrottle(edge, at)) { return; } - store.dispatch(networkUp(edge, at)); + store.dispatch(networkUp(edge, at) as unknown as UnknownAction); bumpThrottle(edge, at); }; export const dispatchNetworkDown = (edge: Edge, at: number) => { if (shouldThrottle(edge, at)) { return; } - store.dispatch(networkDown(edge, at)); + store.dispatch(networkDown(edge, at) as unknown as UnknownAction); bumpThrottle(edge, at); }; diff --git a/frontend/devices/connectivity/generate_data.ts b/frontend/devices/connectivity/generate_data.ts index 0459983cb1..fdbabb95c9 100644 --- a/frontend/devices/connectivity/generate_data.ts +++ b/frontend/devices/connectivity/generate_data.ts @@ -26,7 +26,7 @@ export const connectivityData = (props: ConnectivityDataProps) => { userMQTT: browserToMQTT({ state: "up", at: moment().valueOf() }), userAPI: browserToAPI({ state: "up", at: moment().valueOf() }), botMQTT: botToMQTT({ state: "up", at: moment().valueOf() }), - botAPI: botToAPI(moment().toISOString()), + botAPI: botToAPI("" + moment().toISOString()), botFirmware: botToFirmware("0.0.0.E", "express_k10"), } : { diff --git a/frontend/farm_events/add_farm_event.tsx b/frontend/farm_events/add_farm_event.tsx index 01c2a34421..faf103284f 100644 --- a/frontend/farm_events/add_farm_event.tsx +++ b/frontend/farm_events/add_farm_event.tsx @@ -57,8 +57,8 @@ export class RawAddFarmEvent const { uuid } = this.props.findExecutable(executable_type, executable_id); const varData = this.props.resources.sequenceMetas[uuid]; const action = init("FarmEvent", { - end_time: moment().add(63, "minutes").toISOString(), - start_time: moment().add(3, "minutes").toISOString(), + end_time: "" + moment().add(63, "minutes").toISOString(), + start_time: "" + moment().add(3, "minutes").toISOString(), time_unit: "never", executable_id, executable_type, diff --git a/frontend/farm_events/map_state_to_props_add_edit.ts b/frontend/farm_events/map_state_to_props_add_edit.ts index 5f827a6bf8..70c5a86324 100644 --- a/frontend/farm_events/map_state_to_props_add_edit.ts +++ b/frontend/farm_events/map_state_to_props_add_edit.ts @@ -60,7 +60,7 @@ const handleTime = ( .toISOString(); // Set the time of the already existing iso string - const newStartISO = moment(currentStartISO) + const newStartISO = "" + moment(currentStartISO) .set("hours", hours) .set("minutes", minutes) .toISOString(); @@ -71,7 +71,7 @@ const handleTime = ( const currentEndISO = new Date((currentISO || "").toString()) .toISOString(); - const newEndISO = moment(currentEndISO) + const newEndISO = "" + moment(currentEndISO) .set("hours", hours) .set("minutes", minutes) .toISOString(); diff --git a/frontend/folders/actions.ts b/frontend/folders/actions.ts index 6e6911c4d3..2487330c0b 100644 --- a/frontend/folders/actions.ts +++ b/frontend/folders/actions.ts @@ -13,6 +13,7 @@ import { stepGet, STEP_DATATRANSFER_IDENTIFER } from "../draggable/actions"; import { joinKindAndId } from "../resources/reducer_support"; import { maybeGetSequence } from "../resources/selectors"; import { Path } from "../internal_urls"; +import { UnknownAction } from "redux"; export const setFolderColor = (id: number, color: Color) => { const d = store.dispatch as Function; @@ -51,7 +52,7 @@ export const addNewSequenceToFolder = (config: DeepPartial = {}) => { kind: "sequence", body: [], }; - store.dispatch(init("Sequence", newSequence)); + store.dispatch(init("Sequence", newSequence) as unknown as UnknownAction); push(Path.sequences(urlFriendly(newSequence.name))); setActiveSequenceByName(); }; diff --git a/frontend/photos/photo_filter_settings/__tests__/image_filter_menu_test.tsx b/frontend/photos/photo_filter_settings/__tests__/image_filter_menu_test.tsx index 2623e9cebb..739a9b070a 100644 --- a/frontend/photos/photo_filter_settings/__tests__/image_filter_menu_test.tsx +++ b/frontend/photos/photo_filter_settings/__tests__/image_filter_menu_test.tsx @@ -113,8 +113,7 @@ describe("", () => { currentTarget: { value: "" } }); expect(wrapper.instance().state[filter]).toEqual(undefined); - // eslint-disable-next-line no-null/no-null - expect(edit).toHaveBeenCalledWith(config, { [key]: null }); + expect(edit).toHaveBeenCalledWith(config, { [key]: undefined }); expect(save).toHaveBeenCalledWith(config.uuid); }); diff --git a/frontend/photos/photo_filter_settings/image_filter_menu.tsx b/frontend/photos/photo_filter_settings/image_filter_menu.tsx index 8216eade77..ad8ca5324b 100644 --- a/frontend/photos/photo_filter_settings/image_filter_menu.tsx +++ b/frontend/photos/photo_filter_settings/image_filter_menu.tsx @@ -84,7 +84,9 @@ export class ImageFilterMenu let value = undefined; switch (datetime) { case "beginDate": - value = offsetTime(input, beginTime || "00:00", timeSettings); + if (input) { + value = offsetTime(input, beginTime || "00:00", timeSettings); + } this.setValues({ photo_filter_begin: value }); break; case "beginTime": @@ -94,7 +96,9 @@ export class ImageFilterMenu } break; case "endDate": - value = offsetTime(input, endTime || "00:00", timeSettings); + if (input) { + value = offsetTime(input, endTime || "00:00", timeSettings); + } this.setValues({ photo_filter_end: value }); break; case "endTime": diff --git a/frontend/photos/photo_filter_settings/util.ts b/frontend/photos/photo_filter_settings/util.ts index 25368f2cb1..3086c6c2e6 100644 --- a/frontend/photos/photo_filter_settings/util.ts +++ b/frontend/photos/photo_filter_settings/util.ts @@ -32,7 +32,7 @@ export const parseFilterSetting = (getConfigValue: GetWebAppConfigValue) => }; export const filterTime = (direction: "before" | "after", seconds = 1) => - (image: TaggedImage) => + (image: TaggedImage): string => moment(image.body.created_at) .add(direction == "before" ? -seconds : seconds, "second") .toISOString(); diff --git a/frontend/plants/__tests__/plant_inventory_item_test.tsx b/frontend/plants/__tests__/plant_inventory_item_test.tsx index 33b811209d..3761195347 100644 --- a/frontend/plants/__tests__/plant_inventory_item_test.tsx +++ b/frontend/plants/__tests__/plant_inventory_item_test.tsx @@ -48,7 +48,7 @@ describe("", () => { const p = fakeProps(); const plant = fakePlant(); plant.body.name = ""; - plant.body.planted_at = moment().toISOString(); + plant.body.planted_at = "" + moment().toISOString(); p.plant = plant; const wrapper = shallow(); expect(wrapper.text()).toEqual("Unknown plant1 day old"); diff --git a/frontend/plants/edit_plant_status.tsx b/frontend/plants/edit_plant_status.tsx index 3b78211072..e4d6f13fa1 100644 --- a/frontend/plants/edit_plant_status.tsx +++ b/frontend/plants/edit_plant_status.tsx @@ -84,7 +84,7 @@ const getUpdateByPlantStage = (plant_stage: PlantStage): PlantOptions => { update.planted_at = undefined; break; case "planted": - update.planted_at = moment().toISOString(); + update.planted_at = "" + moment().toISOString(); } return update; }; @@ -164,7 +164,7 @@ export const PlantDateBulkUpdate = (props: PlantDateBulkUpdateProps) => { })) && plants.map(plant => { props.dispatch(edit(plant, { - planted_at: moment(e.currentTarget.value) + planted_at: "" + moment(e.currentTarget.value) .utcOffset(props.timeSettings.utcOffset).toISOString() })); props.dispatch(save(plant.uuid)); diff --git a/frontend/plants/plant_panel.tsx b/frontend/plants/plant_panel.tsx index d4432a583d..a2b510602e 100644 --- a/frontend/plants/plant_panel.tsx +++ b/frontend/plants/plant_panel.tsx @@ -66,7 +66,7 @@ export const EditDatePlanted = (props: EditDatePlantedProps) => { value={datePlanted?.utcOffset(timeSettings.utcOffset) .format("YYYY-MM-DD") || ""} onCommit={e => updatePlant(uuid, { - planted_at: moment(e.currentTarget.value) + planted_at: "" + moment(e.currentTarget.value) .utcOffset(timeSettings.utcOffset).toISOString() })} />; }; diff --git a/frontend/redux/store.ts b/frontend/redux/store.ts index 9bb2562714..1e32560d70 100644 --- a/frontend/redux/store.ts +++ b/frontend/redux/store.ts @@ -1,14 +1,13 @@ -import { createStore, PreloadedState } from "redux"; +import { createStore } from "redux"; import { EnvName, Store } from "./interfaces"; import { rootReducer } from "./root_reducer"; import { registerSubscribers } from "./subscribers"; import { getMiddleware } from "./middlewares"; import { set } from "lodash"; -import { Everything } from "../interfaces"; function getStore(envName: EnvName): Store { return createStore(rootReducer, - {} as PreloadedState, + {}, getMiddleware(envName)); } diff --git a/frontend/redux/version_tracker_middleware.ts b/frontend/redux/version_tracker_middleware.ts index 47cbcef17e..d7485dc13a 100644 --- a/frontend/redux/version_tracker_middleware.ts +++ b/frontend/redux/version_tracker_middleware.ts @@ -18,7 +18,7 @@ function getVersionFromState(state: Everything) { const fn: MW = (store: Store) => - (dispatch: Dispatch>) => + (dispatch: Dispatch>) => // eslint-disable-next-line @typescript-eslint/no-explicit-any (action: any) => { const fbos = getVersionFromState(store.getState()); diff --git a/frontend/regimens/set_active_regimen_by_name.ts b/frontend/regimens/set_active_regimen_by_name.ts index 946e18d28c..a781294a44 100644 --- a/frontend/regimens/set_active_regimen_by_name.ts +++ b/frontend/regimens/set_active_regimen_by_name.ts @@ -3,8 +3,10 @@ import { store } from "../redux/store"; import { urlFriendly } from "../util"; import { selectRegimen } from "./actions"; import { Path } from "../internal_urls"; +import { UnknownAction } from "redux"; -const setRegimen = (uuid: string) => store.dispatch(selectRegimen(uuid)); +const setRegimen = (uuid: string) => + store.dispatch(selectRegimen(uuid) as unknown as UnknownAction); export function setActiveRegimenByName() { const chunk = Path.getLastChunk(); diff --git a/frontend/resources/reducer_support.ts b/frontend/resources/reducer_support.ts index 3113a2f7e7..4ba797fd19 100644 --- a/frontend/resources/reducer_support.ts +++ b/frontend/resources/reducer_support.ts @@ -1,7 +1,7 @@ import { ResourceName, SpecialStatus, TaggedResource, TaggedSequence, } from "farmbot"; -import { combineReducers, ReducersMapObject } from "redux"; +import { combineReducers, ReducersMapObject, UnknownAction } from "redux"; import { helpReducer as help } from "../help/reducer"; import { designer as farm_designer } from "../farm_designer/reducer"; import { photosReducer as photos } from "../photos/reducer"; @@ -40,6 +40,7 @@ import { getFbosConfig } from "./getters"; import { ingest, PARENTLESS as NO_PARENT } from "../folders/data_transfer"; import { FolderNode, FolderMeta } from "../folders/interfaces"; import { pointsSelectedByGroup } from "../point_groups/criteria/apply"; +import { Everything } from "../interfaces"; export function findByUuid(index: ResourceIndex, uuid: string): TaggedResource { const x = index.references[uuid]; @@ -275,8 +276,8 @@ const consumerReducer = combineReducers({ farmware, help, alerts - // eslint-disable-next-line @typescript-eslint/no-explicit-any -} as ReducersMapObject); +} as ReducersMapObject, +) as Function; /** The resource reducer must have the first say when a resource-related action * fires off. Afterwards, sub-reducers are allowed to make sense of data diff --git a/frontend/sequences/set_active_sequence_by_name.ts b/frontend/sequences/set_active_sequence_by_name.ts index f87be74f31..8dd030029e 100644 --- a/frontend/sequences/set_active_sequence_by_name.ts +++ b/frontend/sequences/set_active_sequence_by_name.ts @@ -4,8 +4,10 @@ import { urlFriendly } from "../util"; import { selectSequence } from "./actions"; import { setMenuOpen } from "./test_button"; import { Path } from "../internal_urls"; +import { UnknownAction } from "redux"; -const setSequence = (uuid: string) => store.dispatch(selectSequence(uuid)); +const setSequence = (uuid: string) => + store.dispatch(selectSequence(uuid) as unknown as UnknownAction); export function setActiveSequenceByName() { const chunk = Path.getLastChunk(); diff --git a/frontend/wizard/actions.ts b/frontend/wizard/actions.ts index 6e0a62d562..7496d0c15a 100644 --- a/frontend/wizard/actions.ts +++ b/frontend/wizard/actions.ts @@ -31,7 +31,7 @@ export const destroyAllWizardStepResults = export const completeSetup = (device: TaggedDevice | undefined) => device && setDeviceProperty(device, { - setup_completed_at: moment().toISOString() + setup_completed_at: "" + moment().toISOString() }); export const resetSetup = (device: TaggedDevice | undefined) => diff --git a/package.json b/package.json index 43972ffa2e..c84ea35ade 100644 --- a/package.json +++ b/package.json @@ -38,22 +38,22 @@ "@parcel/transformer-typescript-tsc": "2.10.3", "@types/lodash": "4.14.202", "@types/markdown-it": "13.0.7", - "@types/node": "20.10.4", + "@types/node": "20.10.5", "@types/promise-timeout": "1.3.3", - "@types/react": "18.2.45", + "@types/react": "18.2.46", "@types/react-color": "3.0.10", - "@types/react-dom": "18.2.17", + "@types/react-dom": "18.2.18", "@types/ws": "8.5.10", - "axios": "1.6.2", + "axios": "1.6.3", "bowser": "2.11.0", "browser-speech": "1.1.1", "events": "3.3.0", "farmbot": "15.8.5", - "i18next": "23.7.11", + "i18next": "23.7.12", "lodash": "4.17.21", "markdown-it": "14.0.0", "markdown-it-emoji": "3.0.0", - "moment": "2.29.4", + "moment": "2.30.1", "monaco-editor": "0.45.0", "mqtt": "5.1.4", "npm": "10.2.5", @@ -65,8 +65,8 @@ "react": "18.2.0", "react-color": "2.19.3", "react-dom": "18.2.0", - "react-redux": "8.1.3", - "redux": "4.2.1", + "react-redux": "9.0.4", + "redux": "5.0.1", "redux-immutable-state-invariant": "2.1.0", "redux-thunk": "3.1.0", "takeme": "0.12.0", @@ -78,12 +78,12 @@ "@types/enzyme": "3.10.12", "@types/jest": "29.5.11", "@types/readable-stream": "4.0.10", - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", + "@typescript-eslint/eslint-plugin": "6.16.0", + "@typescript-eslint/parser": "6.16.0", "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", "coveralls": "3.1.1", "enzyme": "3.11.0", - "eslint": "8.55.0", + "eslint": "8.56.0", "eslint-plugin-eslint-comments": "3.2.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jest": "27.6.0", From 42bc5f3f8a33174acfb043a14e4ce7d4a041fb11 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Thu, 28 Dec 2023 15:50:37 -0800 Subject: [PATCH 2/8] fix blinky ruby throttle test --- spec/controllers/api/ai/ai_controller_spec.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/spec/controllers/api/ai/ai_controller_spec.rb b/spec/controllers/api/ai/ai_controller_spec.rb index 43f98cc342..d6e83779c3 100644 --- a/spec/controllers/api/ai/ai_controller_spec.rb +++ b/spec/controllers/api/ai/ai_controller_spec.rb @@ -119,11 +119,17 @@ def chunk(content, done=nil) expect(response.status).to eq(200) expect(response.body).to eq("title") - (0..20).map do |_| + statuses = [] + bodies = [] + + (0..30).map do |_| post :create, body: payload.to_json + statuses.push(response.status) + bodies.push(response.body) end - expect(response.status).to eq(403) - expect(response.body).to eq({error: "Too many requests. Try again later."}.to_json) + if statuses.last() != 403 then puts statuses.join(" ") end + expect(statuses).to include(403) + expect(bodies).to include({error: "Too many requests. Try again later."}.to_json) end end From 1d47bedb0f84a8522750475bf3238c0324b34826 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Thu, 28 Dec 2023 17:01:32 -0800 Subject: [PATCH 3/8] quiet test warning --- frontend/farm_designer/map/__tests__/actions_test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/farm_designer/map/__tests__/actions_test.ts b/frontend/farm_designer/map/__tests__/actions_test.ts index 88e2d16294..d67f3064e3 100644 --- a/frontend/farm_designer/map/__tests__/actions_test.ts +++ b/frontend/farm_designer/map/__tests__/actions_test.ts @@ -194,7 +194,7 @@ describe("clickMapPlant", () => { mockPath = Path.mock(Path.plants("select")); const state = fakeState(); const plant = fakePlant(); - plant.uuid = "fakePlantUuid"; + plant.uuid = "Point.fakePlantUuid"; state.resources = buildResourceIndex([plant]); const dispatch = jest.fn(); const getState: GetState = jest.fn(() => state); @@ -209,7 +209,7 @@ describe("clickMapPlant", () => { mockPath = Path.mock(Path.plants("select")); const state = fakeState(); const plant = fakePlant(); - plant.uuid = "fakePlantUuid"; + plant.uuid = "Point.fakePlantUuid"; state.resources = buildResourceIndex([plant]); state.resources.consumers.farm_designer.selectedPoints = [plant.uuid]; const dispatch = jest.fn(); From 804c898e74b8a10b3228f0dd8a26dd200ae63077 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Wed, 3 Jan 2024 09:43:33 -0800 Subject: [PATCH 4/8] make name field values unique --- frontend/controls/pin_form_fields.tsx | 2 +- frontend/points/create_points.tsx | 2 +- frontend/points/point_edit_actions.tsx | 2 +- frontend/saved_gardens/garden_snapshot.tsx | 2 +- frontend/settings/account/account_settings.tsx | 2 +- frontend/settings/fbos_settings/name_row.tsx | 2 +- frontend/tools/add_tool.tsx | 2 +- frontend/tools/edit_tool.tsx | 2 +- frontend/zones/edit_zone.tsx | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/controls/pin_form_fields.tsx b/frontend/controls/pin_form_fields.tsx index 9337fccb5c..af298dcfdd 100644 --- a/frontend/controls/pin_form_fields.tsx +++ b/frontend/controls/pin_form_fields.tsx @@ -19,7 +19,7 @@ interface NameInputBoxProps { export const NameInputBox = (props: NameInputBoxProps) => props.dispatch(edit(props.resource, { diff --git a/frontend/points/create_points.tsx b/frontend/points/create_points.tsx index 86bcad2f86..3d6c4375b9 100644 --- a/frontend/points/create_points.tsx +++ b/frontend/points/create_points.tsx @@ -220,7 +220,7 @@ export class RawCreatePoints diff --git a/frontend/points/point_edit_actions.tsx b/frontend/points/point_edit_actions.tsx index a6ca2a2cbc..b9f8b00b0f 100644 --- a/frontend/points/point_edit_actions.tsx +++ b/frontend/points/point_edit_actions.tsx @@ -140,7 +140,7 @@ export const EditPointName = (props: EditPointNameProps) => props.updatePoint({ name: e.currentTarget.value })} /> diff --git a/frontend/saved_gardens/garden_snapshot.tsx b/frontend/saved_gardens/garden_snapshot.tsx index 7b350891f2..0992a424d1 100644 --- a/frontend/saved_gardens/garden_snapshot.tsx +++ b/frontend/saved_gardens/garden_snapshot.tsx @@ -42,7 +42,7 @@ export class GardenSnapshot render() { return
- this.setState({ gardenName: e.currentTarget.value })} value={this.state.gardenName} /> diff --git a/frontend/settings/account/account_settings.tsx b/frontend/settings/account/account_settings.tsx index 41e0e47df3..c05d6e8afe 100644 --- a/frontend/settings/account/account_settings.tsx +++ b/frontend/settings/account/account_settings.tsx @@ -40,7 +40,7 @@ export const AccountSettings = (props: AccountSettingsProps) => { props.dispatch(edit( diff --git a/frontend/settings/fbos_settings/name_row.tsx b/frontend/settings/fbos_settings/name_row.tsx index 2b0f87fd61..dd3fe20133 100644 --- a/frontend/settings/fbos_settings/name_row.tsx +++ b/frontend/settings/fbos_settings/name_row.tsx @@ -9,7 +9,7 @@ import { getModifiedClassNameSpecifyDefault } from "../default_values"; export class NameRow extends React.Component { NameInput = () => - { env={this.props.env} /> this.setState({ toolName: e.currentTarget.value })} /> {reduceToolName(toolName) == ToolName.wateringNozzle && diff --git a/frontend/tools/edit_tool.tsx b/frontend/tools/edit_tool.tsx index 1d062ed9e7..b56c7a1c1f 100644 --- a/frontend/tools/edit_tool.tsx +++ b/frontend/tools/edit_tool.tsx @@ -134,7 +134,7 @@ export class RawEditTool extends React.Component { saveFarmwareEnv={this.props.saveFarmwareEnv} env={this.props.env} /> - this.setState({ toolName: e.currentTarget.value })} /> {reduceToolName(toolName) == ToolName.wateringNozzle && diff --git a/frontend/zones/edit_zone.tsx b/frontend/zones/edit_zone.tsx index 0029a782a3..f3f15007d4 100644 --- a/frontend/zones/edit_zone.tsx +++ b/frontend/zones/edit_zone.tsx @@ -50,7 +50,7 @@ export class RawEditZone extends React.Component { {zone ?
- { this.props.dispatch(edit(zone, { name: e.currentTarget.value })); From 4a1dc1dc2b3a9388ffff8ad5fab0dc7431023d9d Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Wed, 3 Jan 2024 10:12:56 -0800 Subject: [PATCH 5/8] cache grid starting coordinates --- frontend/__test_support__/fake_designer_state.ts | 1 + frontend/constants.ts | 1 + frontend/farm_designer/__tests__/reducer_test.ts | 10 ++++++++++ frontend/farm_designer/interfaces.ts | 1 + frontend/farm_designer/reducer.ts | 5 +++++ frontend/plants/grid/__tests__/plant_grid_test.tsx | 10 ++++++++++ frontend/plants/grid/plant_grid.tsx | 10 ++++++++-- 7 files changed, 36 insertions(+), 2 deletions(-) diff --git a/frontend/__test_support__/fake_designer_state.ts b/frontend/__test_support__/fake_designer_state.ts index b05c304aec..957f5b70b2 100644 --- a/frontend/__test_support__/fake_designer_state.ts +++ b/frontend/__test_support__/fake_designer_state.ts @@ -40,6 +40,7 @@ export const fakeDesignerState = (): DesignerState => ({ hoveredMapImage: undefined, cameraViewGridId: undefined, gridIds: [], + gridStart: { x: 100, y: 100 }, soilHeightLabels: false, profileOpen: false, profileAxis: "x", diff --git a/frontend/constants.ts b/frontend/constants.ts index f6572b1ee0..e653fa49bb 100644 --- a/frontend/constants.ts +++ b/frontend/constants.ts @@ -2391,6 +2391,7 @@ export enum Actions { HIGHLIGHT_MAP_IMAGE = "HIGHLIGHT_MAP_IMAGE", SHOW_CAMERA_VIEW_POINTS = "SHOW_CAMERA_VIEW_POINTS", TOGGLE_GRID_ID = "TOGGLE_GRID_ID", + SET_GRID_START = "SET_GRID_START", TOGGLE_SOIL_HEIGHT_LABELS = "TOGGLE_SOIL_HEIGHT_LABELS", SET_PROFILE_OPEN = "SET_PROFILE_OPEN", SET_PROFILE_AXIS = "SET_PROFILE_AXIS", diff --git a/frontend/farm_designer/__tests__/reducer_test.ts b/frontend/farm_designer/__tests__/reducer_test.ts index 71ac71edd4..7c7558d688 100644 --- a/frontend/farm_designer/__tests__/reducer_test.ts +++ b/frontend/farm_designer/__tests__/reducer_test.ts @@ -503,6 +503,16 @@ describe("designer reducer", () => { expect(newState.gridIds).toEqual([]); }); + it("sets grid start", () => { + const state = oldState(); + state.gridStart = { x: 100, y: 100 }; + const action: ReduxAction> = { + type: Actions.SET_GRID_START, payload: { x: 200, y: 300 } + }; + const newState = designer(state, action); + expect(newState.gridStart).toEqual({ x: 200, y: 300 }); + }); + it("toggle soil height labels", () => { const state = oldState(); state.soilHeightLabels = false; diff --git a/frontend/farm_designer/interfaces.ts b/frontend/farm_designer/interfaces.ts index bbf966016a..4078f9f8bc 100644 --- a/frontend/farm_designer/interfaces.ts +++ b/frontend/farm_designer/interfaces.ts @@ -181,6 +181,7 @@ export interface DesignerState { hoveredMapImage: number | undefined; cameraViewGridId: string | undefined; gridIds: string[]; + gridStart: Record<"x" | "y", number>; soilHeightLabels: boolean; profileOpen: boolean; profileAxis: "x" | "y"; diff --git a/frontend/farm_designer/reducer.ts b/frontend/farm_designer/reducer.ts index a71d8648d8..02e1a2338f 100644 --- a/frontend/farm_designer/reducer.ts +++ b/frontend/farm_designer/reducer.ts @@ -54,6 +54,7 @@ export const initialState: DesignerState = { hoveredMapImage: undefined, cameraViewGridId: undefined, gridIds: [], + gridStart: { x: 100, y: 100 }, soilHeightLabels: false, profileOpen: false, profileAxis: "x", @@ -262,6 +263,10 @@ export const designer = generateReducer(initialState) : s.gridIds.concat(payload); return s; }) + .add>(Actions.SET_GRID_START, (s, { payload }) => { + s.gridStart = payload; + return s; + }) .add(Actions.TOGGLE_SOIL_HEIGHT_LABELS, (s) => { s.soilHeightLabels = !s.soilHeightLabels; return s; diff --git a/frontend/plants/grid/__tests__/plant_grid_test.tsx b/frontend/plants/grid/__tests__/plant_grid_test.tsx index d09bb81c74..329563ede5 100644 --- a/frontend/plants/grid/__tests__/plant_grid_test.tsx +++ b/frontend/plants/grid/__tests__/plant_grid_test.tsx @@ -176,6 +176,16 @@ describe("", () => { expect(wrapper.state().grid.numPlantsH).toEqual(6); }); + it("handles data changes: starting coordinates", () => { + const props = fakeProps(); + const wrapper = mount(); + wrapper.instance().onChange("startX", 600); + expect(wrapper.state().grid.startX).toEqual(600); + expect(props.dispatch).toHaveBeenCalledWith({ + type: Actions.SET_GRID_START, payload: { x: 600, y: 100 }, + }); + }); + it("uses current position", () => { const props = fakeProps(); const wrapper = mount(); diff --git a/frontend/plants/grid/plant_grid.tsx b/frontend/plants/grid/plant_grid.tsx index 0eb7691656..6a1449a54c 100644 --- a/frontend/plants/grid/plant_grid.tsx +++ b/frontend/plants/grid/plant_grid.tsx @@ -29,9 +29,10 @@ export class PlantGrid extends React.Component { get initGridState() { const spread = (this.props.spread || DEFAULT_PLANT_RADIUS) * 10; + const gridStart = this.props.designer?.gridStart || { x: 100, y: 100 }; return { - startX: 100, - startY: 100, + startX: gridStart.x, + startY: gridStart.y, spacingH: spread, spacingV: spread, numPlantsH: 2, @@ -46,6 +47,11 @@ export class PlantGrid extends React.Component { onChange = (key: PlantGridKey, val: number) => { const grid = { ...this.state.grid, [key]: val }; + ["startX", "startY"].includes(key) && + this.props.dispatch({ + type: Actions.SET_GRID_START, + payload: { x: grid.startX, y: grid.startY }, + }); this.setState({ grid }, this.performPreview()); }; From af492785d9a0ff1b7db3c6d2fc5c37901f46f43d Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Thu, 18 Jan 2024 15:14:22 -0800 Subject: [PATCH 6/8] add 3D electronics box top UI --- config/application.rb | 1 + ...d_electronics_box_top_to_web_app_config.rb | 9 + db/structure.sql | 6 +- .../__test_support__/additional_mocks.tsx | 7 + .../__test_support__/fake_state/resources.ts | 4 + frontend/constants.ts | 7 +- frontend/controls/controls.tsx | 1 + .../peripherals/__tests__/index_test.tsx | 1 + frontend/controls/peripherals/index.tsx | 29 +- frontend/controls/peripherals/interfaces.ts | 2 + frontend/css/global.scss | 76 +++- frontend/session_keys.ts | 5 +- .../settings/account/account_settings.tsx | 5 + frontend/settings/default_values.ts | 1 + frontend/settings/maybe_highlight.tsx | 1 + .../pin_bindings/__tests__/actions_test.ts | 105 +++++ .../__tests__/box_top_gpio_diagram_test.tsx | 81 +--- .../pin_bindings/__tests__/box_top_test.tsx | 36 ++ .../pin_bindings/__tests__/model_test.tsx | 231 +++++++++++ frontend/settings/pin_bindings/actions.ts | 101 +++++ frontend/settings/pin_bindings/box_top.tsx | 11 + .../pin_bindings/box_top_gpio_diagram.tsx | 107 +---- frontend/settings/pin_bindings/interfaces.ts | 14 + frontend/settings/pin_bindings/model.tsx | 381 ++++++++++++++++++ .../pin_bindings/pin_binding_input_group.tsx | 52 +-- frontend/wizard/__tests__/checks_test.tsx | 1 + frontend/wizard/__tests__/step_test.tsx | 2 +- frontend/wizard/checks.tsx | 13 +- frontend/wizard/step.tsx | 1 + jest.config.js | 3 +- package.json | 7 +- public/3D/lib/draco_decoder.wasm | Bin 0 -> 192420 bytes public/3D/lib/draco_wasm_wrapper.js | 116 ++++++ public/3D/models/box.glb | Bin 0 -> 22872 bytes public/3D/models/led_indicator.glb | Bin 0 -> 2516 bytes public/3D/models/push_button.glb | Bin 0 -> 2572 bytes tsconfig.json | 1 + 37 files changed, 1212 insertions(+), 206 deletions(-) create mode 100644 db/migrate/20240118204046_add_enable3d_electronics_box_top_to_web_app_config.rb create mode 100644 frontend/settings/pin_bindings/__tests__/actions_test.ts create mode 100644 frontend/settings/pin_bindings/__tests__/box_top_test.tsx create mode 100644 frontend/settings/pin_bindings/__tests__/model_test.tsx create mode 100644 frontend/settings/pin_bindings/actions.ts create mode 100644 frontend/settings/pin_bindings/box_top.tsx create mode 100644 frontend/settings/pin_bindings/model.tsx create mode 100644 public/3D/lib/draco_decoder.wasm create mode 100644 public/3D/lib/draco_wasm_wrapper.js create mode 100644 public/3D/models/box.glb create mode 100644 public/3D/models/led_indicator.glb create mode 100644 public/3D/models/push_button.glb diff --git a/config/application.rb b/config/application.rb index 0ed0d00838..4d7dfbd143 100755 --- a/config/application.rb +++ b/config/application.rb @@ -126,6 +126,7 @@ class Application < Rails::Application "'unsafe-inline'", "'unsafe-eval'", "'self'", + "blob:", # 3D ], style_src: %w( maxcdn.bootstrapcdn.com diff --git a/db/migrate/20240118204046_add_enable3d_electronics_box_top_to_web_app_config.rb b/db/migrate/20240118204046_add_enable3d_electronics_box_top_to_web_app_config.rb new file mode 100644 index 0000000000..7e8c041cd2 --- /dev/null +++ b/db/migrate/20240118204046_add_enable3d_electronics_box_top_to_web_app_config.rb @@ -0,0 +1,9 @@ +class AddEnable3dElectronicsBoxTopToWebAppConfig < ActiveRecord::Migration[6.1] + def up + add_column :web_app_configs, :enable_3d_electronics_box_top, :boolean, default: true + end + + def down + remove_column :web_app_configs, :enable_3d_electronics_box_top + end +end diff --git a/db/structure.sql b/db/structure.sql index e3b9308799..71deecdafd 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2032,7 +2032,8 @@ CREATE TABLE public.web_app_configs ( go_button_axes character varying(3) DEFAULT 'XY'::character varying NOT NULL, show_uncropped_camera_view_area boolean DEFAULT false, default_plant_depth integer DEFAULT 5, - show_missed_step_plot boolean DEFAULT false + show_missed_step_plot boolean DEFAULT false, + enable_3d_electronics_box_top boolean DEFAULT true ); @@ -3979,6 +3980,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230712201622'), ('20230714010144'), ('20230714173031'), -('20230808192946'); +('20230808192946'), +('20240118204046'); diff --git a/frontend/__test_support__/additional_mocks.tsx b/frontend/__test_support__/additional_mocks.tsx index e3b6c6b62f..4afb22db17 100644 --- a/frontend/__test_support__/additional_mocks.tsx +++ b/frontend/__test_support__/additional_mocks.tsx @@ -35,3 +35,10 @@ jest.mock("../history", () => ({ push: jest.fn(), getPathArray: () => [], })); + +window.ResizeObserver = (() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any +})) as any; diff --git a/frontend/__test_support__/fake_state/resources.ts b/frontend/__test_support__/fake_state/resources.ts index 59e1c65ebc..6781372928 100644 --- a/frontend/__test_support__/fake_state/resources.ts +++ b/frontend/__test_support__/fake_state/resources.ts @@ -36,6 +36,9 @@ import { } from "farmbot/dist/resources/api_resources"; import { MessageType } from "../../sequences/interfaces"; import { TaggedPointGroup } from "../../resources/interfaces"; +import { + BooleanConfigKey as BooleanWebAppConfigKey, +} from "farmbot/dist/resources/configs/web_app"; export const resources: Everything["resources"] = buildResourceIndex(); let idCounter = 1; @@ -333,6 +336,7 @@ export function fakeWebAppConfig(): TaggedWebAppConfig { display_map_missed_steps: false, display_trail: false, dynamic_map: false, + ["enable_3d_electronics_box_top" as BooleanWebAppConfigKey]: true, encoder_figure: false, go_button_axes: "XY", hide_webcam_widget: false, diff --git a/frontend/constants.ts b/frontend/constants.ts index e653fa49bb..d2d3a4c00a 100644 --- a/frontend/constants.ts +++ b/frontend/constants.ts @@ -782,6 +782,10 @@ export namespace Content { trim(`If not using sensors, use this setting to remove the panel from the Farm Designer.`); + export const ENABLE_3D_ELECTRONICS_BOX_TOP = + trim(`Show a 3D model of FarmBot's electronics box instead of a 2D view + in the Peripherals tab of the Controls pop-up.`); + export const BROWSER_SPEAK_LOGS = trim(`Have the browser also read aloud log messages on the "Speak" channel that are spoken by FarmBot.`); @@ -1868,7 +1872,7 @@ export namespace SetupWizardContent { trim(`Customize which Action or Sequence you want FarmBot to execute when you press Button 3, 4, or 5 on the electronics box. To start, we recommend setting Button 5 to the 'Find Home' sequence. You can change - this later from the controls panel.`); + this later from the controls pop-up.`); export const PROBLEM_GETTING_IMAGE = trim(`There is a 'camera not detected' or 'problem getting image' error @@ -2197,6 +2201,7 @@ export enum DeviceSetting { showSecondsInTime = `Show seconds in time`, hideWebcamWidget = `Hide Webcam widget`, hideSensorsPanel = `Hide Sensors panel`, + enable3dElectronicsBox = `Enable 3D electronics box`, readSpeakLogsInBrowser = `Read speak logs in browser`, landingPage = `Landing page`, browserFarmbotActivityBeep = `Browser FarmBot activity beep`, diff --git a/frontend/controls/controls.tsx b/frontend/controls/controls.tsx index 3a2e1b5585..f1d8fe1b92 100644 --- a/frontend/controls/controls.tsx +++ b/frontend/controls/controls.tsx @@ -83,6 +83,7 @@ export class ControlsPanel extends React.Component { Peripherals = () => { return
", () => { dispatch: jest.fn(), firmwareHardware: undefined, resources: buildResourceIndex([]).index, + getConfigValue: () => false, }); it("renders", () => { diff --git a/frontend/controls/peripherals/index.tsx b/frontend/controls/peripherals/index.tsx index ab76f0412f..b9dfcb906f 100644 --- a/frontend/controls/peripherals/index.tsx +++ b/frontend/controls/peripherals/index.tsx @@ -10,8 +10,10 @@ import { Content } from "../../constants"; import { uniq, isNumber } from "lodash"; import { t } from "../../i18next_wrapper"; import { DIGITAL } from "farmbot"; -import { isBotOnlineFromState } from "../../devices/must_be_online"; -import { BoxTopButtons } from "../../settings/pin_bindings/box_top_gpio_diagram"; +import { isBotOnline } from "../../devices/must_be_online"; +import { getStatus } from "../../connectivity/reducer_support"; +import { BoxTop } from "../../settings/pin_bindings/box_top"; +import { BooleanSetting } from "../../session_keys"; export class Peripherals extends React.Component { @@ -20,9 +22,16 @@ export class Peripherals this.state = { isEditing: false }; } + get botOnline() { + const { hardware, connectivity } = this.props.bot; + const { sync_status } = hardware.informational_settings; + const botToMqttStatus = getStatus(connectivity.uptime["bot.mqtt"]); + return isBotOnline(sync_status, botToMqttStatus); + } + get disabled() { return !!this.props.bot.hardware.informational_settings.busy - || !isBotOnlineFromState(this.props.bot); + || !this.botOnline; } toggle = () => this.setState({ isEditing: !this.state.isEditing }); @@ -114,15 +123,15 @@ export class Peripherals : t("Edit"); return
{!this.props.hidePinBindings && - } -
+ firmwareHardware={this.props.firmwareHardware} + bot={this.props.bot} + botOnline={this.botOnline} />} 0 || isEditing} graphic={EmptyStateGraphic.regimens} diff --git a/frontend/controls/peripherals/interfaces.ts b/frontend/controls/peripherals/interfaces.ts index af7097aa10..47b96ac51b 100644 --- a/frontend/controls/peripherals/interfaces.ts +++ b/frontend/controls/peripherals/interfaces.ts @@ -1,6 +1,7 @@ import { Pins, TaggedPeripheral, FirmwareHardware } from "farmbot"; import { BotState } from "../../devices/interfaces"; import { ResourceIndex } from "../../resources/interfaces"; +import { GetWebAppConfigValue } from "../../config_storage/actions"; export interface PeripheralState { isEditing: boolean; @@ -26,4 +27,5 @@ export interface PeripheralsProps { firmwareHardware: FirmwareHardware | undefined; resources: ResourceIndex; hidePinBindings?: boolean; + getConfigValue: GetWebAppConfigValue; } diff --git a/frontend/css/global.scss b/frontend/css/global.scss index b3c9e5ddaa..29b9c61808 100644 --- a/frontend/css/global.scss +++ b/frontend/css/global.scss @@ -858,11 +858,6 @@ fieldset { margin-top: 2rem; } -.peripherals-tab { - overflow-y: scroll; - overflow-x: hidden; -} - .webcam-stream-unavailable { position: relative; width: 100%; @@ -2011,6 +2006,69 @@ ul { } } +.electronics-box-3d-model { + margin-bottom: 1rem; + .led-label, + .btn-label { + display: block; + margin: auto; + width: max-content; + color: $off_white; + font-size: 1rem; + background: $dark_gray; + padding: 0.25rem 0.5rem; + border-radius: 0.5rem; + white-space: normal; + max-height: 3.4rem; + overflow-y: hidden; + font-weight: 700; + line-height: 1; + text-align: center; + &.hovered { + background: $black; + } + } + .led-label { + max-width: 7rem; + } + .btn-label { + max-width: 5.8rem; + } + .filter-search { + max-width: 5.5rem; + .fa-caret-down { + bottom: -0.5rem; + right: 0.25rem; + color: $off_white; + } + button { + padding: 0.25rem; + background: $dark_gray !important; + border-radius: 0.5rem; + height: max-content !important; + min-height: 0; + &:hover { + background: $dark_gray !important; + } + } + span { + color: $off_white; + white-space: normal !important; + text-overflow: revert !important; + font-weight: 700; + font-size: 1rem; + text-align: center; + margin-right: 0.5rem; + } + } + div { + z-index: 0 !important; + } + canvas { + height: 23rem; + } +} + .webcam-widget { .no-flipper-image-container { background: none !important; @@ -2030,7 +2088,6 @@ ul { } .peripheral-list { - margin-top: 1rem; label { margin-top: 0 !important; } @@ -2043,7 +2100,12 @@ ul { } } -.box-top-wrapper { +.peripheral-form, +.peripheral-list { + padding-top: 1rem; +} + +.box-top-2d-wrapper { margin-top: 2rem; .box-top-leds, .box-top-buttons { diff --git a/frontend/session_keys.ts b/frontend/session_keys.ts index 3c9033dfb2..73e225a2ce 100644 --- a/frontend/session_keys.ts +++ b/frontend/session_keys.ts @@ -4,7 +4,8 @@ import { StringConfigKey as WebAppStringConfigKey, } from "farmbot/dist/resources/configs/web_app"; -type WebAppBooleanConfigKeyAll = WebAppBooleanConfigKey; +type WebAppBooleanConfigKeyAll = WebAppBooleanConfigKey + | "enable_3d_electronics_box_top"; type WebAppNumberConfigKeyAll = WebAppNumberConfigKey; type WebAppStringConfigKeyAll = WebAppStringConfigKey; @@ -62,6 +63,8 @@ export const BooleanSetting: BooleanSettings = { disable_i18n: "disable_i18n", hide_webcam_widget: "hide_webcam_widget", hide_sensors: "hide_sensors", + enable_3d_electronics_box_top: + "enable_3d_electronics_box_top" as WebAppBooleanConfigKey, enable_browser_speak: "enable_browser_speak", discard_unsaved: "discard_unsaved", time_format_24_hour: "time_format_24_hour", diff --git a/frontend/settings/account/account_settings.tsx b/frontend/settings/account/account_settings.tsx index c05d6e8afe..c29c3db47a 100644 --- a/frontend/settings/account/account_settings.tsx +++ b/frontend/settings/account/account_settings.tsx @@ -155,6 +155,11 @@ const APP_SETTINGS = (): SettingDescriptionProps[] => ([ description: Content.HIDE_SENSORS_WIDGET, setting: BooleanSetting.hide_sensors, }, + { + title: DeviceSetting.enable3dElectronicsBox, + description: Content.ENABLE_3D_ELECTRONICS_BOX_TOP, + setting: BooleanSetting.enable_3d_electronics_box_top, + }, { title: DeviceSetting.readSpeakLogsInBrowser, description: Content.BROWSER_SPEAK_LOGS, diff --git a/frontend/settings/default_values.ts b/frontend/settings/default_values.ts index d5fe4e281d..6ba086369f 100644 --- a/frontend/settings/default_values.ts +++ b/frontend/settings/default_values.ts @@ -19,6 +19,7 @@ const DEFAULT_WEB_APP_CONFIG_VALUES: Record = { disable_i18n: false, display_trail: true, dynamic_map: false, + ["enable_3d_electronics_box_top" as Key]: true, encoder_figure: false, go_button_axes: "XY", hide_webcam_widget: false, diff --git a/frontend/settings/maybe_highlight.tsx b/frontend/settings/maybe_highlight.tsx index fbf495f4df..0eb1bb8204 100644 --- a/frontend/settings/maybe_highlight.tsx +++ b/frontend/settings/maybe_highlight.tsx @@ -205,6 +205,7 @@ const APP_SETTINGS = [ DeviceSetting.showSecondsInTime, DeviceSetting.hideWebcamWidget, DeviceSetting.hideSensorsPanel, + DeviceSetting.enable3dElectronicsBox, DeviceSetting.readSpeakLogsInBrowser, DeviceSetting.landingPage, DeviceSetting.browserFarmbotActivityBeep, diff --git a/frontend/settings/pin_bindings/__tests__/actions_test.ts b/frontend/settings/pin_bindings/__tests__/actions_test.ts new file mode 100644 index 0000000000..fcc70e26c5 --- /dev/null +++ b/frontend/settings/pin_bindings/__tests__/actions_test.ts @@ -0,0 +1,105 @@ +jest.mock("../../../api/crud", () => ({ + overwrite: jest.fn(), + save: jest.fn(), + destroy: jest.fn(), + initSave: jest.fn(), +})); + +import { + buildResourceIndex, +} from "../../../__test_support__/resource_index_builder"; +import { + fakePinBinding, fakeSequence, +} from "../../../__test_support__/fake_state/resources"; +import { + PinBindingType, StandardPinBinding, +} from "farmbot/dist/resources/api_resources"; +import { destroy, overwrite, initSave, save } from "../../../api/crud"; +import { SetPinBindingProps, setPinBinding } from "../actions"; +import { PinBindingListItems } from "../interfaces"; +import { error } from "../../../toast/toast"; + +describe("setPinBinding()", () => { + const fakeProps = (): SetPinBindingProps => { + const pinBinding = fakePinBinding(); + pinBinding.body.pin_num = 20; + pinBinding.body.binding_type = PinBindingType.standard; + (pinBinding.body as StandardPinBinding).sequence_id = 1; + const sequence = fakeSequence(); + sequence.body.id = 1; + sequence.body.name = "my sequence"; + const resources = buildResourceIndex([sequence, pinBinding]).index; + const binding: PinBindingListItems = { + pin_number: pinBinding.body.pin_num, + sequence_id: sequence.body.id, + uuid: pinBinding.uuid, + binding_type: PinBindingType.standard, + special_action: undefined, + }; + return { + binding, + dispatch: jest.fn(), + resources, + pinNumber: 20, + }; + }; + + it("un-binds pin", () => { + const p = fakeProps(); + setPinBinding(p)({ + isNull: true, label: "", value: "", + }); + expect(error).not.toHaveBeenCalled(); + expect(initSave).not.toHaveBeenCalled(); + expect(overwrite).not.toHaveBeenCalled(); + expect(destroy).toHaveBeenCalled(); + expect(save).not.toHaveBeenCalled(); + }); + + it("re-binds pin: standard", () => { + const p = fakeProps(); + setPinBinding(p)({ + headingId: PinBindingType.standard, label: "", value: 1, + }); + expect(error).not.toHaveBeenCalled(); + expect(destroy).not.toHaveBeenCalled(); + expect(initSave).not.toHaveBeenCalled(); + expect(overwrite).toHaveBeenCalledWith(expect.any(Object), + expect.objectContaining({ + pin_num: 20, sequence_id: 1, binding_type: PinBindingType.standard, + special_action: undefined, + })); + expect(save).toHaveBeenCalled(); + }); + + it("re-binds pin: special", () => { + const p = fakeProps(); + setPinBinding(p)({ + headingId: PinBindingType.special, label: "", value: "sync", + }); + expect(error).not.toHaveBeenCalled(); + expect(destroy).not.toHaveBeenCalled(); + expect(initSave).not.toHaveBeenCalled(); + expect(overwrite).toHaveBeenCalledWith(expect.any(Object), + expect.objectContaining({ + pin_num: 20, special_action: "sync", binding_type: PinBindingType.special, + sequence_id: undefined, + })); + expect(save).toHaveBeenCalled(); + }); + + it("binds new pin", () => { + const p = fakeProps(); + p.pinNumber = 5; + p.binding = undefined; + setPinBinding(p)({ + headingId: PinBindingType.special, label: "", value: "sync", + }); + expect(error).not.toHaveBeenCalled(); + expect(destroy).not.toHaveBeenCalled(); + expect(overwrite).not.toHaveBeenCalled(); + expect(initSave).toHaveBeenCalledWith("PinBinding", { + pin_num: 5, special_action: "sync", binding_type: PinBindingType.special, + }); + }); +}); diff --git a/frontend/settings/pin_bindings/__tests__/box_top_gpio_diagram_test.tsx b/frontend/settings/pin_bindings/__tests__/box_top_gpio_diagram_test.tsx index c074a26957..b4e04d5cf1 100644 --- a/frontend/settings/pin_bindings/__tests__/box_top_gpio_diagram_test.tsx +++ b/frontend/settings/pin_bindings/__tests__/box_top_gpio_diagram_test.tsx @@ -3,18 +3,10 @@ jest.mock("../../../devices/actions", () => ({ sendRPC: jest.fn(), })); -jest.mock("../../../api/crud", () => ({ - overwrite: jest.fn(), - save: jest.fn(), - destroy: jest.fn(), - initSave: jest.fn(), -})); - import React from "react"; import { mount } from "enzyme"; import { BoxTopButtons, - BoxTopButtonsProps, BoxTopGpioDiagram, BoxTopGpioDiagramProps, } from "../box_top_gpio_diagram"; @@ -29,7 +21,8 @@ import { PinBindingSpecialAction, PinBindingType, SpecialPinBinding, StandardPinBinding, } from "farmbot/dist/resources/api_resources"; -import { destroy, overwrite, initSave, save } from "../../../api/crud"; +import { BoxTopBaseProps } from "../interfaces"; +import { bot } from "../../../__test_support__/fake_state/bot"; describe("", () => { const fakeProps = (): BoxTopGpioDiagramProps => ({ @@ -76,7 +69,7 @@ describe("", () => { }); describe("", () => { - const fakeProps = (): BoxTopButtonsProps => { + const fakeProps = (): BoxTopBaseProps => { const pinBinding = fakePinBinding(); pinBinding.body.pin_num = 20; pinBinding.body.binding_type = PinBindingType.standard; @@ -85,17 +78,32 @@ describe("", () => { sequence.body.id = 1; sequence.body.name = "my sequence"; const resources = buildResourceIndex([sequence, pinBinding]).index; + bot.hardware.informational_settings.sync_status = "synced"; + bot.hardware.informational_settings.locked = false; return { firmwareHardware: "arduino", isEditing: true, dispatch: jest.fn(), resources, botOnline: true, - syncStatus: "synced", - locked: false, + bot, }; }; + it("renders: genesis", () => { + const p = fakeProps(); + p.firmwareHardware = "arduino"; + const wrapper = mount(); + expect(wrapper.find("#button").length).toEqual(9); + }); + + it("renders: express", () => { + const p = fakeProps(); + p.firmwareHardware = "express_k10"; + const wrapper = mount(); + expect(wrapper.find("#button").length).toEqual(1); + }); + it("renders: not editing", () => { const p = fakeProps(); p.isEditing = false; @@ -107,58 +115,13 @@ describe("", () => { it("renders: blinking", () => { const p = fakeProps(); - p.syncStatus = "syncing"; - p.locked = true; + p.bot.hardware.informational_settings.sync_status = "syncing"; + p.bot.hardware.informational_settings.locked = true; const wrapper = mount(); expect(wrapper.find(".fast-blink").length).toEqual(1); expect(wrapper.find(".slow-blink").length).toEqual(1); }); - it("un-binds pin", () => { - const wrapper = mount(); - wrapper.instance().bind(20)({ - isNull: true, label: "", value: "", - }); - expect(destroy).toHaveBeenCalled(); - expect(save).not.toHaveBeenCalled(); - }); - - it("re-binds pin: standard", () => { - const wrapper = mount(); - wrapper.instance().bind(20)({ - headingId: PinBindingType.standard, label: "", value: 1, - }); - expect(overwrite).toHaveBeenCalledWith(expect.any(Object), - expect.objectContaining({ - pin_num: 20, sequence_id: 1, binding_type: PinBindingType.standard, - special_action: undefined, - })); - expect(save).toHaveBeenCalled(); - }); - - it("re-binds pin: special", () => { - const wrapper = mount(); - wrapper.instance().bind(20)({ - headingId: PinBindingType.special, label: "", value: "sync", - }); - expect(overwrite).toHaveBeenCalledWith(expect.any(Object), - expect.objectContaining({ - pin_num: 20, special_action: "sync", binding_type: PinBindingType.special, - sequence_id: undefined, - })); - expect(save).toHaveBeenCalled(); - }); - - it("binds new pin", () => { - const wrapper = mount(); - wrapper.instance().bind(5)({ - headingId: PinBindingType.special, label: "", value: "sync", - }); - expect(initSave).toHaveBeenCalledWith("PinBinding", { - pin_num: 5, special_action: "sync", binding_type: PinBindingType.special, - }); - }); - it("executes sequence", () => { const wrapper = mount(); wrapper.find("#button").first().simulate("click"); diff --git a/frontend/settings/pin_bindings/__tests__/box_top_test.tsx b/frontend/settings/pin_bindings/__tests__/box_top_test.tsx new file mode 100644 index 0000000000..fd6c905b67 --- /dev/null +++ b/frontend/settings/pin_bindings/__tests__/box_top_test.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { mount } from "enzyme"; +import { BoxTop } from "../box_top"; +import { BoxTopProps } from "../interfaces"; +import { + buildResourceIndex, +} from "../../../__test_support__/resource_index_builder"; +import { bot } from "../../../__test_support__/fake_state/bot"; + +describe("", () => { + const fakeProps = (): BoxTopProps => ({ + threeDimensions: false, + firmwareHardware: "arduino", + isEditing: true, + dispatch: jest.fn(), + resources: buildResourceIndex().index, + botOnline: true, + bot, + }); + + it("renders 2D box", () => { + const p = fakeProps(); + p.threeDimensions = false; + const wrapper = mount(); + expect(wrapper.find(".box-top-2d-wrapper").length).toEqual(1); + expect(wrapper.find(".electronics-box-3d-model").length).toEqual(0); + }); + + it("renders 3D box", () => { + const p = fakeProps(); + p.threeDimensions = true; + const wrapper = mount(); + expect(wrapper.find(".box-top-2d-wrapper").length).toEqual(0); + expect(wrapper.find(".electronics-box-3d-model").length).toEqual(1); + }); +}); diff --git a/frontend/settings/pin_bindings/__tests__/model_test.tsx b/frontend/settings/pin_bindings/__tests__/model_test.tsx new file mode 100644 index 0000000000..a0fb7657ef --- /dev/null +++ b/frontend/settings/pin_bindings/__tests__/model_test.tsx @@ -0,0 +1,231 @@ +jest.mock("@react-three/drei", () => { + const useGLTF = jest.fn(() => ({ + nodes: { + Electronics_Box: { + geometry: jest.fn(), + material: { color: { set: jest.fn() } }, + }, + Electronics_Box_Gasket: { + geometry: jest.fn(), + material: { color: { set: jest.fn() } }, + }, + Electronics_Box_Lid: { + geometry: jest.fn(), + material: { color: { set: jest.fn() } }, + }, + ["Push_Button_-_Red"]: { + geometry: jest.fn(), + material: { color: { set: jest.fn() } }, + }, + LED: { + geometry: jest.fn(), + material: { color: { set: jest.fn() } }, + }, + }, + materials: { + [Material.box]: { + color: { set: jest.fn() }, + transparent: false, + }, + [Material.gasket]: { + color: { set: jest.fn() }, + transparent: false, + }, + [Material.lid]: { + color: { set: jest.fn() }, + transparent: false, + }, + [Material.button]: { + color: { set: jest.fn() }, + transparent: false, + }, + [Material.led]: { + color: { set: jest.fn() }, + transparent: false, + }, + }, + })); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (useGLTF as any).preload = jest.fn(); + return { + useGLTF, + Cylinder: () =>
, + Html: ({ children }: { children: ReactNode }) =>
{children}
, + PerspectiveCamera: () =>
, + useCursor: jest.fn(), + }; +}); + +let mockElapsedTime = 0; +jest.mock("@react-three/fiber", () => ({ + Canvas: () =>
, + ThreeEvent: jest.fn(), + useFrame: jest.fn(x => x({ + clock: { getElapsedTime: jest.fn(() => mockElapsedTime) } + })), +})); + +const mockSetColor = jest.fn(); +jest.mock("react", () => { + const originReact = jest.requireActual("react"); + const mockRef = jest.fn(() => ({ + current: { + material: { color: { set: mockSetColor } }, + setOptions: jest.fn(), + } + })); + return { + ...originReact, + useRef: mockRef, + }; +}); + +const lodash = require("lodash"); +lodash.debounce = jest.fn(x => x); + +jest.mock("../../../devices/actions", () => ({ + execSequence: jest.fn(), +})); + +import React, { ReactNode } from "react"; +import { mount } from "enzyme"; +import { ThreeEvent } from "@react-three/fiber"; +import { IColor, Material, Model, setZForAllInGroup } from "../model"; +import { + buildResourceIndex, +} from "../../../__test_support__/resource_index_builder"; +import { + StandardPinBinding, +} from "farmbot/dist/resources/api_resources"; +import { + fakePinBinding, fakeSequence, +} from "../../../__test_support__/fake_state/resources"; +import { bot } from "../../../__test_support__/fake_state/bot"; +import { execSequence } from "../../../devices/actions"; +import { ButtonPin } from "../list_and_label_support"; +import { BoxTopBaseProps } from "../interfaces"; + +describe("setZForAllInGroup()", () => { + it("sets z", () => { + const e = { + object: { + parent: { + children: [ + { name: "button-center", position: { z: 0 }, children: [] }, + ] + } + } + } as unknown as ThreeEvent; + setZForAllInGroup(e, 100); + expect(e.object.parent?.children[0].position.z).toEqual(100); + }); +}); + +describe("", () => { + const fakeProps = (): BoxTopBaseProps => { + const binding = fakePinBinding(); + binding.body.pin_num = ButtonPin.estop; + (binding.body as StandardPinBinding).sequence_id = 1; + const sequence = fakeSequence(); + sequence.body.id = 1; + sequence.body.name = "e-stop"; + return { + isEditing: false, + dispatch: jest.fn(), + resources: buildResourceIndex([binding, sequence]).index, + botOnline: true, + bot, + firmwareHardware: "arduino", + }; + }; + + const e = { + object: { + parent: { + children: [ + { name: "button-center", position: { z: 0 }, children: [] }, + ] + } + } + }; + + it("triggers binding", () => { + const p = fakeProps(); + p.isEditing = false; + p.botOnline = true; + const wrapper = mount(); + wrapper.find({ name: "action-group" }).first().simulate("pointerdown", e); + expect(execSequence).toHaveBeenCalledWith(1); + }); + + it("hovers button", () => { + const wrapper = mount(); + const btnBefore = wrapper.find({ name: "button-center" }).first(); + expect(btnBefore.props()["material-color"]).toEqual(13421772); + wrapper.find({ name: "action-group" }).first().simulate("pointerover", e); + const btnAfter = wrapper.find({ name: "button-center" }).first(); + expect(btnAfter.props()["material-color"]).toEqual(14540253); + expect(e.object.parent?.children[0].position.z).toEqual(128); + }); + + it("un-hovers button", () => { + const wrapper = mount(); + wrapper.find({ name: "action-group" }).first().simulate("pointerout", e); + expect(e.object.parent?.children[0].position.z).toEqual(131); + }); + + it("resets z", () => { + const wrapper = mount(); + wrapper.find({ name: "button-group" }).first().simulate("pointerup", e); + expect(e.object.parent?.children[0].position.z).toEqual(131); + }); + + it("renders: off", () => { + const p = fakeProps(); + p.isEditing = true; + p.botOnline = false; + p.bot.hardware.informational_settings.locked = false; + p.bot.hardware.informational_settings.sync_status = "booting"; + const sequence = fakeSequence(); + p.resources = buildResourceIndex([sequence]).index; + mount(); + expect(mockSetColor).toHaveBeenCalledWith(IColor.estop.off); + }); + + it("renders: on", () => { + const p = fakeProps(); + p.botOnline = true; + p.bot.hardware.informational_settings.locked = false; + p.bot.hardware.informational_settings.busy = false; + mount(); + expect(mockSetColor).toHaveBeenCalledWith(IColor.estop.on); + }); + + it("renders: blinking on", () => { + const p = fakeProps(); + p.isEditing = true; + p.bot.hardware.informational_settings.locked = true; + p.bot.hardware.informational_settings.sync_status = "syncing"; + mount(); + expect(mockSetColor).toHaveBeenCalledWith(IColor.unlock.on); + }); + + it("renders: blinking off", () => { + mockElapsedTime = 1; + const p = fakeProps(); + p.bot.hardware.informational_settings.locked = true; + p.bot.hardware.informational_settings.sync_status = "syncing"; + mount(); + expect(mockSetColor).toHaveBeenCalledWith(IColor.unlock.off); + }); + + it("renders: express", () => { + mockElapsedTime = 1; + const p = fakeProps(); + p.bot.hardware.informational_settings.locked = true; + p.bot.hardware.informational_settings.sync_status = "syncing"; + p.firmwareHardware = "express_k10"; + mount(); + expect(mockSetColor).not.toHaveBeenCalledWith(IColor.unlock.on); + }); +}); diff --git a/frontend/settings/pin_bindings/actions.ts b/frontend/settings/pin_bindings/actions.ts new file mode 100644 index 0000000000..a177c936c3 --- /dev/null +++ b/frontend/settings/pin_bindings/actions.ts @@ -0,0 +1,101 @@ +import { DropDownItem } from "../../ui"; +import { + PinBinding, + PinBindingSpecialAction, PinBindingType, SpecialPinBinding, StandardPinBinding, +} from "farmbot/dist/resources/api_resources"; +import { initSave, overwrite, save, destroy } from "../../api/crud"; +import { pinBindingBody } from "./tagged_pin_binding_init"; +import { ResourceIndex } from "../../resources/interfaces"; +import { findByUuid } from "../../resources/reducer_support"; +import { cloneDeep, isUndefined } from "lodash"; +import { error } from "../../toast/toast"; +import { t } from "../../i18next_wrapper"; +import { PinBindingListItems } from "./interfaces"; +import { apiPinBindings } from "./pin_bindings_content"; +import { execSequence, sendRPC } from "../../devices/actions"; +import { RpcRequestBodyItem } from "farmbot"; + +export interface SetPinBindingProps { + dispatch: Function; + resources: ResourceIndex; + binding: PinBindingListItems | undefined; + pinNumber: number | undefined; +} + +export const setPinBinding = (props: SetPinBindingProps) => + // eslint-disable-next-line complexity + (ddi: DropDownItem): boolean => { + const { binding, dispatch, resources, pinNumber } = props; + const bindingUuid = binding?.uuid; + if (isUndefined(pinNumber)) { + error(t("Pin number cannot be blank.")); + return false; + } + if (bindingUuid && ddi.isNull) { + dispatch(destroy(bindingUuid)); + return false; + } + const bindingType = ddi.headingId as PinBindingType | undefined; + const sequenceIdInput = ddi.headingId == PinBindingType.standard + ? parseInt("" + ddi.value) + : undefined; + const specialActionInput = ddi.headingId == PinBindingType.special + ? ddi.value as PinBindingSpecialAction + : undefined; + const noSequenceId = isUndefined(sequenceIdInput) || isNaN(sequenceIdInput); + if (isUndefined(bindingType) + || (noSequenceId && isUndefined(specialActionInput))) { + error(t("Please select a sequence or action.")); + return false; + } + const body = pinBindingBody(bindingType == PinBindingType.special + ? { + pin_num: pinNumber, + special_action: specialActionInput, + binding_type: bindingType + } + : { + pin_num: pinNumber, + sequence_id: sequenceIdInput, + binding_type: bindingType + }); + if (bindingUuid) { + const binding = findByUuid(resources, bindingUuid); + const newBody = cloneDeep(binding.body as PinBinding); + newBody.binding_type = bindingType; + if (bindingType == PinBindingType.standard && sequenceIdInput) { + (newBody as StandardPinBinding).sequence_id = sequenceIdInput; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (newBody as any).special_action = undefined; + } + if (bindingType == PinBindingType.special && specialActionInput) { + (newBody as SpecialPinBinding).special_action = specialActionInput; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (newBody as any).sequence_id = undefined; + } + dispatch(overwrite(binding, newBody)); + dispatch(save(binding.uuid)); + } else { + dispatch(initSave("PinBinding", body)); + } + return true; + }; + +export const findBinding = (resources: ResourceIndex) => (pin: number) => + apiPinBindings(resources).filter(b => b.pin_number == pin)[0]; + +export const triggerBinding = + (resources: ResourceIndex, botOnline: boolean) => + (pin: number) => + () => { + const binding = findBinding(resources)(pin); + if (!botOnline || !binding) { return; } + if (binding.sequence_id) { + execSequence(binding.sequence_id); + } + if (binding.special_action) { + sendRPC({ + kind: binding.special_action, args: {}, + } as RpcRequestBodyItem); + } + }; diff --git a/frontend/settings/pin_bindings/box_top.tsx b/frontend/settings/pin_bindings/box_top.tsx new file mode 100644 index 0000000000..a8bcc97602 --- /dev/null +++ b/frontend/settings/pin_bindings/box_top.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import { ElectronicsBoxModel } from "./model"; +import { BoxTopButtons } from "./box_top_gpio_diagram"; +import { BoxTopProps } from "./interfaces"; + +export const BoxTop = (props: BoxTopProps) => +
+ {props.threeDimensions + ? + : } +
; diff --git a/frontend/settings/pin_bindings/box_top_gpio_diagram.tsx b/frontend/settings/pin_bindings/box_top_gpio_diagram.tsx index 54ad97febd..e2781da239 100644 --- a/frontend/settings/pin_bindings/box_top_gpio_diagram.tsx +++ b/frontend/settings/pin_bindings/box_top_gpio_diagram.tsx @@ -1,21 +1,12 @@ import React from "react"; import { t } from "../../i18next_wrapper"; import { Color } from "../../ui/colors"; -import { FirmwareHardware, RpcRequestBodyItem, SyncStatus } from "farmbot"; +import { FirmwareHardware, SyncStatus } from "farmbot"; import { hasExtraButtons } from "../firmware/firmware_hardware_support"; -import { DropDownItem } from "../../ui"; import { BindingTargetDropdown, pinBindingLabel } from "./pin_binding_input_group"; -import { - PinBinding, - PinBindingSpecialAction, PinBindingType, SpecialPinBinding, StandardPinBinding, -} from "farmbot/dist/resources/api_resources"; -import { initSave, overwrite, save, destroy } from "../../api/crud"; -import { pinBindingBody } from "./tagged_pin_binding_init"; -import { ResourceIndex } from "../../resources/interfaces"; -import { findByUuid } from "../../resources/reducer_support"; -import { apiPinBindings } from "./pin_bindings_content"; -import { execSequence, sendRPC } from "../../devices/actions"; -import { cloneDeep, isUndefined } from "lodash"; +import { isUndefined } from "lodash"; +import { findBinding, setPinBinding, triggerBinding } from "./actions"; +import { BoxTopBaseProps } from "./interfaces"; export interface BoxTopGpioDiagramProps { boundPins: number[] | undefined; @@ -126,22 +117,12 @@ export class BoxTopGpioDiagram } } -export interface BoxTopButtonsProps { - firmwareHardware: FirmwareHardware | undefined; - isEditing: boolean; - dispatch: Function; - resources: ResourceIndex; - botOnline: boolean; - syncStatus: SyncStatus | undefined; - locked: boolean; -} - interface BoxTopButtonsState { hoveredPin: number | undefined; } export class BoxTopButtons - extends React.Component { + extends React.Component { state: BoxTopGpioDiagramState = { hoveredPin: undefined }; hover = (hovered: number | undefined) => @@ -154,73 +135,34 @@ export class BoxTopButtons this.setState({ hoveredPin: hovered }); }; - findBinding = (pin: number) => apiPinBindings(this.props.resources) - .filter(b => b.pin_number == pin)[0]; - - bind = (pin: number) => (ddi: DropDownItem) => { - const bindingUuid = this.findBinding(pin)?.uuid; - if (bindingUuid && ddi.isNull) { - this.props.dispatch(destroy(bindingUuid)); - return; - } - const bindingType = ddi.headingId as PinBindingType; - const sequenceIdInput = ddi.headingId == PinBindingType.standard - ? parseInt("" + ddi.value) - : undefined; - const specialActionInput = ddi.headingId == PinBindingType.special - ? ddi.value as PinBindingSpecialAction - : undefined; - const body = pinBindingBody(bindingType == PinBindingType.special - ? { - pin_num: pin, - special_action: specialActionInput, - binding_type: bindingType - } - : { - pin_num: pin, - sequence_id: sequenceIdInput, - binding_type: bindingType - }); - if (bindingUuid) { - const binding = findByUuid(this.props.resources, bindingUuid); - const newBody = cloneDeep(binding.body as PinBinding); - newBody.binding_type = bindingType; - if (bindingType == PinBindingType.standard && sequenceIdInput) { - (newBody as StandardPinBinding).sequence_id = sequenceIdInput; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (newBody as any).special_action = undefined; - } - if (bindingType == PinBindingType.special && specialActionInput) { - (newBody as SpecialPinBinding).special_action = specialActionInput; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (newBody as any).sequence_id = undefined; - } - this.props.dispatch(overwrite(binding, newBody)); - this.props.dispatch(save(binding.uuid)); - } else { - this.props.dispatch(initSave("PinBinding", body)); - } - }; + findBinding = findBinding(this.props.resources); render() { - const { firmwareHardware, botOnline, locked, syncStatus } = this.props; + const { + firmwareHardware, botOnline, resources, dispatch, bot, + } = this.props; + const { locked, sync_status } = bot.hardware.informational_settings; + const syncStatus = sync_status; const circlesProps = { firmwareHardware, clean: true }; const buttons = CIRCLES(circlesProps).filter(circle => circle.r > 4); const leds = CIRCLES(circlesProps).filter(circle => circle.r < 5); - return
+ return
{buttons.map(circle => { const binding = this.findBinding(circle.pin); return this.props.isEditing && [5, 20, 26].includes(circle.pin) ? :

{(pinBindingLabel({ - resources: this.props.resources, + resources: resources, sequenceIdInput: binding?.sequence_id, specialActionInput: binding?.special_action, }) || circle).label} @@ -236,18 +178,7 @@ export class BoxTopButtons on={ledOn(statusProps)} blinking={ledBlinking(statusProps)} hasBinding={!!binding} - press={() => { - const binding = this.findBinding(circle.pin); - if (!botOnline || !binding) { return; } - if (binding.sequence_id) { - execSequence(binding.sequence_id); - } - if (binding.special_action) { - sendRPC({ - kind: binding.special_action, args: {}, - } as RpcRequestBodyItem); - } - }} />; + press={triggerBinding(resources, botOnline)(circle.pin)} />; })}

diff --git a/frontend/settings/pin_bindings/interfaces.ts b/frontend/settings/pin_bindings/interfaces.ts index a37d49ef0d..d7c9744380 100644 --- a/frontend/settings/pin_bindings/interfaces.ts +++ b/frontend/settings/pin_bindings/interfaces.ts @@ -5,6 +5,7 @@ import { } from "farmbot/dist/resources/api_resources"; import { FirmwareHardware } from "farmbot"; import { SettingsPanelState } from "../../interfaces"; +import { BotState } from "../../devices/interfaces"; export interface PinBindingsProps { dispatch: Function; @@ -47,3 +48,16 @@ export interface PinBindingInputGroupState { specialActionInput: PinBindingSpecialAction | undefined; bindingType: PinBindingType; } + +export interface BoxTopBaseProps { + isEditing: boolean; + dispatch: Function; + resources: ResourceIndex; + botOnline: boolean; + bot: BotState; + firmwareHardware: FirmwareHardware | undefined; +} + +export interface BoxTopProps extends BoxTopBaseProps { + threeDimensions: boolean; +} diff --git a/frontend/settings/pin_bindings/model.tsx b/frontend/settings/pin_bindings/model.tsx new file mode 100644 index 0000000000..f8b4724c56 --- /dev/null +++ b/frontend/settings/pin_bindings/model.tsx @@ -0,0 +1,381 @@ +/* eslint-disable react/no-unknown-property */ +/* eslint-disable no-null/no-null */ +import React, { useRef } from "react"; +import { + Cylinder, Html, PerspectiveCamera, useCursor, useGLTF, +} from "@react-three/drei"; +import { Canvas, ThreeEvent, useFrame } from "@react-three/fiber"; +import { GLTF } from "three-stdlib"; +import { BindingTargetDropdown, pinBindingLabel } from "./pin_binding_input_group"; +import { BoxTopBaseProps, PinBindingListItems } from "./interfaces"; +import { setPinBinding, findBinding, triggerBinding } from "./actions"; +import { BufferGeometry } from "three"; +import { debounce, isUndefined, some } from "lodash"; +import { t } from "../../i18next_wrapper"; +import { isExpress } from "../../settings/firmware/firmware_hardware_support"; +import { ButtonPin } from "./list_and_label_support"; + +const ASSETS = "/3D/"; +const LIB_DIR = `${ASSETS}lib/`; + +const MODELS = { + box: `${ASSETS}models/box.glb`, + btn: `${ASSETS}models/push_button.glb`, + led: `${ASSETS}models/led_indicator.glb`, +}; + +type Box = GLTF & { + nodes: { + Electronics_Box: THREE.Mesh; + Electronics_Box_Gasket: THREE.Mesh; + Electronics_Box_Lid: THREE.Mesh; + }; + materials: { + [Material.box]: THREE.MeshStandardMaterial; + [Material.gasket]: THREE.MeshStandardMaterial; + [Material.lid]: THREE.MeshStandardMaterial; + }; +} + +export enum Material { + box = "0.901961_0.901961_0.901961_0.000000_0.000000", + gasket = "0.301961_0.301961_0.301961_0.000000_0.000000", + lid = "0.564706_0.811765_0.945098_0.000000_0.623529", + button = "0.701961_0.701961_0.701961_0.000000_0.000000", + led = "0.600000_0.600000_0.600000_0.000000_0.000000", +} + +type Btn = GLTF & { + nodes: { + ["Push_Button_-_Red"]: THREE.Mesh; + }; + materials: { + [Material.button]: THREE.MeshStandardMaterial; + }; +} + +type Led = GLTF & { + nodes: { + LED: THREE.Mesh; + }; + materials: { + [Material.led]: THREE.MeshStandardMaterial; + }; +} + +Object.values(MODELS).map(model => useGLTF.preload(model, LIB_DIR)); + +type MeshObject = THREE.Mesh; + +const Z = 131; +export namespace IColor { + export enum estop { + on = 0xef4037, + off = 0xd89a97, + } + export enum unlock { + on = 0xf5e909, + off = 0xe1de94, + } + export enum connect { + on = 0x1073e0, + off = 0x88a4c3, + } + export enum sync { + on = 0x62c020, + off = 0x94b87b, + } + export enum blank { + on = 0xffffff, + off = 0xf4f4f4, + } +} + +const changeItemsInGroup = ( + meshObject: MeshObject, + cb: (x: MeshObject) => void, + items = ["button-center", "button-color"], +) => { + meshObject.children.map(child => { + const object = child as MeshObject; + if (some(items.map(item => child.name.includes(item)))) { + cb(object); + } + changeItemsInGroup(object, cb, items); + }); +}; + +export const setZForAllInGroup = (e: ThreeEvent, z: number) => { + changeItemsInGroup( + e.object.parent as MeshObject, + x => x.position.z = z); +}; + +interface ButtonOrLedItem { + label: string; + pinNumber: number; + blink?: boolean; + on?: boolean; + position: number; + color: { on: number, off: number }; + ref?: React.MutableRefObject; +} + +export const Model = (props: BoxTopBaseProps) => { + const box = useGLTF(MODELS.box, LIB_DIR) as Box; + const btn = useGLTF(MODELS.btn, LIB_DIR) as Btn; + const led = useGLTF(MODELS.led, LIB_DIR) as Led; + const SCALE = 1000; + + const syncLed = useRef(null); + const connLed = useRef(null); + const unlock = useRef(null); + const estop = useRef(null); + + const { + locked, sync_status, + } = props.bot.hardware.informational_settings; + const findPinBinding = findBinding(props.resources); + const clickBinding = triggerBinding(props.resources, props.botOnline); + const express = isExpress(props.firmwareHardware); + + const BUTTONS: ButtonOrLedItem[] = [ + { + label: t("E-Stop"), + pinNumber: ButtonPin.estop, + on: props.botOnline && !locked, + position: -60, + color: { + on: IColor.estop.on, + off: IColor.estop.off, + }, + ref: estop, + }, + { + label: t("Unlock"), + pinNumber: ButtonPin.unlock, + blink: props.botOnline && locked, + position: -30, + color: { + on: IColor.unlock.on, + off: IColor.unlock.off, + }, + ref: unlock, + }, + { + label: t("Button 3"), + pinNumber: ButtonPin.btn3, + position: 0, + color: { + on: IColor.blank.on, + off: IColor.blank.off, + }, + }, + { + label: t("Button 4"), + pinNumber: ButtonPin.btn4, + position: 30, + color: { + on: IColor.blank.on, + off: IColor.blank.off, + }, + }, + { + label: t("Button 5"), + pinNumber: ButtonPin.btn5, + position: 60, + color: { + on: IColor.blank.on, + off: IColor.blank.off, + }, + }, + ]; + + const LEDS: ButtonOrLedItem[] = [ + { + label: t("Sync"), + pinNumber: -1, + on: sync_status == "synced", + blink: sync_status == "syncing", + position: -45, + color: { + on: IColor.sync.on, + off: IColor.sync.off, + }, + ref: syncLed, + }, + { + label: t("Connectivity"), + pinNumber: -1, + on: props.botOnline, + position: -15, + color: { + on: IColor.connect.on, + off: IColor.connect.off, + }, + ref: connLed, + }, + { + label: t("LED 3"), + pinNumber: -1, + position: 15, + color: { + on: IColor.blank.on, + off: IColor.blank.off, + }, + }, + { + label: t("LED 4"), + pinNumber: -1, + position: 45, + color: { + on: IColor.blank.on, + off: IColor.blank.off, + }, + }, + ]; + + useFrame((state) => { + const t = state.clock.getElapsedTime(); + BUTTONS.concat(LEDS).map(item => { + const current = item.ref?.current; + const { on, off } = item.color; + if (current) { + if (item.blink) { + current.material.color.set(t % 2 < 1 ? on : off); + } else { + current.material.color.set(item.on ? on : off); + } + } + }); + }); + + const getLabel = (binding: PinBindingListItems | undefined) => { + return pinBindingLabel({ + resources: props.resources, + sequenceIdInput: binding?.sequence_id, + specialActionInput: binding?.special_action, + })?.label; + }; + + const [hovered, setHovered] = React.useState(); + useCursor(!isUndefined(hovered)); + const leave = (e: ThreeEvent) => { + setHovered(undefined); + setZForAllInGroup(e, Z); + }; + return + + + + + + + + {BUTTONS + .filter((_, index) => express ? index == 0 : true) + .map(button => { + const { position, color, ref, label, pinNumber } = button; + const btnPosition = express ? 0 : position; + const binding = findPinBinding(pinNumber); + const isHovered = hovered == pinNumber; + const click = debounce(clickBinding(pinNumber)); + const enter = () => !props.isEditing && setHovered(pinNumber); + return + + { + if (!props.isEditing) { + setZForAllInGroup(e, Z - 3); + click(); + } + }}> + + + + {props.isEditing + ? + :

+ {getLabel(binding) || label} +

} + +
+
; + })} + {LEDS + .filter(() => !express) + .map(ledIndicator => { + const { position, color, ref } = ledIndicator; + return + + + +

{ledIndicator.label}

+ +
; + })} +
; +}; + +export const ElectronicsBoxModel = (props: BoxTopBaseProps) => { + return
+ + + +
; +}; diff --git a/frontend/settings/pin_bindings/pin_binding_input_group.tsx b/frontend/settings/pin_bindings/pin_binding_input_group.tsx index a2498797a2..e49df2f33a 100644 --- a/frontend/settings/pin_bindings/pin_binding_input_group.tsx +++ b/frontend/settings/pin_bindings/pin_binding_input_group.tsx @@ -7,8 +7,6 @@ import { PinBindingInputGroupState, } from "./interfaces"; import { isNumber, includes } from "lodash"; -import { initSave } from "../../api/crud"; -import { pinBindingBody } from "./tagged_pin_binding_init"; import { error, warning } from "../../toast/toast"; import { validGpioPins, sysBindings, generatePinLabel, RpiPinList, @@ -25,6 +23,7 @@ import { BoxTopGpioDiagram } from "./box_top_gpio_diagram"; import { findSequenceById, selectAllSequences } from "../../resources/selectors"; import { ResourceIndex } from "../../resources/interfaces"; import { FirmwareHardware } from "farmbot"; +import { setPinBinding } from "./actions"; export class PinBindingInputGroup extends React.Component { @@ -60,34 +59,26 @@ export class PinBindingInputGroup /** Validate and save a pin binding. */ bindPin = () => { - const { dispatch } = this.props; + const { dispatch, resources } = this.props; const { pinNumberInput, sequenceIdInput, bindingType, specialActionInput } = this.state; - if (isNumber(pinNumberInput)) { - if (bindingType && (sequenceIdInput || specialActionInput)) { - bindingType == PinBindingType.special - ? dispatch(initSave("PinBinding", pinBindingBody({ - pin_num: pinNumberInput, - special_action: specialActionInput, - binding_type: bindingType - }))) - : dispatch(initSave("PinBinding", pinBindingBody({ - pin_num: pinNumberInput, - sequence_id: sequenceIdInput, - binding_type: bindingType - }))); - this.setState({ - pinNumberInput: undefined, - sequenceIdInput: undefined, - specialActionInput: undefined, - bindingType: PinBindingType.standard, - }); - } else { - error(t("Please select a sequence or action.")); - } - } else { - error(t("Pin number cannot be blank.")); + const success = setPinBinding({ + binding: undefined, + dispatch, + resources, + pinNumber: pinNumberInput, + })({ + headingId: bindingType, label: "", + value: "" + (sequenceIdInput || specialActionInput), + }); + if (success) { + this.setState({ + pinNumberInput: undefined, + sequenceIdInput: undefined, + specialActionInput: undefined, + bindingType: PinBindingType.standard, + }); } }; @@ -219,12 +210,13 @@ export const BindingTargetDropdown = (props: BindingTargetDropdownProps) => { }); return dropDownList; }; - + const selectedItem = pinBindingLabel(props); return ; + customNullLabel={t("Select")} />; }; interface PinBindingLabelProps { diff --git a/frontend/wizard/__tests__/checks_test.tsx b/frontend/wizard/__tests__/checks_test.tsx index 2b9d16f0bd..569020913e 100644 --- a/frontend/wizard/__tests__/checks_test.tsx +++ b/frontend/wizard/__tests__/checks_test.tsx @@ -601,6 +601,7 @@ describe("", () => { const p = fakeProps(); const pinBinding = fakePinBinding(); p.resources = buildResourceIndex([pinBinding]).index; + p.getConfigValue = () => false; const wrapper = mount(); expect(wrapper.text().toLowerCase()).toContain("button 5"); diff --git a/frontend/wizard/__tests__/step_test.tsx b/frontend/wizard/__tests__/step_test.tsx index 6a125bc304..95fb1ae801 100644 --- a/frontend/wizard/__tests__/step_test.tsx +++ b/frontend/wizard/__tests__/step_test.tsx @@ -197,7 +197,7 @@ describe("", () => { const p = fakeProps(); p.step.pinBindingOptions = { editing: false }; const wrapper = mount(); - expect(wrapper.find(".box-top-buttons").length).toEqual(1); + expect(wrapper.find(".electronics-box-top").length).toEqual(1); }); }); diff --git a/frontend/wizard/checks.tsx b/frontend/wizard/checks.tsx index 3511f83ba8..d033f582d8 100644 --- a/frontend/wizard/checks.tsx +++ b/frontend/wizard/checks.tsx @@ -63,7 +63,7 @@ import { BooleanSetting } from "../session_keys"; import { BooleanConfigKey as BooleanWebAppConfigKey, } from "farmbot/dist/resources/configs/web_app"; -import { toggleWebAppBool } from "../config_storage/actions"; +import { GetWebAppConfigValue, toggleWebAppBool } from "../config_storage/actions"; import { PLACEHOLDER_FARMBOT } from "../photos/images/image_flipper"; import { OriginSelector } from "../settings/farm_designer_settings"; import { Sensors } from "../sensors"; @@ -90,7 +90,6 @@ import { BotPositionRows } from "../controls/move/bot_position_rows"; import { BootSequenceSelector, } from "../settings/fbos_settings/boot_sequence_selector"; -import { BoxTopButtons } from "../settings/pin_bindings/box_top_gpio_diagram"; import { getImageJobs } from "../photos/state_to_props"; import { ResourceIndex } from "../resources/interfaces"; import { BotState } from "../devices/interfaces"; @@ -99,6 +98,7 @@ import { } from "../farm_designer/map/tool_graphics/all_tools"; import { WaterFlowRateInput } from "../tools/edit_tool"; import { RPI_OPTIONS } from "../settings/fbos_settings/rpi_model"; +import { BoxTop } from "../settings/pin_bindings/box_top"; export const Language = (props: WizardStepComponentProps) => { const user = getUserAccountSettings(props.resources); @@ -575,6 +575,7 @@ export const PeripheralsCheck = (props: WizardStepComponentProps) => { const firmwareHardware = getFwHardwareValue(getFbosConfig(props.resources)); return
{ @@ -605,13 +607,14 @@ export const PinBinding = (props: PinBindingProps) => { onClick={() => emergencyUnlock()}> {t("UNLOCK")} - : ; }; diff --git a/frontend/wizard/step.tsx b/frontend/wizard/step.tsx index addf23d018..fd967ab87b 100644 --- a/frontend/wizard/step.tsx +++ b/frontend/wizard/step.tsx @@ -95,6 +95,7 @@ export const WizardStepContainer = (props: WizardStepContainerProps) => { controlsCheckOptions={step.controlsCheckOptions} />} {step.pinBindingOptions && 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 literal 0 HcmV?d00001 diff --git a/public/3D/lib/draco_wasm_wrapper.js b/public/3D/lib/draco_wasm_wrapper.js new file mode 100644 index 0000000000..43f1556838 --- /dev/null +++ b/public/3D/lib/draco_wasm_wrapper.js @@ -0,0 +1,116 @@ +var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE? +$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))}; +$jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)}; +var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q=y}},"es6","es3"); +$jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(kp;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3"); +$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); +$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); +var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e); +e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)}); +if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+ +12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+ +16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0=d?b++:2047>=d?b+=2:55296<=d&&57343>= +d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"=== +typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]= +this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr= +Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3=e[1]?!0:0!=e[0]||10< +e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e, +b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)} +function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")|| +P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0= +function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0= +a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0= +a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0= +function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0= +a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0= +function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1= +function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb= +a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0= +a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb= +a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null, +arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0= +a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0= +a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0= +a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib= +a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0= +function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1= +a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1= +function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0= +a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0= +function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0= +a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0= +function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2= +a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null, +arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0= +function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2= +a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)}, +oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3= +function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc= +a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3= +a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3= +a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3= +a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1= +a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM= +function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM= +a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION= +a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD= +a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD= +a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc= +a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32= +a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32= +function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT= +a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER= +function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)}; +a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0=r.size?(0>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;gb.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule); diff --git a/public/3D/models/box.glb b/public/3D/models/box.glb new file mode 100644 index 0000000000000000000000000000000000000000..dbdd356530e3fe6d04b5dbd8049465e8d8dfce6b GIT binary patch literal 22872 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�w59nZi(E2~y3 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 N+1fp}Z)b$(zX4rzV@3b~ literal 0 HcmV?d00001 diff --git a/public/3D/models/push_button.glb b/public/3D/models/push_button.glb new file mode 100644 index 0000000000000000000000000000000000000000..fab66989e545ee12bacc0d4f31b622cf24bee546 GIT binary patch literal 2572 zcmb7E3se(V8vZ8RP-@p>oUS#i0cXN%!up7yE((H#3<{J&AOnOY8JbKGWmD3k zfMU0(1$4E7&m|EA7N19~@%UJaR$U*p>h=h2(FMU*b=595dlRT0ThBi3$xQD3@Be-O zf4~2~lasHW#{&RwF959oU_oMhTrsXOnHUa_D8_Rb17p;1tPzjEbC7GG+Nd#@vRR{E zQWzXW5|cgd#+NeuO+M4e{vEu~P$IOXRlxg2`<~#IB)` z2UFoBVoS3iu1s@wHe*cEF-r$^8(iZN1ln50G4a{i?xGAWV;MsZmx~k&A;Vvy2ayJi z-le#jHVDN|!&qS$=aI8T%1ZYop)cPxTJ@`h9LkZ=-d z{lAz<5TEXyIEpNI%It=sKq2cb=_lBVMS;V>w`%Y&+|lcT{F0r=4degT-n`(+=6C!uGOawozzKk1o_qj42Ivpt zp$X>U6jsZ{LP{tWN=4|A@+l$ZdVDAmHETnoaD!-tPvZnG*ikNy7atZ^N}VR@_hRS0 z49a;={JqratQ9-?VreipAr?Vmg&DCv-wDMN#3HeeSSaR;kwGdIQX=%A7*q!qSVeww z4R^*}{&nfj3Eh2OzD*}v8!Gf=du|vO_nl0It}sLWJ8AjZrGE%jC;TI{t~|O!wPeTT zr+<87@tv6m?;j{nY1A)s^pF1V@U3ZzO^274PGiSE^4jK%UH43V``DMKwx2jSrpm`$ zw&a64dO2R$9B9f|TiH?a&E;q3_B|a)nY$|e+^*iDN8dFszJ0vr;_$7Z)}1LCU({B(k@lWg9aQE- zS~^jGplV^@!|xXPt-@Ae{lfl{Ha_WL@#qpQeE44Foy2(m@UPSj-fOpriiEv0?|9Cg zG`p?FC>+4}t@1_7{JWBqiZ~T+JAbr^NS(Af)TG^Ubfdg>CSD%-qwUSE314&;&wX@g z?62OP`k+xwqnP#fy7eoguT%}I!0DUaRp255y zeXm3vD#jAm+J9f0=xmm?wM@D2g$+L0;tg?A>(2~xyly0`=_@8>@{($(I{POOw+2CgF$6BY?QL~F zhfkgk2SpMn*Srg(wuXTAwr6K61LQHj^i(7SV4!TP*$;4F9s!`VZMTPx3U%qS7~ zo#Ht@vVmm(4@up58`O_uPLD{Mba&#BJ$3=;%wRnTMFN3x*C?2dL6I;S+7tZY(nlET z3T;>O^Ey#mHH`twZnHN<#e$yMIM|??? zXv_(4=}IBVo8k3W+TuNY3NcH-4Ez-~D!*ea9t?njwI@?S*&}Ethuxrzt_Wtsy_L`{5_FbD!OdpBt{|1K8mpK3c literal 0 HcmV?d00001 diff --git a/tsconfig.json b/tsconfig.json index 2c63ffef76..963525a772 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,7 @@ "noUnusedParameters": true, "pretty": true, "removeComments": true, + "skipLibCheck": true, "sourceMap": true, "strictNullChecks": true, "target": "es5", From feea8d43ef4f1d36c1ad833bf3611e88b0d5c65a Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Thu, 18 Jan 2024 16:14:08 -0800 Subject: [PATCH 7/8] switch to coveralls coverage reporter --- .circleci/config.yml | 10 ++++++++-- package.json | 2 -- run_all_ci_tasks.sh | 5 +---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5eb39b83c1..ee5d6e0e59 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -88,10 +88,16 @@ commands: - run: name: Report coverage to Coveralls command: | - if [ "$COVERAGE_AVAILABLE" ] + if [ "$CIRCLE_BRANCH" == "staging" ]; then set +eo pipefail; fi + if [ "$COVERAGE_AVAILABLE" ] && [ "$COVERALLS_REPO_TOKEN" ] then - sudo docker compose run -e COVERALLS_REPO_TOKEN="$COVERALLS_REPO_TOKEN" web npm run coverage || [ $CIRCLE_BRANCH == "staging" ] + curl -sLO https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-linux.tar.gz + curl -sLO https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-checksums.txt + cat coveralls-checksums.txt | grep coveralls-linux.tar.gz | sha256sum --check + tar -xzf coveralls-linux.tar.gz + ./coveralls report coverage_fe/lcov.info fi + if [ "$CIRCLE_BRANCH" == "staging" ]; then echo; fi when: always # change to `on_success` for a stricter comparison diff --git a/package.json b/package.json index d681649647..1c91b6b134 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "url": "https://github.com/farmbot/farmbot-web-app" }, "scripts": { - "coverage": "cat **/*lcov.info | ./node_modules/coveralls/bin/coveralls.js", "test-very-slow": "node --expose-gc ./node_modules/.bin/jest -i --colors --coverage", "test-slow": "./node_modules/.bin/jest -w 6 --colors", "test": "./node_modules/.bin/jest -w 5 --no-coverage", @@ -85,7 +84,6 @@ "@typescript-eslint/eslint-plugin": "6.16.0", "@typescript-eslint/parser": "6.16.0", "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", - "coveralls": "3.1.1", "enzyme": "3.11.0", "eslint": "8.56.0", "eslint-plugin-eslint-comments": "3.2.0", diff --git a/run_all_ci_tasks.sh b/run_all_ci_tasks.sh index 14bac878d8..f1b169f61b 100755 --- a/run_all_ci_tasks.sh +++ b/run_all_ci_tasks.sh @@ -9,7 +9,4 @@ P2=$! sudo docker compose run web npm run test-slow & P3=$! -sudo docker compose run web npm run coverage & -P4=$! - -wait $P1 $P2 $P3 $P4 +wait $P1 $P2 $P3 From 5edc171cda8d21f0c7266e02aa8f0cd23163313c Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Thu, 18 Jan 2024 16:17:21 -0800 Subject: [PATCH 8/8] upgrade deps --- .../extras/__tests__/fallback_widget_test.tsx | 2 +- package.json | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frontend/extras/__tests__/fallback_widget_test.tsx b/frontend/extras/__tests__/fallback_widget_test.tsx index fb992ad150..c8bfc0a462 100644 --- a/frontend/extras/__tests__/fallback_widget_test.tsx +++ b/frontend/extras/__tests__/fallback_widget_test.tsx @@ -21,6 +21,6 @@ describe("", () => { p.helpText = "This is a fake widget."; const wrapper = shallow(); expect(wrapper.html()) - .toContain(""); + .toContain("