fw param export pop-up

pull/708/head
gabrielburnworth 2018-03-08 20:03:02 -08:00
parent e8a410f217
commit a7cd5e7767
13 changed files with 199 additions and 21 deletions

View File

@ -5,11 +5,13 @@ import {
TaggedTool, TaggedUser, TaggedWebcamFeed,
TaggedPlantPointer, TaggedGenericPointer, TaggedPeripheral, TaggedFbosConfig,
TaggedWebAppConfig,
TaggedSensor
TaggedSensor,
TaggedFirmwareConfig
} from "../../resources/tagged_resources";
import { ExecutableType } from "../../farm_designer/interfaces";
import { fakeResource } from "../fake_resource";
import { emptyToolSlot } from "../../tools/components/empty_tool_slot";
import { FirmwareConfig } from "../../config_storage/firmware_configs";
export let resources: Everything["resources"] = buildResourceIndex();
let idCounter = 1;
@ -201,3 +203,101 @@ export function fakeWebAppConfig(): TaggedWebAppConfig {
discard_unsaved: false
});
}
export function fakeFirmwareConfig(): TaggedFirmwareConfig {
return fakeResource("FirmwareConfig", {
device_id: 1,
created_at: "",
updated_at: "",
encoder_enabled_x: 0,
encoder_enabled_y: 0,
encoder_enabled_z: 0,
encoder_invert_x: 0,
encoder_invert_y: 0,
encoder_invert_z: 0,
encoder_missed_steps_decay_x: 5,
encoder_missed_steps_decay_y: 5,
encoder_missed_steps_decay_z: 5,
encoder_missed_steps_max_x: 5,
encoder_missed_steps_max_y: 5,
encoder_missed_steps_max_z: 5,
encoder_scaling_x: 56,
encoder_scaling_y: 56,
encoder_scaling_z: 56,
encoder_type_x: 0,
encoder_type_y: 0,
encoder_type_z: 0,
encoder_use_for_pos_x: 0,
encoder_use_for_pos_y: 0,
encoder_use_for_pos_z: 0,
movement_axis_nr_steps_x: 0,
movement_axis_nr_steps_y: 0,
movement_axis_nr_steps_z: 0,
movement_enable_endpoints_x: 0,
movement_enable_endpoints_y: 0,
movement_enable_endpoints_z: 0,
movement_home_at_boot_x: 0,
movement_home_at_boot_y: 0,
movement_home_at_boot_z: 0,
movement_home_spd_x: 50,
movement_home_spd_y: 50,
movement_home_spd_z: 50,
movement_home_up_x: 0,
movement_home_up_y: 0,
movement_home_up_z: 1,
movement_invert_endpoints_x: 0,
movement_invert_endpoints_y: 0,
movement_invert_endpoints_z: 0,
movement_invert_motor_x: 0,
movement_invert_motor_y: 0,
movement_invert_motor_z: 0,
movement_keep_active_x: 0,
movement_keep_active_y: 0,
movement_keep_active_z: 1,
movement_max_spd_x: 400,
movement_max_spd_y: 400,
movement_max_spd_z: 400,
movement_min_spd_x: 50,
movement_min_spd_y: 50,
movement_min_spd_z: 50,
movement_secondary_motor_invert_x: 1,
movement_secondary_motor_x: 1,
movement_step_per_mm_x: 5,
movement_step_per_mm_y: 5,
movement_step_per_mm_z: 25,
movement_steps_acc_dec_x: 300,
movement_steps_acc_dec_y: 300,
movement_steps_acc_dec_z: 300,
movement_stop_at_home_x: 0,
movement_stop_at_home_y: 0,
movement_stop_at_home_z: 0,
movement_stop_at_max_x: 0,
movement_stop_at_max_y: 0,
movement_stop_at_max_z: 0,
movement_timeout_x: 120,
movement_timeout_y: 120,
movement_timeout_z: 120,
param_config_ok: 0,
param_e_stop_on_mov_err: 0,
param_mov_nr_retry: 3,
param_test: 0,
param_use_eeprom: 1,
param_version: 1,
pin_guard_1_active_state: 1,
pin_guard_1_pin_nr: 0,
pin_guard_1_time_out: 60,
pin_guard_2_active_state: 1,
pin_guard_2_pin_nr: 0,
pin_guard_2_time_out: 60,
pin_guard_3_active_state: 1,
pin_guard_3_pin_nr: 0,
pin_guard_3_time_out: 60,
pin_guard_4_active_state: 1,
pin_guard_4_pin_nr: 0,
pin_guard_4_time_out: 60,
pin_guard_5_active_state: 1,
pin_guard_5_pin_nr: 0,
pin_guard_5_time_out: 60,
api_migrated: false
} as FirmwareConfig);
}

View File

@ -119,6 +119,8 @@ export class API {
get webAppConfigPath() { return `${this.baseUrl}/api/web_app_config/`; }
/** /api/fbos_config */
get fbosConfigPath() { return `${this.baseUrl}/api/fbos_config/`; }
/** /api/firmware_config */
get firmwareConfigPath() { return `${this.baseUrl}/api/firmware_config/`; }
/** /api/sensor_readings */
get sensorReadingPath() { return `${this.baseUrl}/api/sensor_readings`; }
/** /api/sensor_readings */

View File

@ -219,7 +219,8 @@ export function urlFor(tag: ResourceName) {
Log: API.current.logsPath,
WebcamFeed: API.current.webcamFeedPath,
FbosConfig: API.current.fbosConfigPath,
WebAppConfig: API.current.webAppConfigPath
WebAppConfig: API.current.webAppConfigPath,
FirmwareConfig: API.current.firmwareConfigPath,
};
const url = OPTIONS[tag];
if (url) {
@ -230,7 +231,8 @@ export function urlFor(tag: ResourceName) {
}
}
const SINGULAR_RESOURCE: ResourceName[] = ["WebAppConfig", "FbosConfig"];
const SINGULAR_RESOURCE: ResourceName[] =
["WebAppConfig", "FbosConfig", "FirmwareConfig"];
/** Shared functionality in create() and update(). */
function updateViaAjax(payl: AjaxUpdatePayload) {

View File

@ -228,6 +228,20 @@ a {
text-align: center;
padding: 0.25rem;
}
.fa-download {
margin-top: -20px;
float: right;
color: $dark_gray !important;
}
}
.firmware-setting-export-menu {
button {
margin-bottom: 1rem;
}
ul {
font-size: 0.3rem;
}
}
.change-ownership-form {

View File

@ -25,7 +25,8 @@ describe("<Devices/>", () => {
dispatch: jest.fn(),
resources: buildResourceIndex(FAKE_RESOURCES).index,
sourceFbosConfig: jest.fn(),
shouldDisplay: jest.fn()
shouldDisplay: jest.fn(),
firmwareConfig: undefined,
});
it("renders relevant panels", () => {

View File

@ -5,6 +5,7 @@ const mockImages: TaggedImage | undefined = fakeImage();
jest.mock("../../resources/selectors_by_kind", () => ({
getFbosConfig: () => mockFbosConfig,
getFirmwareConfig: () => undefined,
selectAllImages: () => [mockImages],
}));

View File

@ -1,10 +1,11 @@
import * as React from "react";
import { mount } from "enzyme";
import { HardwareSettings } from "../hardware_settings";
import { mount, shallow } from "enzyme";
import { HardwareSettings, FwParamExportMenu } from "../hardware_settings";
import { HardwareSettingsProps } from "../../interfaces";
import { Actions } from "../../../constants";
import { bot } from "../../../__test_support__/fake_state/bot";
import { panelState } from "../../../__test_support__/control_panel_state";
import { fakeFirmwareConfig } from "../../../__test_support__/fake_state/resources";
describe("<HardwareSettings />", () => {
beforeEach(() => {
@ -19,12 +20,13 @@ describe("<HardwareSettings />", () => {
dispatch: jest.fn(),
sourceFbosConfig: (x) => {
return { value: bot.hardware.configuration[x], consistent: true };
}
},
firmwareConfig: undefined,
};
};
it("renders", () => {
const wrapper = mount(<HardwareSettings {...fakeProps() } />);
const wrapper = mount(<HardwareSettings {...fakeProps()} />);
["expand all", "x axis", "motors"].map(string =>
expect(wrapper.text().toLowerCase()).toContain(string));
});
@ -57,4 +59,25 @@ describe("<HardwareSettings />", () => {
checkDispatch("h4", 1, "motors",
Actions.TOGGLE_CONTROL_PANEL_OPTION, "motors");
});
it("shows param export menu", () => {
const p = fakeProps();
p.firmwareConfig = fakeFirmwareConfig().body;
p.firmwareConfig.api_migrated = true;
const wrapper = shallow(<HardwareSettings {...p} />);
expect(wrapper.find("Popover").length).toEqual(1);
});
it("doesn't show param export menu", () => {
const wrapper = shallow(<HardwareSettings {...fakeProps()} />);
expect(wrapper.find("Popover").length).toEqual(0);
});
});
describe("<FwParamExportMenu />", () => {
it("lists all params", () => {
const config = fakeFirmwareConfig().body;
const wrapper = mount(<FwParamExportMenu firmwareConfig={config} />);
expect(wrapper.text()).toContain("movement_max_spd_");
});
});

View File

@ -14,6 +14,22 @@ import {
HomingAndCalibration
} from "./hardware_settings/homing_and_calibration";
import { SpecialStatus } from "../../resources/tagged_resources";
import { Popover, Position } from "@blueprintjs/core";
import { FirmwareConfig } from "../../config_storage/firmware_configs";
import { pickBy } from "lodash";
export const FwParamExportMenu = (props: { firmwareConfig: FirmwareConfig }) => {
const filteredConfig = pickBy(props.firmwareConfig, (value, key) =>
!["id", "device_id", "api_migrated", "created_at", "updated_at",
"param_test", "param_version"]
.includes(key));
return <div className={"firmware-setting-export-menu"}>
<ul>
{Object.entries(filteredConfig).map(([param, value]) =>
<li key={param}>{param}: {value}</li>)}
</ul>
</div>;
};
export class HardwareSettings extends
React.Component<HardwareSettingsProps, {}> {
@ -41,12 +57,17 @@ export class HardwareSettings extends
className={"fb-button gray no-float"}
onClick={() => dispatch(bulkToggleControlPanel(true))}>
{t("Expand All")}
</button>
</button>
<button
className={"fb-button gray no-float"}
onClick={() => dispatch(bulkToggleControlPanel(false))}>
{t("Collapse All")}
</button>
</button>
{this.props.firmwareConfig &&
<Popover position={Position.BOTTOM_RIGHT}>
<i className="fa fa-download" />
<FwParamExportMenu firmwareConfig={this.props.firmwareConfig} />
</Popover>}
<MustBeOnline
networkState={this.props.botToMqttStatus}
syncStatus={sync_status}

View File

@ -85,7 +85,8 @@ export class Devices extends React.Component<Props, {}> {
dispatch={this.props.dispatch}
bot={this.props.bot}
botToMqttStatus={botToMqttStatus}
sourceFbosConfig={this.props.sourceFbosConfig} />
sourceFbosConfig={this.props.sourceFbosConfig}
firmwareConfig={this.props.firmwareConfig} />
{this.props.bot.hardware.gpio_registry &&
<PinBindings
dispatch={this.props.dispatch}

View File

@ -18,6 +18,7 @@ import { WD_ENV } from "../farmware/weed_detector/remote_env/interfaces";
import { ConnectionStatus, ConnectionState, NetworkState } from "../connectivity/interfaces";
import { IntegerSize } from "../util";
import { WebAppConfig } from "../config_storage/web_app_configs";
import { FirmwareConfig } from "../config_storage/firmware_configs";
export interface Props {
userToApi: ConnectionStatus | undefined;
@ -31,6 +32,7 @@ export interface Props {
resources: ResourceIndex;
sourceFbosConfig: SourceFbosConfig;
shouldDisplay: ShouldDisplay;
firmwareConfig: FirmwareConfig | undefined;
}
export type SourceFbosConfig = (config: ConfigurationName) =>
@ -190,6 +192,7 @@ export interface HardwareSettingsProps {
botToMqttStatus: NetworkState;
bot: BotState;
sourceFbosConfig: SourceFbosConfig;
firmwareConfig: FirmwareConfig | undefined;
}
export interface ControlPanelState {

View File

@ -3,17 +3,23 @@ import { Props } from "./interfaces";
import {
selectAllImages,
getDeviceAccountSettings,
maybeGetDevice
maybeGetDevice,
getFirmwareConfig
} from "../resources/selectors";
import { sourceFbosConfigValue } from "./components/source_fbos_config_value";
import { getFbosConfig } from "../resources/selectors_by_kind";
import { determineInstalledOsVersion, shouldDisplay } from "../util";
export function mapStateToProps(props: Everything): Props {
const conf = getFbosConfig(props.resources.index);
const { hardware } = props.bot;
const fbosConfig = (conf && conf.body && conf.body.api_migrated)
? conf.body : undefined;
const maybeFbosConfig = getFbosConfig(props.resources.index);
const fbosConfig = maybeFbosConfig && maybeFbosConfig.body.api_migrated
? maybeFbosConfig.body
: undefined;
const maybeFirmwareConfig = getFirmwareConfig(props.resources.index);
const firmwareConfig = maybeFirmwareConfig && maybeFirmwareConfig.body.api_migrated
? maybeFirmwareConfig.body
: undefined;
const installedOsVersion = determineInstalledOsVersion(
props.bot, maybeGetDevice(props.resources.index));
return {
@ -28,5 +34,6 @@ export function mapStateToProps(props: Everything): Props {
resources: props.resources.index,
sourceFbosConfig: sourceFbosConfigValue(fbosConfig, hardware.configuration),
shouldDisplay: shouldDisplay(installedOsVersion, props.bot.minOsFeatureData),
firmwareConfig,
};
}

View File

@ -17,6 +17,7 @@ import {
TaggedSensor,
TaggedPeripheral,
TaggedWebAppConfig,
TaggedFirmwareConfig,
} from "./tagged_resources";
import { sortResourcesById } from "../util";
import { error } from "farmbot-toastr";
@ -76,9 +77,9 @@ export const selectAllWebcamFeeds =
(i: ResourceIndex) => findAll<TaggedWebcamFeed>(i, "WebcamFeed");
export const getAllSavedPeripherals =
(input: ResourceIndex) => selectAllPeripherals(input).filter(isSaved);
export const getFbosConfig =
(i: ResourceIndex): TaggedFbosConfig | undefined => findAll<TaggedFbosConfig>(i, "FbosConfig")[0];
export const getWebAppConfig = (i: ResourceIndex): TaggedWebAppConfig | undefined => {
return findAll<TaggedWebAppConfig>(i, "WebAppConfig")[0];
};
export const getFbosConfig = (i: ResourceIndex): TaggedFbosConfig | undefined =>
findAll<TaggedFbosConfig>(i, "FbosConfig")[0];
export const getWebAppConfig = (i: ResourceIndex): TaggedWebAppConfig | undefined =>
findAll<TaggedWebAppConfig>(i, "WebAppConfig")[0];
export const getFirmwareConfig = (i: ResourceIndex): TaggedFirmwareConfig | undefined =>
findAll<TaggedFirmwareConfig>(i, "FirmwareConfig")[0];

View File

@ -16,6 +16,7 @@ import { WebAppConfig } from "../config_storage/web_app_configs";
import { Session } from "../session";
import { FbosConfig } from "../config_storage/fbos_configs";
import { FarmwareInstallation } from "../farmware/interfaces";
import { FirmwareConfig } from "../config_storage/firmware_configs";
export interface ResourceReadyPayl {
name: ResourceName;
@ -44,6 +45,7 @@ export function fetchSyncData(dispatch: Function) {
fetch<WebcamFeed>("WebcamFeed", API.current.webcamFeedPath);
fetch<FbosConfig>("FbosConfig", API.current.fbosConfigPath);
fetch<WebAppConfig>("WebAppConfig", API.current.webAppConfigPath);
fetch<FirmwareConfig>("FirmwareConfig", API.current.firmwareConfigPath);
fetch<FarmEvent[]>("FarmEvent", API.current.farmEventsPath);
fetch<Image[]>("Image", API.current.imagesPath);
fetch<Log[]>("Log", API.current.logsPath);