Skip to content

Commit

Permalink
Merge pull request #281 from justinlampley/ForceUpload
Browse files Browse the repository at this point in the history
Add Force Upload Support
  • Loading branch information
jurgelenas authored Mar 4, 2022
2 parents 5b78122 + 2ca6e48 commit 6ad1aa7
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 17 deletions.
12 changes: 12 additions & 0 deletions graphql.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2329,6 +2329,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "TargetMismatch",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
Expand Down Expand Up @@ -2455,6 +2461,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "ForceFlash",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
Expand Down
12 changes: 10 additions & 2 deletions src/api/src/library/FirmwareBuilder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from 'path';
import Platformio from '../Platformio';
import { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander';
import UserDefineKey from './Enum/UserDefineKey';
import UploadType from '../Platformio/Enum/UploadType';

interface UserDefinesCompatiblityResult {
compatible: boolean;
Expand Down Expand Up @@ -75,9 +76,16 @@ export default class FirmwareBuilder {
userDefines: string,
firmwarePath: string,
serialPort: string | undefined,
onOutput: OnOutputFunc = NoOpFunc
onOutput: OnOutputFunc = NoOpFunc,
uploadType: UploadType
): Promise<CommandResult> {
await this.storeUserDefines(firmwarePath, userDefines);
return this.platformio.flash(firmwarePath, target, serialPort, onOutput);
return this.platformio.flash(
firmwarePath,
target,
serialPort,
onOutput,
uploadType
);
}
}
6 changes: 6 additions & 0 deletions src/api/src/library/Platformio/Enum/UploadType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
enum UploadType {
Normal = 'upload',
Force = 'uploadforce',
}

export default UploadType;
6 changes: 4 additions & 2 deletions src/api/src/library/Platformio/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from 'path';
import child_process from 'child_process';
import Commander, { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander';
import { LoggerService } from '../../logger';
import UploadType from './Enum/UploadType';

interface PlatformioCoreState {
core_version: string;
Expand Down Expand Up @@ -229,7 +230,8 @@ export default class Platformio {
projectDir: string,
environment: string,
serialPort: string | undefined,
onUpdate: OnOutputFunc = NoOpFunc
onUpdate: OnOutputFunc = NoOpFunc,
uploadType: UploadType
) {
const params = [
'run',
Expand All @@ -238,7 +240,7 @@ export default class Platformio {
'--environment',
environment,
'--target',
'upload',
uploadType,
];
if (serialPort !== undefined && serialPort !== null) {
params.push('--upload-port');
Expand Down
1 change: 1 addition & 0 deletions src/api/src/models/enum/BuildFirmwareErrorType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ enum BuildFirmwareErrorType {
BuildError = 'BuildError',
FlashError = 'FlashError',
GenericError = 'GenericError',
TargetMismatch = 'TargetMismatch',
}

registerEnumType(BuildFirmwareErrorType, {
Expand Down
1 change: 1 addition & 0 deletions src/api/src/models/enum/BuildJobType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { registerEnumType } from 'type-graphql';
enum BuildJobType {
Build = 'Build',
BuildAndFlash = 'BuildAndFlash',
ForceFlash = 'ForceFlash',
}

registerEnumType(BuildJobType, {
Expand Down
32 changes: 29 additions & 3 deletions src/api/src/services/Firmware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import FirmwareBuilder from '../../library/FirmwareBuilder';
import { LoggerService } from '../../logger';
import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey';
import PullRequest from '../../models/PullRequest';
import UploadType from '../../library/Platformio/Enum/UploadType';

interface FirmwareVersionData {
source: FirmwareSource;
Expand Down Expand Up @@ -454,29 +455,54 @@ export default class FirmwareService {
);
}

if (params.type === BuildJobType.BuildAndFlash) {
if (
params.type === BuildJobType.BuildAndFlash ||
params.type === BuildJobType.ForceFlash
) {
await this.updateProgress(
BuildProgressNotificationType.Info,
BuildFirmwareStep.FLASHING_FIRMWARE
);

let uploadType: UploadType;
switch (params.type) {
case BuildJobType.BuildAndFlash:
uploadType = UploadType.Normal;
break;
case BuildJobType.ForceFlash:
uploadType = UploadType.Force;
break;
default:
throw new Error(`Unknown build job type ${params.type}`);
}

const flashResult = await this.builder.flash(
params.target,
userDefines,
firmwarePath,
params.serialDevice,
(output) => {
this.updateLogs(output);
}
},
uploadType
);
if (!flashResult.success) {
this.logger?.error('flash error', undefined, {
stderr: flashResult.stderr,
stdout: flashResult.stdout,
});
const uploadErrorRegexp = /\*\*\* \[upload\] Error (-*\d+)/g;
let uploadError = 0;
const matches = [...flashResult.stderr.matchAll(uploadErrorRegexp)];
if (matches.length > 0) {
uploadError = Number.parseInt(matches[0][1], 10);
}
return new BuildFlashFirmwareResult(
false,
flashResult.stderr,
BuildFirmwareErrorType.FlashError
uploadError === -2
? BuildFirmwareErrorType.TargetMismatch
: BuildFirmwareErrorType.FlashError
);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/ui/components/BuildProgressBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const BuildProgressBar: FunctionComponent<BuildProgressBarProps> = memo(
}
break;
case BuildJobType.BuildAndFlash:
case BuildJobType.ForceFlash:
switch (notification.step) {
case BuildFirmwareStep.VERIFYING_BUILD_SYSTEM:
return 5;
Expand Down
2 changes: 2 additions & 0 deletions src/ui/components/BuildResponse/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const BuildResponse: FunctionComponent<BuildResponseProps> = memo(
return 'Build error';
case BuildFirmwareErrorType.FlashError:
return 'Flash error';
case BuildFirmwareErrorType.TargetMismatch:
return 'The target you are trying to flash does not match the devices current target, if you are sure you want to do this, click Force Flash below';
default:
return '';
}
Expand Down
112 changes: 112 additions & 0 deletions src/ui/components/SplitButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as React from 'react';
import Button from '@mui/material/Button';
import ButtonGroup, { ButtonGroupProps } from '@mui/material/ButtonGroup';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import Grow from '@mui/material/Grow';
import Paper from '@mui/material/Paper';
import Popper from '@mui/material/Popper';
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
import { FunctionComponent, useState } from 'react';
import { uniqueId } from 'lodash';

export interface Option {
label: string;
value: string;
}

interface SplitButtonProps extends ButtonGroupProps {
options: Option[];
onButtonClick: (value: string | null) => void;
}

const SplitButton: FunctionComponent<SplitButtonProps> = ({
options,
onButtonClick,
...props
}) => {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef<HTMLDivElement | null>(null);
const [selectedIndex, setSelectedIndex] = React.useState(0);
const [splitButtonMenuId] = useState(() => uniqueId('split-button-menu-'));

const handleClick = () => {
onButtonClick(options[selectedIndex].value);
};

const handleMenuItemClick = (event: any, index: number) => {
setSelectedIndex(index);
setOpen(false);
};

const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};

const handleClose = (event: any) => {
if (
anchorRef.current &&
event.target &&
anchorRef.current.contains(event.target)
) {
return;
}

setOpen(false);
};

return (
<>
<ButtonGroup {...props} ref={anchorRef}>
<Button onClick={handleClick}>{options[selectedIndex].label}</Button>
<Button
size="small"
aria-controls={open ? splitButtonMenuId : undefined}
aria-expanded={open ? 'true' : undefined}
aria-label="select button"
aria-haspopup="menu"
onClick={handleToggle}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
style={{ zIndex: 2 }}
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id={splitButtonMenuId}>
{options.map((option, index) => (
<MenuItem
key={option.value}
selected={index === selectedIndex}
onClick={(event) => handleMenuItemClick(event, index)}
>
{option.label}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
);
};

export default SplitButton;
2 changes: 2 additions & 0 deletions src/ui/gql/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ export enum BuildFirmwareErrorType {
BuildError = 'BuildError',
FlashError = 'FlashError',
GenericError = 'GenericError',
TargetMismatch = 'TargetMismatch',
}

export type BuildFlashFirmwareInput = {
Expand All @@ -309,6 +310,7 @@ export type BuildFlashFirmwareInput = {
export enum BuildJobType {
Build = 'Build',
BuildAndFlash = 'BuildAndFlash',
ForceFlash = 'ForceFlash',
}

export type FirmwareVersionDataInput = {
Expand Down
Loading

0 comments on commit 6ad1aa7

Please sign in to comment.