commit
a5b1d5631e
|
@ -27,7 +27,7 @@ module Devices
|
||||||
add_tool_slot(name: ToolNames::SEED_TROUGH_1,
|
add_tool_slot(name: ToolNames::SEED_TROUGH_1,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 25,
|
y: 25,
|
||||||
z: -200,
|
z: 0,
|
||||||
tool: tools_seed_trough_1,
|
tool: tools_seed_trough_1,
|
||||||
pullout_direction: ToolSlot::NONE,
|
pullout_direction: ToolSlot::NONE,
|
||||||
gantry_mounted: true)
|
gantry_mounted: true)
|
||||||
|
@ -37,25 +37,18 @@ module Devices
|
||||||
add_tool_slot(name: ToolNames::SEED_TROUGH_2,
|
add_tool_slot(name: ToolNames::SEED_TROUGH_2,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 50,
|
y: 50,
|
||||||
z: -200,
|
z: 0,
|
||||||
tool: tools_seed_trough_2,
|
tool: tools_seed_trough_2,
|
||||||
pullout_direction: ToolSlot::NONE,
|
pullout_direction: ToolSlot::NONE,
|
||||||
gantry_mounted: true)
|
gantry_mounted: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tool_slots_slot_3
|
def tool_slots_slot_3; end
|
||||||
add_tool_slot(name: ToolNames::SEED_TROUGH_3,
|
|
||||||
x: 0,
|
|
||||||
y: 75,
|
|
||||||
z: -200,
|
|
||||||
tool: tools_seed_trough_3,
|
|
||||||
pullout_direction: ToolSlot::NONE,
|
|
||||||
gantry_mounted: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def tool_slots_slot_4; end
|
def tool_slots_slot_4; end
|
||||||
def tool_slots_slot_5; end
|
def tool_slots_slot_5; end
|
||||||
def tool_slots_slot_6; end
|
def tool_slots_slot_6; end
|
||||||
|
def tool_slots_slot_7; end
|
||||||
|
def tool_slots_slot_8; end
|
||||||
def tools_seed_bin; end
|
def tools_seed_bin; end
|
||||||
def tools_seed_tray; end
|
def tools_seed_tray; end
|
||||||
|
|
||||||
|
@ -69,11 +62,6 @@ module Devices
|
||||||
add_tool(ToolNames::SEED_TROUGH_2)
|
add_tool(ToolNames::SEED_TROUGH_2)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tools_seed_trough_3
|
|
||||||
@tools_seed_trough_3 ||=
|
|
||||||
add_tool(ToolNames::SEED_TROUGH_3)
|
|
||||||
end
|
|
||||||
|
|
||||||
def tools_seeder; end
|
def tools_seeder; end
|
||||||
def tools_soil_sensor; end
|
def tools_soil_sensor; end
|
||||||
def tools_watering_nozzle; end
|
def tools_watering_nozzle; end
|
||||||
|
|
|
@ -75,6 +75,9 @@ module Devices
|
||||||
tool: tools_weeder)
|
tool: tools_weeder)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def tool_slots_slot_7; end
|
||||||
|
def tool_slots_slot_8; end
|
||||||
|
|
||||||
def tools_seed_bin
|
def tools_seed_bin
|
||||||
@tools_seed_bin ||=
|
@tools_seed_bin ||=
|
||||||
add_tool(ToolNames::SEED_BIN)
|
add_tool(ToolNames::SEED_BIN)
|
||||||
|
@ -87,7 +90,6 @@ module Devices
|
||||||
|
|
||||||
def tools_seed_trough_1; end
|
def tools_seed_trough_1; end
|
||||||
def tools_seed_trough_2; end
|
def tools_seed_trough_2; end
|
||||||
def tools_seed_trough_3; end
|
|
||||||
|
|
||||||
def tools_seeder
|
def tools_seeder
|
||||||
@tools_seeder ||=
|
@tools_seeder ||=
|
||||||
|
|
|
@ -37,7 +37,6 @@ module Devices
|
||||||
:tools_seed_tray,
|
:tools_seed_tray,
|
||||||
:tools_seed_trough_1,
|
:tools_seed_trough_1,
|
||||||
:tools_seed_trough_2,
|
:tools_seed_trough_2,
|
||||||
:tools_seed_trough_3,
|
|
||||||
:tools_seeder,
|
:tools_seeder,
|
||||||
:tools_soil_sensor,
|
:tools_soil_sensor,
|
||||||
:tools_watering_nozzle,
|
:tools_watering_nozzle,
|
||||||
|
@ -50,6 +49,8 @@ module Devices
|
||||||
:tool_slots_slot_4,
|
:tool_slots_slot_4,
|
||||||
:tool_slots_slot_5,
|
:tool_slots_slot_5,
|
||||||
:tool_slots_slot_6,
|
:tool_slots_slot_6,
|
||||||
|
:tool_slots_slot_7,
|
||||||
|
:tool_slots_slot_8,
|
||||||
|
|
||||||
# WEBCAM FEEDS ===========================
|
# WEBCAM FEEDS ===========================
|
||||||
:webcam_feeds,
|
:webcam_feeds,
|
||||||
|
@ -152,11 +153,12 @@ module Devices
|
||||||
def tool_slots_slot_4; end
|
def tool_slots_slot_4; end
|
||||||
def tool_slots_slot_5; end
|
def tool_slots_slot_5; end
|
||||||
def tool_slots_slot_6; end
|
def tool_slots_slot_6; end
|
||||||
|
def tool_slots_slot_7; end
|
||||||
|
def tool_slots_slot_8; end
|
||||||
def tools_seed_bin; end
|
def tools_seed_bin; end
|
||||||
def tools_seed_tray; end
|
def tools_seed_tray; end
|
||||||
def tools_seed_trough_1; end
|
def tools_seed_trough_1; end
|
||||||
def tools_seed_trough_2; end
|
def tools_seed_trough_2; end
|
||||||
def tools_seed_trough_3; end
|
|
||||||
def tools_seeder; end
|
def tools_seeder; end
|
||||||
def tools_soil_sensor; end
|
def tools_soil_sensor; end
|
||||||
def tools_watering_nozzle; end
|
def tools_watering_nozzle; end
|
||||||
|
|
|
@ -31,7 +31,6 @@ module Devices
|
||||||
LIGHTING = "Lighting"
|
LIGHTING = "Lighting"
|
||||||
SEED_TROUGH_1 = "Seed Trough 1"
|
SEED_TROUGH_1 = "Seed Trough 1"
|
||||||
SEED_TROUGH_2 = "Seed Trough 2"
|
SEED_TROUGH_2 = "Seed Trough 2"
|
||||||
SEED_TROUGH_3 = "Seed Trough 3"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Stub plants ==============================
|
# Stub plants ==============================
|
||||||
|
|
|
@ -6,6 +6,36 @@ module Devices
|
||||||
.fbos_config
|
.fbos_config
|
||||||
.update!(firmware_hardware: FbosConfig::FARMDUINO_K15)
|
.update!(firmware_hardware: FbosConfig::FARMDUINO_K15)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def tool_slots_slot_7
|
||||||
|
add_tool_slot(name: ToolNames::SEED_TROUGH_1,
|
||||||
|
x: 0,
|
||||||
|
y: 25,
|
||||||
|
z: 0,
|
||||||
|
tool: tools_seed_trough_1,
|
||||||
|
pullout_direction: ToolSlot::NONE,
|
||||||
|
gantry_mounted: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tool_slots_slot_8
|
||||||
|
add_tool_slot(name: ToolNames::SEED_TROUGH_2,
|
||||||
|
x: 0,
|
||||||
|
y: 50,
|
||||||
|
z: 0,
|
||||||
|
tool: tools_seed_trough_2,
|
||||||
|
pullout_direction: ToolSlot::NONE,
|
||||||
|
gantry_mounted: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tools_seed_trough_1
|
||||||
|
@tools_seed_trough_1 ||=
|
||||||
|
add_tool(ToolNames::SEED_TROUGH_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tools_seed_trough_2
|
||||||
|
@tools_seed_trough_2 ||=
|
||||||
|
add_tool(ToolNames::SEED_TROUGH_2)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,36 @@ module Devices
|
||||||
def settings_default_map_size_y
|
def settings_default_map_size_y
|
||||||
device.web_app_config.update!(map_size_y: 2_900)
|
device.web_app_config.update!(map_size_y: 2_900)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def tool_slots_slot_7
|
||||||
|
add_tool_slot(name: ToolNames::SEED_TROUGH_1,
|
||||||
|
x: 0,
|
||||||
|
y: 25,
|
||||||
|
z: 0,
|
||||||
|
tool: tools_seed_trough_1,
|
||||||
|
pullout_direction: ToolSlot::NONE,
|
||||||
|
gantry_mounted: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tool_slots_slot_8
|
||||||
|
add_tool_slot(name: ToolNames::SEED_TROUGH_2,
|
||||||
|
x: 0,
|
||||||
|
y: 50,
|
||||||
|
z: 0,
|
||||||
|
tool: tools_seed_trough_2,
|
||||||
|
pullout_direction: ToolSlot::NONE,
|
||||||
|
gantry_mounted: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tools_seed_trough_1
|
||||||
|
@tools_seed_trough_1 ||=
|
||||||
|
add_tool(ToolNames::SEED_TROUGH_1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tools_seed_trough_2
|
||||||
|
@tools_seed_trough_2 ||=
|
||||||
|
add_tool(ToolNames::SEED_TROUGH_2)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,11 +28,12 @@ module Devices
|
||||||
def tool_slots_slot_4; end
|
def tool_slots_slot_4; end
|
||||||
def tool_slots_slot_5; end
|
def tool_slots_slot_5; end
|
||||||
def tool_slots_slot_6; end
|
def tool_slots_slot_6; end
|
||||||
|
def tool_slots_slot_7; end
|
||||||
|
def tool_slots_slot_8; end
|
||||||
def tools_seed_bin; end
|
def tools_seed_bin; end
|
||||||
def tools_seed_tray; end
|
def tools_seed_tray; end
|
||||||
def tools_seed_trough_1; end
|
def tools_seed_trough_1; end
|
||||||
def tools_seed_trough_2; end
|
def tools_seed_trough_2; end
|
||||||
def tools_seed_trough_3; end
|
|
||||||
def tools_seeder; end
|
def tools_seeder; end
|
||||||
def tools_soil_sensor; end
|
def tools_soil_sensor; end
|
||||||
def tools_watering_nozzle; end
|
def tools_watering_nozzle; end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
module Tools
|
module Tools
|
||||||
class Destroy < Mutations::Command
|
class Destroy < Mutations::Command
|
||||||
STILL_IN_USE = "Can't delete tool because the following sequences are "\
|
STILL_IN_USE = "Can't delete tool because the following sequences are " \
|
||||||
"still using it: %s"
|
"still using it: %s"
|
||||||
STILL_IN_SLOT = "Can't delete tool because it is still in a tool slot. "\
|
STILL_IN_SLOT = "Can't delete tool because it is still in a tool slot. " \
|
||||||
"Please remove it from the tool slot first."
|
"Please remove it from the tool slot first."
|
||||||
|
|
||||||
required do
|
required do
|
||||||
|
@ -15,10 +15,11 @@ module Tools
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
|
maybe_unmount_tool
|
||||||
tool.destroy!
|
tool.destroy!
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def slot
|
def slot
|
||||||
@slot ||= tool.tool_slot
|
@slot ||= tool.tool_slot
|
||||||
|
@ -33,8 +34,14 @@ private
|
||||||
end
|
end
|
||||||
|
|
||||||
def names
|
def names
|
||||||
@names ||= \
|
@names ||=
|
||||||
InUseTool.where(tool_id: tool.id).pluck(:sequence_name).join(", ")
|
InUseTool.where(tool_id: tool.id).pluck(:sequence_name).join(", ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_unmount_tool
|
||||||
|
if tool.device.mounted_tool_id == tool.id
|
||||||
|
tool.device.update!(mounted_tool_id: nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,10 @@ export const panelState = (): ControlPanelState => {
|
||||||
return {
|
return {
|
||||||
homing_and_calibration: false,
|
homing_and_calibration: false,
|
||||||
motors: false,
|
motors: false,
|
||||||
encoders_and_endstops: false,
|
encoders: false,
|
||||||
|
endstops: false,
|
||||||
|
error_handling: false,
|
||||||
|
pin_bindings: false,
|
||||||
danger_zone: false,
|
danger_zone: false,
|
||||||
power_and_reset: false,
|
power_and_reset: false,
|
||||||
pin_guard: false
|
pin_guard: false
|
||||||
|
|
|
@ -4,12 +4,15 @@ export const bot: Everything["bot"] = {
|
||||||
"consistent": true,
|
"consistent": true,
|
||||||
"stepSize": 100,
|
"stepSize": 100,
|
||||||
"controlPanelState": {
|
"controlPanelState": {
|
||||||
"homing_and_calibration": false,
|
homing_and_calibration: false,
|
||||||
"motors": false,
|
motors: false,
|
||||||
"encoders_and_endstops": false,
|
encoders: false,
|
||||||
"danger_zone": false,
|
endstops: false,
|
||||||
"power_and_reset": false,
|
error_handling: false,
|
||||||
"pin_guard": false,
|
pin_bindings: false,
|
||||||
|
danger_zone: false,
|
||||||
|
power_and_reset: false,
|
||||||
|
pin_guard: false,
|
||||||
},
|
},
|
||||||
"hardware": {
|
"hardware": {
|
||||||
"gpio_registry": {},
|
"gpio_registry": {},
|
||||||
|
|
|
@ -157,7 +157,6 @@ describe("mapStateToProps()", () => {
|
||||||
const state = fakeState();
|
const state = fakeState();
|
||||||
const config = fakeFbosConfig();
|
const config = fakeFbosConfig();
|
||||||
config.body.auto_sync = true;
|
config.body.auto_sync = true;
|
||||||
config.body.api_migrated = true;
|
|
||||||
const fakeEnv = fakeFarmwareEnv();
|
const fakeEnv = fakeFarmwareEnv();
|
||||||
state.resources = buildResourceIndex([config, fakeEnv]);
|
state.resources = buildResourceIndex([config, fakeEnv]);
|
||||||
state.bot.minOsFeatureData = { api_farmware_env: "8.0.0" };
|
state.bot.minOsFeatureData = { api_farmware_env: "8.0.0" };
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
jest.mock("../util", () => {
|
jest.mock("../util", () => ({
|
||||||
return {
|
attachToRoot: jest.fn(),
|
||||||
attachToRoot: jest.fn(),
|
// Incidental mock. Can be removed if errors go away.
|
||||||
// Incidental mock. Can be removed if errors go away.
|
trim: jest.fn(x => x),
|
||||||
trim: jest.fn(x => x)
|
urlFriendly: jest.fn(),
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
jest.mock("../redux/store", () => {
|
jest.mock("../redux/store", () => {
|
||||||
return { store: { dispatch: jest.fn() } };
|
return { store: { dispatch: jest.fn() } };
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
jest.unmock("../external_urls");
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
|
/* tslint:disable:max-line-length */
|
||||||
|
|
||||||
|
describe("ExternalUrl", () => {
|
||||||
|
it("returns urls", () => {
|
||||||
|
expect(ExternalUrl.featureMinVersions)
|
||||||
|
.toEqual("https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/FEATURE_MIN_VERSIONS.json");
|
||||||
|
expect(ExternalUrl.osReleaseNotes)
|
||||||
|
.toEqual("https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/RELEASE_NOTES.md");
|
||||||
|
expect(ExternalUrl.latestRelease)
|
||||||
|
.toEqual("https://api.github.com/repos/FarmBot/farmbot_os/releases/latest");
|
||||||
|
expect(ExternalUrl.webAppRepo)
|
||||||
|
.toEqual("https://github.com/FarmBot/Farmbot-Web-App");
|
||||||
|
expect(ExternalUrl.gitHubFarmBot)
|
||||||
|
.toEqual("https://github.com/FarmBot");
|
||||||
|
expect(ExternalUrl.softwareDocs)
|
||||||
|
.toEqual("https://software.farm.bot/docs");
|
||||||
|
expect(ExternalUrl.softwareForum)
|
||||||
|
.toEqual("https://forum.farmbot.org/c/software");
|
||||||
|
expect(ExternalUrl.OpenFarm.cropApi)
|
||||||
|
.toEqual("https://openfarm.cc/api/v1/crops/");
|
||||||
|
expect(ExternalUrl.OpenFarm.cropBrowse)
|
||||||
|
.toEqual("https://openfarm.cc/crops/");
|
||||||
|
expect(ExternalUrl.OpenFarm.newCrop)
|
||||||
|
.toEqual("https://openfarm.cc/en/crops/new");
|
||||||
|
expect(ExternalUrl.Video.desktop)
|
||||||
|
.toEqual("https://cdn.shopify.com/s/files/1/2040/0289/files/Farm_Designer_Loop.mp4?9552037556691879018");
|
||||||
|
expect(ExternalUrl.Video.mobile)
|
||||||
|
.toEqual("https://cdn.shopify.com/s/files/1/2040/0289/files/Controls.png?9668345515035078097");
|
||||||
|
});
|
||||||
|
});
|
|
@ -158,6 +158,10 @@ export class API {
|
||||||
get farmwareInstallationPath() {
|
get farmwareInstallationPath() {
|
||||||
return `${this.baseUrl}/api/farmware_installations/`;
|
return `${this.baseUrl}/api/farmware_installations/`;
|
||||||
}
|
}
|
||||||
|
/** /api/first_party_farmwares */
|
||||||
|
get firstPartyFarmwarePath() {
|
||||||
|
return `${this.baseUrl}/api/first_party_farmwares`;
|
||||||
|
}
|
||||||
/** /api/alerts/:id */
|
/** /api/alerts/:id */
|
||||||
get alertPath() { return `${this.baseUrl}/api/alerts/`; }
|
get alertPath() { return `${this.baseUrl}/api/alerts/`; }
|
||||||
/** /api/global_bulletins/:id */
|
/** /api/global_bulletins/:id */
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Session } from "./session";
|
import { Session } from "./session";
|
||||||
|
import { ExternalUrl } from "./external_urls";
|
||||||
|
|
||||||
const OUTER_STYLE: React.CSSProperties = {
|
const OUTER_STYLE: React.CSSProperties = {
|
||||||
borderRadius: "10px",
|
borderRadius: "10px",
|
||||||
|
@ -47,7 +48,7 @@ export function Apology(_: {}) {
|
||||||
<li>
|
<li>
|
||||||
<span>
|
<span>
|
||||||
Send a report to our developer team via the
|
Send a report to our developer team via the
|
||||||
<a href="http://forum.farmbot.org/c/software">FarmBot software
|
<a href={ExternalUrl.softwareForum}>FarmBot software
|
||||||
forum</a>. Including additional information (such as steps leading up
|
forum</a>. Including additional information (such as steps leading up
|
||||||
to the error) helps us identify solutions more quickly.
|
to the error) helps us identify solutions more quickly.
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {
|
import {
|
||||||
fetchReleases, fetchMinOsFeatureData, FEATURE_MIN_VERSIONS_URL,
|
fetchReleases, fetchMinOsFeatureData,
|
||||||
fetchLatestGHBetaRelease
|
fetchLatestGHBetaRelease
|
||||||
} from "../devices/actions";
|
} from "../devices/actions";
|
||||||
import { AuthState } from "./interfaces";
|
import { AuthState } from "./interfaces";
|
||||||
|
@ -16,6 +16,7 @@ import { Actions } from "../constants";
|
||||||
import { connectDevice } from "../connectivity/connect_device";
|
import { connectDevice } from "../connectivity/connect_device";
|
||||||
import { getFirstPartyFarmwareList } from "../farmware/actions";
|
import { getFirstPartyFarmwareList } from "../farmware/actions";
|
||||||
import { readOnlyInterceptor } from "../read_only_mode";
|
import { readOnlyInterceptor } from "../read_only_mode";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
export function didLogin(authState: AuthState, dispatch: Function) {
|
export function didLogin(authState: AuthState, dispatch: Function) {
|
||||||
API.setBaseUrl(authState.token.unencoded.iss);
|
API.setBaseUrl(authState.token.unencoded.iss);
|
||||||
|
@ -24,7 +25,7 @@ export function didLogin(authState: AuthState, dispatch: Function) {
|
||||||
beta_os_update_server && beta_os_update_server != "NOT_SET" &&
|
beta_os_update_server && beta_os_update_server != "NOT_SET" &&
|
||||||
dispatch(fetchLatestGHBetaRelease(beta_os_update_server));
|
dispatch(fetchLatestGHBetaRelease(beta_os_update_server));
|
||||||
dispatch(getFirstPartyFarmwareList());
|
dispatch(getFirstPartyFarmwareList());
|
||||||
dispatch(fetchMinOsFeatureData(FEATURE_MIN_VERSIONS_URL));
|
dispatch(fetchMinOsFeatureData(ExternalUrl.featureMinVersions));
|
||||||
dispatch(setToken(authState));
|
dispatch(setToken(authState));
|
||||||
Sync.fetchSyncData(dispatch);
|
Sync.fetchSyncData(dispatch);
|
||||||
dispatch(connectDevice(authState));
|
dispatch(connectDevice(authState));
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
jest.mock("../../slow_down", () => {
|
jest.mock("../../slow_down", () => ({
|
||||||
return {
|
slowDown: jest.fn((fn: Function) => fn)
|
||||||
slowDown: jest.fn((fn: Function) => fn),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.mock("../../../devices/actions", () => ({
|
|
||||||
badVersion: jest.fn(),
|
|
||||||
EXPECTED_MAJOR: 1,
|
|
||||||
EXPECTED_MINOR: 0,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock("../../../devices/actions", () => ({ badVersion: jest.fn() }));
|
||||||
|
|
||||||
import {
|
import {
|
||||||
onStatus,
|
onStatus,
|
||||||
incomingStatus,
|
incomingStatus,
|
||||||
|
@ -49,8 +43,10 @@ describe("onStatus()", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("version ok", () => {
|
it("version ok", () => {
|
||||||
|
globalConfig.MINIMUM_FBOS_VERSION = "1.0.0";
|
||||||
callOnStatus("1.0.0");
|
callOnStatus("1.0.0");
|
||||||
expect(badVersion).not.toHaveBeenCalled();
|
expect(badVersion).not.toHaveBeenCalled();
|
||||||
|
delete globalConfig.MINIMUM_FBOS_VERSION;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,7 @@ import { success, error, info, warning, fun, busy } from "../toast/toast";
|
||||||
import { HardwareState } from "../devices/interfaces";
|
import { HardwareState } from "../devices/interfaces";
|
||||||
import { GetState, ReduxAction } from "../redux/interfaces";
|
import { GetState, ReduxAction } from "../redux/interfaces";
|
||||||
import { Content, Actions } from "../constants";
|
import { Content, Actions } from "../constants";
|
||||||
import {
|
import { commandOK, badVersion, commandErr } from "../devices/actions";
|
||||||
EXPECTED_MAJOR,
|
|
||||||
EXPECTED_MINOR,
|
|
||||||
commandOK,
|
|
||||||
badVersion,
|
|
||||||
commandErr
|
|
||||||
} from "../devices/actions";
|
|
||||||
import { init } from "../api/crud";
|
import { init } from "../api/crud";
|
||||||
import { AuthState } from "../auth/interfaces";
|
import { AuthState } from "../auth/interfaces";
|
||||||
import { autoSync } from "./auto_sync";
|
import { autoSync } from "./auto_sync";
|
||||||
|
@ -123,7 +117,7 @@ const setBothUp = () => bothUp();
|
||||||
const legacyChecks = (getState: GetState) => {
|
const legacyChecks = (getState: GetState) => {
|
||||||
const { controller_version } = getState().bot.hardware.informational_settings;
|
const { controller_version } = getState().bot.hardware.informational_settings;
|
||||||
if (HACKY_FLAGS.needVersionCheck && controller_version) {
|
if (HACKY_FLAGS.needVersionCheck && controller_version) {
|
||||||
const IS_OK = versionOK(controller_version, EXPECTED_MAJOR, EXPECTED_MINOR);
|
const IS_OK = versionOK(controller_version);
|
||||||
if (!IS_OK) { badVersion(); }
|
if (!IS_OK) { badVersion(); }
|
||||||
HACKY_FLAGS.needVersionCheck = false;
|
HACKY_FLAGS.needVersionCheck = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@ export namespace ToolTips {
|
||||||
few sequences to verify that everything works as expected.`);
|
few sequences to verify that everything works as expected.`);
|
||||||
|
|
||||||
export const PIN_BINDINGS =
|
export const PIN_BINDINGS =
|
||||||
trim(`Assign a sequence to execute when a Raspberry Pi GPIO pin is
|
trim(`Assign an action or sequence to execute when a Raspberry Pi
|
||||||
activated.`);
|
GPIO pin is activated.`);
|
||||||
|
|
||||||
export const PIN_BINDING_WARNING =
|
export const PIN_BINDING_WARNING =
|
||||||
trim(`Warning: Binding to a pin without a physical button and
|
trim(`Warning: Binding to a pin without a physical button and
|
||||||
|
@ -51,24 +51,38 @@ export namespace ToolTips {
|
||||||
trim(`Diagnose connectivity issues with FarmBot and the browser.`);
|
trim(`Diagnose connectivity issues with FarmBot and the browser.`);
|
||||||
|
|
||||||
// Hardware Settings: Homing and Calibration
|
// Hardware Settings: Homing and Calibration
|
||||||
export const HOMING =
|
export const HOMING_ENCODERS =
|
||||||
trim(`If encoders or end-stops are enabled, home axis (find zero).`);
|
trim(`If encoders or end-stops are enabled, home axis (find zero).`);
|
||||||
|
|
||||||
export const CALIBRATION =
|
export const HOMING_STALL_DETECTION =
|
||||||
|
trim(`If stall detection or end-stops are enabled, home axis
|
||||||
|
(find zero).`);
|
||||||
|
|
||||||
|
export const CALIBRATION_ENCODERS =
|
||||||
trim(`If encoders or end-stops are enabled, home axis and determine
|
trim(`If encoders or end-stops are enabled, home axis and determine
|
||||||
maximum.`);
|
maximum.`);
|
||||||
|
|
||||||
|
export const CALIBRATION_STALL_DETECTION =
|
||||||
|
trim(`If stall detection or end-stops are enabled, home axis and
|
||||||
|
determine maximum.`);
|
||||||
|
|
||||||
export const SET_ZERO_POSITION =
|
export const SET_ZERO_POSITION =
|
||||||
trim(`Set the current location as zero.`);
|
trim(`Set the current location as zero.`);
|
||||||
|
|
||||||
export const FIND_HOME_ON_BOOT =
|
export const FIND_HOME_ON_BOOT_ENCODERS =
|
||||||
trim(`If encoders or end-stops are enabled, find the home position
|
trim(`If encoders or end-stops are enabled, find the home position
|
||||||
when the device powers on.
|
when the device powers on. Warning! This will perform homing on all
|
||||||
Warning! This will perform homing on all axes when the
|
axes when the device powers on. Encoders or endstops must be enabled.
|
||||||
device powers on. Encoders or endstops must be enabled.
|
|
||||||
It is recommended to make sure homing works properly before enabling
|
It is recommended to make sure homing works properly before enabling
|
||||||
this feature. (default: disabled)`);
|
this feature. (default: disabled)`);
|
||||||
|
|
||||||
|
export const FIND_HOME_ON_BOOT_STALL_DETECTION =
|
||||||
|
trim(`If stall detection or end-stops are enabled, find the home
|
||||||
|
position when the device powers on. Warning! This will perform homing
|
||||||
|
on all axes when the device powers on. Stall detection or endstops
|
||||||
|
must be enabled. It is recommended to make sure homing works properly
|
||||||
|
before enabling this feature. (default: disabled)`);
|
||||||
|
|
||||||
export const STOP_AT_HOME =
|
export const STOP_AT_HOME =
|
||||||
trim(`Stop at the home location of the axis. (default: disabled)`);
|
trim(`Stop at the home location of the axis. (default: disabled)`);
|
||||||
|
|
||||||
|
@ -85,18 +99,7 @@ export namespace ToolTips {
|
||||||
trim(`Set the length of each axis to provide software limits.
|
trim(`Set the length of each axis to provide software limits.
|
||||||
Used only if STOP AT MAX is enabled. (default: 0 (disabled))`);
|
Used only if STOP AT MAX is enabled. (default: 0 (disabled))`);
|
||||||
|
|
||||||
export const TIMEOUT_AFTER =
|
|
||||||
trim(`Amount of time to wait for a command to execute before stopping.
|
|
||||||
(default: 120s)`);
|
|
||||||
|
|
||||||
// Hardware Settings: Motors
|
// Hardware Settings: Motors
|
||||||
export const MAX_MOVEMENT_RETRIES =
|
|
||||||
trim(`Number of times to retry a movement before stopping. (default: 3)`);
|
|
||||||
|
|
||||||
export const E_STOP_ON_MOV_ERR =
|
|
||||||
trim(`Emergency stop if movement is not complete after the maximum
|
|
||||||
number of retries. (default: disabled)`);
|
|
||||||
|
|
||||||
export const MAX_SPEED =
|
export const MAX_SPEED =
|
||||||
trim(`Maximum travel speed after acceleration in millimeters per second.
|
trim(`Maximum travel speed after acceleration in millimeters per second.
|
||||||
(default: x: 80mm/s, y: 80mm/s, z: 16mm/s)`);
|
(default: x: 80mm/s, y: 80mm/s, z: 16mm/s)`);
|
||||||
|
@ -132,18 +135,22 @@ export namespace ToolTips {
|
||||||
export const MOTOR_CURRENT =
|
export const MOTOR_CURRENT =
|
||||||
trim(`Motor current in milliamps. (default: 600)`);
|
trim(`Motor current in milliamps. (default: 600)`);
|
||||||
|
|
||||||
export const STALL_SENSITIVITY =
|
|
||||||
trim(`Motor stall sensitivity. (default: 30)`);
|
|
||||||
|
|
||||||
export const ENABLE_X2_MOTOR =
|
export const ENABLE_X2_MOTOR =
|
||||||
trim(`Enable use of a second x-axis motor. Connects to E0 on RAMPS.
|
trim(`Enable use of a second x-axis motor. Connects to E0 on RAMPS.
|
||||||
(default: enabled)`);
|
(default: enabled)`);
|
||||||
|
|
||||||
// Hardware Settings: Encoders and Endstops
|
// Hardware Settings: Encoders / Stall Detection
|
||||||
export const ENABLE_ENCODERS =
|
export const ENABLE_ENCODERS =
|
||||||
trim(`Enable use of rotary encoders for stall detection,
|
trim(`Enable use of rotary encoders for stall detection,
|
||||||
calibration and homing. (default: enabled)`);
|
calibration and homing. (default: enabled)`);
|
||||||
|
|
||||||
|
export const ENABLE_STALL_DETECTION =
|
||||||
|
trim(`Enable use of motor stall detection for detecting missed steps,
|
||||||
|
calibration and homing. (default: enabled)`);
|
||||||
|
|
||||||
|
export const STALL_SENSITIVITY =
|
||||||
|
trim(`Motor stall sensitivity. (default: 30)`);
|
||||||
|
|
||||||
export const ENCODER_POSITIONING =
|
export const ENCODER_POSITIONING =
|
||||||
trim(`Use encoders for positioning. (default: disabled)`);
|
trim(`Use encoders for positioning. (default: disabled)`);
|
||||||
|
|
||||||
|
@ -151,17 +158,22 @@ export namespace ToolTips {
|
||||||
trim(`Reverse the direction of encoder position reading.
|
trim(`Reverse the direction of encoder position reading.
|
||||||
(default: disabled)`);
|
(default: disabled)`);
|
||||||
|
|
||||||
export const MAX_MISSED_STEPS =
|
export const MAX_MISSED_STEPS_ENCODERS =
|
||||||
trim(`Number of steps missed (determined by encoder) before motor is
|
trim(`Number of steps missed (determined by encoder) before motor is
|
||||||
considered to have stalled. (default: 5)`);
|
considered to have stalled. (default: 5)`);
|
||||||
|
|
||||||
export const ENCODER_MISSED_STEP_DECAY =
|
export const MAX_MISSED_STEPS_STALL_DETECTION =
|
||||||
|
trim(`Number of steps missed (determined by motor stall detection) before
|
||||||
|
motor is considered to have stalled. (default: 5)`);
|
||||||
|
|
||||||
|
export const MISSED_STEP_DECAY =
|
||||||
trim(`Reduction to missed step total for every good step. (default: 5)`);
|
trim(`Reduction to missed step total for every good step. (default: 5)`);
|
||||||
|
|
||||||
export const ENCODER_SCALING =
|
export const ENCODER_SCALING =
|
||||||
trim(`encoder scaling factor = 10000 * (motor resolution * microsteps)
|
trim(`encoder scaling factor = 10000 * (motor resolution * microsteps)
|
||||||
/ (encoder resolution). (default: 5556 (10000*200/360))`);
|
/ (encoder resolution). (default: 5556 (10000*200/360))`);
|
||||||
|
|
||||||
|
// Hardware Settings: Endstops
|
||||||
export const ENABLE_ENDSTOPS =
|
export const ENABLE_ENDSTOPS =
|
||||||
trim(`Enable use of electronic end-stops for end detection,
|
trim(`Enable use of electronic end-stops for end detection,
|
||||||
calibration and homing. (default: disabled)`);
|
calibration and homing. (default: disabled)`);
|
||||||
|
@ -173,6 +185,18 @@ export namespace ToolTips {
|
||||||
trim(`Invert axis end-stops. Enable for normally closed (NC),
|
trim(`Invert axis end-stops. Enable for normally closed (NC),
|
||||||
disable for normally open (NO). (default: disabled)`);
|
disable for normally open (NO). (default: disabled)`);
|
||||||
|
|
||||||
|
// Hardware Settings: Error Handling
|
||||||
|
export const TIMEOUT_AFTER =
|
||||||
|
trim(`Amount of time to wait for a command to execute before stopping.
|
||||||
|
(default: 120s)`);
|
||||||
|
|
||||||
|
export const MAX_MOVEMENT_RETRIES =
|
||||||
|
trim(`Number of times to retry a movement before stopping. (default: 3)`);
|
||||||
|
|
||||||
|
export const E_STOP_ON_MOV_ERR =
|
||||||
|
trim(`Emergency stop if movement is not complete after the maximum
|
||||||
|
number of retries. (default: disabled)`);
|
||||||
|
|
||||||
// Hardware Settings: Pin Guard
|
// Hardware Settings: Pin Guard
|
||||||
export const PIN_GUARD_PIN_NUMBER =
|
export const PIN_GUARD_PIN_NUMBER =
|
||||||
trim(`The number of the pin to guard. This pin will be set to the specified
|
trim(`The number of the pin to guard. This pin will be set to the specified
|
||||||
|
@ -263,8 +287,12 @@ export namespace ToolTips {
|
||||||
|
|
||||||
export const FIND_HOME =
|
export const FIND_HOME =
|
||||||
trim(`The Find Home step instructs the device to perform a homing
|
trim(`The Find Home step instructs the device to perform a homing
|
||||||
command (using encoders or endstops) to find and set zero for
|
command (using encoders, stall detection, or endstops) to find and set
|
||||||
the chosen axis or axes.`);
|
zero for the chosen axis or axes.`);
|
||||||
|
|
||||||
|
export const CALIBRATE =
|
||||||
|
trim(`If encoders, stall detection, or end-stops are enabled,
|
||||||
|
home axis and determine maximum.`);
|
||||||
|
|
||||||
export const IF =
|
export const IF =
|
||||||
trim(`Execute a sequence if a condition is satisfied. If the condition
|
trim(`Execute a sequence if a condition is satisfied. If the condition
|
||||||
|
@ -674,9 +702,9 @@ export namespace Content {
|
||||||
trim(`FarmBot sent a malformed message. You may need to upgrade
|
trim(`FarmBot sent a malformed message. You may need to upgrade
|
||||||
FarmBot OS. Please upgrade FarmBot OS and log back in.`);
|
FarmBot OS. Please upgrade FarmBot OS and log back in.`);
|
||||||
|
|
||||||
export const OLD_FBOS_REC_UPGRADE = trim(`Your version of FarmBot OS is
|
export const OLD_FBOS_REC_UPGRADE =
|
||||||
outdated and will soon no longer be supported. Please update your device as
|
trim(`Your version of FarmBot OS is outdated and will soon no longer
|
||||||
soon as possible.`);
|
be supported. Please update your device as soon as possible.`);
|
||||||
|
|
||||||
export const EXPERIMENTAL_WARNING =
|
export const EXPERIMENTAL_WARNING =
|
||||||
trim(`Warning! This is an EXPERIMENTAL feature. This feature may be
|
trim(`Warning! This is an EXPERIMENTAL feature. This feature may be
|
||||||
|
@ -715,8 +743,8 @@ export namespace Content {
|
||||||
|
|
||||||
export const END_DETECTION_DISABLED =
|
export const END_DETECTION_DISABLED =
|
||||||
trim(`This command will not execute correctly because you do not have
|
trim(`This command will not execute correctly because you do not have
|
||||||
encoders or endstops enabled for the chosen axis. Enable endstops or
|
encoders, stall detection, or endstops enabled for the chosen axis.
|
||||||
encoders from the Device page for: `);
|
Enable endstops, encoders, or stall detection from the Device page for: `);
|
||||||
|
|
||||||
export const IN_USE =
|
export const IN_USE =
|
||||||
trim(`Used in another resource. Protected from deletion.`);
|
trim(`Used in another resource. Protected from deletion.`);
|
||||||
|
@ -784,7 +812,10 @@ export namespace Content {
|
||||||
trim(`add this crop on OpenFarm?`);
|
trim(`add this crop on OpenFarm?`);
|
||||||
|
|
||||||
export const NO_TOOLS =
|
export const NO_TOOLS =
|
||||||
trim(`Press "+" to add a new tool.`);
|
trim(`Press "+" to add a new tool or seed container.`);
|
||||||
|
|
||||||
|
export const NO_SEED_CONTAINERS =
|
||||||
|
trim(`Press "+" to add a seed container.`);
|
||||||
|
|
||||||
export const MOUNTED_TOOL =
|
export const MOUNTED_TOOL =
|
||||||
trim(`The tool currently mounted to the UTM can be set here or by using
|
trim(`The tool currently mounted to the UTM can be set here or by using
|
||||||
|
@ -859,12 +890,23 @@ export namespace TourContent {
|
||||||
selecting one, and dragging it into the garden.`);
|
selecting one, and dragging it into the garden.`);
|
||||||
|
|
||||||
export const ADD_TOOLS =
|
export const ADD_TOOLS =
|
||||||
trim(`Press edit and then the + button to add tools and seed containers.`);
|
trim(`Press the + button to add tools and seed containers.`);
|
||||||
|
|
||||||
|
export const ADD_SEED_CONTAINERS =
|
||||||
|
trim(`Press the + button to add seed containers.`);
|
||||||
|
|
||||||
|
export const ADD_TOOLS_AND_SLOTS =
|
||||||
|
trim(`Press the + button to add tools and seed containers. Then create
|
||||||
|
tool slots for them to by pressing the tool slot + button.`);
|
||||||
|
|
||||||
|
export const ADD_SEED_CONTAINERS_AND_SLOTS =
|
||||||
|
trim(`Press the + button to add seed containers. Then create
|
||||||
|
slots for them to by pressing the seed container slot + button.`);
|
||||||
|
|
||||||
export const ADD_TOOLS_SLOTS =
|
export const ADD_TOOLS_SLOTS =
|
||||||
trim(`Add the newly created tools and seed containers to the
|
trim(`Add the newly created tools and seed containers to the
|
||||||
corresponding tool slots on FarmBot:
|
corresponding tool slots on FarmBot:
|
||||||
press edit and then + to create a tool slot.`);
|
press the + button to create a tool slot.`);
|
||||||
|
|
||||||
export const ADD_PERIPHERALS =
|
export const ADD_PERIPHERALS =
|
||||||
trim(`Press edit and then the + button to add peripherals.`);
|
trim(`Press edit and then the + button to add peripherals.`);
|
||||||
|
@ -902,6 +944,87 @@ export namespace TourContent {
|
||||||
trim(`Toggle various settings to customize your web app experience.`);
|
trim(`Toggle various settings to customize your web app experience.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DeviceSetting {
|
||||||
|
// Homing and calibration
|
||||||
|
homingAndCalibration = `Homing and Calibration`,
|
||||||
|
homing = `Homing`,
|
||||||
|
calibration = `Calibration`,
|
||||||
|
setZeroPosition = `Set Zero Position`,
|
||||||
|
findHomeOnBoot = `Find Home on Boot`,
|
||||||
|
stopAtHome = `Stop at Home`,
|
||||||
|
stopAtMax = `Stop at Max`,
|
||||||
|
negativeCoordinatesOnly = `Negative Coordinates Only`,
|
||||||
|
axisLength = `Axis Length (mm)`,
|
||||||
|
|
||||||
|
// Motors
|
||||||
|
motors = `Motors`,
|
||||||
|
maxSpeed = `Max Speed (mm/s)`,
|
||||||
|
homingSpeed = `Homing Speed (mm/s)`,
|
||||||
|
minimumSpeed = `Minimum Speed (mm/s)`,
|
||||||
|
accelerateFor = `Accelerate for (mm)`,
|
||||||
|
stepsPerMm = `Steps per MM`,
|
||||||
|
microstepsPerStep = `Microsteps per step`,
|
||||||
|
alwaysPowerMotors = `Always Power Motors`,
|
||||||
|
invertMotors = `Invert Motors`,
|
||||||
|
motorCurrent = `Motor Current`,
|
||||||
|
enable2ndXMotor = `Enable 2nd X Motor`,
|
||||||
|
invert2ndXMotor = `Invert 2nd X Motor`,
|
||||||
|
|
||||||
|
// Encoders / Stall Detection
|
||||||
|
encoders = `Encoders`,
|
||||||
|
stallDetection = `Stall Detection`,
|
||||||
|
enableEncoders = `Enable Encoders`,
|
||||||
|
enableStallDetection = `Enable Stall Detection`,
|
||||||
|
stallSensitivity = `Stall Sensitivity`,
|
||||||
|
useEncodersForPositioning = `Use Encoders for Positioning`,
|
||||||
|
invertEncoders = `Invert Encoders`,
|
||||||
|
maxMissedSteps = `Max Missed Steps`,
|
||||||
|
missedStepDecay = `Missed Step Decay`,
|
||||||
|
encoderScaling = `Encoder Scaling`,
|
||||||
|
|
||||||
|
// Endstops
|
||||||
|
endstops = `Endstops`,
|
||||||
|
enableEndstops = `Enable Endstops`,
|
||||||
|
swapEndstops = `Swap Endstops`,
|
||||||
|
invertEndstops = `Invert Endstops`,
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
errorHandling = `Error Handling`,
|
||||||
|
timeoutAfter = `Timeout after (seconds)`,
|
||||||
|
maxRetries = `Max Retries`,
|
||||||
|
estopOnMovementError = `E-Stop on Movement Error`,
|
||||||
|
|
||||||
|
// Pin Guard
|
||||||
|
pinGuard = `Pin Guard`,
|
||||||
|
|
||||||
|
// Danger Zone
|
||||||
|
dangerZone = `dangerZone`,
|
||||||
|
resetHardwareParams = `Reset hardware parameter defaults`,
|
||||||
|
|
||||||
|
// Pin Bindings
|
||||||
|
pinBindings = `Pin Bindings`,
|
||||||
|
|
||||||
|
// FarmBot OS
|
||||||
|
name = `name`,
|
||||||
|
timezone = `timezone`,
|
||||||
|
camera = `camera`,
|
||||||
|
firmware = `firmware`,
|
||||||
|
farmbotOSAutoUpdate = `Farmbot OS Auto Update`,
|
||||||
|
farmbotOS = `Farmbot OS`,
|
||||||
|
autoSync = `Auto Sync`,
|
||||||
|
bootSequence = `Boot Sequence`,
|
||||||
|
|
||||||
|
// Power and Reset
|
||||||
|
powerAndReset = `Power and Reset`,
|
||||||
|
restartFarmbot = `Restart Farmbot`,
|
||||||
|
shutdownFarmbot = `Shutdown Farmbot`,
|
||||||
|
restartFirmware = `Restart Firmware`,
|
||||||
|
factoryReset = `Factory Reset`,
|
||||||
|
autoFactoryReset = `Automatic Factory Reset`,
|
||||||
|
connectionAttemptPeriod = `Connection Attempt Period`,
|
||||||
|
changeOwnership = `Change Ownership`,
|
||||||
|
}
|
||||||
|
|
||||||
export namespace DiagnosticMessages {
|
export namespace DiagnosticMessages {
|
||||||
export const OK = trim(`All systems nominal.`);
|
export const OK = trim(`All systems nominal.`);
|
||||||
|
|
||||||
|
@ -924,8 +1047,7 @@ export namespace DiagnosticMessages {
|
||||||
but we have no recent record of FarmBot connecting to the internet.
|
but we have no recent record of FarmBot connecting to the internet.
|
||||||
This usually happens because of poor WiFi connectivity in the garden,
|
This usually happens because of poor WiFi connectivity in the garden,
|
||||||
a bad password during configuration, a very long power outage, or
|
a bad password during configuration, a very long power outage, or
|
||||||
blocked ports on FarmBot's local network. Please refer IT staff to
|
blocked ports on FarmBot's local network. Please refer IT staff to:`);
|
||||||
https://software.farm.bot/docs/for-it-security-professionals`);
|
|
||||||
|
|
||||||
export const NO_WS_AVAILABLE = trim(`You are either offline, using a web
|
export const NO_WS_AVAILABLE = trim(`You are either offline, using a web
|
||||||
browser that does not support WebSockets, or are behind a firewall that
|
browser that does not support WebSockets, or are behind a firewall that
|
||||||
|
|
|
@ -38,6 +38,7 @@ export class RawControls extends React.Component<Props, {}> {
|
||||||
getWebAppConfigVal={this.props.getWebAppConfigVal} />
|
getWebAppConfigVal={this.props.getWebAppConfigVal} />
|
||||||
|
|
||||||
peripherals = () => <Peripherals
|
peripherals = () => <Peripherals
|
||||||
|
firmwareHardware={this.props.firmwareHardware}
|
||||||
bot={this.props.bot}
|
bot={this.props.bot}
|
||||||
peripherals={this.props.peripherals}
|
peripherals={this.props.peripherals}
|
||||||
dispatch={this.props.dispatch}
|
dispatch={this.props.dispatch}
|
||||||
|
@ -50,6 +51,7 @@ export class RawControls extends React.Component<Props, {}> {
|
||||||
sensors = () => this.hideSensors
|
sensors = () => this.hideSensors
|
||||||
? <div id="hidden-sensors-widget" />
|
? <div id="hidden-sensors-widget" />
|
||||||
: <Sensors
|
: <Sensors
|
||||||
|
firmwareHardware={this.props.firmwareHardware}
|
||||||
bot={this.props.bot}
|
bot={this.props.bot}
|
||||||
sensors={this.props.sensors}
|
sensors={this.props.sensors}
|
||||||
dispatch={this.props.dispatch}
|
dispatch={this.props.dispatch}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { bot } from "../../../__test_support__/fake_state/bot";
|
||||||
import { PeripheralsProps } from "../../../devices/interfaces";
|
import { PeripheralsProps } from "../../../devices/interfaces";
|
||||||
import { fakePeripheral } from "../../../__test_support__/fake_state/resources";
|
import { fakePeripheral } from "../../../__test_support__/fake_state/resources";
|
||||||
import { clickButton } from "../../../__test_support__/helpers";
|
import { clickButton } from "../../../__test_support__/helpers";
|
||||||
import { SpecialStatus } from "farmbot";
|
import { SpecialStatus, FirmwareHardware } from "farmbot";
|
||||||
import { error } from "../../../toast/toast";
|
import { error } from "../../../toast/toast";
|
||||||
|
|
||||||
describe("<Peripherals />", () => {
|
describe("<Peripherals />", () => {
|
||||||
|
@ -14,7 +14,8 @@ describe("<Peripherals />", () => {
|
||||||
bot,
|
bot,
|
||||||
peripherals: [fakePeripheral()],
|
peripherals: [fakePeripheral()],
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
disabled: false
|
disabled: false,
|
||||||
|
firmwareHardware: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,11 +74,18 @@ describe("<Peripherals />", () => {
|
||||||
expect(p.dispatch).toHaveBeenCalled();
|
expect(p.dispatch).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("adds farmduino peripherals", () => {
|
it.each<[FirmwareHardware, number]>([
|
||||||
|
["arduino", 2],
|
||||||
|
["farmduino", 5],
|
||||||
|
["farmduino_k14", 5],
|
||||||
|
["farmduino_k15", 5],
|
||||||
|
["express_k10", 3],
|
||||||
|
])("adds peripherals: %s", (firmware, expectedAdds) => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
|
p.firmwareHardware = firmware;
|
||||||
const wrapper = mount(<Peripherals {...p} />);
|
const wrapper = mount(<Peripherals {...p} />);
|
||||||
wrapper.setState({ isEditing: true });
|
wrapper.setState({ isEditing: true });
|
||||||
clickButton(wrapper, 3, "farmduino");
|
clickButton(wrapper, 3, "stock");
|
||||||
expect(p.dispatch).toHaveBeenCalledTimes(5);
|
expect(p.dispatch).toHaveBeenCalledTimes(expectedAdds);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -56,12 +56,31 @@ export class Peripherals
|
||||||
this.props.dispatch(init("Peripheral", { pin, label }));
|
this.props.dispatch(init("Peripheral", { pin, label }));
|
||||||
};
|
};
|
||||||
|
|
||||||
farmduinoPeripherals = () => {
|
get stockPeripherals() {
|
||||||
this.newPeripheral(7, t("Lighting"));
|
switch (this.props.firmwareHardware) {
|
||||||
this.newPeripheral(8, t("Water"));
|
case "arduino":
|
||||||
this.newPeripheral(9, t("Vacuum"));
|
return [
|
||||||
this.newPeripheral(10, t("Peripheral ") + "4");
|
{ pin: 8, label: t("Water") },
|
||||||
this.newPeripheral(12, t("Peripheral ") + "5");
|
{ pin: 9, label: t("Vacuum") },
|
||||||
|
];
|
||||||
|
case "farmduino":
|
||||||
|
case "farmduino_k14":
|
||||||
|
case "farmduino_k15":
|
||||||
|
default:
|
||||||
|
return [
|
||||||
|
{ pin: 7, label: t("Lighting") },
|
||||||
|
{ pin: 8, label: t("Water") },
|
||||||
|
{ pin: 9, label: t("Vacuum") },
|
||||||
|
{ pin: 10, label: t("Peripheral ") + "4" },
|
||||||
|
{ pin: 12, label: t("Peripheral ") + "5" },
|
||||||
|
];
|
||||||
|
case "express_k10":
|
||||||
|
return [
|
||||||
|
{ pin: 7, label: t("Lighting") },
|
||||||
|
{ pin: 8, label: t("Water") },
|
||||||
|
{ pin: 9, label: t("Vacuum") },
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -92,10 +111,11 @@ export class Peripherals
|
||||||
hidden={!isEditing}
|
hidden={!isEditing}
|
||||||
className="fb-button green"
|
className="fb-button green"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={this.farmduinoPeripherals}>
|
onClick={() => this.stockPeripherals.map(p =>
|
||||||
|
this.newPeripheral(p.pin, p.label))}>
|
||||||
<i className="fa fa-plus" style={{ marginRight: "0.5rem" }} />
|
<i className="fa fa-plus" style={{ marginRight: "0.5rem" }} />
|
||||||
Farmduino
|
{t("Stock")}
|
||||||
</button>
|
</button>
|
||||||
</WidgetHeader>
|
</WidgetHeader>
|
||||||
<WidgetBody>
|
<WidgetBody>
|
||||||
{this.showPins()}
|
{this.showPins()}
|
||||||
|
|
|
@ -18,7 +18,8 @@ describe("<Sensors />", () => {
|
||||||
bot,
|
bot,
|
||||||
sensors: [fakeSensor1, fakeSensor2],
|
sensors: [fakeSensor1, fakeSensor2],
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
disabled: false
|
disabled: false,
|
||||||
|
firmwareHardware: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +69,16 @@ describe("<Sensors />", () => {
|
||||||
it("adds stock sensors", () => {
|
it("adds stock sensors", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
const wrapper = mount(<Sensors {...p} />);
|
const wrapper = mount(<Sensors {...p} />);
|
||||||
|
expect(wrapper.text().toLowerCase()).toContain("stock sensors");
|
||||||
wrapper.setState({ isEditing: true });
|
wrapper.setState({ isEditing: true });
|
||||||
clickButton(wrapper, 3, "stock sensors");
|
clickButton(wrapper, 3, "stock sensors");
|
||||||
expect(p.dispatch).toHaveBeenCalledTimes(2);
|
expect(p.dispatch).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("doesn't display + stock button", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.firmwareHardware = "express_k10";
|
||||||
|
const wrapper = mount(<Sensors {...p} />);
|
||||||
|
expect(wrapper.text().toLowerCase()).not.toContain("stock sensors");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -100,4 +100,11 @@ describe("<SensorList/>", function () {
|
||||||
readSensorBtn.last().simulate("click");
|
readSensorBtn.last().simulate("click");
|
||||||
expect(mockDevice.readPin).not.toHaveBeenCalled();
|
expect(mockDevice.readPin).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders analog reading", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.pins[50] && (p.pins[50].value = 600);
|
||||||
|
const wrapper = mount(<SensorList {...p} />);
|
||||||
|
expect(wrapper.html()).toContain("margin-left: -3.5rem");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { saveAll, init } from "../../api/crud";
|
||||||
import { ToolTips } from "../../constants";
|
import { ToolTips } from "../../constants";
|
||||||
import { uniq } from "lodash";
|
import { uniq } from "lodash";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
|
import { isExpressBoard } from "../../devices/components/firmware_hardware_support";
|
||||||
|
|
||||||
export class Sensors extends React.Component<SensorsProps, SensorState> {
|
export class Sensors extends React.Component<SensorsProps, SensorState> {
|
||||||
constructor(props: SensorsProps) {
|
constructor(props: SensorsProps) {
|
||||||
|
@ -79,14 +80,15 @@ export class Sensors extends React.Component<SensorsProps, SensorState> {
|
||||||
onClick={() => this.newSensor()}>
|
onClick={() => this.newSensor()}>
|
||||||
<i className="fa fa-plus" />
|
<i className="fa fa-plus" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
{!isExpressBoard(this.props.firmwareHardware) &&
|
||||||
hidden={!isEditing}
|
<button
|
||||||
className="fb-button green"
|
hidden={!isEditing}
|
||||||
type="button"
|
className="fb-button green"
|
||||||
onClick={this.stockSensors}>
|
type="button"
|
||||||
<i className="fa fa-plus" style={{ marginRight: "0.5rem" }} />
|
onClick={this.stockSensors}>
|
||||||
{t("Stock sensors")}
|
<i className="fa fa-plus" style={{ marginRight: "0.5rem" }} />
|
||||||
</button>
|
{t("Stock sensors")}
|
||||||
|
</button>}
|
||||||
</WidgetHeader>
|
</WidgetHeader>
|
||||||
<WidgetBody>
|
<WidgetBody>
|
||||||
{this.showPins()}
|
{this.showPins()}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||||
import { get } from "lodash";
|
import { get } from "lodash";
|
||||||
import { Page } from "./ui/index";
|
import { Page } from "./ui/index";
|
||||||
import { Session } from "./session";
|
import { Session } from "./session";
|
||||||
|
import { ExternalUrl } from "./external_urls";
|
||||||
|
|
||||||
/** Use currying to pass down `error` object for now. */
|
/** Use currying to pass down `error` object for now. */
|
||||||
export function crashPage(error: object) {
|
export function crashPage(error: object) {
|
||||||
|
@ -24,7 +25,7 @@ export function crashPage(error: object) {
|
||||||
<li>Perform a "hard refresh" (<strong>CTRL + SHIFT + R</strong> on most machines).</li>
|
<li>Perform a "hard refresh" (<strong>CTRL + SHIFT + R</strong> on most machines).</li>
|
||||||
<li><span><a onClick={() => Session.clear()}>Log out by clicking here.</a></span></li>
|
<li><span><a onClick={() => Session.clear()}>Log out by clicking here.</a></span></li>
|
||||||
<li>Send the error information (below) to our developer team via the
|
<li>Send the error information (below) to our developer team via the
|
||||||
<a href="http://forum.farmbot.org/c/software">FarmBot software
|
<a href={ExternalUrl.softwareForum}>FarmBot software
|
||||||
forum</a>. Including additional information (such as steps leading up
|
forum</a>. Including additional information (such as steps leading up
|
||||||
to the error) help us identify solutions more quickly. </li>
|
to the error) help us identify solutions more quickly. </li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Padding for the popups.
|
// Padding for the popups.
|
||||||
.bp3-popover-content {
|
.bp3-popover-content {
|
||||||
|
z-index: 999;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -580,6 +580,7 @@
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.tools-header,
|
||||||
.tool-slots-header {
|
.tool-slots-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
@ -624,6 +625,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.add-stock-tools {
|
.add-stock-tools {
|
||||||
|
.filter-search {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
button {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
ul {
|
ul {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
|
|
|
@ -433,14 +433,17 @@ a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pin-bindings-widget {
|
.pin-bindings {
|
||||||
.fa-exclamation-triangle {
|
.fa-exclamation-triangle {
|
||||||
color: $orange;
|
color: $orange;
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
.fa-th-large {
|
.fa-th-large {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75rem;
|
||||||
|
left: 0.5rem;
|
||||||
color: $dark_gray;
|
color: $dark_gray;
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
}
|
||||||
.fb-button {
|
.fb-button {
|
||||||
&.green {
|
&.green {
|
||||||
|
@ -449,16 +452,27 @@ a {
|
||||||
}
|
}
|
||||||
.bindings-list {
|
.bindings-list {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
margin-left: 1rem;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
.binding-type-dropdown {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
.stock-pin-bindings-button {
|
.stock-pin-bindings-button {
|
||||||
button {
|
button {
|
||||||
margin: 0 !important;
|
margin: 1rem;
|
||||||
|
float: left;
|
||||||
|
margin-left: 2rem;
|
||||||
}
|
}
|
||||||
i {
|
i {
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.bp3-popover-wrapper {
|
||||||
|
display: inline;
|
||||||
|
float: none !important;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sensor-history-widget {
|
.sensor-history-widget {
|
||||||
|
@ -1308,6 +1322,12 @@ ul {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.boolean-camera-calibration-config {
|
||||||
|
input[type=checkbox] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tour-list {
|
.tour-list {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
@ -1519,16 +1539,24 @@ textarea:focus {
|
||||||
box-shadow: 0 0 10px rgba(0,0,0,.2);
|
box-shadow: 0 0 10px rgba(0,0,0,.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-path-info-bar {
|
.sort-option-bar {
|
||||||
background: lightgray;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1.1rem;
|
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
white-space: nowrap;
|
border: 2px solid $panel_light_blue;
|
||||||
line-height: 1.75rem;
|
&:hover, &.selected {
|
||||||
&:hover {
|
border: 2px solid $medium_gray;
|
||||||
background: darken(lightgray, 10%);
|
border-radius: 2px;
|
||||||
|
.sort-path-info-bar {
|
||||||
|
background: darken($light_gray, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sort-path-info-bar {
|
||||||
|
background: $light_gray;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 2.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1601,3 +1629,23 @@ textarea:focus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight,
|
||||||
|
.unhighlight {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
background-color: $light_yellow;
|
||||||
|
box-shadow: 0px 0px 7px 4px $light_yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unhighlight {
|
||||||
|
transition: background-color 10s linear, box-shadow 10s linear;
|
||||||
|
background-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
|
@ -97,6 +97,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.camera-calibration,
|
||||||
.weed-detector{
|
.weed-detector{
|
||||||
.farmware-button{
|
.farmware-button{
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -2,16 +2,13 @@ import { connect, MqttClient } from "mqtt";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { uuid } from "farmbot";
|
import { uuid } from "farmbot";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { ExternalUrl } from "../external_urls";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
error: Error | undefined;
|
error: Error | undefined;
|
||||||
stage: string;
|
stage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VIDEO_URL =
|
|
||||||
"https://cdn.shopify.com/s/files/1/2040/0289/files/Farm_Designer_Loop.mp4?9552037556691879018";
|
|
||||||
const PHONE_URL =
|
|
||||||
"https://cdn.shopify.com/s/files/1/2040/0289/files/Controls.png?9668345515035078097";
|
|
||||||
const WS_CONFIG = {
|
const WS_CONFIG = {
|
||||||
username: "farmbot_demo",
|
username: "farmbot_demo",
|
||||||
password: "required, but not used.",
|
password: "required, but not used.",
|
||||||
|
@ -63,9 +60,9 @@ export class DemoIframe extends React.Component<{}, State> {
|
||||||
|
|
||||||
return <div className="demo-container">
|
return <div className="demo-container">
|
||||||
<video muted={true} autoPlay={true} loop={true} className="demo-video">
|
<video muted={true} autoPlay={true} loop={true} className="demo-video">
|
||||||
<source src={VIDEO_URL} type="video/mp4" />
|
<source src={ExternalUrl.Video.desktop} type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
<img className="demo-phone" src={PHONE_URL} />
|
<img className="demo-phone" src={ExternalUrl.Video.mobile} />
|
||||||
<button className="demo-button" onClick={this.requestAccount}>
|
<button className="demo-button" onClick={this.requestAccount}>
|
||||||
{this.state.stage}
|
{this.state.stage}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -307,7 +307,7 @@ describe("commandErr()", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("toggleControlPanel()", function () {
|
describe("toggleControlPanel()", () => {
|
||||||
it("toggles", () => {
|
it("toggles", () => {
|
||||||
const action = actions.toggleControlPanel("homing_and_calibration");
|
const action = actions.toggleControlPanel("homing_and_calibration");
|
||||||
expect(action.payload).toEqual("homing_and_calibration");
|
expect(action.payload).toEqual("homing_and_calibration");
|
||||||
|
|
|
@ -35,7 +35,6 @@ describe("mapStateToProps()", () => {
|
||||||
it("uses the API as the source of FBOS settings", () => {
|
it("uses the API as the source of FBOS settings", () => {
|
||||||
const fakeApiConfig = fakeFbosConfig();
|
const fakeApiConfig = fakeFbosConfig();
|
||||||
fakeApiConfig.body.auto_sync = true;
|
fakeApiConfig.body.auto_sync = true;
|
||||||
fakeApiConfig.body.api_migrated = true;
|
|
||||||
mockFbosConfig = fakeApiConfig;
|
mockFbosConfig = fakeApiConfig;
|
||||||
const props = mapStateToProps(fakeState());
|
const props = mapStateToProps(fakeState());
|
||||||
expect(props.sourceFbosConfig("auto_sync")).toEqual({
|
expect(props.sourceFbosConfig("auto_sync")).toEqual({
|
||||||
|
@ -53,19 +52,6 @@ describe("mapStateToProps()", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses the bot as the source of FBOS settings: ignore API defaults", () => {
|
|
||||||
const state = fakeState();
|
|
||||||
state.bot.hardware.configuration.auto_sync = false;
|
|
||||||
const fakeApiConfig = fakeFbosConfig();
|
|
||||||
fakeApiConfig.body.auto_sync = true;
|
|
||||||
fakeApiConfig.body.api_migrated = false;
|
|
||||||
mockFbosConfig = fakeApiConfig;
|
|
||||||
const props = mapStateToProps(state);
|
|
||||||
expect(props.sourceFbosConfig("auto_sync")).toEqual({
|
|
||||||
value: false, consistent: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns API Farmware env vars", () => {
|
it("returns API Farmware env vars", () => {
|
||||||
const state = fakeState();
|
const state = fakeState();
|
||||||
state.bot.hardware.user_env = {};
|
state.bot.hardware.user_env = {};
|
||||||
|
|
|
@ -26,11 +26,6 @@ import { t } from "../i18next_wrapper";
|
||||||
|
|
||||||
const ON = 1, OFF = 0;
|
const ON = 1, OFF = 0;
|
||||||
export type ConfigKey = keyof McuParams;
|
export type ConfigKey = keyof McuParams;
|
||||||
export const EXPECTED_MAJOR = 6;
|
|
||||||
export const EXPECTED_MINOR = 0;
|
|
||||||
export const FEATURE_MIN_VERSIONS_URL =
|
|
||||||
"https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/" +
|
|
||||||
"FEATURE_MIN_VERSIONS.json";
|
|
||||||
// Already filtering messages in FarmBot OS and the API- this is just for
|
// Already filtering messages in FarmBot OS and the API- this is just for
|
||||||
// an additional layer of safety.
|
// an additional layer of safety.
|
||||||
const BAD_WORDS = ["WPA", "PSK", "PASSWORD", "NERVES"];
|
const BAD_WORDS = ["WPA", "PSK", "PASSWORD", "NERVES"];
|
||||||
|
@ -132,7 +127,7 @@ export function sync(): Thunk {
|
||||||
return function (_dispatch, getState) {
|
return function (_dispatch, getState) {
|
||||||
const currentFBOSversion =
|
const currentFBOSversion =
|
||||||
getState().bot.hardware.informational_settings.controller_version;
|
getState().bot.hardware.informational_settings.controller_version;
|
||||||
const IS_OK = versionOK(currentFBOSversion, EXPECTED_MAJOR, EXPECTED_MINOR);
|
const IS_OK = versionOK(currentFBOSversion);
|
||||||
if (IS_OK) {
|
if (IS_OK) {
|
||||||
getDevice()
|
getDevice()
|
||||||
.sync()
|
.sync()
|
||||||
|
|
|
@ -7,13 +7,14 @@ import { ToggleButton } from "../../../controls/toggle_button";
|
||||||
import { settingToggle } from "../../actions";
|
import { settingToggle } from "../../actions";
|
||||||
import { bot } from "../../../__test_support__/fake_state/bot";
|
import { bot } from "../../../__test_support__/fake_state/bot";
|
||||||
import { BooleanMCUInputGroupProps } from "../interfaces";
|
import { BooleanMCUInputGroupProps } from "../interfaces";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
|
||||||
describe("BooleanMCUInputGroup", () => {
|
describe("BooleanMCUInputGroup", () => {
|
||||||
const fakeProps = (): BooleanMCUInputGroupProps => ({
|
const fakeProps = (): BooleanMCUInputGroupProps => ({
|
||||||
sourceFwConfig: x => ({ value: bot.hardware.mcu_params[x], consistent: true }),
|
sourceFwConfig: x => ({ value: bot.hardware.mcu_params[x], consistent: true }),
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
tooltip: "Tooltip",
|
tooltip: "Tooltip",
|
||||||
name: "Name",
|
label: DeviceSetting.invertEncoders,
|
||||||
x: "encoder_invert_x",
|
x: "encoder_invert_x",
|
||||||
y: "encoder_invert_y",
|
y: "encoder_invert_y",
|
||||||
z: "encoder_invert_z",
|
z: "encoder_invert_z",
|
||||||
|
|
|
@ -22,6 +22,8 @@ import axios from "axios";
|
||||||
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
||||||
import { edit } from "../../../api/crud";
|
import { edit } from "../../../api/crud";
|
||||||
import { fakeWebAppConfig } from "../../../__test_support__/fake_state/resources";
|
import { fakeWebAppConfig } from "../../../__test_support__/fake_state/resources";
|
||||||
|
import { formEvent } from "../../../__test_support__/fake_html_events";
|
||||||
|
import { Content } from "../../../constants";
|
||||||
|
|
||||||
describe("<FarmbotOsSettings />", () => {
|
describe("<FarmbotOsSettings />", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -54,8 +56,8 @@ describe("<FarmbotOsSettings />", () => {
|
||||||
const osSettings = mount(<FarmbotOsSettings {...fakeProps()} />);
|
const osSettings = mount(<FarmbotOsSettings {...fakeProps()} />);
|
||||||
expect(osSettings.find("input").length).toBe(1);
|
expect(osSettings.find("input").length).toBe(1);
|
||||||
expect(osSettings.find("button").length).toBe(7);
|
expect(osSettings.find("button").length).toBe(7);
|
||||||
["NAME", "TIME ZONE", "FARMBOT OS", "CAMERA", "FIRMWARE"]
|
["name", "time zone", "farmbot os", "camera", "firmware"]
|
||||||
.map(string => expect(osSettings.text()).toContain(string));
|
.map(string => expect(osSettings.text().toLowerCase()).toContain(string));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("fetches OS release notes", async () => {
|
it("fetches OS release notes", async () => {
|
||||||
|
@ -115,4 +117,18 @@ describe("<FarmbotOsSettings />", () => {
|
||||||
const osSettings = shallow(<FarmbotOsSettings {...p} />);
|
const osSettings = shallow(<FarmbotOsSettings {...p} />);
|
||||||
expect(osSettings.find("BootSequenceSelector").length).toEqual(1);
|
expect(osSettings.find("BootSequenceSelector").length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prevents default form submit action", () => {
|
||||||
|
const osSettings = shallow(<FarmbotOsSettings {...fakeProps()} />);
|
||||||
|
const e = formEvent();
|
||||||
|
osSettings.find("form").simulate("submit", e);
|
||||||
|
expect(e.preventDefault).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("warns about timezone mismatch", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.deviceAccount.body.timezone = "different";
|
||||||
|
const osSettings = mount(<FarmbotOsSettings {...p} />);
|
||||||
|
expect(osSettings.text()).toContain(Content.DIFFERENT_TZ_WARNING);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { boardType } from "../firmware_hardware_support";
|
import { boardType, getFwHardwareValue } from "../firmware_hardware_support";
|
||||||
|
import { fakeFbosConfig } from "../../../__test_support__/fake_state/resources";
|
||||||
|
|
||||||
describe("boardType()", () => {
|
describe("boardType()", () => {
|
||||||
it("returns Farmduino", () => {
|
it("returns Farmduino", () => {
|
||||||
|
@ -32,3 +33,18 @@ describe("boardType()", () => {
|
||||||
expect(boardType("none")).toEqual("none");
|
expect(boardType("none")).toEqual("none");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getFwHardwareValue()", () => {
|
||||||
|
it("returns undefined", () => {
|
||||||
|
const fbosConfig = fakeFbosConfig();
|
||||||
|
fbosConfig.body.firmware_hardware = "wrong";
|
||||||
|
expect(getFwHardwareValue(fbosConfig)).toEqual(undefined);
|
||||||
|
expect(getFwHardwareValue(undefined)).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns real value", () => {
|
||||||
|
const fbosConfig = fakeFbosConfig();
|
||||||
|
fbosConfig.body.firmware_hardware = "express_k10";
|
||||||
|
expect(getFwHardwareValue(fbosConfig)).toEqual("express_k10");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -65,13 +65,7 @@ describe("<HardwareSettings />", () => {
|
||||||
it("shows param export menu", () => {
|
it("shows param export menu", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.firmwareConfig = fakeFirmwareConfig().body;
|
p.firmwareConfig = fakeFirmwareConfig().body;
|
||||||
p.firmwareConfig.api_migrated = true;
|
|
||||||
const wrapper = shallow(<HardwareSettings {...p} />);
|
const wrapper = shallow(<HardwareSettings {...p} />);
|
||||||
expect(wrapper.html()).toContain("fa-download");
|
expect(wrapper.html()).toContain("fa-download");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't show param export menu", () => {
|
|
||||||
const wrapper = shallow(<HardwareSettings {...fakeProps()} />);
|
|
||||||
expect(wrapper.html()).not.toContain("fa-download");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
jest.mock("../../actions", () => ({
|
||||||
|
toggleControlPanel: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import {
|
||||||
|
Highlight, HighlightProps, maybeHighlight, maybeOpenPanel, highlight
|
||||||
|
} from "../maybe_highlight";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
import { panelState } from "../../../__test_support__/control_panel_state";
|
||||||
|
import { toggleControlPanel } from "../../actions";
|
||||||
|
|
||||||
|
describe("<Highlight />", () => {
|
||||||
|
const fakeProps = (): HighlightProps => ({
|
||||||
|
settingName: DeviceSetting.motors,
|
||||||
|
children: <div />,
|
||||||
|
className: "section",
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fades highlight", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const wrapper = mount<Highlight>(<Highlight {...p} />);
|
||||||
|
wrapper.setState({ className: "highlight" });
|
||||||
|
wrapper.instance().componentDidMount();
|
||||||
|
expect(wrapper.state().className).toEqual("unhighlight");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("maybeHighlight()", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
highlight.opened = false;
|
||||||
|
highlight.highlighted = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("highlights only once", () => {
|
||||||
|
location.search = "?highlight=motors";
|
||||||
|
expect(maybeHighlight(DeviceSetting.motors)).toEqual("highlight");
|
||||||
|
expect(maybeHighlight(DeviceSetting.motors)).toEqual("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't highlight: different setting", () => {
|
||||||
|
location.search = "?highlight=name";
|
||||||
|
expect(maybeHighlight(DeviceSetting.motors)).toEqual("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't highlight: no matches", () => {
|
||||||
|
location.search = "?highlight=na";
|
||||||
|
expect(maybeHighlight(DeviceSetting.motors)).toEqual("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("maybeOpenPanel()", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
highlight.opened = false;
|
||||||
|
highlight.highlighted = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("opens panel only once", () => {
|
||||||
|
location.search = "?highlight=motors";
|
||||||
|
maybeOpenPanel(panelState())(jest.fn());
|
||||||
|
expect(toggleControlPanel).toHaveBeenCalledWith("motors");
|
||||||
|
jest.resetAllMocks();
|
||||||
|
maybeOpenPanel(panelState())(jest.fn());
|
||||||
|
expect(toggleControlPanel).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't open panel: already open", () => {
|
||||||
|
location.search = "?highlight=motors";
|
||||||
|
const panels = panelState();
|
||||||
|
panels.motors = true;
|
||||||
|
maybeOpenPanel(panels)(jest.fn());
|
||||||
|
expect(toggleControlPanel).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't open panel: no search term", () => {
|
||||||
|
location.search = "";
|
||||||
|
maybeOpenPanel(panelState())(jest.fn());
|
||||||
|
expect(toggleControlPanel).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,12 +4,14 @@ import { settingToggle } from "../actions";
|
||||||
import { Row, Col, Help } from "../../ui/index";
|
import { Row, Col, Help } from "../../ui/index";
|
||||||
import { BooleanMCUInputGroupProps } from "./interfaces";
|
import { BooleanMCUInputGroupProps } from "./interfaces";
|
||||||
import { Position } from "@blueprintjs/core";
|
import { Position } from "@blueprintjs/core";
|
||||||
|
import { t } from "../../i18next_wrapper";
|
||||||
|
import { Highlight } from "./maybe_highlight";
|
||||||
|
|
||||||
export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) {
|
export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tooltip,
|
tooltip,
|
||||||
name,
|
label,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z,
|
z,
|
||||||
|
@ -26,40 +28,42 @@ export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) {
|
||||||
const zParam = sourceFwConfig(z);
|
const zParam = sourceFwConfig(z);
|
||||||
|
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={6} className={"widget-body-tooltips"}>
|
<Highlight settingName={label}>
|
||||||
<label>
|
<Col xs={6} className={"widget-body-tooltips"}>
|
||||||
{name}
|
<label>
|
||||||
{caution &&
|
{t(label)}
|
||||||
<i className="fa fa-exclamation-triangle caution-icon" />}
|
{caution &&
|
||||||
</label>
|
<i className="fa fa-exclamation-triangle caution-icon" />}
|
||||||
<Help text={tooltip} requireClick={true} position={Position.RIGHT} />
|
</label>
|
||||||
</Col>
|
<Help text={tooltip} requireClick={true} position={Position.RIGHT} />
|
||||||
<Col xs={2} className={"centered-button-div"}>
|
</Col>
|
||||||
<ToggleButton
|
<Col xs={2} className={"centered-button-div"}>
|
||||||
grayscale={grayscale?.x}
|
<ToggleButton
|
||||||
disabled={disable?.x}
|
grayscale={grayscale?.x}
|
||||||
dim={!xParam.consistent}
|
disabled={disable?.x}
|
||||||
toggleValue={xParam.value}
|
dim={!xParam.consistent}
|
||||||
toggleAction={() =>
|
toggleValue={xParam.value}
|
||||||
dispatch(settingToggle(x, sourceFwConfig, displayAlert))} />
|
toggleAction={() =>
|
||||||
</Col>
|
dispatch(settingToggle(x, sourceFwConfig, displayAlert))} />
|
||||||
<Col xs={2} className={"centered-button-div"}>
|
</Col>
|
||||||
<ToggleButton
|
<Col xs={2} className={"centered-button-div"}>
|
||||||
grayscale={grayscale?.y}
|
<ToggleButton
|
||||||
disabled={disable?.y}
|
grayscale={grayscale?.y}
|
||||||
dim={!yParam.consistent}
|
disabled={disable?.y}
|
||||||
toggleValue={yParam.value}
|
dim={!yParam.consistent}
|
||||||
toggleAction={() =>
|
toggleValue={yParam.value}
|
||||||
dispatch(settingToggle(y, sourceFwConfig, displayAlert))} />
|
toggleAction={() =>
|
||||||
</Col>
|
dispatch(settingToggle(y, sourceFwConfig, displayAlert))} />
|
||||||
<Col xs={2} className={"centered-button-div"}>
|
</Col>
|
||||||
<ToggleButton
|
<Col xs={2} className={"centered-button-div"}>
|
||||||
grayscale={grayscale?.z}
|
<ToggleButton
|
||||||
disabled={disable?.z}
|
grayscale={grayscale?.z}
|
||||||
dim={!zParam.consistent}
|
disabled={disable?.z}
|
||||||
toggleValue={zParam.value}
|
dim={!zParam.consistent}
|
||||||
toggleAction={() =>
|
toggleValue={zParam.value}
|
||||||
dispatch(settingToggle(z, sourceFwConfig, displayAlert))} />
|
toggleAction={() =>
|
||||||
</Col>
|
dispatch(settingToggle(z, sourceFwConfig, displayAlert))} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { t } from "../../i18next_wrapper";
|
||||||
import { FarmbotOsProps, FarmbotOsState, Feature } from "../interfaces";
|
import { FarmbotOsProps, FarmbotOsState, Feature } from "../interfaces";
|
||||||
import { Widget, WidgetHeader, WidgetBody, Row, Col } from "../../ui";
|
import { Widget, WidgetHeader, WidgetBody, Row, Col } from "../../ui";
|
||||||
import { save, edit } from "../../api/crud";
|
import { save, edit } from "../../api/crud";
|
||||||
import { MustBeOnline, isBotOnline } from "../must_be_online";
|
import { isBotOnline } from "../must_be_online";
|
||||||
import { Content } from "../../constants";
|
import { Content, DeviceSetting } from "../../constants";
|
||||||
import { TimezoneSelector } from "../timezones/timezone_selector";
|
import { TimezoneSelector } from "../timezones/timezone_selector";
|
||||||
import { timezoneMismatch } from "../timezones/guess_timezone";
|
import { timezoneMismatch } from "../timezones/guess_timezone";
|
||||||
import { CameraSelection } from "./fbos_settings/camera_selection";
|
import { CameraSelection } from "./fbos_settings/camera_selection";
|
||||||
|
@ -15,6 +15,8 @@ import { AutoUpdateRow } from "./fbos_settings/auto_update_row";
|
||||||
import { AutoSyncRow } from "./fbos_settings/auto_sync_row";
|
import { AutoSyncRow } from "./fbos_settings/auto_sync_row";
|
||||||
import { PowerAndReset } from "./fbos_settings/power_and_reset";
|
import { PowerAndReset } from "./fbos_settings/power_and_reset";
|
||||||
import { BootSequenceSelector } from "./fbos_settings/boot_sequence_selector";
|
import { BootSequenceSelector } from "./fbos_settings/boot_sequence_selector";
|
||||||
|
import { ExternalUrl } from "../../external_urls";
|
||||||
|
import { Highlight } from "./maybe_highlight";
|
||||||
|
|
||||||
export enum ColWidth {
|
export enum ColWidth {
|
||||||
label = 3,
|
label = 3,
|
||||||
|
@ -22,15 +24,12 @@ export enum ColWidth {
|
||||||
button = 2
|
button = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
const OS_RELEASE_NOTES_URL =
|
|
||||||
"https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/RELEASE_NOTES.md";
|
|
||||||
|
|
||||||
export class FarmbotOsSettings
|
export class FarmbotOsSettings
|
||||||
extends React.Component<FarmbotOsProps, FarmbotOsState> {
|
extends React.Component<FarmbotOsProps, FarmbotOsState> {
|
||||||
state: FarmbotOsState = { allOsReleaseNotes: "" };
|
state: FarmbotOsState = { allOsReleaseNotes: "" };
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.fetchReleaseNotes(OS_RELEASE_NOTES_URL);
|
this.fetchReleaseNotes(ExternalUrl.osReleaseNotes);
|
||||||
}
|
}
|
||||||
|
|
||||||
get osMajorVersion() {
|
get osMajorVersion() {
|
||||||
|
@ -87,83 +86,79 @@ export class FarmbotOsSettings
|
||||||
</WidgetHeader>
|
</WidgetHeader>
|
||||||
<WidgetBody>
|
<WidgetBody>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.name}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("NAME")}
|
<label>
|
||||||
</label>
|
{t(DeviceSetting.name)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={9}>
|
</Col>
|
||||||
<input name="name"
|
<Col xs={9}>
|
||||||
onChange={this.changeBot}
|
<input name="name"
|
||||||
onBlur={this.updateBot}
|
onChange={this.changeBot}
|
||||||
value={this.props.deviceAccount.body.name} />
|
onBlur={this.updateBot}
|
||||||
</Col>
|
value={this.props.deviceAccount.body.name} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.timezone}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("TIME ZONE")}
|
<label>
|
||||||
</label>
|
{t("TIME ZONE")}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<div className="note">
|
<Col xs={ColWidth.description}>
|
||||||
{this.maybeWarnTz()}
|
<div className="note">
|
||||||
</div>
|
{this.maybeWarnTz()}
|
||||||
<div>
|
</div>
|
||||||
<TimezoneSelector
|
<div>
|
||||||
currentTimezone={this.props.deviceAccount.body.timezone}
|
<TimezoneSelector
|
||||||
onUpdate={this.handleTimezone} />
|
currentTimezone={this.props.deviceAccount.body.timezone}
|
||||||
</div>
|
onUpdate={this.handleTimezone} />
|
||||||
</Col>
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>
|
</Row>
|
||||||
<MustBeOnline
|
<CameraSelection
|
||||||
syncStatus={sync_status}
|
env={this.props.env}
|
||||||
networkState={this.props.botToMqttStatus}
|
botOnline={botOnline}
|
||||||
lockOpen={process.env.NODE_ENV !== "production"
|
saveFarmwareEnv={this.props.saveFarmwareEnv}
|
||||||
|| this.props.isValidFbosConfig}>
|
shouldDisplay={this.props.shouldDisplay}
|
||||||
<CameraSelection
|
dispatch={this.props.dispatch} />
|
||||||
env={this.props.env}
|
<BoardType
|
||||||
botOnline={botOnline}
|
botOnline={botOnline}
|
||||||
saveFarmwareEnv={this.props.saveFarmwareEnv}
|
bot={bot}
|
||||||
shouldDisplay={this.props.shouldDisplay}
|
alerts={this.props.alerts}
|
||||||
dispatch={this.props.dispatch} />
|
dispatch={this.props.dispatch}
|
||||||
<BoardType
|
shouldDisplay={this.props.shouldDisplay}
|
||||||
botOnline={botOnline}
|
timeSettings={this.props.timeSettings}
|
||||||
bot={bot}
|
sourceFbosConfig={sourceFbosConfig} />
|
||||||
alerts={this.props.alerts}
|
<AutoUpdateRow
|
||||||
dispatch={this.props.dispatch}
|
timeFormat={timeFormat}
|
||||||
shouldDisplay={this.props.shouldDisplay}
|
device={this.props.deviceAccount}
|
||||||
timeSettings={this.props.timeSettings}
|
dispatch={this.props.dispatch}
|
||||||
sourceFbosConfig={sourceFbosConfig} />
|
sourceFbosConfig={sourceFbosConfig} />
|
||||||
<AutoUpdateRow
|
<FarmbotOsRow
|
||||||
shouldDisplay={this.props.shouldDisplay}
|
bot={this.props.bot}
|
||||||
timeFormat={timeFormat}
|
osReleaseNotesHeading={this.osReleaseNotes.heading}
|
||||||
device={this.props.deviceAccount}
|
osReleaseNotes={this.osReleaseNotes.notes}
|
||||||
dispatch={this.props.dispatch}
|
dispatch={this.props.dispatch}
|
||||||
sourceFbosConfig={sourceFbosConfig} />
|
sourceFbosConfig={sourceFbosConfig}
|
||||||
<FarmbotOsRow
|
shouldDisplay={this.props.shouldDisplay}
|
||||||
bot={this.props.bot}
|
botOnline={botOnline}
|
||||||
osReleaseNotesHeading={this.osReleaseNotes.heading}
|
botToMqttLastSeen={new Date(this.props.botToMqttLastSeen).getTime()}
|
||||||
osReleaseNotes={this.osReleaseNotes.notes}
|
timeSettings={this.props.timeSettings}
|
||||||
dispatch={this.props.dispatch}
|
deviceAccount={this.props.deviceAccount} />
|
||||||
sourceFbosConfig={sourceFbosConfig}
|
<AutoSyncRow
|
||||||
shouldDisplay={this.props.shouldDisplay}
|
dispatch={this.props.dispatch}
|
||||||
botOnline={botOnline}
|
sourceFbosConfig={sourceFbosConfig} />
|
||||||
botToMqttLastSeen={new Date(this.props.botToMqttLastSeen).getTime()}
|
{this.props.shouldDisplay(Feature.boot_sequence) &&
|
||||||
timeSettings={this.props.timeSettings}
|
<BootSequenceSelector />}
|
||||||
deviceAccount={this.props.deviceAccount} />
|
<PowerAndReset
|
||||||
<AutoSyncRow
|
controlPanelState={this.props.bot.controlPanelState}
|
||||||
dispatch={this.props.dispatch}
|
dispatch={this.props.dispatch}
|
||||||
sourceFbosConfig={sourceFbosConfig} />
|
sourceFbosConfig={sourceFbosConfig}
|
||||||
{this.props.shouldDisplay(Feature.boot_sequence) &&
|
botOnline={botOnline} />
|
||||||
<BootSequenceSelector />}
|
|
||||||
<PowerAndReset
|
|
||||||
controlPanelState={this.props.bot.controlPanelState}
|
|
||||||
dispatch={this.props.dispatch}
|
|
||||||
sourceFbosConfig={sourceFbosConfig}
|
|
||||||
shouldDisplay={this.props.shouldDisplay}
|
|
||||||
botOnline={botOnline} />
|
|
||||||
</MustBeOnline>
|
|
||||||
</WidgetBody>
|
</WidgetBody>
|
||||||
</form>
|
</form>
|
||||||
</Widget>;
|
</Widget>;
|
||||||
|
|
|
@ -21,7 +21,6 @@ describe("<AutoUpdateRow/>", () => {
|
||||||
|
|
||||||
const fakeProps = (): AutoUpdateRowProps => ({
|
const fakeProps = (): AutoUpdateRowProps => ({
|
||||||
timeFormat: "12h",
|
timeFormat: "12h",
|
||||||
shouldDisplay: jest.fn(() => true),
|
|
||||||
device: fakeDevice(),
|
device: fakeDevice(),
|
||||||
dispatch: jest.fn(x => x(jest.fn(), () => state)),
|
dispatch: jest.fn(x => x(jest.fn(), () => state)),
|
||||||
sourceFbosConfig: () => ({ value: 1, consistent: true })
|
sourceFbosConfig: () => ({ value: 1, consistent: true })
|
||||||
|
|
|
@ -69,6 +69,9 @@ describe("<BoardType/>", () => {
|
||||||
{ label: "Arduino/RAMPS (Genesis v1.2)", value: "arduino" },
|
{ label: "Arduino/RAMPS (Genesis v1.2)", value: "arduino" },
|
||||||
{ label: "Farmduino (Genesis v1.3)", value: "farmduino" },
|
{ label: "Farmduino (Genesis v1.3)", value: "farmduino" },
|
||||||
{ label: "Farmduino (Genesis v1.4)", value: "farmduino_k14" },
|
{ label: "Farmduino (Genesis v1.4)", value: "farmduino_k14" },
|
||||||
|
{ label: "Farmduino (Genesis v1.5)", value: "farmduino_k15" },
|
||||||
|
{ label: "Farmduino (Express v1.0)", value: "express_k10" },
|
||||||
|
{ label: "None", value: "none" },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ describe("<FirmwareHardwareStatusDetails />", () => {
|
||||||
apiFirmwareValue: undefined,
|
apiFirmwareValue: undefined,
|
||||||
botFirmwareValue: undefined,
|
botFirmwareValue: undefined,
|
||||||
mcuFirmwareValue: undefined,
|
mcuFirmwareValue: undefined,
|
||||||
shouldDisplay: () => true,
|
|
||||||
timeSettings: fakeTimeSettings(),
|
timeSettings: fakeTimeSettings(),
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
});
|
});
|
||||||
|
@ -79,7 +78,6 @@ describe("<FirmwareHardwareStatus />", () => {
|
||||||
alerts: [],
|
alerts: [],
|
||||||
botOnline: true,
|
botOnline: true,
|
||||||
apiFirmwareValue: undefined,
|
apiFirmwareValue: undefined,
|
||||||
shouldDisplay: () => true,
|
|
||||||
timeSettings: fakeTimeSettings(),
|
timeSettings: fakeTimeSettings(),
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,30 +25,25 @@ describe("<PowerAndReset/>", () => {
|
||||||
const state = fakeState();
|
const state = fakeState();
|
||||||
state.resources = buildResourceIndex([fakeConfig]);
|
state.resources = buildResourceIndex([fakeConfig]);
|
||||||
|
|
||||||
const fakeProps = (): PowerAndResetProps => {
|
const fakeProps = (): PowerAndResetProps => ({
|
||||||
return {
|
controlPanelState: panelState(),
|
||||||
controlPanelState: panelState(),
|
dispatch: jest.fn(x => x(jest.fn(), () => state)),
|
||||||
dispatch: jest.fn(x => x(jest.fn(), () => state)),
|
sourceFbosConfig: () => ({ value: true, consistent: true }),
|
||||||
sourceFbosConfig: () => ({ value: true, consistent: true }),
|
botOnline: true,
|
||||||
shouldDisplay: jest.fn(),
|
});
|
||||||
botOnline: true,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
it("open", () => {
|
it("renders in open state", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.controlPanelState.power_and_reset = true;
|
p.controlPanelState.power_and_reset = true;
|
||||||
const wrapper = mount(<PowerAndReset {...p} />);
|
const wrapper = mount(<PowerAndReset {...p} />);
|
||||||
["Power and Reset", "Restart", "Shutdown", "Factory Reset",
|
["Power and Reset", "Restart", "Shutdown", "Restart Firmware",
|
||||||
"Automatic Factory Reset", "Connection Attempt Period", "Change Ownership"]
|
"Factory Reset", "Automatic Factory Reset",
|
||||||
|
"Connection Attempt Period", "Change Ownership"]
|
||||||
.map(string => expect(wrapper.text().toLowerCase())
|
.map(string => expect(wrapper.text().toLowerCase())
|
||||||
.toContain(string.toLowerCase()));
|
.toContain(string.toLowerCase()));
|
||||||
["Restart Firmware"]
|
|
||||||
.map(string => expect(wrapper.text().toLowerCase())
|
|
||||||
.not.toContain(string.toLowerCase()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closed", () => {
|
it("renders as closed", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.controlPanelState.power_and_reset = false;
|
p.controlPanelState.power_and_reset = false;
|
||||||
const wrapper = mount(<PowerAndReset {...p} />);
|
const wrapper = mount(<PowerAndReset {...p} />);
|
||||||
|
@ -73,7 +68,7 @@ describe("<PowerAndReset/>", () => {
|
||||||
p.sourceFbosConfig = () => ({ value: false, consistent: true });
|
p.sourceFbosConfig = () => ({ value: false, consistent: true });
|
||||||
p.controlPanelState.power_and_reset = true;
|
p.controlPanelState.power_and_reset = true;
|
||||||
const wrapper = mount(<PowerAndReset {...p} />);
|
const wrapper = mount(<PowerAndReset {...p} />);
|
||||||
clickButton(wrapper, 3, "yes");
|
clickButton(wrapper, 4, "yes");
|
||||||
expect(edit).toHaveBeenCalledWith(fakeConfig, { disable_factory_reset: true });
|
expect(edit).toHaveBeenCalledWith(fakeConfig, { disable_factory_reset: true });
|
||||||
expect(save).toHaveBeenCalledWith(fakeConfig.uuid);
|
expect(save).toHaveBeenCalledWith(fakeConfig.uuid);
|
||||||
});
|
});
|
||||||
|
@ -81,7 +76,6 @@ describe("<PowerAndReset/>", () => {
|
||||||
it("restarts firmware", () => {
|
it("restarts firmware", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.controlPanelState.power_and_reset = true;
|
p.controlPanelState.power_and_reset = true;
|
||||||
p.shouldDisplay = () => true;
|
|
||||||
const wrapper = mount(<PowerAndReset {...p} />);
|
const wrapper = mount(<PowerAndReset {...p} />);
|
||||||
expect(wrapper.text().toLowerCase())
|
expect(wrapper.text().toLowerCase())
|
||||||
.toContain("Restart Firmware".toLowerCase());
|
.toContain("Restart Firmware".toLowerCase());
|
||||||
|
|
|
@ -1,32 +1,35 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Row, Col } from "../../../ui/index";
|
import { Row, Col } from "../../../ui/index";
|
||||||
import { ToggleButton } from "../../../controls/toggle_button";
|
import { ToggleButton } from "../../../controls/toggle_button";
|
||||||
import { Content } from "../../../constants";
|
import { Content, DeviceSetting } from "../../../constants";
|
||||||
import { updateConfig } from "../../actions";
|
import { updateConfig } from "../../actions";
|
||||||
import { ColWidth } from "../farmbot_os_settings";
|
import { ColWidth } from "../farmbot_os_settings";
|
||||||
import { AutoSyncRowProps } from "./interfaces";
|
import { AutoSyncRowProps } from "./interfaces";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
export function AutoSyncRow(props: AutoSyncRowProps) {
|
export function AutoSyncRow(props: AutoSyncRowProps) {
|
||||||
const autoSync = props.sourceFbosConfig("auto_sync");
|
const autoSync = props.sourceFbosConfig("auto_sync");
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.autoSync}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("AUTO SYNC")}
|
<label>
|
||||||
</label>
|
{t("AUTO SYNC")}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<p>
|
<Col xs={ColWidth.description}>
|
||||||
{t(Content.AUTO_SYNC)}
|
<p>
|
||||||
</p>
|
{t(Content.AUTO_SYNC)}
|
||||||
</Col>
|
</p>
|
||||||
<Col xs={ColWidth.button}>
|
</Col>
|
||||||
<ToggleButton
|
<Col xs={ColWidth.button}>
|
||||||
toggleValue={autoSync.value}
|
<ToggleButton
|
||||||
dim={!autoSync.consistent}
|
toggleValue={autoSync.value}
|
||||||
toggleAction={() => {
|
dim={!autoSync.consistent}
|
||||||
props.dispatch(updateConfig({ auto_sync: !autoSync.value }));
|
toggleAction={() => {
|
||||||
}} />
|
props.dispatch(updateConfig({ auto_sync: !autoSync.value }));
|
||||||
</Col>
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,39 +3,41 @@ import { Row, Col } from "../../../ui/index";
|
||||||
import { ColWidth } from "../farmbot_os_settings";
|
import { ColWidth } from "../farmbot_os_settings";
|
||||||
import { ToggleButton } from "../../../controls/toggle_button";
|
import { ToggleButton } from "../../../controls/toggle_button";
|
||||||
import { updateConfig } from "../../actions";
|
import { updateConfig } from "../../actions";
|
||||||
import { Content } from "../../../constants";
|
import { Content, DeviceSetting } from "../../../constants";
|
||||||
import { AutoUpdateRowProps } from "./interfaces";
|
import { AutoUpdateRowProps } from "./interfaces";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
import { OtaTimeSelector, changeOtaHour } from "./ota_time_selector";
|
import { OtaTimeSelector, changeOtaHour } from "./ota_time_selector";
|
||||||
import { Feature } from "../../interfaces";
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
export function AutoUpdateRow(props: AutoUpdateRowProps) {
|
export function AutoUpdateRow(props: AutoUpdateRowProps) {
|
||||||
const osAutoUpdate = props.sourceFbosConfig("os_auto_update");
|
const osAutoUpdate = props.sourceFbosConfig("os_auto_update");
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
{props.shouldDisplay(Feature.ota_update_hour) && <OtaTimeSelector
|
<OtaTimeSelector
|
||||||
timeFormat={props.timeFormat}
|
timeFormat={props.timeFormat}
|
||||||
disabled={!osAutoUpdate.value}
|
disabled={!osAutoUpdate.value}
|
||||||
value={props.device.body.ota_hour}
|
value={props.device.body.ota_hour}
|
||||||
onChange={changeOtaHour(props.dispatch, props.device)} />}
|
onChange={changeOtaHour(props.dispatch, props.device)} />
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.farmbotOSAutoUpdate}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("FARMBOT OS AUTO UPDATE")}
|
<label>
|
||||||
</label>
|
{t(DeviceSetting.farmbotOSAutoUpdate)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<p>
|
<Col xs={ColWidth.description}>
|
||||||
{t(Content.OS_AUTO_UPDATE)}
|
<p>
|
||||||
</p>
|
{t(Content.OS_AUTO_UPDATE)}
|
||||||
</Col>
|
</p>
|
||||||
<Col xs={ColWidth.button}>
|
</Col>
|
||||||
<ToggleButton toggleValue={osAutoUpdate.value}
|
<Col xs={ColWidth.button}>
|
||||||
dim={!osAutoUpdate.consistent}
|
<ToggleButton toggleValue={osAutoUpdate.value}
|
||||||
toggleAction={() => props.dispatch(updateConfig({
|
dim={!osAutoUpdate.consistent}
|
||||||
os_auto_update: !osAutoUpdate.value
|
toggleAction={() => props.dispatch(updateConfig({
|
||||||
}))} />
|
os_auto_update: !osAutoUpdate.value
|
||||||
</Col>
|
}))} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>
|
</Row>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { FirmwareHardwareStatus } from "./firmware_hardware_status";
|
||||||
import {
|
import {
|
||||||
isFwHardwareValue, getFirmwareChoices, FIRMWARE_CHOICES_DDI
|
isFwHardwareValue, getFirmwareChoices, FIRMWARE_CHOICES_DDI
|
||||||
} from "../firmware_hardware_support";
|
} from "../firmware_hardware_support";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
|
||||||
interface BoardTypeState { sending: boolean }
|
interface BoardTypeState { sending: boolean }
|
||||||
|
|
||||||
|
@ -47,31 +49,32 @@ export class BoardType extends React.Component<BoardTypeProps, BoardTypeState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.firmware}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("FIRMWARE")}
|
<label>
|
||||||
</label>
|
{t("FIRMWARE")}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<div>
|
<Col xs={ColWidth.description}>
|
||||||
<FBSelect
|
<div>
|
||||||
key={this.apiValue}
|
<FBSelect
|
||||||
extraClass={this.state.sending ? "dim" : ""}
|
key={this.apiValue}
|
||||||
list={getFirmwareChoices(this.props.shouldDisplay)}
|
extraClass={this.state.sending ? "dim" : ""}
|
||||||
selectedItem={this.selectedBoard}
|
list={getFirmwareChoices()}
|
||||||
onChange={this.sendOffConfig} />
|
selectedItem={this.selectedBoard}
|
||||||
</div>
|
onChange={this.sendOffConfig} />
|
||||||
</Col>
|
</div>
|
||||||
<Col xs={ColWidth.button}>
|
</Col>
|
||||||
<FirmwareHardwareStatus
|
<Col xs={ColWidth.button}>
|
||||||
botOnline={this.props.botOnline}
|
<FirmwareHardwareStatus
|
||||||
apiFirmwareValue={this.apiValue}
|
botOnline={this.props.botOnline}
|
||||||
alerts={this.props.alerts}
|
apiFirmwareValue={this.apiValue}
|
||||||
bot={this.props.bot}
|
alerts={this.props.alerts}
|
||||||
dispatch={this.props.dispatch}
|
bot={this.props.bot}
|
||||||
timeSettings={this.props.timeSettings}
|
dispatch={this.props.dispatch}
|
||||||
shouldDisplay={this.props.shouldDisplay} />
|
timeSettings={this.props.timeSettings} />
|
||||||
</Col>
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { selectAllSequences, findSequenceById } from "../../../resources/selecto
|
||||||
import { betterCompact } from "../../../util";
|
import { betterCompact } from "../../../util";
|
||||||
import { ColWidth } from "../farmbot_os_settings";
|
import { ColWidth } from "../farmbot_os_settings";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
list: DropDownItem[];
|
list: DropDownItem[];
|
||||||
|
@ -56,18 +58,20 @@ export class RawBootSequenceSelector extends React.Component<Props, {}> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.bootSequence}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("BOOT SEQUENCE")}
|
<label>
|
||||||
</label>
|
{t("BOOT SEQUENCE")}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={7}>
|
</Col>
|
||||||
<FBSelect
|
<Col xs={7}>
|
||||||
allowEmpty={true}
|
<FBSelect
|
||||||
list={this.props.list}
|
allowEmpty={true}
|
||||||
selectedItem={this.props.selectedItem}
|
list={this.props.list}
|
||||||
onChange={this.onChange} />
|
selectedItem={this.props.selectedItem}
|
||||||
</Col>
|
onChange={this.onChange} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { getDevice } from "../../../device";
|
||||||
import { ColWidth } from "../farmbot_os_settings";
|
import { ColWidth } from "../farmbot_os_settings";
|
||||||
import { Feature, UserEnv } from "../../interfaces";
|
import { Feature, UserEnv } from "../../interfaces";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
import { Content, ToolTips } from "../../../constants";
|
import { Content, ToolTips, DeviceSetting } from "../../../constants";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
/** Check if the camera has been disabled. */
|
/** Check if the camera has been disabled. */
|
||||||
export const cameraDisabled = (env: UserEnv): boolean =>
|
export const cameraDisabled = (env: UserEnv): boolean =>
|
||||||
|
@ -84,21 +85,23 @@ export class CameraSelection
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.camera}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("CAMERA")}
|
<label>
|
||||||
</label>
|
{t("CAMERA")}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<div>
|
<Col xs={ColWidth.description}>
|
||||||
<FBSelect
|
<div>
|
||||||
allowEmpty={false}
|
<FBSelect
|
||||||
list={CAMERA_CHOICES()}
|
allowEmpty={false}
|
||||||
selectedItem={this.selectedCamera()}
|
list={CAMERA_CHOICES()}
|
||||||
onChange={this.sendOffConfig}
|
selectedItem={this.selectedCamera()}
|
||||||
extraClass={this.props.botOnline ? "" : "disabled"} />
|
onChange={this.sendOffConfig}
|
||||||
</div>
|
extraClass={this.props.botOnline ? "" : "disabled"} />
|
||||||
</Col>
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Row, Col } from "../../../ui/index";
|
import { Row, Col } from "../../../ui/index";
|
||||||
import { Content } from "../../../constants";
|
import { Content, DeviceSetting } from "../../../constants";
|
||||||
import { factoryReset, updateConfig } from "../../actions";
|
import { factoryReset, updateConfig } from "../../actions";
|
||||||
import { ToggleButton } from "../../../controls/toggle_button";
|
import { ToggleButton } from "../../../controls/toggle_button";
|
||||||
import { BotConfigInputBox } from "../bot_config_input_box";
|
import { BotConfigInputBox } from "../bot_config_input_box";
|
||||||
import { FactoryResetRowProps } from "./interfaces";
|
import { FactoryResetRowProps } from "./interfaces";
|
||||||
import { ColWidth } from "../farmbot_os_settings";
|
import { ColWidth } from "../farmbot_os_settings";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
export function FactoryResetRow(props: FactoryResetRowProps) {
|
export function FactoryResetRow(props: FactoryResetRowProps) {
|
||||||
const { dispatch, sourceFbosConfig, botOnline } = props;
|
const { dispatch, sourceFbosConfig, botOnline } = props;
|
||||||
|
@ -14,66 +15,72 @@ export function FactoryResetRow(props: FactoryResetRowProps) {
|
||||||
const maybeDisableTimer = disableFactoryReset.value ? { color: "grey" } : {};
|
const maybeDisableTimer = disableFactoryReset.value ? { color: "grey" } : {};
|
||||||
return <div>
|
return <div>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.factoryReset}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("Factory Reset")}
|
<label>
|
||||||
</label>
|
{t(DeviceSetting.factoryReset)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<p>
|
<Col xs={ColWidth.description}>
|
||||||
{t(Content.FACTORY_RESET_WARNING)}
|
<p>
|
||||||
</p>
|
{t(Content.FACTORY_RESET_WARNING)}
|
||||||
</Col>
|
</p>
|
||||||
<Col xs={ColWidth.button}>
|
</Col>
|
||||||
<button
|
<Col xs={ColWidth.button}>
|
||||||
className="fb-button red"
|
<button
|
||||||
type="button"
|
className="fb-button red"
|
||||||
onClick={factoryReset}
|
type="button"
|
||||||
disabled={!botOnline}>
|
onClick={factoryReset}
|
||||||
{t("FACTORY RESET")}
|
disabled={!botOnline}>
|
||||||
</button>
|
{t("FACTORY RESET")}
|
||||||
</Col>
|
</button>
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.autoFactoryReset}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("Automatic Factory Reset")}
|
<label>
|
||||||
</label>
|
{t(DeviceSetting.autoFactoryReset)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<p>
|
<Col xs={ColWidth.description}>
|
||||||
{t(Content.AUTO_FACTORY_RESET)}
|
<p>
|
||||||
</p>
|
{t(Content.AUTO_FACTORY_RESET)}
|
||||||
</Col>
|
</p>
|
||||||
<Col xs={ColWidth.button}>
|
</Col>
|
||||||
<ToggleButton
|
<Col xs={ColWidth.button}>
|
||||||
toggleValue={!disableFactoryReset.value}
|
<ToggleButton
|
||||||
dim={!disableFactoryReset.consistent}
|
toggleValue={!disableFactoryReset.value}
|
||||||
toggleAction={() => {
|
dim={!disableFactoryReset.consistent}
|
||||||
dispatch(updateConfig({
|
toggleAction={() => {
|
||||||
disable_factory_reset: !disableFactoryReset.value
|
dispatch(updateConfig({
|
||||||
}));
|
disable_factory_reset: !disableFactoryReset.value
|
||||||
}} />
|
}));
|
||||||
</Col>
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.connectionAttemptPeriod}>
|
||||||
<label style={maybeDisableTimer}>
|
<Col xs={ColWidth.label}>
|
||||||
{t("Connection Attempt Period")}
|
<label style={maybeDisableTimer}>
|
||||||
</label>
|
{t(DeviceSetting.connectionAttemptPeriod)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<p style={maybeDisableTimer}>
|
<Col xs={ColWidth.description}>
|
||||||
{t(Content.AUTO_FACTORY_RESET_PERIOD)}
|
<p style={maybeDisableTimer}>
|
||||||
</p>
|
{t(Content.AUTO_FACTORY_RESET_PERIOD)}
|
||||||
</Col>
|
</p>
|
||||||
<Col xs={ColWidth.button}>
|
</Col>
|
||||||
<BotConfigInputBox
|
<Col xs={ColWidth.button}>
|
||||||
setting="network_not_found_timer"
|
<BotConfigInputBox
|
||||||
dispatch={dispatch}
|
setting="network_not_found_timer"
|
||||||
disabled={!!disableFactoryReset.value}
|
dispatch={dispatch}
|
||||||
sourceFbosConfig={sourceFbosConfig} />
|
disabled={!!disableFactoryReset.value}
|
||||||
</Col>
|
sourceFbosConfig={sourceFbosConfig} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>
|
</Row>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { FarmbotOsRowProps } from "./interfaces";
|
||||||
import { FbosDetails } from "./fbos_details";
|
import { FbosDetails } from "./fbos_details";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
import { ErrorBoundary } from "../../../error_boundary";
|
import { ErrorBoundary } from "../../../error_boundary";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
|
||||||
const getVersionString =
|
const getVersionString =
|
||||||
(fbosVersion: string | undefined, onBeta: boolean | undefined): string => {
|
(fbosVersion: string | undefined, onBeta: boolean | undefined): string => {
|
||||||
|
@ -21,48 +23,50 @@ export function FarmbotOsRow(props: FarmbotOsRowProps) {
|
||||||
} = bot.hardware.informational_settings;
|
} = bot.hardware.informational_settings;
|
||||||
const version = getVersionString(controller_version, currently_on_beta);
|
const version = getVersionString(controller_version, currently_on_beta);
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={DeviceSetting.farmbotOS}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t("FARMBOT OS")}
|
<label>
|
||||||
</label>
|
{t(DeviceSetting.farmbotOS)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={3}>
|
</Col>
|
||||||
<Popover position={Position.BOTTOM_LEFT}>
|
<Col xs={3}>
|
||||||
<p>
|
<Popover position={Position.BOTTOM_LEFT}>
|
||||||
{t("Version {{ version }}", { version })}
|
<p>
|
||||||
</p>
|
{t("Version {{ version }}", { version })}
|
||||||
<ErrorBoundary>
|
</p>
|
||||||
<FbosDetails
|
<ErrorBoundary>
|
||||||
botInfoSettings={bot.hardware.informational_settings}
|
<FbosDetails
|
||||||
dispatch={dispatch}
|
botInfoSettings={bot.hardware.informational_settings}
|
||||||
shouldDisplay={props.shouldDisplay}
|
dispatch={dispatch}
|
||||||
sourceFbosConfig={sourceFbosConfig}
|
shouldDisplay={props.shouldDisplay}
|
||||||
botToMqttLastSeen={props.botToMqttLastSeen}
|
sourceFbosConfig={sourceFbosConfig}
|
||||||
timeSettings={props.timeSettings}
|
botToMqttLastSeen={props.botToMqttLastSeen}
|
||||||
deviceAccount={props.deviceAccount} />
|
timeSettings={props.timeSettings}
|
||||||
</ErrorBoundary>
|
deviceAccount={props.deviceAccount} />
|
||||||
</Popover>
|
</ErrorBoundary>
|
||||||
</Col>
|
</Popover>
|
||||||
<Col xs={3}>
|
</Col>
|
||||||
<Popover position={Position.BOTTOM}>
|
<Col xs={3}>
|
||||||
<p className="release-notes-button">
|
<Popover position={Position.BOTTOM}>
|
||||||
{t("Release Notes")}
|
<p className="release-notes-button">
|
||||||
|
{t("Release Notes")}
|
||||||
<i className="fa fa-caret-down" />
|
<i className="fa fa-caret-down" />
|
||||||
</p>
|
</p>
|
||||||
<div className="release-notes">
|
<div className="release-notes">
|
||||||
<h1>{props.osReleaseNotesHeading}</h1>
|
<h1>{props.osReleaseNotesHeading}</h1>
|
||||||
<Markdown>
|
<Markdown>
|
||||||
{osReleaseNotes}
|
{osReleaseNotes}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={3}>
|
<Col xs={3}>
|
||||||
<OsUpdateButton
|
<OsUpdateButton
|
||||||
bot={bot}
|
bot={bot}
|
||||||
sourceFbosConfig={sourceFbosConfig}
|
sourceFbosConfig={sourceFbosConfig}
|
||||||
shouldDisplay={props.shouldDisplay}
|
shouldDisplay={props.shouldDisplay}
|
||||||
botOnline={botOnline} />
|
botOnline={botOnline} />
|
||||||
</Col>
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@ import * as React from "react";
|
||||||
import { Row, Col } from "../../../ui";
|
import { Row, Col } from "../../../ui";
|
||||||
import { ColWidth } from "../farmbot_os_settings";
|
import { ColWidth } from "../farmbot_os_settings";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
|
||||||
export interface FbosButtonRowProps {
|
export interface FbosButtonRowProps {
|
||||||
botOnline: boolean;
|
botOnline: boolean;
|
||||||
label: string;
|
label: DeviceSetting;
|
||||||
description: string;
|
description: string;
|
||||||
buttonText: string;
|
buttonText: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
@ -14,24 +16,26 @@ export interface FbosButtonRowProps {
|
||||||
|
|
||||||
export const FbosButtonRow = (props: FbosButtonRowProps) => {
|
export const FbosButtonRow = (props: FbosButtonRowProps) => {
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={ColWidth.label}>
|
<Highlight settingName={props.label}>
|
||||||
<label>
|
<Col xs={ColWidth.label}>
|
||||||
{t(props.label)}
|
<label>
|
||||||
</label>
|
{t(props.label)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={ColWidth.description}>
|
</Col>
|
||||||
<p>
|
<Col xs={ColWidth.description}>
|
||||||
{t(props.description)}
|
<p>
|
||||||
</p>
|
{t(props.description)}
|
||||||
</Col>
|
</p>
|
||||||
<Col xs={ColWidth.button}>
|
</Col>
|
||||||
<button
|
<Col xs={ColWidth.button}>
|
||||||
className={`fb-button ${props.color}`}
|
<button
|
||||||
type="button"
|
className={`fb-button ${props.color}`}
|
||||||
onClick={props.action}
|
type="button"
|
||||||
disabled={!props.botOnline}>
|
onClick={props.action}
|
||||||
{t(props.buttonText)}
|
disabled={!props.botOnline}>
|
||||||
</button>
|
{t(props.buttonText)}
|
||||||
</Col>
|
</button>
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { timeFormatString } from "../../../util";
|
||||||
import { TimeSettings } from "../../../interfaces";
|
import { TimeSettings } from "../../../interfaces";
|
||||||
import { StringConfigKey } from "farmbot/dist/resources/configs/fbos";
|
import { StringConfigKey } from "farmbot/dist/resources/configs/fbos";
|
||||||
import { boardType, FIRMWARE_CHOICES_DDI } from "../firmware_hardware_support";
|
import { boardType, FIRMWARE_CHOICES_DDI } from "../firmware_hardware_support";
|
||||||
|
import { ExternalUrl, FarmBotRepo } from "../../../external_urls";
|
||||||
|
|
||||||
/** Return an indicator color for the given temperature (C). */
|
/** Return an indicator color for the given temperature (C). */
|
||||||
export const colorFromTemp = (temp: number | undefined): string => {
|
export const colorFromTemp = (temp: number | undefined): string => {
|
||||||
|
@ -170,7 +171,7 @@ const shortenCommit = (longCommit: string) => (longCommit || "").slice(0, 8);
|
||||||
|
|
||||||
interface CommitDisplayProps {
|
interface CommitDisplayProps {
|
||||||
title: string;
|
title: string;
|
||||||
repo: string;
|
repo: FarmBotRepo;
|
||||||
commit: string;
|
commit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +185,7 @@ const CommitDisplay = (
|
||||||
{shortCommit === "---"
|
{shortCommit === "---"
|
||||||
? shortCommit
|
? shortCommit
|
||||||
: <a
|
: <a
|
||||||
href={`https://github.com/FarmBot/${repo}/tree/${shortCommit}`}
|
href={`${ExternalUrl.gitHubFarmBot}/${repo}/tree/${shortCommit}`}
|
||||||
target="_blank">
|
target="_blank">
|
||||||
{shortCommit}
|
{shortCommit}
|
||||||
</a>}
|
</a>}
|
||||||
|
@ -270,14 +271,15 @@ export function FbosDetails(props: FbosDetailsProps) {
|
||||||
timeSettings={props.timeSettings}
|
timeSettings={props.timeSettings}
|
||||||
device={props.deviceAccount} />
|
device={props.deviceAccount} />
|
||||||
<p><b>{t("Environment")}: </b>{env}</p>
|
<p><b>{t("Environment")}: </b>{env}</p>
|
||||||
<CommitDisplay title={t("Commit")} repo={"farmbot_os"} commit={commit} />
|
<CommitDisplay title={t("Commit")}
|
||||||
|
repo={FarmBotRepo.FarmBotOS} commit={commit} />
|
||||||
<p><b>{t("Target")}: </b>{target}</p>
|
<p><b>{t("Target")}: </b>{target}</p>
|
||||||
<p><b>{t("Node name")}: </b>{last((node_name || "").split("@"))}</p>
|
<p><b>{t("Node name")}: </b>{last((node_name || "").split("@"))}</p>
|
||||||
<p><b>{t("Device ID")}: </b>{props.deviceAccount.body.id}</p>
|
<p><b>{t("Device ID")}: </b>{props.deviceAccount.body.id}</p>
|
||||||
{isString(private_ip) && <p><b>{t("Local IP address")}: </b>{private_ip}</p>}
|
{isString(private_ip) && <p><b>{t("Local IP address")}: </b>{private_ip}</p>}
|
||||||
<p><b>{t("Firmware")}: </b>{reformatFwVersion(firmware_version)}</p>
|
<p><b>{t("Firmware")}: </b>{reformatFwVersion(firmware_version)}</p>
|
||||||
<CommitDisplay title={t("Firmware commit")}
|
<CommitDisplay title={t("Firmware commit")}
|
||||||
repo={"farmbot-arduino-firmware"} commit={firmwareCommit} />
|
repo={FarmBotRepo.FarmBotArduinoFirmware} commit={firmwareCommit} />
|
||||||
<p><b>{t("Firmware code")}: </b>{firmware_version}</p>
|
<p><b>{t("Firmware code")}: </b>{firmware_version}</p>
|
||||||
{isNumber(uptime) && <UptimeDisplay uptime_sec={uptime} />}
|
{isNumber(uptime) && <UptimeDisplay uptime_sec={uptime} />}
|
||||||
{isNumber(memory_usage) &&
|
{isNumber(memory_usage) &&
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Popover, Position } from "@blueprintjs/core";
|
||||||
import { FIRMWARE_CHOICES_DDI } from "../firmware_hardware_support";
|
import { FIRMWARE_CHOICES_DDI } from "../firmware_hardware_support";
|
||||||
import { flashFirmware } from "../../actions";
|
import { flashFirmware } from "../../actions";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
import { BotState, Feature, ShouldDisplay } from "../../interfaces";
|
import { BotState } from "../../interfaces";
|
||||||
import { FirmwareAlerts } from "../../../messages/alerts";
|
import { FirmwareAlerts } from "../../../messages/alerts";
|
||||||
import { TimeSettings } from "../../../interfaces";
|
import { TimeSettings } from "../../../interfaces";
|
||||||
import { trim } from "../../../util";
|
import { trim } from "../../../util";
|
||||||
|
@ -36,7 +36,6 @@ export interface FirmwareHardwareStatusDetailsProps {
|
||||||
apiFirmwareValue: string | undefined;
|
apiFirmwareValue: string | undefined;
|
||||||
botFirmwareValue: string | undefined;
|
botFirmwareValue: string | undefined;
|
||||||
mcuFirmwareValue: string | undefined;
|
mcuFirmwareValue: string | undefined;
|
||||||
shouldDisplay: ShouldDisplay;
|
|
||||||
timeSettings: TimeSettings;
|
timeSettings: TimeSettings;
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
}
|
}
|
||||||
|
@ -81,13 +80,10 @@ export const FirmwareHardwareStatusDetails =
|
||||||
<p>{lookup(props.botFirmwareValue) || t("unknown")}</p>
|
<p>{lookup(props.botFirmwareValue) || t("unknown")}</p>
|
||||||
<label>{t("Arduino/Farmduino")}</label>
|
<label>{t("Arduino/Farmduino")}</label>
|
||||||
<p>{lookup(props.mcuFirmwareValue) || t("unknown")}</p>
|
<p>{lookup(props.mcuFirmwareValue) || t("unknown")}</p>
|
||||||
{props.shouldDisplay(Feature.flash_firmware) &&
|
<label>{t("Actions")}</label>
|
||||||
<div>
|
<FirmwareActions
|
||||||
<label>{t("Actions")}</label>
|
apiFirmwareValue={props.apiFirmwareValue}
|
||||||
<FirmwareActions
|
botOnline={props.botOnline} />
|
||||||
apiFirmwareValue={props.apiFirmwareValue}
|
|
||||||
botOnline={props.botOnline} />
|
|
||||||
</div>}
|
|
||||||
<FirmwareAlerts
|
<FirmwareAlerts
|
||||||
alerts={props.alerts}
|
alerts={props.alerts}
|
||||||
dispatch={props.dispatch}
|
dispatch={props.dispatch}
|
||||||
|
@ -102,7 +98,6 @@ export interface FirmwareHardwareStatusProps {
|
||||||
bot: BotState;
|
bot: BotState;
|
||||||
botOnline: boolean;
|
botOnline: boolean;
|
||||||
timeSettings: TimeSettings;
|
timeSettings: TimeSettings;
|
||||||
shouldDisplay: ShouldDisplay;
|
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +117,6 @@ export const FirmwareHardwareStatus = (props: FirmwareHardwareStatusProps) => {
|
||||||
botFirmwareValue={firmware_hardware}
|
botFirmwareValue={firmware_hardware}
|
||||||
mcuFirmwareValue={boardType(firmware_version)}
|
mcuFirmwareValue={boardType(firmware_version)}
|
||||||
timeSettings={props.timeSettings}
|
timeSettings={props.timeSettings}
|
||||||
dispatch={props.dispatch}
|
dispatch={props.dispatch} />
|
||||||
shouldDisplay={props.shouldDisplay} />
|
|
||||||
</Popover>;
|
</Popover>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,6 @@ export interface AutoUpdateRowProps {
|
||||||
timeFormat: PreferredHourFormat;
|
timeFormat: PreferredHourFormat;
|
||||||
sourceFbosConfig: SourceFbosConfig;
|
sourceFbosConfig: SourceFbosConfig;
|
||||||
device: TaggedDevice;
|
device: TaggedDevice;
|
||||||
shouldDisplay: ShouldDisplay;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CameraSelectionProps {
|
export interface CameraSelectionProps {
|
||||||
|
@ -53,7 +52,6 @@ export interface PowerAndResetProps {
|
||||||
controlPanelState: ControlPanelState;
|
controlPanelState: ControlPanelState;
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
sourceFbosConfig: SourceFbosConfig;
|
sourceFbosConfig: SourceFbosConfig;
|
||||||
shouldDisplay: ShouldDisplay;
|
|
||||||
botOnline: boolean;
|
botOnline: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,59 +4,58 @@ import { Collapse, Popover, Position } from "@blueprintjs/core";
|
||||||
import { FactoryResetRow } from "./factory_reset_row";
|
import { FactoryResetRow } from "./factory_reset_row";
|
||||||
import { PowerAndResetProps } from "./interfaces";
|
import { PowerAndResetProps } from "./interfaces";
|
||||||
import { ChangeOwnershipForm } from "./change_ownership_form";
|
import { ChangeOwnershipForm } from "./change_ownership_form";
|
||||||
import { Feature } from "../../interfaces";
|
|
||||||
import { FbosButtonRow } from "./fbos_button_row";
|
import { FbosButtonRow } from "./fbos_button_row";
|
||||||
import { Content } from "../../../constants";
|
import { Content, DeviceSetting } from "../../../constants";
|
||||||
import { reboot, powerOff, restartFirmware } from "../../actions";
|
import { reboot, powerOff, restartFirmware } from "../../actions";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
export function PowerAndReset(props: PowerAndResetProps) {
|
export function PowerAndReset(props: PowerAndResetProps) {
|
||||||
const { dispatch, sourceFbosConfig, shouldDisplay, botOnline } = props;
|
const { dispatch, sourceFbosConfig, botOnline } = props;
|
||||||
const { power_and_reset } = props.controlPanelState;
|
const { power_and_reset } = props.controlPanelState;
|
||||||
return <section>
|
return <Highlight className={"section"}
|
||||||
<div style={{ fontSize: "1px" }}>
|
settingName={DeviceSetting.powerAndReset}>
|
||||||
<Header
|
<Header
|
||||||
expanded={power_and_reset}
|
expanded={power_and_reset}
|
||||||
title={t("Power and Reset")}
|
title={DeviceSetting.powerAndReset}
|
||||||
name={"power_and_reset"}
|
panel={"power_and_reset"}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
</div>
|
|
||||||
<Collapse isOpen={!!power_and_reset}>
|
<Collapse isOpen={!!power_and_reset}>
|
||||||
<FbosButtonRow
|
<FbosButtonRow
|
||||||
botOnline={botOnline}
|
botOnline={botOnline}
|
||||||
label={t("RESTART FARMBOT")}
|
label={DeviceSetting.restartFarmbot}
|
||||||
description={Content.RESTART_FARMBOT}
|
description={Content.RESTART_FARMBOT}
|
||||||
buttonText={t("RESTART")}
|
buttonText={t("RESTART")}
|
||||||
color={"yellow"}
|
color={"yellow"}
|
||||||
action={reboot} />
|
action={reboot} />
|
||||||
<FbosButtonRow
|
<FbosButtonRow
|
||||||
botOnline={botOnline}
|
botOnline={botOnline}
|
||||||
label={t("SHUTDOWN FARMBOT")}
|
label={DeviceSetting.shutdownFarmbot}
|
||||||
description={Content.SHUTDOWN_FARMBOT}
|
description={Content.SHUTDOWN_FARMBOT}
|
||||||
buttonText={t("SHUTDOWN")}
|
buttonText={t("SHUTDOWN")}
|
||||||
color={"red"}
|
color={"red"}
|
||||||
action={powerOff} />
|
action={powerOff} />
|
||||||
{shouldDisplay(Feature.firmware_restart) &&
|
<FbosButtonRow
|
||||||
<FbosButtonRow
|
botOnline={botOnline}
|
||||||
botOnline={botOnline}
|
label={DeviceSetting.restartFirmware}
|
||||||
label={t("RESTART FIRMWARE")}
|
description={Content.RESTART_FIRMWARE}
|
||||||
description={Content.RESTART_FIRMWARE}
|
buttonText={t("RESTART")}
|
||||||
buttonText={t("RESTART")}
|
color={"yellow"}
|
||||||
color={"yellow"}
|
action={restartFirmware} />
|
||||||
action={restartFirmware} />}
|
|
||||||
<FactoryResetRow
|
<FactoryResetRow
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
sourceFbosConfig={sourceFbosConfig}
|
sourceFbosConfig={sourceFbosConfig}
|
||||||
botOnline={botOnline} />
|
botOnline={botOnline} />
|
||||||
{botOnline &&
|
{botOnline &&
|
||||||
<Popover position={Position.BOTTOM_LEFT}>
|
<Highlight settingName={DeviceSetting.changeOwnership}>
|
||||||
<p className={"release-notes-button"}>
|
<Popover position={Position.BOTTOM_LEFT}>
|
||||||
{t("Change Ownership")}
|
<p className={"release-notes-button"}>
|
||||||
<i className="fa fa-caret-down" />
|
{t(DeviceSetting.changeOwnership)}
|
||||||
</p>
|
<i className="fa fa-caret-down" />
|
||||||
<ChangeOwnershipForm />
|
</p>
|
||||||
</Popover>
|
<ChangeOwnershipForm />
|
||||||
}
|
</Popover>
|
||||||
|
</Highlight>}
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</section>;
|
</Highlight>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { FirmwareHardware } from "farmbot";
|
import { FirmwareHardware, TaggedFbosConfig } from "farmbot";
|
||||||
import { ShouldDisplay, Feature } from "../interfaces";
|
|
||||||
|
|
||||||
export const isFwHardwareValue = (x?: unknown): x is FirmwareHardware => {
|
export const isFwHardwareValue = (x?: unknown): x is FirmwareHardware => {
|
||||||
const values: FirmwareHardware[] = [
|
const values: FirmwareHardware[] = [
|
||||||
|
@ -11,6 +10,12 @@ export const isFwHardwareValue = (x?: unknown): x is FirmwareHardware => {
|
||||||
return !!values.includes(x as FirmwareHardware);
|
return !!values.includes(x as FirmwareHardware);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getFwHardwareValue =
|
||||||
|
(fbosConfig: TaggedFbosConfig | undefined) => {
|
||||||
|
const value = fbosConfig?.body.firmware_hardware;
|
||||||
|
return isFwHardwareValue(value) ? value : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const TMC_BOARDS = ["express_k10", "farmduino_k15"];
|
const TMC_BOARDS = ["express_k10", "farmduino_k15"];
|
||||||
const EXPRESS_BOARDS = ["express_k10"];
|
const EXPRESS_BOARDS = ["express_k10"];
|
||||||
|
|
||||||
|
@ -77,12 +82,11 @@ export const FIRMWARE_CHOICES_DDI = {
|
||||||
[NONE.value]: NONE
|
[NONE.value]: NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFirmwareChoices =
|
export const getFirmwareChoices = () => ([
|
||||||
(shouldDisplay: ShouldDisplay = () => true) => ([
|
ARDUINO,
|
||||||
ARDUINO,
|
FARMDUINO,
|
||||||
FARMDUINO,
|
FARMDUINO_K14,
|
||||||
FARMDUINO_K14,
|
FARMDUINO_K15,
|
||||||
...(shouldDisplay(Feature.farmduino_k15) ? [FARMDUINO_K15] : []),
|
EXPRESS_K10,
|
||||||
...(shouldDisplay(Feature.express_k10) ? [EXPRESS_K10] : []),
|
NONE,
|
||||||
...(shouldDisplay(Feature.none_firmware) ? [NONE] : []),
|
]);
|
||||||
]);
|
|
||||||
|
|
|
@ -2,11 +2,12 @@ import * as React from "react";
|
||||||
import { MCUFactoryReset, bulkToggleControlPanel } from "../actions";
|
import { MCUFactoryReset, bulkToggleControlPanel } from "../actions";
|
||||||
import { Widget, WidgetHeader, WidgetBody } from "../../ui/index";
|
import { Widget, WidgetHeader, WidgetBody } from "../../ui/index";
|
||||||
import { HardwareSettingsProps } from "../interfaces";
|
import { HardwareSettingsProps } from "../interfaces";
|
||||||
import { MustBeOnline, isBotOnline } from "../must_be_online";
|
import { isBotOnline } from "../must_be_online";
|
||||||
import { ToolTips } from "../../constants";
|
import { ToolTips } from "../../constants";
|
||||||
import { DangerZone } from "./hardware_settings/danger_zone";
|
import { DangerZone } from "./hardware_settings/danger_zone";
|
||||||
import { PinGuard } from "./hardware_settings/pin_guard";
|
import { PinGuard } from "./hardware_settings/pin_guard";
|
||||||
import { EncodersAndEndStops } from "./hardware_settings/encoders_and_endstops";
|
import { Encoders } from "./hardware_settings/encoders";
|
||||||
|
import { EndStops } from "./hardware_settings/endstops";
|
||||||
import { Motors } from "./hardware_settings/motors";
|
import { Motors } from "./hardware_settings/motors";
|
||||||
import { SpacePanelHeader } from "./hardware_settings/space_panel_header";
|
import { SpacePanelHeader } from "./hardware_settings/space_panel_header";
|
||||||
import {
|
import {
|
||||||
|
@ -15,10 +16,16 @@ import {
|
||||||
import { Popover, Position } from "@blueprintjs/core";
|
import { Popover, Position } from "@blueprintjs/core";
|
||||||
import { FwParamExportMenu } from "./hardware_settings/export_menu";
|
import { FwParamExportMenu } from "./hardware_settings/export_menu";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
|
import { PinBindings } from "./hardware_settings/pin_bindings";
|
||||||
|
import { ErrorHandling } from "./hardware_settings/error_handling";
|
||||||
|
import { maybeOpenPanel } from "./maybe_highlight";
|
||||||
|
|
||||||
export class HardwareSettings extends
|
export class HardwareSettings extends
|
||||||
React.Component<HardwareSettingsProps, {}> {
|
React.Component<HardwareSettingsProps, {}> {
|
||||||
|
|
||||||
|
componentDidMount = () =>
|
||||||
|
this.props.dispatch(maybeOpenPanel(this.props.controlPanelState));
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
bot, dispatch, sourceFwConfig, controlPanelState, firmwareConfig,
|
bot, dispatch, sourceFwConfig, controlPanelState, firmwareConfig,
|
||||||
|
@ -27,15 +34,9 @@ export class HardwareSettings extends
|
||||||
const { informational_settings } = this.props.bot.hardware;
|
const { informational_settings } = this.props.bot.hardware;
|
||||||
const { sync_status } = informational_settings;
|
const { sync_status } = informational_settings;
|
||||||
const botDisconnected = !isBotOnline(sync_status, botToMqttStatus);
|
const botDisconnected = !isBotOnline(sync_status, botToMqttStatus);
|
||||||
|
const commonProps = { dispatch, controlPanelState };
|
||||||
return <Widget className="hardware-widget">
|
return <Widget className="hardware-widget">
|
||||||
<WidgetHeader title={t("Hardware")} helpText={ToolTips.HW_SETTINGS}>
|
<WidgetHeader title={t("Hardware")} helpText={ToolTips.HW_SETTINGS} />
|
||||||
<MustBeOnline
|
|
||||||
hideBanner={true}
|
|
||||||
syncStatus={sync_status}
|
|
||||||
networkState={this.props.botToMqttStatus}
|
|
||||||
lockOpen={process.env.NODE_ENV !== "production"}>
|
|
||||||
</MustBeOnline>
|
|
||||||
</WidgetHeader>
|
|
||||||
<WidgetBody>
|
<WidgetBody>
|
||||||
<button
|
<button
|
||||||
className={"fb-button gray no-float"}
|
className={"fb-button gray no-float"}
|
||||||
|
@ -47,46 +48,37 @@ export class HardwareSettings extends
|
||||||
onClick={() => dispatch(bulkToggleControlPanel(false))}>
|
onClick={() => dispatch(bulkToggleControlPanel(false))}>
|
||||||
{t("Collapse All")}
|
{t("Collapse All")}
|
||||||
</button>
|
</button>
|
||||||
{firmwareConfig &&
|
<Popover position={Position.BOTTOM_RIGHT}>
|
||||||
<Popover position={Position.BOTTOM_RIGHT}>
|
<i className="fa fa-download" />
|
||||||
<i className="fa fa-download" />
|
<FwParamExportMenu firmwareConfig={firmwareConfig} />
|
||||||
<FwParamExportMenu firmwareConfig={firmwareConfig} />
|
</Popover>
|
||||||
</Popover>}
|
<div className="label-headings">
|
||||||
<MustBeOnline
|
<SpacePanelHeader />
|
||||||
networkState={this.props.botToMqttStatus}
|
</div>
|
||||||
syncStatus={sync_status}
|
<HomingAndCalibration {...commonProps}
|
||||||
lockOpen={process.env.NODE_ENV !== "production" || !!firmwareConfig}>
|
bot={bot}
|
||||||
<div className="label-headings">
|
sourceFwConfig={sourceFwConfig}
|
||||||
<SpacePanelHeader />
|
firmwareConfig={firmwareConfig}
|
||||||
</div>
|
firmwareHardware={firmwareHardware}
|
||||||
<HomingAndCalibration
|
botDisconnected={botDisconnected} />
|
||||||
dispatch={dispatch}
|
<Motors {...commonProps}
|
||||||
bot={bot}
|
sourceFwConfig={sourceFwConfig}
|
||||||
sourceFwConfig={sourceFwConfig}
|
firmwareHardware={firmwareHardware} />
|
||||||
firmwareConfig={firmwareConfig}
|
<Encoders {...commonProps}
|
||||||
botDisconnected={botDisconnected} />
|
sourceFwConfig={sourceFwConfig}
|
||||||
<Motors
|
firmwareHardware={firmwareHardware} />
|
||||||
dispatch={dispatch}
|
<EndStops {...commonProps}
|
||||||
controlPanelState={controlPanelState}
|
sourceFwConfig={sourceFwConfig} />
|
||||||
sourceFwConfig={sourceFwConfig}
|
<ErrorHandling {...commonProps}
|
||||||
firmwareHardware={firmwareHardware} />
|
sourceFwConfig={sourceFwConfig} />
|
||||||
<EncodersAndEndStops
|
<PinGuard {...commonProps}
|
||||||
dispatch={dispatch}
|
resources={resources}
|
||||||
shouldDisplay={this.props.shouldDisplay}
|
sourceFwConfig={sourceFwConfig} />
|
||||||
controlPanelState={controlPanelState}
|
<DangerZone {...commonProps}
|
||||||
sourceFwConfig={sourceFwConfig}
|
onReset={MCUFactoryReset}
|
||||||
firmwareHardware={firmwareHardware} />
|
botDisconnected={botDisconnected} />
|
||||||
<PinGuard
|
<PinBindings {...commonProps}
|
||||||
dispatch={dispatch}
|
resources={resources} />
|
||||||
resources={resources}
|
|
||||||
controlPanelState={controlPanelState}
|
|
||||||
sourceFwConfig={sourceFwConfig} />
|
|
||||||
<DangerZone
|
|
||||||
dispatch={dispatch}
|
|
||||||
controlPanelState={controlPanelState}
|
|
||||||
onReset={MCUFactoryReset}
|
|
||||||
botDisconnected={botDisconnected} />
|
|
||||||
</MustBeOnline>
|
|
||||||
</WidgetBody>
|
</WidgetBody>
|
||||||
</Widget>;
|
</Widget>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,41 @@
|
||||||
const mockDevice = {
|
|
||||||
calibrate: jest.fn(() => Promise.resolve({}))
|
|
||||||
};
|
|
||||||
jest.mock("../../../../device", () => ({
|
|
||||||
getDevice: () => (mockDevice)
|
|
||||||
}));
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { mount } from "enzyme";
|
import { mount } from "enzyme";
|
||||||
import { CalibrationRow } from "../calibration_row";
|
import { CalibrationRow } from "../calibration_row";
|
||||||
import { bot } from "../../../../__test_support__/fake_state/bot";
|
import { bot } from "../../../../__test_support__/fake_state/bot";
|
||||||
|
import { CalibrationRowProps } from "../../interfaces";
|
||||||
|
import { DeviceSetting } from "../../../../constants";
|
||||||
|
|
||||||
|
describe("<CalibrationRow />", () => {
|
||||||
|
const fakeProps = (): CalibrationRowProps => ({
|
||||||
|
type: "calibrate",
|
||||||
|
hardware: bot.hardware.mcu_params,
|
||||||
|
botDisconnected: false,
|
||||||
|
action: jest.fn(),
|
||||||
|
toolTip: "calibrate",
|
||||||
|
title: DeviceSetting.calibration,
|
||||||
|
axisTitle: "calibrate",
|
||||||
|
});
|
||||||
|
|
||||||
describe("<HomingRow />", () => {
|
|
||||||
it("calls device", () => {
|
it("calls device", () => {
|
||||||
const result = mount(<CalibrationRow
|
const p = fakeProps();
|
||||||
hardware={bot.hardware.mcu_params}
|
const result = mount(<CalibrationRow {...p} />);
|
||||||
botDisconnected={false} />);
|
p.hardware.encoder_enabled_x = 1;
|
||||||
|
p.hardware.encoder_enabled_y = 1;
|
||||||
|
p.hardware.encoder_enabled_z = 0;
|
||||||
[0, 1, 2].map(i => result.find("LockableButton").at(i).simulate("click"));
|
[0, 1, 2].map(i => result.find("LockableButton").at(i).simulate("click"));
|
||||||
expect(mockDevice.calibrate).toHaveBeenCalledTimes(2);
|
expect(p.action).toHaveBeenCalledTimes(2);
|
||||||
[{ axis: "y" }, { axis: "x" }].map(x =>
|
["y", "x"].map(x => expect(p.action).toHaveBeenCalledWith(x));
|
||||||
expect(mockDevice.calibrate).toHaveBeenCalledWith(x));
|
});
|
||||||
|
|
||||||
|
it("is not disabled", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.type = "zero";
|
||||||
|
const result = mount(<CalibrationRow {...p} />);
|
||||||
|
p.hardware.encoder_enabled_x = 0;
|
||||||
|
p.hardware.encoder_enabled_y = 1;
|
||||||
|
p.hardware.encoder_enabled_z = 0;
|
||||||
|
[0, 1, 2].map(i => result.find("LockableButton").at(i).simulate("click"));
|
||||||
|
expect(p.action).toHaveBeenCalledTimes(3);
|
||||||
|
["x", "y", "z"].map(x => expect(p.action).toHaveBeenCalledWith(x));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { mount, shallow } from "enzyme";
|
|
||||||
import { EncodersAndEndStops } from "../encoders_and_endstops";
|
|
||||||
import { EncodersProps, NumericMCUInputGroupProps } from "../../interfaces";
|
|
||||||
import { panelState } from "../../../../__test_support__/control_panel_state";
|
|
||||||
import { bot } from "../../../../__test_support__/fake_state/bot";
|
|
||||||
import { Dictionary } from "farmbot";
|
|
||||||
|
|
||||||
describe("<EncodersAndEndStops />", () => {
|
|
||||||
const mockFeatures: Dictionary<boolean> = {};
|
|
||||||
const fakeProps = (): EncodersProps => ({
|
|
||||||
dispatch: jest.fn(),
|
|
||||||
controlPanelState: panelState(),
|
|
||||||
sourceFwConfig: x =>
|
|
||||||
({ value: bot.hardware.mcu_params[x], consistent: true }),
|
|
||||||
shouldDisplay: jest.fn(key => mockFeatures[key]),
|
|
||||||
firmwareHardware: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows encoder labels", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.firmwareHardware = undefined;
|
|
||||||
const wrapper = mount(<EncodersAndEndStops {...p} />);
|
|
||||||
expect(wrapper.text().toLowerCase()).toContain("encoder");
|
|
||||||
expect(wrapper.text().toLowerCase()).not.toContain("stall");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows stall labels", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.firmwareHardware = "express_k10";
|
|
||||||
const wrapper = mount(<EncodersAndEndStops {...p} />);
|
|
||||||
expect(wrapper.text().toLowerCase()).not.toContain("encoder");
|
|
||||||
expect(wrapper.text().toLowerCase()).toContain("stall");
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each<["short" | "long"]>([
|
|
||||||
["short"],
|
|
||||||
["long"],
|
|
||||||
])("uses %s int scaling factor", (size) => {
|
|
||||||
mockFeatures.long_scaling_factor = size === "short" ? false : true;
|
|
||||||
const wrapper = shallow(<EncodersAndEndStops {...fakeProps()} />);
|
|
||||||
const sfProps = wrapper.find("NumericMCUInputGroup").at(2)
|
|
||||||
.props() as NumericMCUInputGroupProps;
|
|
||||||
expect(sfProps.name).toEqual("Encoder Scaling");
|
|
||||||
expect(sfProps.intSize).toEqual(size);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import { Encoders } from "../encoders";
|
||||||
|
import { EncodersProps } from "../../interfaces";
|
||||||
|
import { panelState } from "../../../../__test_support__/control_panel_state";
|
||||||
|
import { bot } from "../../../../__test_support__/fake_state/bot";
|
||||||
|
|
||||||
|
describe("<Encoders />", () => {
|
||||||
|
const fakeProps = (): EncodersProps => ({
|
||||||
|
dispatch: jest.fn(),
|
||||||
|
controlPanelState: panelState(),
|
||||||
|
sourceFwConfig: x =>
|
||||||
|
({ value: bot.hardware.mcu_params[x], consistent: true }),
|
||||||
|
firmwareHardware: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows encoder labels", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.firmwareHardware = undefined;
|
||||||
|
const wrapper = mount(<Encoders {...p} />);
|
||||||
|
expect(wrapper.text().toLowerCase()).toContain("encoder");
|
||||||
|
expect(wrapper.text().toLowerCase()).not.toContain("stall");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows stall labels", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.firmwareHardware = "express_k10";
|
||||||
|
const wrapper = mount(<Encoders {...p} />);
|
||||||
|
expect(wrapper.text().toLowerCase()).not.toContain("encoder");
|
||||||
|
expect(wrapper.text().toLowerCase()).toContain("stall");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,21 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import { EndStops } from "../endstops";
|
||||||
|
import { EndStopsProps } from "../../interfaces";
|
||||||
|
import { panelState } from "../../../../__test_support__/control_panel_state";
|
||||||
|
import { bot } from "../../../../__test_support__/fake_state/bot";
|
||||||
|
|
||||||
|
describe("<EndStops />", () => {
|
||||||
|
const fakeProps = (): EndStopsProps => ({
|
||||||
|
dispatch: jest.fn(),
|
||||||
|
controlPanelState: panelState(),
|
||||||
|
sourceFwConfig: x =>
|
||||||
|
({ value: bot.hardware.mcu_params[x], consistent: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows endstop labels", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const wrapper = mount(<EndStops {...p} />);
|
||||||
|
expect(wrapper.text().toLowerCase()).toContain("endstop");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
jest.mock("../../../../api/crud", () => ({
|
||||||
|
edit: jest.fn(),
|
||||||
|
save: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import { ErrorHandling } from "../error_handling";
|
||||||
|
import { ErrorHandlingProps } from "../../interfaces";
|
||||||
|
import { panelState } from "../../../../__test_support__/control_panel_state";
|
||||||
|
import { bot } from "../../../../__test_support__/fake_state/bot";
|
||||||
|
import { edit, save } from "../../../../api/crud";
|
||||||
|
import { fakeState } from "../../../../__test_support__/fake_state";
|
||||||
|
import {
|
||||||
|
fakeFirmwareConfig
|
||||||
|
} from "../../../../__test_support__/fake_state/resources";
|
||||||
|
import {
|
||||||
|
buildResourceIndex
|
||||||
|
} from "../../../../__test_support__/resource_index_builder";
|
||||||
|
|
||||||
|
describe("<ErrorHandling />", () => {
|
||||||
|
const fakeConfig = fakeFirmwareConfig();
|
||||||
|
const state = fakeState();
|
||||||
|
state.resources = buildResourceIndex([fakeConfig]);
|
||||||
|
const fakeProps = (): ErrorHandlingProps => ({
|
||||||
|
dispatch: jest.fn(x => x(jest.fn(), () => state)),
|
||||||
|
controlPanelState: panelState(),
|
||||||
|
sourceFwConfig: x =>
|
||||||
|
({ value: bot.hardware.mcu_params[x], consistent: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error handling labels", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const wrapper = mount(<ErrorHandling {...p} />);
|
||||||
|
expect(wrapper.text().toLowerCase()).toContain("error handling");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toggles retries e-stop parameter", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.controlPanelState.error_handling = true;
|
||||||
|
p.sourceFwConfig = () => ({ value: 1, consistent: true });
|
||||||
|
const wrapper = mount(<ErrorHandling {...p} />);
|
||||||
|
wrapper.find("button").at(0).simulate("click");
|
||||||
|
expect(edit).toHaveBeenCalledWith(fakeConfig, { param_e_stop_on_mov_err: 0 });
|
||||||
|
expect(save).toHaveBeenCalledWith(fakeConfig.uuid);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,16 +1,17 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Header } from "../header";
|
import { Header } from "../header";
|
||||||
import { mount } from "enzyme";
|
import { mount } from "enzyme";
|
||||||
|
import { DeviceSetting } from "../../../../constants";
|
||||||
|
|
||||||
describe("<Header/>", () => {
|
describe("<Header/>", () => {
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
const fn = jest.fn();
|
const fn = jest.fn();
|
||||||
const el = mount(<Header
|
const el = mount(<Header
|
||||||
title="FOO"
|
title={DeviceSetting.motors}
|
||||||
expanded={true}
|
expanded={true}
|
||||||
name={"motors"}
|
panel={"motors"}
|
||||||
dispatch={fn} />);
|
dispatch={fn} />);
|
||||||
expect(el.text()).toContain("FOO");
|
expect(el.text().toLowerCase()).toContain("motors");
|
||||||
expect(el.find(".fa-minus").length).toBe(1);
|
expect(el.find(".fa-minus").length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
jest.mock("../../../actions", () => ({ updateMCU: jest.fn() }));
|
jest.mock("../../../actions", () => ({
|
||||||
|
updateMCU: jest.fn(),
|
||||||
|
commandErr: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockDevice = {
|
||||||
|
calibrate: jest.fn(() => Promise.resolve({})),
|
||||||
|
findHome: jest.fn(() => Promise.resolve({})),
|
||||||
|
setZero: jest.fn(() => Promise.resolve({})),
|
||||||
|
};
|
||||||
|
jest.mock("../../../../device", () => ({ getDevice: () => mockDevice }));
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { mount } from "enzyme";
|
import { mount, shallow } from "enzyme";
|
||||||
import { HomingAndCalibration } from "../homing_and_calibration";
|
import { HomingAndCalibration } from "../homing_and_calibration";
|
||||||
import { bot } from "../../../../__test_support__/fake_state/bot";
|
import { bot } from "../../../../__test_support__/fake_state/bot";
|
||||||
import { updateMCU } from "../../../actions";
|
import { updateMCU } from "../../../actions";
|
||||||
|
@ -10,20 +20,28 @@ import {
|
||||||
} from "../../../../__test_support__/fake_state/resources";
|
} from "../../../../__test_support__/fake_state/resources";
|
||||||
import { error, warning } from "../../../../toast/toast";
|
import { error, warning } from "../../../../toast/toast";
|
||||||
import { inputEvent } from "../../../../__test_support__/fake_html_events";
|
import { inputEvent } from "../../../../__test_support__/fake_html_events";
|
||||||
|
import { panelState } from "../../../../__test_support__/control_panel_state";
|
||||||
|
import { HomingAndCalibrationProps } from "../../interfaces";
|
||||||
|
import { CalibrationRow } from "../calibration_row";
|
||||||
|
|
||||||
describe("<HomingAndCalibration />", () => {
|
describe("<HomingAndCalibration />", () => {
|
||||||
|
const fakeProps = (): HomingAndCalibrationProps => ({
|
||||||
|
dispatch: jest.fn(),
|
||||||
|
bot,
|
||||||
|
controlPanelState: panelState(),
|
||||||
|
sourceFwConfig: x => ({
|
||||||
|
value: bot.hardware.mcu_params[x], consistent: true
|
||||||
|
}),
|
||||||
|
firmwareConfig: fakeFirmwareConfig().body,
|
||||||
|
botDisconnected: false,
|
||||||
|
firmwareHardware: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
function testAxisLengthInput(
|
function testAxisLengthInput(
|
||||||
provided: string, expected: string | undefined) {
|
provided: string, expected: string | undefined) {
|
||||||
const dispatch = jest.fn();
|
const p = fakeProps();
|
||||||
bot.controlPanelState.homing_and_calibration = true;
|
p.bot.controlPanelState.homing_and_calibration = true;
|
||||||
const result = mount(<HomingAndCalibration
|
const result = mount(<HomingAndCalibration {...p} />);
|
||||||
dispatch={dispatch}
|
|
||||||
bot={bot}
|
|
||||||
firmwareConfig={fakeFirmwareConfig().body}
|
|
||||||
sourceFwConfig={x => ({
|
|
||||||
value: bot.hardware.mcu_params[x], consistent: true
|
|
||||||
})}
|
|
||||||
botDisconnected={false} />);
|
|
||||||
const e = inputEvent(provided);
|
const e = inputEvent(provided);
|
||||||
const input = result.find("input").first().props();
|
const input = result.find("input").first().props();
|
||||||
input.onChange && input.onChange(e);
|
input.onChange && input.onChange(e);
|
||||||
|
@ -45,4 +63,33 @@ describe("<HomingAndCalibration />", () => {
|
||||||
expect(warning).not.toHaveBeenCalled();
|
expect(warning).not.toHaveBeenCalled();
|
||||||
expect(error).not.toHaveBeenCalled();
|
expect(error).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("finds home", () => {
|
||||||
|
const wrapper = shallow(<HomingAndCalibration {...fakeProps()} />);
|
||||||
|
wrapper.find(CalibrationRow).first().props().action("x");
|
||||||
|
expect(mockDevice.findHome).toHaveBeenCalledWith({
|
||||||
|
axis: "x", speed: 100
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calibrates", () => {
|
||||||
|
const wrapper = shallow(<HomingAndCalibration {...fakeProps()} />);
|
||||||
|
wrapper.find(CalibrationRow).at(1).props().action("all");
|
||||||
|
expect(mockDevice.calibrate).toHaveBeenCalledWith({ axis: "all" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets zero", () => {
|
||||||
|
const wrapper = shallow(<HomingAndCalibration {...fakeProps()} />);
|
||||||
|
wrapper.find(CalibrationRow).last().props().action("all");
|
||||||
|
expect(mockDevice.setZero).toHaveBeenCalledWith("all");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows express board related labels", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
p.firmwareHardware = "express_k10";
|
||||||
|
p.controlPanelState.homing_and_calibration = true;
|
||||||
|
const wrapper = shallow(<HomingAndCalibration {...p} />);
|
||||||
|
expect(wrapper.find(CalibrationRow).first().props().toolTip)
|
||||||
|
.toContain("stall detection");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
const mockDevice = {
|
|
||||||
findHome: jest.fn(() => Promise.resolve({}))
|
|
||||||
};
|
|
||||||
|
|
||||||
jest.mock("../../../../device", () => ({
|
|
||||||
getDevice: () => (mockDevice)
|
|
||||||
}));
|
|
||||||
import * as React from "react";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
import { HomingRow } from "../homing_row";
|
|
||||||
import { bot } from "../../../../__test_support__/fake_state/bot";
|
|
||||||
|
|
||||||
describe("<HomingRow />", () => {
|
|
||||||
it("renders three buttons", () => {
|
|
||||||
const wrapper = mount(<HomingRow
|
|
||||||
hardware={bot.hardware.mcu_params}
|
|
||||||
botDisconnected={false} />);
|
|
||||||
const txt = wrapper.text().toUpperCase();
|
|
||||||
["X", "Y", "Z"].map(function (axis) {
|
|
||||||
expect(txt).toContain(`HOME ${axis}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("calls device", () => {
|
|
||||||
const result = mount(<HomingRow
|
|
||||||
hardware={bot.hardware.mcu_params}
|
|
||||||
botDisconnected={false} />);
|
|
||||||
[0, 1, 2].map(i =>
|
|
||||||
result.find("LockableButton").at(i).simulate("click"));
|
|
||||||
[{ axis: "x", speed: 100 }, { axis: "y", speed: 100 }].map(x =>
|
|
||||||
expect(mockDevice.findHome).toHaveBeenCalledWith(x));
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -37,8 +37,6 @@ describe("<Motors/>", () => {
|
||||||
it("renders the base case", () => {
|
it("renders the base case", () => {
|
||||||
const wrapper = render(<Motors {...fakeProps()} />);
|
const wrapper = render(<Motors {...fakeProps()} />);
|
||||||
["Enable 2nd X Motor",
|
["Enable 2nd X Motor",
|
||||||
"Max Retries",
|
|
||||||
"E-Stop on Movement Error",
|
|
||||||
"Max Speed (mm/s)"
|
"Max Speed (mm/s)"
|
||||||
].map(string =>
|
].map(string =>
|
||||||
expect(wrapper.text().toLowerCase()).toContain(string.toLowerCase()));
|
expect(wrapper.text().toLowerCase()).toContain(string.toLowerCase()));
|
||||||
|
@ -48,16 +46,14 @@ describe("<Motors/>", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.firmwareHardware = "express_k10";
|
p.firmwareHardware = "express_k10";
|
||||||
const wrapper = render(<Motors {...p} />);
|
const wrapper = render(<Motors {...p} />);
|
||||||
expect(wrapper.text()).toContain("Stall");
|
expect(wrapper.text()).toContain("Motor Current");
|
||||||
expect(wrapper.text()).toContain("Current");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't show TMC parameters", () => {
|
it("doesn't show TMC parameters", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
p.firmwareHardware = "farmduino";
|
p.firmwareHardware = "farmduino";
|
||||||
const wrapper = render(<Motors {...p} />);
|
const wrapper = render(<Motors {...p} />);
|
||||||
expect(wrapper.text()).not.toContain("Stall");
|
expect(wrapper.text()).not.toContain("Motor Current");
|
||||||
expect(wrapper.text()).not.toContain("Current");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const testParamToggle = (
|
const testParamToggle = (
|
||||||
|
@ -72,15 +68,6 @@ describe("<Motors/>", () => {
|
||||||
expect(save).toHaveBeenCalledWith(fakeConfig.uuid);
|
expect(save).toHaveBeenCalledWith(fakeConfig.uuid);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
testParamToggle("toggles retries e-stop parameter", "param_e_stop_on_mov_err", 0);
|
testParamToggle("toggles enable X2", "movement_secondary_motor_x", 6);
|
||||||
testParamToggle("toggles enable X2", "movement_secondary_motor_x", 7);
|
testParamToggle("toggles invert X2", "movement_secondary_motor_invert_x", 7);
|
||||||
testParamToggle("toggles invert X2", "movement_secondary_motor_invert_x", 8);
|
|
||||||
|
|
||||||
it("renders TMC params", () => {
|
|
||||||
const p = fakeProps();
|
|
||||||
p.firmwareHardware = "express_k10";
|
|
||||||
const wrapper = render(<Motors {...p} />);
|
|
||||||
expect(wrapper.text()).toContain("Motor Current");
|
|
||||||
expect(wrapper.text()).toContain("Stall Sensitivity");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import { PinBindings } from "../pin_bindings";
|
||||||
|
import { PinBindingsProps } from "../../interfaces";
|
||||||
|
import { panelState } from "../../../../__test_support__/control_panel_state";
|
||||||
|
import {
|
||||||
|
buildResourceIndex
|
||||||
|
} from "../../../../__test_support__/resource_index_builder";
|
||||||
|
|
||||||
|
describe("<PinBindings />", () => {
|
||||||
|
const fakeProps = (): PinBindingsProps => ({
|
||||||
|
dispatch: jest.fn(),
|
||||||
|
controlPanelState: panelState(),
|
||||||
|
resources: buildResourceIndex([]).index,
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows pin binding labels", () => {
|
||||||
|
const p = fakeProps();
|
||||||
|
const wrapper = mount(<PinBindings {...p} />);
|
||||||
|
expect(wrapper.text().toLowerCase()).toContain("pin bindings");
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,19 +0,0 @@
|
||||||
const mockDevice = {
|
|
||||||
setZero: jest.fn(() => Promise.resolve())
|
|
||||||
};
|
|
||||||
jest.mock("../../../../device", () => ({
|
|
||||||
getDevice: () => (mockDevice)
|
|
||||||
}));
|
|
||||||
import * as React from "react";
|
|
||||||
import { mount } from "enzyme";
|
|
||||||
import { ZeroRow } from "../zero_row";
|
|
||||||
|
|
||||||
describe("<HomingRow />", () => {
|
|
||||||
it("calls device", () => {
|
|
||||||
const result = mount(<ZeroRow botDisconnected={false} />);
|
|
||||||
[0, 1, 2].map(i => result.find("ZeroButton").at(i).simulate("click"));
|
|
||||||
["x", "y", "z"].map(x =>
|
|
||||||
expect(mockDevice.setZero).toHaveBeenCalledWith(x));
|
|
||||||
expect(mockDevice.setZero).toHaveBeenCalledTimes(3);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,40 +1,37 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { getDevice } from "../../../device";
|
|
||||||
import { Axis } from "../../interfaces";
|
|
||||||
import { LockableButton } from "../lockable_button";
|
import { LockableButton } from "../lockable_button";
|
||||||
import { axisTrackingStatus } from "../axis_tracking_status";
|
import { axisTrackingStatus } from "../axis_tracking_status";
|
||||||
import { ToolTips } from "../../../constants";
|
|
||||||
import { Row, Col, Help } from "../../../ui/index";
|
import { Row, Col, Help } from "../../../ui/index";
|
||||||
import { CalibrationRowProps } from "../interfaces";
|
import { CalibrationRowProps } from "../interfaces";
|
||||||
import { commandErr } from "../../actions";
|
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
import { Position } from "@blueprintjs/core";
|
import { Position } from "@blueprintjs/core";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
const calibrate = (axis: Axis) => getDevice()
|
|
||||||
.calibrate({ axis })
|
|
||||||
.catch(commandErr("Calibration"));
|
|
||||||
|
|
||||||
export function CalibrationRow(props: CalibrationRowProps) {
|
export function CalibrationRow(props: CalibrationRowProps) {
|
||||||
|
|
||||||
const { hardware, botDisconnected } = props;
|
const { hardware, botDisconnected } = props;
|
||||||
|
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={6} className={"widget-body-tooltips"}>
|
<Highlight settingName={props.title}>
|
||||||
<label>
|
<Col xs={6} className={"widget-body-tooltips"}>
|
||||||
{t("CALIBRATION")}
|
<label>
|
||||||
</label>
|
{t(props.title)}
|
||||||
<Help text={ToolTips.CALIBRATION} requireClick={true} position={Position.RIGHT} />
|
</label>
|
||||||
</Col>
|
<Help text={t(props.toolTip)}
|
||||||
{axisTrackingStatus(hardware)
|
requireClick={true} position={Position.RIGHT} />
|
||||||
.map(row => {
|
</Col>
|
||||||
const { axis, disabled } = row;
|
{axisTrackingStatus(hardware)
|
||||||
return <Col xs={2} key={axis} className={"centered-button-div"}>
|
.map(row => {
|
||||||
<LockableButton
|
const { axis } = row;
|
||||||
disabled={disabled || botDisconnected}
|
const hardwareDisabled = props.type == "zero" ? false : row.disabled;
|
||||||
onClick={() => calibrate(axis)}>
|
return <Col xs={2} key={axis} className={"centered-button-div"}>
|
||||||
{t("CALIBRATE {{axis}}", { axis })}
|
<LockableButton
|
||||||
</LockableButton>
|
disabled={hardwareDisabled || botDisconnected}
|
||||||
</Col>;
|
onClick={() => props.action(axis)}>
|
||||||
})}
|
{`${t(props.axisTitle)} ${axis}`}
|
||||||
|
</LockableButton>
|
||||||
|
</Col>;
|
||||||
|
})}
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,41 +3,45 @@ import { DangerZoneProps } from "../interfaces";
|
||||||
import { Row, Col } from "../../../ui/index";
|
import { Row, Col } from "../../../ui/index";
|
||||||
import { Header } from "./header";
|
import { Header } from "./header";
|
||||||
import { Collapse } from "@blueprintjs/core";
|
import { Collapse } from "@blueprintjs/core";
|
||||||
import { Content } from "../../../constants";
|
import { Content, DeviceSetting } from "../../../constants";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
export function DangerZone(props: DangerZoneProps) {
|
export function DangerZone(props: DangerZoneProps) {
|
||||||
|
|
||||||
const { dispatch, onReset, botDisconnected } = props;
|
const { dispatch, onReset, botDisconnected } = props;
|
||||||
const { danger_zone } = props.controlPanelState;
|
const { danger_zone } = props.controlPanelState;
|
||||||
|
|
||||||
return <section>
|
return <Highlight className={"section"}
|
||||||
|
settingName={DeviceSetting.dangerZone}>
|
||||||
<Header
|
<Header
|
||||||
expanded={danger_zone}
|
expanded={danger_zone}
|
||||||
title={t("Danger Zone")}
|
title={DeviceSetting.dangerZone}
|
||||||
name={"danger_zone"}
|
panel={"danger_zone"}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
<Collapse isOpen={!!danger_zone}>
|
<Collapse isOpen={!!danger_zone}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={4}>
|
<Highlight settingName={DeviceSetting.resetHardwareParams}>
|
||||||
<label>
|
<Col xs={4}>
|
||||||
{t("Reset hardware parameter defaults")}
|
<label>
|
||||||
</label>
|
{t(DeviceSetting.resetHardwareParams)}
|
||||||
</Col>
|
</label>
|
||||||
<Col xs={6}>
|
</Col>
|
||||||
<p>
|
<Col xs={6}>
|
||||||
{t(Content.RESTORE_DEFAULT_HARDWARE_SETTINGS)}
|
<p>
|
||||||
</p>
|
{t(Content.RESTORE_DEFAULT_HARDWARE_SETTINGS)}
|
||||||
</Col>
|
</p>
|
||||||
<Col xs={2} className={"centered-button-div"}>
|
</Col>
|
||||||
<button
|
<Col xs={2} className={"centered-button-div"}>
|
||||||
className="fb-button red"
|
<button
|
||||||
disabled={botDisconnected}
|
className="fb-button red"
|
||||||
onClick={onReset}>
|
disabled={botDisconnected}
|
||||||
{t("RESET")}
|
onClick={onReset}>
|
||||||
</button>
|
{t("RESET")}
|
||||||
</Col>
|
</button>
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>
|
</Row>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</section>;
|
</Highlight>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { BooleanMCUInputGroup } from "../boolean_mcu_input_group";
|
||||||
|
import { ToolTips, DeviceSetting } from "../../../constants";
|
||||||
|
import { NumericMCUInputGroup } from "../numeric_mcu_input_group";
|
||||||
|
import { EncodersProps } from "../interfaces";
|
||||||
|
import { Header } from "./header";
|
||||||
|
import { Collapse } from "@blueprintjs/core";
|
||||||
|
import { isExpressBoard } from "../firmware_hardware_support";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
|
export function Encoders(props: EncodersProps) {
|
||||||
|
|
||||||
|
const { encoders } = props.controlPanelState;
|
||||||
|
const { dispatch, sourceFwConfig, firmwareHardware } = props;
|
||||||
|
|
||||||
|
const encodersDisabled = {
|
||||||
|
x: !sourceFwConfig("encoder_enabled_x").value,
|
||||||
|
y: !sourceFwConfig("encoder_enabled_y").value,
|
||||||
|
z: !sourceFwConfig("encoder_enabled_z").value
|
||||||
|
};
|
||||||
|
const isExpress = isExpressBoard(firmwareHardware);
|
||||||
|
|
||||||
|
return <Highlight className={"section"}
|
||||||
|
settingName={DeviceSetting.encoders}>
|
||||||
|
<Header
|
||||||
|
expanded={encoders}
|
||||||
|
title={isExpress
|
||||||
|
? DeviceSetting.stallDetection
|
||||||
|
: DeviceSetting.encoders}
|
||||||
|
panel={"encoders"}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
<Collapse isOpen={!!encoders}>
|
||||||
|
<BooleanMCUInputGroup
|
||||||
|
label={isExpress
|
||||||
|
? DeviceSetting.enableStallDetection
|
||||||
|
: DeviceSetting.enableEncoders}
|
||||||
|
tooltip={isExpress
|
||||||
|
? ToolTips.ENABLE_STALL_DETECTION
|
||||||
|
: ToolTips.ENABLE_ENCODERS}
|
||||||
|
x={"encoder_enabled_x"}
|
||||||
|
y={"encoder_enabled_y"}
|
||||||
|
z={"encoder_enabled_z"}
|
||||||
|
dispatch={dispatch}
|
||||||
|
sourceFwConfig={sourceFwConfig} />
|
||||||
|
{isExpress &&
|
||||||
|
<NumericMCUInputGroup
|
||||||
|
label={DeviceSetting.stallSensitivity}
|
||||||
|
tooltip={ToolTips.STALL_SENSITIVITY}
|
||||||
|
x={"movement_stall_sensitivity_x"}
|
||||||
|
y={"movement_stall_sensitivity_y"}
|
||||||
|
z={"movement_stall_sensitivity_z"}
|
||||||
|
gray={encodersDisabled}
|
||||||
|
dispatch={dispatch}
|
||||||
|
sourceFwConfig={sourceFwConfig} />}
|
||||||
|
{!isExpress &&
|
||||||
|
<BooleanMCUInputGroup
|
||||||
|
label={DeviceSetting.useEncodersForPositioning}
|
||||||
|
tooltip={ToolTips.ENCODER_POSITIONING}
|
||||||
|
x={"encoder_use_for_pos_x"}
|
||||||
|
y={"encoder_use_for_pos_y"}
|
||||||
|
z={"encoder_use_for_pos_z"}
|
||||||
|
grayscale={encodersDisabled}
|
||||||
|
dispatch={dispatch}
|
||||||
|
sourceFwConfig={sourceFwConfig} />}
|
||||||
|
{!isExpress &&
|
||||||
|
<BooleanMCUInputGroup
|
||||||
|
label={DeviceSetting.invertEncoders}
|
||||||
|
tooltip={ToolTips.INVERT_ENCODERS}
|
||||||
|
x={"encoder_invert_x"}
|
||||||
|
y={"encoder_invert_y"}
|
||||||
|
z={"encoder_invert_z"}
|
||||||
|
grayscale={encodersDisabled}
|
||||||
|
dispatch={dispatch}
|
||||||
|
sourceFwConfig={sourceFwConfig} />}
|
||||||
|
<NumericMCUInputGroup
|
||||||
|
label={DeviceSetting.maxMissedSteps}
|
||||||
|
tooltip={isExpress
|
||||||
|
? ToolTips.MAX_MISSED_STEPS_STALL_DETECTION
|
||||||
|
: ToolTips.MAX_MISSED_STEPS_ENCODERS}
|
||||||
|
x={"encoder_missed_steps_max_x"}
|
||||||
|
y={"encoder_missed_steps_max_y"}
|
||||||
|
z={"encoder_missed_steps_max_z"}
|
||||||
|
gray={encodersDisabled}
|
||||||
|
sourceFwConfig={sourceFwConfig}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
<NumericMCUInputGroup
|
||||||
|
label={DeviceSetting.missedStepDecay}
|
||||||
|
tooltip={ToolTips.MISSED_STEP_DECAY}
|
||||||
|
x={"encoder_missed_steps_decay_x"}
|
||||||
|
y={"encoder_missed_steps_decay_y"}
|
||||||
|
z={"encoder_missed_steps_decay_z"}
|
||||||
|
gray={encodersDisabled}
|
||||||
|
sourceFwConfig={sourceFwConfig}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
{!isExpress &&
|
||||||
|
<NumericMCUInputGroup
|
||||||
|
label={DeviceSetting.encoderScaling}
|
||||||
|
tooltip={ToolTips.ENCODER_SCALING}
|
||||||
|
x={"encoder_scaling_x"}
|
||||||
|
y={"encoder_scaling_y"}
|
||||||
|
z={"encoder_scaling_z"}
|
||||||
|
xScale={sourceFwConfig("movement_microsteps_x").value}
|
||||||
|
yScale={sourceFwConfig("movement_microsteps_y").value}
|
||||||
|
zScale={sourceFwConfig("movement_microsteps_z").value}
|
||||||
|
intSize={"long"}
|
||||||
|
gray={encodersDisabled}
|
||||||
|
sourceFwConfig={sourceFwConfig}
|
||||||
|
dispatch={dispatch} />}
|
||||||
|
</Collapse>
|
||||||
|
</Highlight>;
|
||||||
|
}
|
|
@ -1,130 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { BooleanMCUInputGroup } from "../boolean_mcu_input_group";
|
|
||||||
import { ToolTips } from "../../../constants";
|
|
||||||
import { NumericMCUInputGroup } from "../numeric_mcu_input_group";
|
|
||||||
import { EncodersProps } from "../interfaces";
|
|
||||||
import { Header } from "./header";
|
|
||||||
import { Collapse } from "@blueprintjs/core";
|
|
||||||
import { Feature } from "../../interfaces";
|
|
||||||
import { t } from "../../../i18next_wrapper";
|
|
||||||
import { isExpressBoard } from "../firmware_hardware_support";
|
|
||||||
|
|
||||||
export function EncodersAndEndStops(props: EncodersProps) {
|
|
||||||
|
|
||||||
const { encoders_and_endstops } = props.controlPanelState;
|
|
||||||
const { dispatch, sourceFwConfig, shouldDisplay, firmwareHardware } = props;
|
|
||||||
|
|
||||||
const encodersDisabled = {
|
|
||||||
x: !sourceFwConfig("encoder_enabled_x").value,
|
|
||||||
y: !sourceFwConfig("encoder_enabled_y").value,
|
|
||||||
z: !sourceFwConfig("encoder_enabled_z").value
|
|
||||||
};
|
|
||||||
|
|
||||||
return <section>
|
|
||||||
<Header
|
|
||||||
expanded={encoders_and_endstops}
|
|
||||||
title={isExpressBoard(firmwareHardware)
|
|
||||||
? t("Stall Detection and Endstops")
|
|
||||||
: t("Encoders and Endstops")}
|
|
||||||
name={"encoders_and_endstops"}
|
|
||||||
dispatch={dispatch} />
|
|
||||||
<Collapse isOpen={!!encoders_and_endstops}>
|
|
||||||
<BooleanMCUInputGroup
|
|
||||||
name={isExpressBoard(firmwareHardware)
|
|
||||||
? t("Enable Stall Detection")
|
|
||||||
: t("Enable Encoders")}
|
|
||||||
tooltip={ToolTips.ENABLE_ENCODERS}
|
|
||||||
x={"encoder_enabled_x"}
|
|
||||||
y={"encoder_enabled_y"}
|
|
||||||
z={"encoder_enabled_z"}
|
|
||||||
dispatch={dispatch}
|
|
||||||
sourceFwConfig={sourceFwConfig} />
|
|
||||||
{!isExpressBoard(firmwareHardware) &&
|
|
||||||
<BooleanMCUInputGroup
|
|
||||||
name={t("Use Encoders for Positioning")}
|
|
||||||
tooltip={ToolTips.ENCODER_POSITIONING}
|
|
||||||
x={"encoder_use_for_pos_x"}
|
|
||||||
y={"encoder_use_for_pos_y"}
|
|
||||||
z={"encoder_use_for_pos_z"}
|
|
||||||
grayscale={encodersDisabled}
|
|
||||||
dispatch={dispatch}
|
|
||||||
sourceFwConfig={sourceFwConfig} />}
|
|
||||||
{!isExpressBoard(firmwareHardware) &&
|
|
||||||
<BooleanMCUInputGroup
|
|
||||||
name={t("Invert Encoders")}
|
|
||||||
tooltip={ToolTips.INVERT_ENCODERS}
|
|
||||||
x={"encoder_invert_x"}
|
|
||||||
y={"encoder_invert_y"}
|
|
||||||
z={"encoder_invert_z"}
|
|
||||||
grayscale={encodersDisabled}
|
|
||||||
dispatch={dispatch}
|
|
||||||
sourceFwConfig={sourceFwConfig} />}
|
|
||||||
<NumericMCUInputGroup
|
|
||||||
name={t("Max Missed Steps")}
|
|
||||||
tooltip={ToolTips.MAX_MISSED_STEPS}
|
|
||||||
x={"encoder_missed_steps_max_x"}
|
|
||||||
y={"encoder_missed_steps_max_y"}
|
|
||||||
z={"encoder_missed_steps_max_z"}
|
|
||||||
gray={encodersDisabled}
|
|
||||||
sourceFwConfig={sourceFwConfig}
|
|
||||||
dispatch={dispatch} />
|
|
||||||
<NumericMCUInputGroup
|
|
||||||
name={t("Missed Step Decay")}
|
|
||||||
tooltip={ToolTips.ENCODER_MISSED_STEP_DECAY}
|
|
||||||
x={"encoder_missed_steps_decay_x"}
|
|
||||||
y={"encoder_missed_steps_decay_y"}
|
|
||||||
z={"encoder_missed_steps_decay_z"}
|
|
||||||
gray={encodersDisabled}
|
|
||||||
sourceFwConfig={sourceFwConfig}
|
|
||||||
dispatch={dispatch} />
|
|
||||||
{!isExpressBoard(firmwareHardware) &&
|
|
||||||
<NumericMCUInputGroup
|
|
||||||
name={t("Encoder Scaling")}
|
|
||||||
tooltip={ToolTips.ENCODER_SCALING}
|
|
||||||
x={"encoder_scaling_x"}
|
|
||||||
y={"encoder_scaling_y"}
|
|
||||||
z={"encoder_scaling_z"}
|
|
||||||
xScale={sourceFwConfig("movement_microsteps_x").value}
|
|
||||||
yScale={sourceFwConfig("movement_microsteps_y").value}
|
|
||||||
zScale={sourceFwConfig("movement_microsteps_z").value}
|
|
||||||
intSize={shouldDisplay(Feature.long_scaling_factor) ? "long" : "short"}
|
|
||||||
gray={encodersDisabled}
|
|
||||||
sourceFwConfig={sourceFwConfig}
|
|
||||||
dispatch={dispatch} />}
|
|
||||||
<BooleanMCUInputGroup
|
|
||||||
name={t("Enable Endstops")}
|
|
||||||
tooltip={ToolTips.ENABLE_ENDSTOPS}
|
|
||||||
x={"movement_enable_endpoints_x"}
|
|
||||||
y={"movement_enable_endpoints_y"}
|
|
||||||
z={"movement_enable_endpoints_z"}
|
|
||||||
dispatch={dispatch}
|
|
||||||
sourceFwConfig={sourceFwConfig} />
|
|
||||||
<BooleanMCUInputGroup
|
|
||||||
name={t("Swap Endstops")}
|
|
||||||
tooltip={ToolTips.SWAP_ENDPOINTS}
|
|
||||||
x={"movement_invert_endpoints_x"}
|
|
||||||
y={"movement_invert_endpoints_y"}
|
|
||||||
z={"movement_invert_endpoints_z"}
|
|
||||||
grayscale={{
|
|
||||||
x: !sourceFwConfig("movement_enable_endpoints_x").value,
|
|
||||||
y: !sourceFwConfig("movement_enable_endpoints_y").value,
|
|
||||||
z: !sourceFwConfig("movement_enable_endpoints_z").value
|
|
||||||
}}
|
|
||||||
dispatch={dispatch}
|
|
||||||
sourceFwConfig={sourceFwConfig} />
|
|
||||||
<BooleanMCUInputGroup
|
|
||||||
name={t("Invert Endstops")}
|
|
||||||
tooltip={ToolTips.INVERT_ENDPOINTS}
|
|
||||||
x={"movement_invert_2_endpoints_x"}
|
|
||||||
y={"movement_invert_2_endpoints_y"}
|
|
||||||
z={"movement_invert_2_endpoints_z"}
|
|
||||||
grayscale={{
|
|
||||||
x: !sourceFwConfig("movement_enable_endpoints_x").value,
|
|
||||||
y: !sourceFwConfig("movement_enable_endpoints_y").value,
|
|
||||||
z: !sourceFwConfig("movement_enable_endpoints_z").value
|
|
||||||
}}
|
|
||||||
dispatch={dispatch}
|
|
||||||
sourceFwConfig={sourceFwConfig} />
|
|
||||||
</Collapse>
|
|
||||||
</section>;
|
|
||||||
}
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { BooleanMCUInputGroup } from "../boolean_mcu_input_group";
|
||||||
|
import { ToolTips, DeviceSetting } from "../../../constants";
|
||||||
|
import { EndStopsProps } from "../interfaces";
|
||||||
|
import { Header } from "./header";
|
||||||
|
import { Collapse } from "@blueprintjs/core";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
|
export function EndStops(props: EndStopsProps) {
|
||||||
|
|
||||||
|
const { endstops } = props.controlPanelState;
|
||||||
|
const { dispatch, sourceFwConfig } = props;
|
||||||
|
|
||||||
|
return <Highlight className={"section"}
|
||||||
|
settingName={DeviceSetting.endstops}>
|
||||||
|
<Header
|
||||||
|
expanded={endstops}
|
||||||
|
title={DeviceSetting.endstops}
|
||||||
|
panel={"endstops"}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
<Collapse isOpen={!!endstops}>
|
||||||
|
<BooleanMCUInputGroup
|
||||||
|
label={DeviceSetting.enableEndstops}
|
||||||
|
tooltip={ToolTips.ENABLE_ENDSTOPS}
|
||||||
|
x={"movement_enable_endpoints_x"}
|
||||||
|
y={"movement_enable_endpoints_y"}
|
||||||
|
z={"movement_enable_endpoints_z"}
|
||||||
|
dispatch={dispatch}
|
||||||
|
sourceFwConfig={sourceFwConfig} />
|
||||||
|
<BooleanMCUInputGroup
|
||||||
|
label={DeviceSetting.swapEndstops}
|
||||||
|
tooltip={ToolTips.SWAP_ENDPOINTS}
|
||||||
|
x={"movement_invert_endpoints_x"}
|
||||||
|
y={"movement_invert_endpoints_y"}
|
||||||
|
z={"movement_invert_endpoints_z"}
|
||||||
|
grayscale={{
|
||||||
|
x: !sourceFwConfig("movement_enable_endpoints_x").value,
|
||||||
|
y: !sourceFwConfig("movement_enable_endpoints_y").value,
|
||||||
|
z: !sourceFwConfig("movement_enable_endpoints_z").value
|
||||||
|
}}
|
||||||
|
dispatch={dispatch}
|
||||||
|
sourceFwConfig={sourceFwConfig} />
|
||||||
|
<BooleanMCUInputGroup
|
||||||
|
label={DeviceSetting.invertEndstops}
|
||||||
|
tooltip={ToolTips.INVERT_ENDPOINTS}
|
||||||
|
x={"movement_invert_2_endpoints_x"}
|
||||||
|
y={"movement_invert_2_endpoints_y"}
|
||||||
|
z={"movement_invert_2_endpoints_z"}
|
||||||
|
grayscale={{
|
||||||
|
x: !sourceFwConfig("movement_enable_endpoints_x").value,
|
||||||
|
y: !sourceFwConfig("movement_enable_endpoints_y").value,
|
||||||
|
z: !sourceFwConfig("movement_enable_endpoints_z").value
|
||||||
|
}}
|
||||||
|
dispatch={dispatch}
|
||||||
|
sourceFwConfig={sourceFwConfig} />
|
||||||
|
</Collapse>
|
||||||
|
</Highlight>;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { NumericMCUInputGroup } from "../numeric_mcu_input_group";
|
||||||
|
import { ToolTips, DeviceSetting } from "../../../constants";
|
||||||
|
import { ErrorHandlingProps } from "../interfaces";
|
||||||
|
import { Header } from "./header";
|
||||||
|
import { Collapse } from "@blueprintjs/core";
|
||||||
|
import { McuInputBox } from "../mcu_input_box";
|
||||||
|
import { settingToggle } from "../../actions";
|
||||||
|
import { SingleSettingRow } from "./single_setting_row";
|
||||||
|
import { ToggleButton } from "../../../controls/toggle_button";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
|
export function ErrorHandling(props: ErrorHandlingProps) {
|
||||||
|
|
||||||
|
const { error_handling } = props.controlPanelState;
|
||||||
|
const { dispatch, sourceFwConfig } = props;
|
||||||
|
const eStopOnMoveError = sourceFwConfig("param_e_stop_on_mov_err");
|
||||||
|
|
||||||
|
return <Highlight className={"section"}
|
||||||
|
settingName={DeviceSetting.errorHandling}>
|
||||||
|
<Header
|
||||||
|
expanded={error_handling}
|
||||||
|
title={DeviceSetting.errorHandling}
|
||||||
|
panel={"error_handling"}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
<Collapse isOpen={!!error_handling}>
|
||||||
|
<NumericMCUInputGroup
|
||||||
|
label={DeviceSetting.timeoutAfter}
|
||||||
|
tooltip={ToolTips.TIMEOUT_AFTER}
|
||||||
|
x={"movement_timeout_x"}
|
||||||
|
y={"movement_timeout_y"}
|
||||||
|
z={"movement_timeout_z"}
|
||||||
|
sourceFwConfig={sourceFwConfig}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
<SingleSettingRow settingType="input"
|
||||||
|
label={DeviceSetting.maxRetries}
|
||||||
|
tooltip={ToolTips.MAX_MOVEMENT_RETRIES}>
|
||||||
|
<McuInputBox
|
||||||
|
setting="param_mov_nr_retry"
|
||||||
|
sourceFwConfig={sourceFwConfig}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
</SingleSettingRow>
|
||||||
|
<SingleSettingRow settingType="button"
|
||||||
|
label={DeviceSetting.estopOnMovementError}
|
||||||
|
tooltip={ToolTips.E_STOP_ON_MOV_ERR}>
|
||||||
|
<ToggleButton
|
||||||
|
toggleValue={eStopOnMoveError.value}
|
||||||
|
dim={!eStopOnMoveError.consistent}
|
||||||
|
toggleAction={() => dispatch(
|
||||||
|
settingToggle("param_e_stop_on_mov_err", sourceFwConfig))} />
|
||||||
|
</SingleSettingRow>
|
||||||
|
</Collapse>
|
||||||
|
</Highlight>;
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ const getSubKeyName = (key: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FwParamExportMenu =
|
export const FwParamExportMenu =
|
||||||
({ firmwareConfig }: { firmwareConfig: FirmwareConfig }) => {
|
({ firmwareConfig }: { firmwareConfig: FirmwareConfig | undefined }) => {
|
||||||
/** Filter out unnecessary parameters. */
|
/** Filter out unnecessary parameters. */
|
||||||
const filteredConfig = pickBy(firmwareConfig, (_, key) =>
|
const filteredConfig = pickBy(firmwareConfig, (_, key) =>
|
||||||
!["id", "device_id", "api_migrated", "created_at", "updated_at",
|
!["id", "device_id", "api_migrated", "created_at", "updated_at",
|
||||||
|
|
|
@ -2,18 +2,20 @@ import * as React from "react";
|
||||||
import { ControlPanelState } from "../../interfaces";
|
import { ControlPanelState } from "../../interfaces";
|
||||||
import { toggleControlPanel } from "../../actions";
|
import { toggleControlPanel } from "../../actions";
|
||||||
import { ExpandableHeader } from "../../../ui/expandable_header";
|
import { ExpandableHeader } from "../../../ui/expandable_header";
|
||||||
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
name: keyof ControlPanelState;
|
panel: keyof ControlPanelState;
|
||||||
title: string;
|
title: DeviceSetting;
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Header = (props: Props) => {
|
export const Header = (props: Props) => {
|
||||||
const { dispatch, name, title, expanded } = props;
|
const { dispatch, panel, title, expanded } = props;
|
||||||
return <ExpandableHeader
|
return <ExpandableHeader
|
||||||
expanded={expanded}
|
expanded={expanded}
|
||||||
title={title}
|
title={t(title)}
|
||||||
onClick={() => dispatch(toggleControlPanel(name))} />;
|
onClick={() => dispatch(toggleControlPanel(panel))} />;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { BooleanMCUInputGroup } from "../boolean_mcu_input_group";
|
import { BooleanMCUInputGroup } from "../boolean_mcu_input_group";
|
||||||
import { ToolTips } from "../../../constants";
|
import { ToolTips, DeviceSetting } from "../../../constants";
|
||||||
import { NumericMCUInputGroup } from "../numeric_mcu_input_group";
|
import { NumericMCUInputGroup } from "../numeric_mcu_input_group";
|
||||||
import { HomingRow } from "./homing_row";
|
|
||||||
import { CalibrationRow } from "./calibration_row";
|
import { CalibrationRow } from "./calibration_row";
|
||||||
import { ZeroRow } from "./zero_row";
|
|
||||||
import { disabledAxisMap } from "../axis_tracking_status";
|
import { disabledAxisMap } from "../axis_tracking_status";
|
||||||
import { HomingAndCalibrationProps } from "../interfaces";
|
import { HomingAndCalibrationProps } from "../interfaces";
|
||||||
import { Header } from "./header";
|
import { Header } from "./header";
|
||||||
import { Collapse } from "@blueprintjs/core";
|
import { Collapse } from "@blueprintjs/core";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
import { calculateScale } from "./motors";
|
import { calculateScale } from "./motors";
|
||||||
|
import { isExpressBoard } from "../firmware_hardware_support";
|
||||||
|
import { getDevice } from "../../../device";
|
||||||
|
import { commandErr } from "../../actions";
|
||||||
|
import { CONFIG_DEFAULTS } from "farmbot/dist/config";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
||||||
|
|
||||||
const { dispatch, bot, sourceFwConfig, firmwareConfig, botDisconnected
|
const {
|
||||||
|
dispatch, bot, sourceFwConfig, firmwareConfig, botDisconnected,
|
||||||
|
firmwareHardware
|
||||||
} = props;
|
} = props;
|
||||||
const hardware = firmwareConfig ? firmwareConfig : bot.hardware.mcu_params;
|
const hardware = firmwareConfig ? firmwareConfig : bot.hardware.mcu_params;
|
||||||
const { homing_and_calibration } = props.bot.controlPanelState;
|
const { homing_and_calibration } = props.bot.controlPanelState;
|
||||||
|
@ -27,19 +32,51 @@ export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
||||||
|
|
||||||
const scale = calculateScale(sourceFwConfig);
|
const scale = calculateScale(sourceFwConfig);
|
||||||
|
|
||||||
return <section>
|
return <Highlight className={"section"}
|
||||||
|
settingName={DeviceSetting.homingAndCalibration}>
|
||||||
<Header
|
<Header
|
||||||
title={t("Homing and Calibration")}
|
title={DeviceSetting.homingAndCalibration}
|
||||||
name={"homing_and_calibration"}
|
panel={"homing_and_calibration"}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
expanded={homing_and_calibration} />
|
expanded={homing_and_calibration} />
|
||||||
<Collapse isOpen={!!homing_and_calibration}>
|
<Collapse isOpen={!!homing_and_calibration}>
|
||||||
<HomingRow hardware={hardware} botDisconnected={botDisconnected} />
|
<CalibrationRow
|
||||||
<CalibrationRow hardware={hardware} botDisconnected={botDisconnected} />
|
type={"find_home"}
|
||||||
<ZeroRow botDisconnected={botDisconnected} />
|
title={DeviceSetting.homing}
|
||||||
|
axisTitle={t("FIND HOME")}
|
||||||
|
toolTip={isExpressBoard(firmwareHardware)
|
||||||
|
? ToolTips.HOMING_STALL_DETECTION
|
||||||
|
: ToolTips.HOMING_ENCODERS}
|
||||||
|
action={axis => getDevice()
|
||||||
|
.findHome({ speed: CONFIG_DEFAULTS.speed, axis })
|
||||||
|
.catch(commandErr("'Find Home' request"))}
|
||||||
|
hardware={hardware}
|
||||||
|
botDisconnected={botDisconnected} />
|
||||||
|
<CalibrationRow
|
||||||
|
type={"calibrate"}
|
||||||
|
title={DeviceSetting.calibration}
|
||||||
|
axisTitle={t("CALIBRATE")}
|
||||||
|
toolTip={isExpressBoard(firmwareHardware)
|
||||||
|
? ToolTips.CALIBRATION_STALL_DETECTION
|
||||||
|
: ToolTips.CALIBRATION_ENCODERS}
|
||||||
|
action={axis => getDevice().calibrate({ axis })
|
||||||
|
.catch(commandErr("Calibration"))}
|
||||||
|
hardware={hardware}
|
||||||
|
botDisconnected={botDisconnected} />
|
||||||
|
<CalibrationRow
|
||||||
|
type={"zero"}
|
||||||
|
title={DeviceSetting.setZeroPosition}
|
||||||
|
axisTitle={t("ZERO")}
|
||||||
|
toolTip={ToolTips.SET_ZERO_POSITION}
|
||||||
|
action={axis => getDevice().setZero(axis)
|
||||||
|
.catch(commandErr("Zeroing"))}
|
||||||
|
hardware={hardware}
|
||||||
|
botDisconnected={botDisconnected} />
|
||||||
<BooleanMCUInputGroup
|
<BooleanMCUInputGroup
|
||||||
name={t("Find Home on Boot")}
|
label={DeviceSetting.findHomeOnBoot}
|
||||||
tooltip={ToolTips.FIND_HOME_ON_BOOT}
|
tooltip={isExpressBoard(firmwareHardware)
|
||||||
|
? ToolTips.FIND_HOME_ON_BOOT_STALL_DETECTION
|
||||||
|
: ToolTips.FIND_HOME_ON_BOOT_ENCODERS}
|
||||||
disable={disabled}
|
disable={disabled}
|
||||||
x={"movement_home_at_boot_x"}
|
x={"movement_home_at_boot_x"}
|
||||||
y={"movement_home_at_boot_y"}
|
y={"movement_home_at_boot_y"}
|
||||||
|
@ -48,7 +85,7 @@ export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
||||||
sourceFwConfig={sourceFwConfig}
|
sourceFwConfig={sourceFwConfig}
|
||||||
caution={true} />
|
caution={true} />
|
||||||
<BooleanMCUInputGroup
|
<BooleanMCUInputGroup
|
||||||
name={t("Stop at Home")}
|
label={DeviceSetting.stopAtHome}
|
||||||
tooltip={ToolTips.STOP_AT_HOME}
|
tooltip={ToolTips.STOP_AT_HOME}
|
||||||
x={"movement_stop_at_home_x"}
|
x={"movement_stop_at_home_x"}
|
||||||
y={"movement_stop_at_home_y"}
|
y={"movement_stop_at_home_y"}
|
||||||
|
@ -56,7 +93,7 @@ export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
sourceFwConfig={sourceFwConfig} />
|
sourceFwConfig={sourceFwConfig} />
|
||||||
<BooleanMCUInputGroup
|
<BooleanMCUInputGroup
|
||||||
name={t("Stop at Max")}
|
label={DeviceSetting.stopAtMax}
|
||||||
tooltip={ToolTips.STOP_AT_MAX}
|
tooltip={ToolTips.STOP_AT_MAX}
|
||||||
x={"movement_stop_at_max_x"}
|
x={"movement_stop_at_max_x"}
|
||||||
y={"movement_stop_at_max_y"}
|
y={"movement_stop_at_max_y"}
|
||||||
|
@ -64,7 +101,7 @@ export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
sourceFwConfig={sourceFwConfig} />
|
sourceFwConfig={sourceFwConfig} />
|
||||||
<BooleanMCUInputGroup
|
<BooleanMCUInputGroup
|
||||||
name={t("Negative Coordinates Only")}
|
label={DeviceSetting.negativeCoordinatesOnly}
|
||||||
tooltip={ToolTips.NEGATIVE_COORDINATES_ONLY}
|
tooltip={ToolTips.NEGATIVE_COORDINATES_ONLY}
|
||||||
x={"movement_home_up_x"}
|
x={"movement_home_up_x"}
|
||||||
y={"movement_home_up_y"}
|
y={"movement_home_up_y"}
|
||||||
|
@ -72,7 +109,7 @@ export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
sourceFwConfig={sourceFwConfig} />
|
sourceFwConfig={sourceFwConfig} />
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Axis Length (mm)")}
|
label={DeviceSetting.axisLength}
|
||||||
tooltip={ToolTips.LENGTH}
|
tooltip={ToolTips.LENGTH}
|
||||||
x={"movement_axis_nr_steps_x"}
|
x={"movement_axis_nr_steps_x"}
|
||||||
y={"movement_axis_nr_steps_y"}
|
y={"movement_axis_nr_steps_y"}
|
||||||
|
@ -88,14 +125,6 @@ export function HomingAndCalibration(props: HomingAndCalibrationProps) {
|
||||||
sourceFwConfig={sourceFwConfig}
|
sourceFwConfig={sourceFwConfig}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
intSize={"long"} />
|
intSize={"long"} />
|
||||||
<NumericMCUInputGroup
|
|
||||||
name={t("Timeout after (seconds)")}
|
|
||||||
tooltip={ToolTips.TIMEOUT_AFTER}
|
|
||||||
x={"movement_timeout_x"}
|
|
||||||
y={"movement_timeout_y"}
|
|
||||||
z={"movement_timeout_z"}
|
|
||||||
sourceFwConfig={sourceFwConfig}
|
|
||||||
dispatch={dispatch} />
|
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</section>;
|
</Highlight>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { HomingRowProps } from "../interfaces";
|
|
||||||
import { LockableButton } from "../lockable_button";
|
|
||||||
import { axisTrackingStatus } from "../axis_tracking_status";
|
|
||||||
import { ToolTips } from "../../../constants";
|
|
||||||
import { Row, Col, Help } from "../../../ui/index";
|
|
||||||
import { CONFIG_DEFAULTS } from "farmbot/dist/config";
|
|
||||||
import { commandErr } from "../../actions";
|
|
||||||
import { Axis } from "../../interfaces";
|
|
||||||
import { getDevice } from "../../../device";
|
|
||||||
import { t } from "../../../i18next_wrapper";
|
|
||||||
import { Position } from "@blueprintjs/core";
|
|
||||||
|
|
||||||
const speed = CONFIG_DEFAULTS.speed;
|
|
||||||
const findHome = (axis: Axis) => getDevice()
|
|
||||||
.findHome({ speed, axis })
|
|
||||||
.catch(commandErr("'Find Home' request"));
|
|
||||||
|
|
||||||
export function HomingRow(props: HomingRowProps) {
|
|
||||||
const { hardware, botDisconnected } = props;
|
|
||||||
|
|
||||||
return <Row>
|
|
||||||
<Col xs={6} className={"widget-body-tooltips"}>
|
|
||||||
<label>
|
|
||||||
{t("HOMING")}
|
|
||||||
</label>
|
|
||||||
<Help text={ToolTips.HOMING} requireClick={true} position={Position.RIGHT}/>
|
|
||||||
</Col>
|
|
||||||
{axisTrackingStatus(hardware)
|
|
||||||
.map((row) => {
|
|
||||||
const { axis, disabled } = row;
|
|
||||||
return <Col xs={2} key={axis} className={"centered-button-div"}>
|
|
||||||
<LockableButton
|
|
||||||
disabled={disabled || botDisconnected}
|
|
||||||
onClick={() => findHome(axis)}>
|
|
||||||
{t("FIND HOME {{axis}}", { axis })}
|
|
||||||
</LockableButton>
|
|
||||||
</Col>;
|
|
||||||
})}
|
|
||||||
</Row>;
|
|
||||||
}
|
|
|
@ -1,36 +1,18 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { BooleanMCUInputGroup } from "../boolean_mcu_input_group";
|
import { BooleanMCUInputGroup } from "../boolean_mcu_input_group";
|
||||||
import { ToolTips } from "../../../constants";
|
import { ToolTips, DeviceSetting } from "../../../constants";
|
||||||
import { ToggleButton } from "../../../controls/toggle_button";
|
import { ToggleButton } from "../../../controls/toggle_button";
|
||||||
import { settingToggle } from "../../actions";
|
import { settingToggle } from "../../actions";
|
||||||
import { NumericMCUInputGroup } from "../numeric_mcu_input_group";
|
import { NumericMCUInputGroup } from "../numeric_mcu_input_group";
|
||||||
import { MotorsProps } from "../interfaces";
|
import { MotorsProps } from "../interfaces";
|
||||||
import { Row, Col, Help } from "../../../ui/index";
|
|
||||||
import { Header } from "./header";
|
import { Header } from "./header";
|
||||||
import { Collapse, Position } from "@blueprintjs/core";
|
import { Collapse } from "@blueprintjs/core";
|
||||||
import { McuInputBox } from "../mcu_input_box";
|
|
||||||
import { t } from "../../../i18next_wrapper";
|
|
||||||
import { Xyz, McuParamName } from "farmbot";
|
import { Xyz, McuParamName } from "farmbot";
|
||||||
import { SourceFwConfig } from "../../interfaces";
|
import { SourceFwConfig } from "../../interfaces";
|
||||||
import { calcMicrostepsPerMm } from "../../../controls/move/direction_axes_props";
|
import { calcMicrostepsPerMm } from "../../../controls/move/direction_axes_props";
|
||||||
import { isTMCBoard, isExpressBoard } from "../firmware_hardware_support";
|
import { isTMCBoard } from "../firmware_hardware_support";
|
||||||
|
import { SingleSettingRow } from "./single_setting_row";
|
||||||
const SingleSettingRow =
|
import { Highlight } from "../maybe_highlight";
|
||||||
({ label, tooltip, settingType, children }: {
|
|
||||||
label: string,
|
|
||||||
tooltip: string,
|
|
||||||
children: React.ReactChild,
|
|
||||||
settingType: "button" | "input",
|
|
||||||
}) =>
|
|
||||||
<Row>
|
|
||||||
<Col xs={6} className={"widget-body-tooltips"}>
|
|
||||||
<label>{label}</label>
|
|
||||||
<Help text={tooltip} requireClick={true} position={Position.RIGHT} />
|
|
||||||
</Col>
|
|
||||||
{settingType === "button"
|
|
||||||
? <Col xs={2} className={"centered-button-div"}>{children}</Col>
|
|
||||||
: <Col xs={6}>{children}</Col>}
|
|
||||||
</Row>;
|
|
||||||
|
|
||||||
export const calculateScale =
|
export const calculateScale =
|
||||||
(sourceFwConfig: SourceFwConfig): Record<Xyz, number | undefined> => {
|
(sourceFwConfig: SourceFwConfig): Record<Xyz, number | undefined> => {
|
||||||
|
@ -51,39 +33,18 @@ export function Motors(props: MotorsProps) {
|
||||||
} = props;
|
} = props;
|
||||||
const enable2ndXMotor = sourceFwConfig("movement_secondary_motor_x");
|
const enable2ndXMotor = sourceFwConfig("movement_secondary_motor_x");
|
||||||
const invert2ndXMotor = sourceFwConfig("movement_secondary_motor_invert_x");
|
const invert2ndXMotor = sourceFwConfig("movement_secondary_motor_invert_x");
|
||||||
const eStopOnMoveError = sourceFwConfig("param_e_stop_on_mov_err");
|
|
||||||
const scale = calculateScale(sourceFwConfig);
|
const scale = calculateScale(sourceFwConfig);
|
||||||
const encodersDisabled = {
|
|
||||||
x: !sourceFwConfig("encoder_enabled_x").value,
|
return <Highlight className={"section"}
|
||||||
y: !sourceFwConfig("encoder_enabled_y").value,
|
settingName={DeviceSetting.motors}>
|
||||||
z: !sourceFwConfig("encoder_enabled_z").value,
|
|
||||||
};
|
|
||||||
return <section>
|
|
||||||
<Header
|
<Header
|
||||||
expanded={controlPanelState.motors}
|
expanded={controlPanelState.motors}
|
||||||
title={t("Motors")}
|
title={DeviceSetting.motors}
|
||||||
name={"motors"}
|
panel={"motors"}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
<Collapse isOpen={!!controlPanelState.motors}>
|
<Collapse isOpen={!!controlPanelState.motors}>
|
||||||
<SingleSettingRow settingType="input"
|
|
||||||
label={t("Max Retries")}
|
|
||||||
tooltip={ToolTips.MAX_MOVEMENT_RETRIES}>
|
|
||||||
<McuInputBox
|
|
||||||
setting="param_mov_nr_retry"
|
|
||||||
sourceFwConfig={sourceFwConfig}
|
|
||||||
dispatch={dispatch} />
|
|
||||||
</SingleSettingRow>
|
|
||||||
<SingleSettingRow settingType="button"
|
|
||||||
label={t("E-Stop on Movement Error")}
|
|
||||||
tooltip={ToolTips.E_STOP_ON_MOV_ERR}>
|
|
||||||
<ToggleButton
|
|
||||||
toggleValue={eStopOnMoveError.value}
|
|
||||||
dim={!eStopOnMoveError.consistent}
|
|
||||||
toggleAction={() => dispatch(
|
|
||||||
settingToggle("param_e_stop_on_mov_err", sourceFwConfig))} />
|
|
||||||
</SingleSettingRow>
|
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Max Speed (mm/s)")}
|
label={DeviceSetting.maxSpeed}
|
||||||
tooltip={ToolTips.MAX_SPEED}
|
tooltip={ToolTips.MAX_SPEED}
|
||||||
x={"movement_max_spd_x"}
|
x={"movement_max_spd_x"}
|
||||||
y={"movement_max_spd_y"}
|
y={"movement_max_spd_y"}
|
||||||
|
@ -94,7 +55,7 @@ export function Motors(props: MotorsProps) {
|
||||||
sourceFwConfig={sourceFwConfig}
|
sourceFwConfig={sourceFwConfig}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Homing Speed (mm/s)")}
|
label={DeviceSetting.homingSpeed}
|
||||||
tooltip={ToolTips.HOME_SPEED}
|
tooltip={ToolTips.HOME_SPEED}
|
||||||
x={"movement_home_spd_x"}
|
x={"movement_home_spd_x"}
|
||||||
y={"movement_home_spd_y"}
|
y={"movement_home_spd_y"}
|
||||||
|
@ -105,7 +66,7 @@ export function Motors(props: MotorsProps) {
|
||||||
sourceFwConfig={sourceFwConfig}
|
sourceFwConfig={sourceFwConfig}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Minimum Speed (mm/s)")}
|
label={DeviceSetting.minimumSpeed}
|
||||||
tooltip={ToolTips.MIN_SPEED}
|
tooltip={ToolTips.MIN_SPEED}
|
||||||
x={"movement_min_spd_x"}
|
x={"movement_min_spd_x"}
|
||||||
y={"movement_min_spd_y"}
|
y={"movement_min_spd_y"}
|
||||||
|
@ -116,7 +77,7 @@ export function Motors(props: MotorsProps) {
|
||||||
sourceFwConfig={sourceFwConfig}
|
sourceFwConfig={sourceFwConfig}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Accelerate for (mm)")}
|
label={DeviceSetting.accelerateFor}
|
||||||
tooltip={ToolTips.ACCELERATE_FOR}
|
tooltip={ToolTips.ACCELERATE_FOR}
|
||||||
x={"movement_steps_acc_dec_x"}
|
x={"movement_steps_acc_dec_x"}
|
||||||
y={"movement_steps_acc_dec_y"}
|
y={"movement_steps_acc_dec_y"}
|
||||||
|
@ -127,7 +88,7 @@ export function Motors(props: MotorsProps) {
|
||||||
sourceFwConfig={sourceFwConfig}
|
sourceFwConfig={sourceFwConfig}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Steps per MM")}
|
label={DeviceSetting.stepsPerMm}
|
||||||
tooltip={ToolTips.STEPS_PER_MM}
|
tooltip={ToolTips.STEPS_PER_MM}
|
||||||
x={"movement_step_per_mm_x"}
|
x={"movement_step_per_mm_x"}
|
||||||
y={"movement_step_per_mm_y"}
|
y={"movement_step_per_mm_y"}
|
||||||
|
@ -139,7 +100,7 @@ export function Motors(props: MotorsProps) {
|
||||||
sourceFwConfig={props.sourceFwConfig}
|
sourceFwConfig={props.sourceFwConfig}
|
||||||
dispatch={props.dispatch} />
|
dispatch={props.dispatch} />
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Microsteps per step")}
|
label={DeviceSetting.microstepsPerStep}
|
||||||
tooltip={ToolTips.MICROSTEPS_PER_STEP}
|
tooltip={ToolTips.MICROSTEPS_PER_STEP}
|
||||||
x={"movement_microsteps_x"}
|
x={"movement_microsteps_x"}
|
||||||
y={"movement_microsteps_y"}
|
y={"movement_microsteps_y"}
|
||||||
|
@ -147,7 +108,7 @@ export function Motors(props: MotorsProps) {
|
||||||
sourceFwConfig={props.sourceFwConfig}
|
sourceFwConfig={props.sourceFwConfig}
|
||||||
dispatch={props.dispatch} />
|
dispatch={props.dispatch} />
|
||||||
<BooleanMCUInputGroup
|
<BooleanMCUInputGroup
|
||||||
name={t("Always Power Motors")}
|
label={DeviceSetting.alwaysPowerMotors}
|
||||||
tooltip={ToolTips.ALWAYS_POWER_MOTORS}
|
tooltip={ToolTips.ALWAYS_POWER_MOTORS}
|
||||||
x={"movement_keep_active_x"}
|
x={"movement_keep_active_x"}
|
||||||
y={"movement_keep_active_y"}
|
y={"movement_keep_active_y"}
|
||||||
|
@ -155,7 +116,7 @@ export function Motors(props: MotorsProps) {
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
sourceFwConfig={sourceFwConfig} />
|
sourceFwConfig={sourceFwConfig} />
|
||||||
<BooleanMCUInputGroup
|
<BooleanMCUInputGroup
|
||||||
name={t("Invert Motors")}
|
label={DeviceSetting.invertMotors}
|
||||||
tooltip={ToolTips.INVERT_MOTORS}
|
tooltip={ToolTips.INVERT_MOTORS}
|
||||||
x={"movement_invert_motor_x"}
|
x={"movement_invert_motor_x"}
|
||||||
y={"movement_invert_motor_y"}
|
y={"movement_invert_motor_y"}
|
||||||
|
@ -164,25 +125,15 @@ export function Motors(props: MotorsProps) {
|
||||||
sourceFwConfig={sourceFwConfig} />
|
sourceFwConfig={sourceFwConfig} />
|
||||||
{isTMCBoard(firmwareHardware) &&
|
{isTMCBoard(firmwareHardware) &&
|
||||||
<NumericMCUInputGroup
|
<NumericMCUInputGroup
|
||||||
name={t("Motor Current")}
|
label={DeviceSetting.motorCurrent}
|
||||||
tooltip={ToolTips.MOTOR_CURRENT}
|
tooltip={ToolTips.MOTOR_CURRENT}
|
||||||
x={"movement_motor_current_x"}
|
x={"movement_motor_current_x"}
|
||||||
y={"movement_motor_current_y"}
|
y={"movement_motor_current_y"}
|
||||||
z={"movement_motor_current_z"}
|
z={"movement_motor_current_z"}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
sourceFwConfig={sourceFwConfig} />}
|
sourceFwConfig={sourceFwConfig} />}
|
||||||
{isExpressBoard(firmwareHardware) &&
|
|
||||||
<NumericMCUInputGroup
|
|
||||||
name={t("Stall Sensitivity")}
|
|
||||||
tooltip={ToolTips.STALL_SENSITIVITY}
|
|
||||||
x={"movement_stall_sensitivity_x"}
|
|
||||||
y={"movement_stall_sensitivity_y"}
|
|
||||||
z={"movement_stall_sensitivity_z"}
|
|
||||||
gray={encodersDisabled}
|
|
||||||
dispatch={dispatch}
|
|
||||||
sourceFwConfig={sourceFwConfig} />}
|
|
||||||
<SingleSettingRow settingType="button"
|
<SingleSettingRow settingType="button"
|
||||||
label={t("Enable 2nd X Motor")}
|
label={DeviceSetting.enable2ndXMotor}
|
||||||
tooltip={ToolTips.ENABLE_X2_MOTOR}>
|
tooltip={ToolTips.ENABLE_X2_MOTOR}>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
toggleValue={enable2ndXMotor.value}
|
toggleValue={enable2ndXMotor.value}
|
||||||
|
@ -191,7 +142,7 @@ export function Motors(props: MotorsProps) {
|
||||||
settingToggle("movement_secondary_motor_x", sourceFwConfig))} />
|
settingToggle("movement_secondary_motor_x", sourceFwConfig))} />
|
||||||
</SingleSettingRow>
|
</SingleSettingRow>
|
||||||
<SingleSettingRow settingType="button"
|
<SingleSettingRow settingType="button"
|
||||||
label={t("Invert 2nd X Motor")}
|
label={DeviceSetting.invert2ndXMotor}
|
||||||
tooltip={ToolTips.INVERT_MOTORS}>
|
tooltip={ToolTips.INVERT_MOTORS}>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
grayscale={!enable2ndXMotor.value}
|
grayscale={!enable2ndXMotor.value}
|
||||||
|
@ -201,5 +152,5 @@ export function Motors(props: MotorsProps) {
|
||||||
settingToggle("movement_secondary_motor_invert_x", sourceFwConfig))} />
|
settingToggle("movement_secondary_motor_invert_x", sourceFwConfig))} />
|
||||||
</SingleSettingRow>
|
</SingleSettingRow>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</section>;
|
</Highlight>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { PinBindingsProps } from "../interfaces";
|
||||||
|
import { Header } from "./header";
|
||||||
|
import { Collapse } from "@blueprintjs/core";
|
||||||
|
import { PinBindingsContent } from "../../pin_bindings/pin_bindings";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
|
export function PinBindings(props: PinBindingsProps) {
|
||||||
|
|
||||||
|
const { pin_bindings } = props.controlPanelState;
|
||||||
|
const { dispatch, resources } = props;
|
||||||
|
|
||||||
|
return <Highlight className={"section"}
|
||||||
|
settingName={DeviceSetting.pinBindings}>
|
||||||
|
<Header
|
||||||
|
expanded={pin_bindings}
|
||||||
|
title={DeviceSetting.pinBindings}
|
||||||
|
panel={"pin_bindings"}
|
||||||
|
dispatch={dispatch} />
|
||||||
|
<Collapse isOpen={!!pin_bindings}>
|
||||||
|
<PinBindingsContent dispatch={dispatch} resources={resources} />
|
||||||
|
</Collapse>
|
||||||
|
</Highlight>;
|
||||||
|
}
|
|
@ -4,19 +4,21 @@ import { PinGuardProps } from "../interfaces";
|
||||||
import { Header } from "./header";
|
import { Header } from "./header";
|
||||||
import { Collapse, Position } from "@blueprintjs/core";
|
import { Collapse, Position } from "@blueprintjs/core";
|
||||||
import { Row, Col, Help } from "../../../ui/index";
|
import { Row, Col, Help } from "../../../ui/index";
|
||||||
import { ToolTips } from "../../../constants";
|
import { ToolTips, DeviceSetting } from "../../../constants";
|
||||||
import { t } from "../../../i18next_wrapper";
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
|
||||||
export function PinGuard(props: PinGuardProps) {
|
export function PinGuard(props: PinGuardProps) {
|
||||||
|
|
||||||
const { pin_guard } = props.controlPanelState;
|
const { pin_guard } = props.controlPanelState;
|
||||||
const { dispatch, sourceFwConfig, resources } = props;
|
const { dispatch, sourceFwConfig, resources } = props;
|
||||||
|
|
||||||
return <section>
|
return <Highlight className={"section"}
|
||||||
|
settingName={DeviceSetting.pinGuard}>
|
||||||
<Header
|
<Header
|
||||||
expanded={pin_guard}
|
expanded={pin_guard}
|
||||||
title={t("Pin Guard")}
|
title={DeviceSetting.pinGuard}
|
||||||
name={"pin_guard"}
|
panel={"pin_guard"}
|
||||||
dispatch={dispatch} />
|
dispatch={dispatch} />
|
||||||
<Collapse isOpen={!!pin_guard}>
|
<Collapse isOpen={!!pin_guard}>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -79,5 +81,5 @@ export function PinGuard(props: PinGuardProps) {
|
||||||
resources={resources}
|
resources={resources}
|
||||||
sourceFwConfig={sourceFwConfig} />
|
sourceFwConfig={sourceFwConfig} />
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</section>;
|
</Highlight>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { Row, Col, Help } from "../../../ui/index";
|
||||||
|
import { Position } from "@blueprintjs/core";
|
||||||
|
import { DeviceSetting } from "../../../constants";
|
||||||
|
import { Highlight } from "../maybe_highlight";
|
||||||
|
import { t } from "../../../i18next_wrapper";
|
||||||
|
|
||||||
|
export interface SingleSettingRowProps {
|
||||||
|
label: DeviceSetting;
|
||||||
|
tooltip: string;
|
||||||
|
children: React.ReactChild;
|
||||||
|
settingType: "button" | "input";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SingleSettingRow =
|
||||||
|
({ label, tooltip, settingType, children }: SingleSettingRowProps) =>
|
||||||
|
<Row>
|
||||||
|
<Highlight settingName={label}>
|
||||||
|
<Col xs={6} className={"widget-body-tooltips"}>
|
||||||
|
<label>{t(label)}</label>
|
||||||
|
<Help text={tooltip} requireClick={true} position={Position.RIGHT} />
|
||||||
|
</Col>
|
||||||
|
{settingType === "button"
|
||||||
|
? <Col xs={2} className={"centered-button-div"}>{children}</Col>
|
||||||
|
: <Col xs={6}>{children}</Col>}
|
||||||
|
</Highlight>
|
||||||
|
</Row>;
|
|
@ -1,40 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { getDevice } from "../../../device";
|
|
||||||
import { Axis } from "../../interfaces";
|
|
||||||
import { ToolTips } from "../../../constants";
|
|
||||||
import { Row, Col, Help } from "../../../ui/index";
|
|
||||||
import { ZeroRowProps } from "../interfaces";
|
|
||||||
import { commandErr } from "../../actions";
|
|
||||||
import { t } from "../../../i18next_wrapper";
|
|
||||||
import { Position } from "@blueprintjs/core";
|
|
||||||
|
|
||||||
const zero =
|
|
||||||
(axis: Axis) => getDevice().setZero(axis).catch(commandErr("Zeroing"));
|
|
||||||
const AXES: Axis[] = ["x", "y", "z"];
|
|
||||||
|
|
||||||
export function ZeroButton(props: { axis: Axis; disabled: boolean; }) {
|
|
||||||
const { axis, disabled } = props;
|
|
||||||
return <button
|
|
||||||
className="fb-button yellow"
|
|
||||||
disabled={disabled}
|
|
||||||
onClick={() => zero(axis)}>
|
|
||||||
{t("zero {{axis}}", { axis })}
|
|
||||||
</button>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ZeroRow({ botDisconnected }: ZeroRowProps) {
|
|
||||||
return <Row>
|
|
||||||
<Col xs={6} className={"widget-body-tooltips"}>
|
|
||||||
<label>
|
|
||||||
{t("SET ZERO POSITION")}
|
|
||||||
</label>
|
|
||||||
<Help text={ToolTips.SET_ZERO_POSITION} requireClick={true}
|
|
||||||
position={Position.RIGHT} />
|
|
||||||
</Col>
|
|
||||||
{AXES.map((axis) => {
|
|
||||||
return <Col xs={2} key={axis} className={"centered-button-div"}>
|
|
||||||
<ZeroButton axis={axis} disabled={botDisconnected} />
|
|
||||||
</Col>;
|
|
||||||
})}
|
|
||||||
</Row>;
|
|
||||||
}
|
|
|
@ -1,16 +1,12 @@
|
||||||
import {
|
import {
|
||||||
BotState, Xyz, SourceFwConfig,
|
BotState, Xyz, SourceFwConfig,
|
||||||
ControlPanelState, ShouldDisplay
|
ControlPanelState, Axis
|
||||||
} from "../interfaces";
|
} from "../interfaces";
|
||||||
import { McuParamName, McuParams, FirmwareHardware } from "farmbot/dist";
|
import { McuParamName, McuParams, FirmwareHardware } from "farmbot/dist";
|
||||||
import { IntegerSize } from "../../util";
|
import { IntegerSize } from "../../util";
|
||||||
import { FirmwareConfig } from "farmbot/dist/resources/configs/firmware";
|
import { FirmwareConfig } from "farmbot/dist/resources/configs/firmware";
|
||||||
import { ResourceIndex } from "../../resources/interfaces";
|
import { ResourceIndex } from "../../resources/interfaces";
|
||||||
|
import { DeviceSetting } from "../../constants";
|
||||||
export interface HomingRowProps {
|
|
||||||
hardware: McuParams;
|
|
||||||
botDisconnected: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ZeroRowProps {
|
export interface ZeroRowProps {
|
||||||
botDisconnected: boolean;
|
botDisconnected: boolean;
|
||||||
|
@ -19,16 +15,18 @@ export interface ZeroRowProps {
|
||||||
export interface HomingAndCalibrationProps {
|
export interface HomingAndCalibrationProps {
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
bot: BotState;
|
bot: BotState;
|
||||||
|
controlPanelState: ControlPanelState;
|
||||||
sourceFwConfig: SourceFwConfig;
|
sourceFwConfig: SourceFwConfig;
|
||||||
firmwareConfig: FirmwareConfig | undefined;
|
firmwareConfig: FirmwareConfig | undefined;
|
||||||
botDisconnected: boolean;
|
botDisconnected: boolean;
|
||||||
|
firmwareHardware: FirmwareHardware | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BooleanMCUInputGroupProps {
|
export interface BooleanMCUInputGroupProps {
|
||||||
sourceFwConfig: SourceFwConfig;
|
sourceFwConfig: SourceFwConfig;
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
tooltip: string;
|
tooltip: string;
|
||||||
name: string;
|
label: DeviceSetting;
|
||||||
x: McuParamName;
|
x: McuParamName;
|
||||||
y: McuParamName;
|
y: McuParamName;
|
||||||
z: McuParamName;
|
z: McuParamName;
|
||||||
|
@ -39,15 +37,20 @@ export interface BooleanMCUInputGroupProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CalibrationRowProps {
|
export interface CalibrationRowProps {
|
||||||
|
type: "find_home" | "calibrate" | "zero";
|
||||||
hardware: McuParams;
|
hardware: McuParams;
|
||||||
botDisconnected: boolean;
|
botDisconnected: boolean;
|
||||||
|
action(axis: Axis): void;
|
||||||
|
toolTip: string;
|
||||||
|
title: DeviceSetting;
|
||||||
|
axisTitle: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NumericMCUInputGroupProps {
|
export interface NumericMCUInputGroupProps {
|
||||||
sourceFwConfig: SourceFwConfig;
|
sourceFwConfig: SourceFwConfig;
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
tooltip: string;
|
tooltip: string;
|
||||||
name: string;
|
label: DeviceSetting;
|
||||||
x: McuParamName;
|
x: McuParamName;
|
||||||
xScale?: number;
|
xScale?: number;
|
||||||
y: McuParamName;
|
y: McuParamName;
|
||||||
|
@ -85,12 +88,29 @@ export interface MotorsProps {
|
||||||
|
|
||||||
export interface EncodersProps {
|
export interface EncodersProps {
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
shouldDisplay: ShouldDisplay;
|
|
||||||
controlPanelState: ControlPanelState;
|
controlPanelState: ControlPanelState;
|
||||||
sourceFwConfig: SourceFwConfig;
|
sourceFwConfig: SourceFwConfig;
|
||||||
firmwareHardware: FirmwareHardware | undefined;
|
firmwareHardware: FirmwareHardware | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EndStopsProps {
|
||||||
|
dispatch: Function;
|
||||||
|
controlPanelState: ControlPanelState;
|
||||||
|
sourceFwConfig: SourceFwConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ErrorHandlingProps {
|
||||||
|
dispatch: Function;
|
||||||
|
controlPanelState: ControlPanelState;
|
||||||
|
sourceFwConfig: SourceFwConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PinBindingsProps {
|
||||||
|
dispatch: Function;
|
||||||
|
controlPanelState: ControlPanelState;
|
||||||
|
resources: ResourceIndex;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DangerZoneProps {
|
export interface DangerZoneProps {
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
controlPanelState: ControlPanelState;
|
controlPanelState: ControlPanelState;
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ControlPanelState } from "../interfaces";
|
||||||
|
import { toggleControlPanel } from "../actions";
|
||||||
|
import { urlFriendly } from "../../util";
|
||||||
|
import { DeviceSetting } from "../../constants";
|
||||||
|
|
||||||
|
const HOMING_PANEL = [
|
||||||
|
DeviceSetting.homingAndCalibration,
|
||||||
|
DeviceSetting.homing,
|
||||||
|
DeviceSetting.calibration,
|
||||||
|
DeviceSetting.setZeroPosition,
|
||||||
|
DeviceSetting.findHomeOnBoot,
|
||||||
|
DeviceSetting.stopAtHome,
|
||||||
|
DeviceSetting.stopAtMax,
|
||||||
|
DeviceSetting.negativeCoordinatesOnly,
|
||||||
|
DeviceSetting.axisLength,
|
||||||
|
];
|
||||||
|
const MOTORS_PANEL = [
|
||||||
|
DeviceSetting.motors,
|
||||||
|
DeviceSetting.maxSpeed,
|
||||||
|
DeviceSetting.homingSpeed,
|
||||||
|
DeviceSetting.minimumSpeed,
|
||||||
|
DeviceSetting.accelerateFor,
|
||||||
|
DeviceSetting.stepsPerMm,
|
||||||
|
DeviceSetting.microstepsPerStep,
|
||||||
|
DeviceSetting.alwaysPowerMotors,
|
||||||
|
DeviceSetting.invertMotors,
|
||||||
|
DeviceSetting.motorCurrent,
|
||||||
|
DeviceSetting.enable2ndXMotor,
|
||||||
|
DeviceSetting.invert2ndXMotor,
|
||||||
|
];
|
||||||
|
const ENCODERS_PANEL = [
|
||||||
|
DeviceSetting.encoders,
|
||||||
|
DeviceSetting.stallDetection,
|
||||||
|
DeviceSetting.enableEncoders,
|
||||||
|
DeviceSetting.enableStallDetection,
|
||||||
|
DeviceSetting.stallSensitivity,
|
||||||
|
DeviceSetting.useEncodersForPositioning,
|
||||||
|
DeviceSetting.invertEncoders,
|
||||||
|
DeviceSetting.maxMissedSteps,
|
||||||
|
DeviceSetting.missedStepDecay,
|
||||||
|
DeviceSetting.encoderScaling,
|
||||||
|
];
|
||||||
|
const ENDSTOPS_PANEL = [
|
||||||
|
DeviceSetting.endstops,
|
||||||
|
DeviceSetting.enableEndstops,
|
||||||
|
DeviceSetting.swapEndstops,
|
||||||
|
DeviceSetting.invertEndstops,
|
||||||
|
];
|
||||||
|
const ERROR_HANDLING_PANEL = [
|
||||||
|
DeviceSetting.errorHandling,
|
||||||
|
DeviceSetting.timeoutAfter,
|
||||||
|
DeviceSetting.maxRetries,
|
||||||
|
DeviceSetting.estopOnMovementError,
|
||||||
|
];
|
||||||
|
const PIN_GUARD_PANEL = [
|
||||||
|
DeviceSetting.pinGuard,
|
||||||
|
];
|
||||||
|
const DANGER_ZONE_PANEL = [
|
||||||
|
DeviceSetting.dangerZone,
|
||||||
|
DeviceSetting.resetHardwareParams,
|
||||||
|
];
|
||||||
|
const PIN_BINDINGS_PANEL = [
|
||||||
|
DeviceSetting.pinBindings,
|
||||||
|
];
|
||||||
|
const POWER_AND_RESET_PANEL = [
|
||||||
|
DeviceSetting.powerAndReset,
|
||||||
|
DeviceSetting.restartFarmbot,
|
||||||
|
DeviceSetting.shutdownFarmbot,
|
||||||
|
DeviceSetting.restartFirmware,
|
||||||
|
DeviceSetting.factoryReset,
|
||||||
|
DeviceSetting.autoFactoryReset,
|
||||||
|
DeviceSetting.connectionAttemptPeriod,
|
||||||
|
DeviceSetting.changeOwnership,
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Look up parent panels for settings. */
|
||||||
|
const SETTING_PANEL_LOOKUP = {} as Record<DeviceSetting, keyof ControlPanelState>;
|
||||||
|
HOMING_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "homing_and_calibration");
|
||||||
|
MOTORS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "motors");
|
||||||
|
ENCODERS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "encoders");
|
||||||
|
ENDSTOPS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "endstops");
|
||||||
|
ERROR_HANDLING_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "error_handling");
|
||||||
|
PIN_GUARD_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "pin_guard");
|
||||||
|
DANGER_ZONE_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "danger_zone");
|
||||||
|
PIN_BINDINGS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "pin_bindings");
|
||||||
|
POWER_AND_RESET_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "power_and_reset");
|
||||||
|
|
||||||
|
/** Look up parent panels for settings using URL-friendly names. */
|
||||||
|
const URL_FRIENDLY_LOOKUP: Record<string, keyof ControlPanelState> = {};
|
||||||
|
Object.entries(SETTING_PANEL_LOOKUP).map(([setting, panel]) =>
|
||||||
|
URL_FRIENDLY_LOOKUP[urlFriendly(setting)] = panel);
|
||||||
|
|
||||||
|
/** Look up all relevant names for the same setting. */
|
||||||
|
const ALTERNATE_NAMES =
|
||||||
|
Object.values(DeviceSetting).reduce((acc, s) => { acc[s] = [s]; return acc; },
|
||||||
|
{} as Record<DeviceSetting, DeviceSetting[]>);
|
||||||
|
ALTERNATE_NAMES[DeviceSetting.encoders].push(DeviceSetting.stallDetection);
|
||||||
|
ALTERNATE_NAMES[DeviceSetting.stallDetection].push(DeviceSetting.encoders);
|
||||||
|
|
||||||
|
/** Generate array of names for the same setting. Most only have one. */
|
||||||
|
const compareValues = (settingName: DeviceSetting) =>
|
||||||
|
(ALTERNATE_NAMES[settingName]).map(s => urlFriendly(s));
|
||||||
|
|
||||||
|
/** Retrieve a highlight search term. */
|
||||||
|
const getHighlightName = () => location.search.split("?highlight=").pop();
|
||||||
|
|
||||||
|
/** Only open panel and highlight once per app load. Exported for tests. */
|
||||||
|
export const highlight = { opened: false, highlighted: false };
|
||||||
|
|
||||||
|
/** Open a panel if a setting in that panel is highlighted. */
|
||||||
|
export const maybeOpenPanel = (panelState: ControlPanelState) =>
|
||||||
|
(dispatch: Function) => {
|
||||||
|
if (highlight.opened) { return; }
|
||||||
|
const urlFriendlySettingName = urlFriendly(getHighlightName() || "");
|
||||||
|
if (!urlFriendlySettingName) { return; }
|
||||||
|
const panel = URL_FRIENDLY_LOOKUP[urlFriendlySettingName];
|
||||||
|
const panelIsOpen = panelState[panel];
|
||||||
|
if (panelIsOpen) { return; }
|
||||||
|
dispatch(toggleControlPanel(panel));
|
||||||
|
highlight.opened = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Highlight a setting if provided as a search term. */
|
||||||
|
export const maybeHighlight = (settingName: DeviceSetting) => {
|
||||||
|
const item = getHighlightName();
|
||||||
|
if (highlight.highlighted || !item) { return ""; }
|
||||||
|
const isCurrentSetting = compareValues(settingName).includes(item);
|
||||||
|
if (!isCurrentSetting) { return ""; }
|
||||||
|
highlight.highlighted = true;
|
||||||
|
return "highlight";
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface HighlightProps {
|
||||||
|
settingName: DeviceSetting;
|
||||||
|
children: React.ReactChild
|
||||||
|
| React.ReactChild[]
|
||||||
|
| (React.ReactChild | React.ReactChild[])[];
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HighlightState {
|
||||||
|
className: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wrap highlight-able settings. */
|
||||||
|
export class Highlight extends React.Component<HighlightProps, HighlightState> {
|
||||||
|
state: HighlightState = { className: maybeHighlight(this.props.settingName) };
|
||||||
|
|
||||||
|
componentDidMount = () => {
|
||||||
|
if (this.state.className == "highlight") {
|
||||||
|
/** Slowly fades highlight. */
|
||||||
|
this.setState({ className: "unhighlight" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div className={`${this.props.className} ${this.state.className}`}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,48 +3,52 @@ import { McuInputBox } from "./mcu_input_box";
|
||||||
import { NumericMCUInputGroupProps } from "./interfaces";
|
import { NumericMCUInputGroupProps } from "./interfaces";
|
||||||
import { Row, Col, Help } from "../../ui/index";
|
import { Row, Col, Help } from "../../ui/index";
|
||||||
import { Position } from "@blueprintjs/core";
|
import { Position } from "@blueprintjs/core";
|
||||||
|
import { Highlight } from "./maybe_highlight";
|
||||||
|
import { t } from "../../i18next_wrapper";
|
||||||
|
|
||||||
export function NumericMCUInputGroup(props: NumericMCUInputGroupProps) {
|
export function NumericMCUInputGroup(props: NumericMCUInputGroupProps) {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
sourceFwConfig, dispatch, tooltip, name, x, y, z, intSize, gray, float,
|
sourceFwConfig, dispatch, tooltip, label, x, y, z, intSize, gray, float,
|
||||||
} = props;
|
} = props;
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={6} className={"widget-body-tooltips"}>
|
<Highlight settingName={label}>
|
||||||
<label>
|
<Col xs={6} className={"widget-body-tooltips"}>
|
||||||
{name}
|
<label>
|
||||||
</label>
|
{t(label)}
|
||||||
<Help text={tooltip} requireClick={true} position={Position.RIGHT} />
|
</label>
|
||||||
</Col>
|
<Help text={tooltip} requireClick={true} position={Position.RIGHT} />
|
||||||
<Col xs={2}>
|
</Col>
|
||||||
<McuInputBox
|
<Col xs={2}>
|
||||||
setting={x}
|
<McuInputBox
|
||||||
sourceFwConfig={sourceFwConfig}
|
setting={x}
|
||||||
dispatch={dispatch}
|
sourceFwConfig={sourceFwConfig}
|
||||||
intSize={intSize}
|
dispatch={dispatch}
|
||||||
float={float}
|
intSize={intSize}
|
||||||
scale={props.xScale}
|
float={float}
|
||||||
gray={gray?.x} />
|
scale={props.xScale}
|
||||||
</Col>
|
gray={gray?.x} />
|
||||||
<Col xs={2}>
|
</Col>
|
||||||
<McuInputBox
|
<Col xs={2}>
|
||||||
setting={y}
|
<McuInputBox
|
||||||
sourceFwConfig={sourceFwConfig}
|
setting={y}
|
||||||
dispatch={dispatch}
|
sourceFwConfig={sourceFwConfig}
|
||||||
intSize={intSize}
|
dispatch={dispatch}
|
||||||
float={float}
|
intSize={intSize}
|
||||||
scale={props.yScale}
|
float={float}
|
||||||
gray={gray?.y} />
|
scale={props.yScale}
|
||||||
</Col>
|
gray={gray?.y} />
|
||||||
<Col xs={2}>
|
</Col>
|
||||||
<McuInputBox
|
<Col xs={2}>
|
||||||
setting={z}
|
<McuInputBox
|
||||||
sourceFwConfig={sourceFwConfig}
|
setting={z}
|
||||||
dispatch={dispatch}
|
sourceFwConfig={sourceFwConfig}
|
||||||
intSize={intSize}
|
dispatch={dispatch}
|
||||||
float={float}
|
intSize={intSize}
|
||||||
scale={props.zScale}
|
float={float}
|
||||||
gray={gray?.z} />
|
scale={props.zScale}
|
||||||
</Col>
|
gray={gray?.z} />
|
||||||
|
</Col>
|
||||||
|
</Highlight>
|
||||||
</Row>;
|
</Row>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,8 +56,12 @@ const pinNumOrNamedPin =
|
||||||
}
|
}
|
||||||
: pin;
|
: pin;
|
||||||
|
|
||||||
|
const DISABLE_DDI = (): DropDownItem => ({
|
||||||
|
label: t("None"), value: 0
|
||||||
|
});
|
||||||
|
|
||||||
const listItems = (resources: ResourceIndex): DropDownItem[] =>
|
const listItems = (resources: ResourceIndex): DropDownItem[] =>
|
||||||
[...peripheralItems(resources), ...pinDropdowns(n => n)];
|
[DISABLE_DDI(), ...peripheralItems(resources), ...pinDropdowns(n => n)];
|
||||||
|
|
||||||
const peripheralItems = (resources: ResourceIndex): DropDownItem[] => {
|
const peripheralItems = (resources: ResourceIndex): DropDownItem[] => {
|
||||||
const list = selectAllSavedPeripherals(resources)
|
const list = selectAllSavedPeripherals(resources)
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import { Dictionary } from "farmbot";
|
import { Dictionary } from "farmbot";
|
||||||
import { DiagnosticMessages } from "../../constants";
|
import { DiagnosticMessages } from "../../constants";
|
||||||
|
import { docLink } from "../../ui/doc_link";
|
||||||
|
import { trim } from "../../util/util";
|
||||||
|
|
||||||
|
const DiagnosticMessagesWiFiOrConfig =
|
||||||
|
trim(`${DiagnosticMessages.WIFI_OR_CONFIG}
|
||||||
|
${docLink("for-it-security-professionals")}`);
|
||||||
|
|
||||||
// I don't like this at all.
|
// I don't like this at all.
|
||||||
// If anyone has a cleaner solution, I'd love to hear it.
|
// If anyone has a cleaner solution, I'd love to hear it.
|
||||||
|
@ -16,13 +22,13 @@ export const TRUTH_TABLE: Readonly<Dictionary<string | undefined>> = {
|
||||||
// 17: No MQTT connections.
|
// 17: No MQTT connections.
|
||||||
[0b10001]: DiagnosticMessages.NO_WS_AVAILABLE,
|
[0b10001]: DiagnosticMessages.NO_WS_AVAILABLE,
|
||||||
// 24: Browser is connected to API and MQTT.
|
// 24: Browser is connected to API and MQTT.
|
||||||
[0b11000]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b11000]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 9: At least the browser is connected to MQTT.
|
// 9: At least the browser is connected to MQTT.
|
||||||
[0b01001]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b01001]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 8: At least the browser is connected to MQTT.
|
// 8: At least the browser is connected to MQTT.
|
||||||
[0b01000]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b01000]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 25: Farmbot offline.
|
// 25: Farmbot offline.
|
||||||
[0b11001]: DiagnosticMessages.WIFI_OR_CONFIG,
|
[0b11001]: DiagnosticMessagesWiFiOrConfig,
|
||||||
// 2: Browser offline. Farmbot last seen by the API recently.
|
// 2: Browser offline. Farmbot last seen by the API recently.
|
||||||
[0b00010]: DiagnosticMessages.NO_WS_AVAILABLE,
|
[0b00010]: DiagnosticMessages.NO_WS_AVAILABLE,
|
||||||
// 18: Farmbot last seen by the API recently.
|
// 18: Farmbot last seen by the API recently.
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { FarmbotOsSettings } from "./components/farmbot_os_settings";
|
||||||
import { Page, Col, Row } from "../ui/index";
|
import { Page, Col, Row } from "../ui/index";
|
||||||
import { mapStateToProps } from "./state_to_props";
|
import { mapStateToProps } from "./state_to_props";
|
||||||
import { Props } from "./interfaces";
|
import { Props } from "./interfaces";
|
||||||
import { PinBindings } from "./pin_bindings/pin_bindings";
|
|
||||||
import { getStatus } from "../connectivity/reducer_support";
|
import { getStatus } from "../connectivity/reducer_support";
|
||||||
import { isFwHardwareValue } from "./components/firmware_hardware_support";
|
import { isFwHardwareValue } from "./components/firmware_hardware_support";
|
||||||
|
|
||||||
|
@ -48,9 +47,6 @@ export class RawDevices extends React.Component<Props, {}> {
|
||||||
firmwareHardware={firmwareHardware}
|
firmwareHardware={firmwareHardware}
|
||||||
sourceFwConfig={this.props.sourceFwConfig}
|
sourceFwConfig={this.props.sourceFwConfig}
|
||||||
firmwareConfig={this.props.firmwareConfig} />
|
firmwareConfig={this.props.firmwareConfig} />
|
||||||
<PinBindings
|
|
||||||
dispatch={this.props.dispatch}
|
|
||||||
resources={this.props.resources} />
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Page>;
|
</Page>;
|
||||||
|
|
|
@ -93,7 +93,7 @@ export enum Feature {
|
||||||
variables = "variables",
|
variables = "variables",
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Object fetched from FEATURE_MIN_VERSIONS_URL. */
|
/** Object fetched from ExternalUrl.featureMinVersions. */
|
||||||
export type MinOsFeatureLookup = Partial<Record<Feature, string>>;
|
export type MinOsFeatureLookup = Partial<Record<Feature, string>>;
|
||||||
|
|
||||||
export interface BotState {
|
export interface BotState {
|
||||||
|
@ -201,6 +201,7 @@ export interface PeripheralsProps {
|
||||||
peripherals: TaggedPeripheral[];
|
peripherals: TaggedPeripheral[];
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
disabled: boolean | undefined;
|
disabled: boolean | undefined;
|
||||||
|
firmwareHardware: FirmwareHardware | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SensorsProps {
|
export interface SensorsProps {
|
||||||
|
@ -208,6 +209,7 @@ export interface SensorsProps {
|
||||||
sensors: TaggedSensor[];
|
sensors: TaggedSensor[];
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
disabled: boolean | undefined;
|
disabled: boolean | undefined;
|
||||||
|
firmwareHardware: FirmwareHardware | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FarmwareProps {
|
export interface FarmwareProps {
|
||||||
|
@ -245,8 +247,11 @@ export interface HardwareSettingsProps {
|
||||||
export interface ControlPanelState {
|
export interface ControlPanelState {
|
||||||
homing_and_calibration: boolean;
|
homing_and_calibration: boolean;
|
||||||
motors: boolean;
|
motors: boolean;
|
||||||
encoders_and_endstops: boolean;
|
encoders: boolean;
|
||||||
danger_zone: boolean;
|
endstops: boolean;
|
||||||
power_and_reset: boolean;
|
error_handling: boolean;
|
||||||
pin_guard: boolean;
|
pin_guard: boolean;
|
||||||
|
danger_zone: boolean;
|
||||||
|
pin_bindings: boolean;
|
||||||
|
power_and_reset: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { sortByNameAndPin, ButtonPin } from "../list_and_label_support";
|
import { sortByNameAndPin, ButtonPin, getSpecialActionLabel } from "../list_and_label_support";
|
||||||
|
import { PinBindingSpecialAction } from "farmbot/dist/resources/api_resources";
|
||||||
|
|
||||||
describe("sortByNameAndPin()", () => {
|
describe("sortByNameAndPin()", () => {
|
||||||
|
|
||||||
|
@ -26,3 +27,11 @@ describe("sortByNameAndPin()", () => {
|
||||||
sortTest(1, 1, Order.equal); // GPIO 1 == GPIO 1
|
sortTest(1, 1, Order.equal); // GPIO 1 == GPIO 1
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getSpecialActionLabel()", () => {
|
||||||
|
it("handles undefined values", () => {
|
||||||
|
expect(getSpecialActionLabel(undefined)).toEqual("None");
|
||||||
|
expect(getSpecialActionLabel("wrong" as PinBindingSpecialAction))
|
||||||
|
.toEqual("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { PinBindings } from "../pin_bindings";
|
import { PinBindingsContent } from "../pin_bindings";
|
||||||
import { mount } from "enzyme";
|
import { mount } from "enzyme";
|
||||||
import { bot } from "../../../__test_support__/fake_state/bot";
|
import { bot } from "../../../__test_support__/fake_state/bot";
|
||||||
import {
|
import {
|
||||||
|
@ -8,15 +8,15 @@ import {
|
||||||
import {
|
import {
|
||||||
fakeSequence, fakePinBinding
|
fakeSequence, fakePinBinding
|
||||||
} from "../../../__test_support__/fake_state/resources";
|
} from "../../../__test_support__/fake_state/resources";
|
||||||
import { PinBindingsProps } from "../interfaces";
|
import { PinBindingsContentProps } from "../interfaces";
|
||||||
import {
|
import {
|
||||||
SpecialPinBinding,
|
SpecialPinBinding,
|
||||||
PinBindingType,
|
PinBindingType,
|
||||||
PinBindingSpecialAction
|
PinBindingSpecialAction
|
||||||
} from "farmbot/dist/resources/api_resources";
|
} from "farmbot/dist/resources/api_resources";
|
||||||
|
|
||||||
describe("<PinBindings/>", () => {
|
describe("<PinBindingsContent/>", () => {
|
||||||
function fakeProps(): PinBindingsProps {
|
function fakeProps(): PinBindingsContentProps {
|
||||||
const fakeSequence1 = fakeSequence();
|
const fakeSequence1 = fakeSequence();
|
||||||
fakeSequence1.body.id = 1;
|
fakeSequence1.body.id = 1;
|
||||||
fakeSequence1.body.name = "Sequence 1";
|
fakeSequence1.body.name = "Sequence 1";
|
||||||
|
@ -51,8 +51,8 @@ describe("<PinBindings/>", () => {
|
||||||
|
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
const wrapper = mount(<PinBindings {...p} />);
|
const wrapper = mount(<PinBindingsContent {...p} />);
|
||||||
["pin bindings", "pin number", "none", "bind", "stock bindings"]
|
["pin number", "none", "bind", "stock bindings"]
|
||||||
.map(string => expect(wrapper.text().toLowerCase()).toContain(string));
|
.map(string => expect(wrapper.text().toLowerCase()).toContain(string));
|
||||||
["26", "action"].map(string =>
|
["26", "action"].map(string =>
|
||||||
expect(wrapper.text().toLowerCase()).toContain(string));
|
expect(wrapper.text().toLowerCase()).toContain(string));
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
PinBindingSpecialAction
|
PinBindingSpecialAction
|
||||||
} from "farmbot/dist/resources/api_resources";
|
} from "farmbot/dist/resources/api_resources";
|
||||||
|
|
||||||
export interface PinBindingsProps {
|
export interface PinBindingsContentProps {
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
resources: ResourceIndex;
|
resources: ResourceIndex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,14 @@ export const specialActionLabelLookup: { [x: string]: string } = {
|
||||||
|
|
||||||
export const specialActionList: DropDownItem[] =
|
export const specialActionList: DropDownItem[] =
|
||||||
Object.values(PinBindingSpecialAction)
|
Object.values(PinBindingSpecialAction)
|
||||||
|
.filter(action => action != PinBindingSpecialAction.dump_info)
|
||||||
.map((action: PinBindingSpecialAction) =>
|
.map((action: PinBindingSpecialAction) =>
|
||||||
({ label: specialActionLabelLookup[action], value: action }));
|
({ label: specialActionLabelLookup[action], value: action }));
|
||||||
|
|
||||||
|
export const getSpecialActionLabel =
|
||||||
|
(action: PinBindingSpecialAction | undefined) =>
|
||||||
|
specialActionLabelLookup[action || ""] || "";
|
||||||
|
|
||||||
/** Pin numbers for standard buttons. */
|
/** Pin numbers for standard buttons. */
|
||||||
export enum ButtonPin {
|
export enum ButtonPin {
|
||||||
estop = 16,
|
estop = 16,
|
||||||
|
@ -84,17 +89,17 @@ export const piSpi1Pins = [16, 17, 18, 19, 20, 21];
|
||||||
/** Pin numbers used for special purposes by the RPi. (internal pullup, etc.) */
|
/** Pin numbers used for special purposes by the RPi. (internal pullup, etc.) */
|
||||||
export const reservedPiGPIO = piI2c0Pins;
|
export const reservedPiGPIO = piI2c0Pins;
|
||||||
|
|
||||||
const LabeledGpioPins: { [x: number]: string } = {
|
const GPIO_PIN_LABELS = (): { [x: number]: string } => ({
|
||||||
[ButtonPin.estop]: "Button 1: E-STOP",
|
[ButtonPin.estop]: t("Button {{ num }}: E-STOP", { num: 1 }),
|
||||||
[ButtonPin.unlock]: "Button 2: UNLOCK",
|
[ButtonPin.unlock]: t("Button {{ num }}: UNLOCK", { num: 2 }),
|
||||||
[ButtonPin.btn3]: "Button 3",
|
[ButtonPin.btn3]: t("Button {{ num }})", { num: 3 }),
|
||||||
[ButtonPin.btn4]: "Button 4",
|
[ButtonPin.btn4]: t("Button {{ num }}", { num: 4 }),
|
||||||
[ButtonPin.btn5]: "Button 5",
|
[ButtonPin.btn5]: t("Button {{ num }}", { num: 5 }),
|
||||||
};
|
});
|
||||||
|
|
||||||
export const generatePinLabel = (pin: number) =>
|
export const generatePinLabel = (pin: number) =>
|
||||||
LabeledGpioPins[pin]
|
GPIO_PIN_LABELS()[pin]
|
||||||
? `${LabeledGpioPins[pin]} (Pi ${pin})`
|
? `${t(GPIO_PIN_LABELS()[pin])} (Pi ${pin})`
|
||||||
: `Pi GPIO ${pin}`;
|
: `Pi GPIO ${pin}`;
|
||||||
|
|
||||||
/** Raspberry Pi GPIO pin numbers. */
|
/** Raspberry Pi GPIO pin numbers. */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Row, Col, FBSelect, NULL_CHOICE, DropDownItem } from "../../ui";
|
import { Row, Col, FBSelect, DropDownItem } from "../../ui";
|
||||||
import { PinBindingColWidth } from "./pin_bindings";
|
import { PinBindingColWidth } from "./pin_bindings";
|
||||||
import { Popover, Position } from "@blueprintjs/core";
|
import { Popover, Position } from "@blueprintjs/core";
|
||||||
import { RpiGpioDiagram } from "./rpi_gpio_diagram";
|
import { RpiGpioDiagram } from "./rpi_gpio_diagram";
|
||||||
|
@ -13,9 +13,10 @@ import { pinBindingBody } from "./tagged_pin_binding_init";
|
||||||
import { error, warning } from "../../toast/toast";
|
import { error, warning } from "../../toast/toast";
|
||||||
import {
|
import {
|
||||||
validGpioPins, sysBindings, generatePinLabel, RpiPinList,
|
validGpioPins, sysBindings, generatePinLabel, RpiPinList,
|
||||||
bindingTypeLabelLookup, specialActionLabelLookup, specialActionList,
|
bindingTypeLabelLookup, specialActionList,
|
||||||
reservedPiGPIO,
|
reservedPiGPIO,
|
||||||
bindingTypeList
|
bindingTypeList,
|
||||||
|
getSpecialActionLabel
|
||||||
} from "./list_and_label_support";
|
} from "./list_and_label_support";
|
||||||
import { SequenceSelectBox } from "../../sequences/sequence_select_box";
|
import { SequenceSelectBox } from "../../sequences/sequence_select_box";
|
||||||
import { ResourceIndex } from "../../resources/interfaces";
|
import { ResourceIndex } from "../../resources/interfaces";
|
||||||
|
@ -119,8 +120,6 @@ export class PinBindingInputGroup
|
||||||
<BindingTypeDropDown
|
<BindingTypeDropDown
|
||||||
bindingType={bindingType}
|
bindingType={bindingType}
|
||||||
setBindingType={this.setBindingType} />
|
setBindingType={this.setBindingType} />
|
||||||
</Col>
|
|
||||||
<Col xs={PinBindingColWidth.target}>
|
|
||||||
{bindingType == PinBindingType.special
|
{bindingType == PinBindingType.special
|
||||||
? <ActionTargetDropDown
|
? <ActionTargetDropDown
|
||||||
specialActionInput={specialActionInput}
|
specialActionInput={specialActionInput}
|
||||||
|
@ -152,10 +151,10 @@ export const PinNumberInputGroup = (props: {
|
||||||
const selectedPinNumber = isNumber(pinNumberInput) ? {
|
const selectedPinNumber = isNumber(pinNumberInput) ? {
|
||||||
label: generatePinLabel(pinNumberInput),
|
label: generatePinLabel(pinNumberInput),
|
||||||
value: "" + pinNumberInput
|
value: "" + pinNumberInput
|
||||||
} : NULL_CHOICE;
|
} : undefined;
|
||||||
|
|
||||||
return <Row>
|
return <Row>
|
||||||
<Col xs={1}>
|
<Col xs={3}>
|
||||||
<Popover position={Position.TOP}>
|
<Popover position={Position.TOP}>
|
||||||
<i className="fa fa-th-large" />
|
<i className="fa fa-th-large" />
|
||||||
<RpiGpioDiagram
|
<RpiGpioDiagram
|
||||||
|
@ -181,7 +180,7 @@ export const BindingTypeDropDown = (props: {
|
||||||
setBindingType: (ddi: DropDownItem) => void,
|
setBindingType: (ddi: DropDownItem) => void,
|
||||||
}) => {
|
}) => {
|
||||||
const { bindingType, setBindingType } = props;
|
const { bindingType, setBindingType } = props;
|
||||||
return <FBSelect
|
return <FBSelect extraClass={"binding-type-dropdown"}
|
||||||
key={"binding_type_input_" + bindingType}
|
key={"binding_type_input_" + bindingType}
|
||||||
onChange={setBindingType}
|
onChange={setBindingType}
|
||||||
selectedItem={{
|
selectedItem={{
|
||||||
|
@ -213,12 +212,13 @@ export const ActionTargetDropDown = (props: {
|
||||||
const { specialActionInput, setSpecialAction } = props;
|
const { specialActionInput, setSpecialAction } = props;
|
||||||
|
|
||||||
const selectedSpecialAction = specialActionInput ? {
|
const selectedSpecialAction = specialActionInput ? {
|
||||||
label: specialActionLabelLookup[specialActionInput || ""],
|
label: getSpecialActionLabel(specialActionInput),
|
||||||
value: "" + specialActionInput
|
value: "" + specialActionInput
|
||||||
} : NULL_CHOICE;
|
} : undefined;
|
||||||
|
|
||||||
return <FBSelect
|
return <FBSelect
|
||||||
key={"special_action_input_" + specialActionInput}
|
key={"special_action_input_" + specialActionInput}
|
||||||
|
customNullLabel={t("Select an action")}
|
||||||
onChange={setSpecialAction}
|
onChange={setSpecialAction}
|
||||||
selectedItem={selectedSpecialAction}
|
selectedItem={selectedSpecialAction}
|
||||||
list={specialActionList} />;
|
list={specialActionList} />;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Widget, WidgetBody, WidgetHeader, Row, Col } from "../../ui";
|
import { Row, Col, Help } from "../../ui";
|
||||||
import { ToolTips } from "../../constants";
|
import { ToolTips } from "../../constants";
|
||||||
import { selectAllPinBindings } from "../../resources/selectors";
|
import { selectAllPinBindings } from "../../resources/selectors";
|
||||||
import { PinBindingsProps, PinBindingListItems } from "./interfaces";
|
import { PinBindingsContentProps, PinBindingListItems } from "./interfaces";
|
||||||
import { PinBindingsList } from "./pin_bindings_list";
|
import { PinBindingsList } from "./pin_bindings_list";
|
||||||
import { PinBindingInputGroup } from "./pin_binding_input_group";
|
import { PinBindingInputGroup } from "./pin_binding_input_group";
|
||||||
import {
|
import {
|
||||||
|
@ -20,9 +20,8 @@ import { t } from "../../i18next_wrapper";
|
||||||
/** Width of UI columns in Pin Bindings widget. */
|
/** Width of UI columns in Pin Bindings widget. */
|
||||||
export enum PinBindingColWidth {
|
export enum PinBindingColWidth {
|
||||||
pin = 4,
|
pin = 4,
|
||||||
type = 3,
|
type = 6,
|
||||||
target = 4,
|
button = 2
|
||||||
button = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Use binding type to return a sequence ID or a special action. */
|
/** Use binding type to return a sequence ID or a special action. */
|
||||||
|
@ -64,34 +63,29 @@ const PinBindingsListHeader = () =>
|
||||||
<label>
|
<label>
|
||||||
{t("Binding")}
|
{t("Binding")}
|
||||||
</label>
|
</label>
|
||||||
</Col>
|
<Help text={ToolTips.PIN_BINDINGS} />
|
||||||
<Col xs={PinBindingColWidth.target}>
|
|
||||||
<label>
|
|
||||||
{t("target")}
|
|
||||||
</label>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>;
|
</Row>;
|
||||||
|
|
||||||
export const PinBindings = (props: PinBindingsProps) => {
|
export const PinBindingsContent = (props: PinBindingsContentProps) => {
|
||||||
const { dispatch, resources } = props;
|
const { dispatch, resources } = props;
|
||||||
const pinBindings = apiPinBindings(resources);
|
const pinBindings = apiPinBindings(resources);
|
||||||
|
|
||||||
return <Widget className="pin-bindings-widget">
|
return <div className="pin-bindings">
|
||||||
<WidgetHeader
|
<Row>
|
||||||
title={t("Pin Bindings")}
|
<StockPinBindingsButton dispatch={dispatch} />
|
||||||
helpText={ToolTips.PIN_BINDINGS}>
|
|
||||||
<Popover
|
<Popover
|
||||||
position={Position.RIGHT_TOP}
|
position={Position.RIGHT_TOP}
|
||||||
interactionKind={PopoverInteractionKind.HOVER}
|
interactionKind={PopoverInteractionKind.HOVER}
|
||||||
|
portalClassName={"bindings-warning-icon"}
|
||||||
popoverClassName={"help"}>
|
popoverClassName={"help"}>
|
||||||
<i className="fa fa-exclamation-triangle" />
|
<i className="fa fa-exclamation-triangle" />
|
||||||
<div>
|
<div>
|
||||||
{t(ToolTips.PIN_BINDING_WARNING)}
|
{t(ToolTips.PIN_BINDING_WARNING)}
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
<StockPinBindingsButton dispatch={dispatch} />
|
</Row>
|
||||||
</WidgetHeader>
|
<div>
|
||||||
<WidgetBody>
|
|
||||||
<PinBindingsListHeader />
|
<PinBindingsListHeader />
|
||||||
<PinBindingsList
|
<PinBindingsList
|
||||||
pinBindings={pinBindings}
|
pinBindings={pinBindings}
|
||||||
|
@ -101,6 +95,6 @@ export const PinBindings = (props: PinBindingsProps) => {
|
||||||
pinBindings={pinBindings}
|
pinBindings={pinBindings}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
resources={resources} />
|
resources={resources} />
|
||||||
</WidgetBody>
|
</div>
|
||||||
</Widget>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
bindingTypeLabelLookup, specialActionLabelLookup,
|
bindingTypeLabelLookup,
|
||||||
generatePinLabel, sortByNameAndPin
|
generatePinLabel, sortByNameAndPin, getSpecialActionLabel
|
||||||
} from "./list_and_label_support";
|
} from "./list_and_label_support";
|
||||||
import { destroy } from "../../api/crud";
|
import { destroy } from "../../api/crud";
|
||||||
import { error } from "../../toast/toast";
|
import { error } from "../../toast/toast";
|
||||||
|
@ -36,12 +36,10 @@ export const PinBindingsList = (props: PinBindingsListProps) => {
|
||||||
{generatePinLabel(pin_number)}
|
{generatePinLabel(pin_number)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={PinBindingColWidth.type}>
|
<Col xs={PinBindingColWidth.type}>
|
||||||
{t(bindingTypeLabelLookup[binding_type || ""])}
|
{t(bindingTypeLabelLookup[binding_type || ""])}:
|
||||||
</Col>
|
|
||||||
<Col xs={PinBindingColWidth.target}>
|
|
||||||
{sequence_id
|
{sequence_id
|
||||||
? findSequenceById(resources, sequence_id).body.name
|
? findSequenceById(resources, sequence_id).body.name
|
||||||
: t(specialActionLabelLookup[special_action || ""])}
|
: t(getSpecialActionLabel(special_action))}
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={PinBindingColWidth.button}>
|
<Col xs={PinBindingColWidth.button}>
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { maybeNegateStatus } from "../connectivity/maybe_negate_status";
|
||||||
import { ReduxAction } from "../redux/interfaces";
|
import { ReduxAction } from "../redux/interfaces";
|
||||||
import { connectivityReducer, PingResultPayload } from "../connectivity/reducer";
|
import { connectivityReducer, PingResultPayload } from "../connectivity/reducer";
|
||||||
import { versionOK } from "../util";
|
import { versionOK } from "../util";
|
||||||
import { EXPECTED_MAJOR, EXPECTED_MINOR } from "./actions";
|
|
||||||
import { DeepPartial } from "redux";
|
import { DeepPartial } from "redux";
|
||||||
import { incomingLegacyStatus } from "../connectivity/connect_device";
|
import { incomingLegacyStatus } from "../connectivity/connect_device";
|
||||||
import { merge } from "lodash";
|
import { merge } from "lodash";
|
||||||
|
@ -27,7 +26,10 @@ export const initialState = (): BotState => ({
|
||||||
controlPanelState: {
|
controlPanelState: {
|
||||||
homing_and_calibration: false,
|
homing_and_calibration: false,
|
||||||
motors: false,
|
motors: false,
|
||||||
encoders_and_endstops: false,
|
encoders: false,
|
||||||
|
endstops: false,
|
||||||
|
error_handling: false,
|
||||||
|
pin_bindings: false,
|
||||||
danger_zone: false,
|
danger_zone: false,
|
||||||
power_and_reset: false,
|
power_and_reset: false,
|
||||||
pin_guard: false
|
pin_guard: false
|
||||||
|
@ -116,7 +118,10 @@ export const botReducer = generateReducer<BotState>(initialState())
|
||||||
.add<boolean>(Actions.BULK_TOGGLE_CONTROL_PANEL, (s, a) => {
|
.add<boolean>(Actions.BULK_TOGGLE_CONTROL_PANEL, (s, a) => {
|
||||||
s.controlPanelState.homing_and_calibration = a.payload;
|
s.controlPanelState.homing_and_calibration = a.payload;
|
||||||
s.controlPanelState.motors = a.payload;
|
s.controlPanelState.motors = a.payload;
|
||||||
s.controlPanelState.encoders_and_endstops = a.payload;
|
s.controlPanelState.encoders = a.payload;
|
||||||
|
s.controlPanelState.endstops = a.payload;
|
||||||
|
s.controlPanelState.error_handling = a.payload;
|
||||||
|
s.controlPanelState.pin_bindings = a.payload;
|
||||||
s.controlPanelState.pin_guard = a.payload;
|
s.controlPanelState.pin_guard = a.payload;
|
||||||
s.controlPanelState.danger_zone = a.payload;
|
s.controlPanelState.danger_zone = a.payload;
|
||||||
return s;
|
return s;
|
||||||
|
@ -199,8 +204,7 @@ function legacyStatusHandler(state: BotState,
|
||||||
|
|
||||||
const nextSyncStatus = maybeNegateStatus(info);
|
const nextSyncStatus = maybeNegateStatus(info);
|
||||||
|
|
||||||
versionOK(informational_settings.controller_version,
|
versionOK(informational_settings.controller_version);
|
||||||
EXPECTED_MAJOR, EXPECTED_MINOR);
|
|
||||||
state.hardware.informational_settings.sync_status = nextSyncStatus;
|
state.hardware.informational_settings.sync_status = nextSyncStatus;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue