New probing experience complete.
This commit is contained in:
@@ -11,13 +11,6 @@ script#settings-view-template(type="text/x-template")
|
||||
option(value="METRIC") METRIC
|
||||
option(value="IMPERIAL") IMPERIAL
|
||||
|
||||
|
||||
fieldset
|
||||
h2 Probing safety prompts
|
||||
templated-input(name="probing-prompts",
|
||||
:model.sync="config.settings['probing-prompts']",
|
||||
:template="template.settings['probing-prompts']")
|
||||
|
||||
fieldset
|
||||
h2 Probe Dimensions
|
||||
templated-input(v-for="templ in template.probe", :name="$key",
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
"max": 100000000,
|
||||
"unit": "mm/min²",
|
||||
"default": 200000
|
||||
},
|
||||
"probing-prompts": {
|
||||
"help": "Enable or disable safety prompts during and after probing",
|
||||
"type": "bool",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
93
src/svelte-components/src/components/DimensionInput.svelte
Normal file
93
src/svelte-components/src/components/DimensionInput.svelte
Normal file
@@ -0,0 +1,93 @@
|
||||
<script lang="ts">
|
||||
import TextField from "@smui/textfield";
|
||||
import Select, { Option } from "@smui/select";
|
||||
import type { MenuComponentDev } from "@smui/menu";
|
||||
import Menu from "@smui/menu";
|
||||
import List, { Item, Text } from "@smui/list";
|
||||
import { onMount } from "svelte";
|
||||
import { set_input_value } from "svelte/internal";
|
||||
|
||||
let menu: MenuComponentDev;
|
||||
|
||||
type Option = {
|
||||
value: number;
|
||||
metric: boolean;
|
||||
};
|
||||
|
||||
export let label: string;
|
||||
export let value: number;
|
||||
export let metric: boolean;
|
||||
export let options: Option[];
|
||||
|
||||
let textValue = "";
|
||||
|
||||
$: if (textValue) {
|
||||
value = Number(textValue);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
textValue = value?.toString() || "";
|
||||
});
|
||||
|
||||
function onOptionSelected(option: Option) {
|
||||
textValue = option.value.toString();
|
||||
metric = option.metric;
|
||||
}
|
||||
|
||||
function filterKeys(event) {
|
||||
const input = event.target;
|
||||
|
||||
if (input.value.match(/[^0-9.]/)) {
|
||||
input.value = input.value.replace(/[^0-9.]/g, "");
|
||||
}
|
||||
|
||||
if (input.value.match(/\.[^.]*\./)) {
|
||||
input.value = input.value.replace(/(\.[^.]*)\./g, "$1");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="value-and-unit">
|
||||
<TextField
|
||||
{label}
|
||||
on:keypress={filterKeys}
|
||||
on:keyup={filterKeys}
|
||||
bind:value={textValue}
|
||||
on:click={() => menu.setOpen(true)}
|
||||
/>
|
||||
<Select bind:value={metric}>
|
||||
<Option value={true}>mm</Option>
|
||||
<Option value={false}>in</Option>
|
||||
</Select>
|
||||
</div>
|
||||
<Menu bind:this={menu} anchorCorner="BOTTOM_LEFT">
|
||||
<List>
|
||||
{#each options as option}
|
||||
<Item on:SMUI:action={() => onOptionSelected(option)}>
|
||||
<Text>
|
||||
{option.value}
|
||||
{option.metric ? "mm" : "in"}
|
||||
</Text>
|
||||
</Item>
|
||||
{/each}
|
||||
</List>
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.value-and-unit {
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
:global {
|
||||
.mdc-select {
|
||||
max-width: 60px;
|
||||
}
|
||||
|
||||
.smui-select--standard .mdc-select__dropdown-icon {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,18 +1,22 @@
|
||||
<script type="ts" context="module">
|
||||
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { Config } from "$lib/ConfigStore";
|
||||
import { waitForChange } from "$lib/StoreHelpers";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
|
||||
type Step =
|
||||
| "None"
|
||||
| "TestingProbe"
|
||||
| "GetToolDiameter"
|
||||
| "Probing"
|
||||
| "ProbingFailed"
|
||||
| "ProbingComplete";
|
||||
| "CheckProbe"
|
||||
| "BitDimensions"
|
||||
| "PlaceProbeBlock"
|
||||
| "Probe"
|
||||
| "Done";
|
||||
|
||||
const stepLabels: Record<Step, string> = {
|
||||
None: "",
|
||||
CheckProbe: "Check probe",
|
||||
BitDimensions: "Bit dimensions",
|
||||
PlaceProbeBlock: "Place probe block",
|
||||
Probe: "Probe",
|
||||
Done: "Done",
|
||||
};
|
||||
|
||||
const cancelled = writable(false);
|
||||
const probingActive = writable(false);
|
||||
@@ -51,50 +55,86 @@
|
||||
</script>
|
||||
|
||||
<script type="ts">
|
||||
import DimensionInput from "$components/DimensionInput.svelte";
|
||||
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { waitForChange } from "$lib/StoreHelpers";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
import { Config } from "$lib/ConfigStore";
|
||||
|
||||
const cutterDiameterOptions = [
|
||||
{ value: 0.5, metric: false },
|
||||
{ value: 10, metric: true },
|
||||
{ value: 0.25, metric: false },
|
||||
{ value: 6, metric: true },
|
||||
{ value: 0.125, metric: false },
|
||||
{ value: 3, metric: true },
|
||||
];
|
||||
|
||||
const cutterLengthOptions = [
|
||||
{ value: 1, metric: false },
|
||||
{ value: 20, metric: true },
|
||||
{ value: 0.5, metric: false },
|
||||
{ value: 10, metric: true },
|
||||
{ value: 0.25, metric: false },
|
||||
{ value: 6, metric: true },
|
||||
];
|
||||
|
||||
export let open;
|
||||
export let probeType: "xyz" | "z";
|
||||
let step: Step = "None";
|
||||
let toolDiameter;
|
||||
let currentStep: Step = "None";
|
||||
let cutterDiameter;
|
||||
let cutterLength;
|
||||
let showCancelButton = true;
|
||||
let steps: Array<Step> = [];
|
||||
let nextButton = {
|
||||
label: "Next",
|
||||
disabled: false,
|
||||
allowClose: false,
|
||||
};
|
||||
|
||||
$: showPrompts = $Config.settings?.["probing-prompts"];
|
||||
$: metric = $Config.settings?.units === "METRIC";
|
||||
|
||||
$: if (open) {
|
||||
toolDiameter = "";
|
||||
cutterDiameter = null;
|
||||
cutterLength = null;
|
||||
|
||||
// Svelte appears not to like it when you invoke
|
||||
// an async function from a reactive statement, so we
|
||||
// use requestAnimationFrame to call begin at a later moment.
|
||||
// use requestAnimationFrame to call 'begin' at a later moment.
|
||||
requestAnimationFrame(begin);
|
||||
}
|
||||
|
||||
$: if (cutterDiameter && cutterLength) {
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
function removeSkippedSteps(steps: Step[]): Step[] {
|
||||
return steps.filter((x) => x);
|
||||
}
|
||||
|
||||
async function begin() {
|
||||
try {
|
||||
$probingActive = true;
|
||||
assertValidProbeType();
|
||||
|
||||
if (showPrompts) {
|
||||
await stepCompleted("TestingProbe", probeContacted);
|
||||
}
|
||||
steps = removeSkippedSteps([
|
||||
"CheckProbe",
|
||||
probeType === "xyz" ? "BitDimensions" : undefined,
|
||||
"PlaceProbeBlock",
|
||||
"Probe",
|
||||
"Done",
|
||||
]);
|
||||
|
||||
await stepCompleted("CheckProbe", probeContacted);
|
||||
|
||||
if (probeType === "xyz") {
|
||||
await stepCompleted("GetToolDiameter", userAcknowledged);
|
||||
await stepCompleted("BitDimensions", userAcknowledged);
|
||||
}
|
||||
|
||||
do {
|
||||
await stepCompleted("Probing", probingComplete, probingFailed);
|
||||
|
||||
if ($probingFailed) {
|
||||
await stepCompleted("ProbingFailed", userAcknowledged);
|
||||
}
|
||||
} while (!$probingComplete);
|
||||
|
||||
await stepCompleted("ProbingComplete", userAcknowledged);
|
||||
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
||||
await stepCompleted("Probe", probingComplete, probingFailed);
|
||||
await stepCompleted("Done", userAcknowledged);
|
||||
|
||||
if (probeType === "xyz") {
|
||||
ControllerMethods.goto_zero(1, 1, 0, 0);
|
||||
@@ -105,7 +145,7 @@
|
||||
}
|
||||
} finally {
|
||||
$probingActive = false;
|
||||
step = "None";
|
||||
currentStep = "None";
|
||||
|
||||
if ($probingStarted) {
|
||||
ControllerMethods.stop();
|
||||
@@ -130,12 +170,12 @@
|
||||
nextStep: Step,
|
||||
...writables: Array<Writable<any>>
|
||||
) {
|
||||
step = nextStep;
|
||||
currentStep = nextStep;
|
||||
|
||||
clearFlags();
|
||||
updateButtons();
|
||||
|
||||
if (step === "Probing") {
|
||||
if (currentStep === "Probe") {
|
||||
executeProbe();
|
||||
}
|
||||
|
||||
@@ -167,13 +207,24 @@
|
||||
allowClose: false,
|
||||
};
|
||||
|
||||
switch (step) {
|
||||
case "TestingProbe":
|
||||
case "Probing":
|
||||
switch (currentStep) {
|
||||
case "CheckProbe":
|
||||
case "Probe":
|
||||
nextButton.disabled = true;
|
||||
break;
|
||||
|
||||
case "ProbingComplete":
|
||||
case "BitDimensions":
|
||||
nextButton.disabled = !(
|
||||
cutterDiameter !== null &&
|
||||
cutterDiameter !== 0 &&
|
||||
isFinite(cutterDiameter) &&
|
||||
cutterLength !== null &&
|
||||
cutterLength !== 0 &&
|
||||
isFinite(cutterLength)
|
||||
);
|
||||
break;
|
||||
|
||||
case "Done":
|
||||
showCancelButton = false;
|
||||
nextButton = {
|
||||
disabled: false,
|
||||
@@ -192,8 +243,8 @@
|
||||
const fastSeek = $Config.probe["probe-fast-seek"];
|
||||
|
||||
const zLift = 1;
|
||||
const xOffset = probeBlockWidth + toolDiameter / 2.0;
|
||||
const yOffset = probeBlockLength + toolDiameter / 2.0;
|
||||
const xOffset = probeBlockWidth + cutterDiameter / 2.0;
|
||||
const yOffset = probeBlockLength + cutterDiameter / 2.0;
|
||||
const zOffset = probeBlockHeight;
|
||||
|
||||
if (probeType === "z") {
|
||||
@@ -213,9 +264,9 @@
|
||||
} else {
|
||||
// After probing Z, we want to drop the bit down:
|
||||
// Ideally, 12.7mm/0.5in
|
||||
// And we don't want to be more than 75% down on the probe block
|
||||
// And we don't want to be more than 90% down on the probe block
|
||||
// Also, add zlift to compensate for the fact that we lift after probing Z
|
||||
const plunge = Math.min(12.7, zOffset * 0.75) + zLift;
|
||||
const plunge = Math.min(cutterLength, zOffset * 0.9) + zLift;
|
||||
|
||||
ControllerMethods.send(`
|
||||
G21
|
||||
@@ -253,38 +304,71 @@
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
class="probe-dialog"
|
||||
scrimClickAction=""
|
||||
aria-labelledby="simple-title"
|
||||
aria-describedby="simple-content"
|
||||
surface$style="width: 700px; height: 400px; max-width: calc(100vw - 32px); overflow: visible;"
|
||||
>
|
||||
<Title id="simple-title">Probe {probeType}</Title>
|
||||
<Title id="simple-title">Probing {probeType?.toUpperCase()}</Title>
|
||||
|
||||
<Content id="simple-content">
|
||||
{#if step === "TestingProbe"}
|
||||
<p>Attach the probe magnet to the collet.</p>
|
||||
<p>Touch the probe block to the bit.</p>
|
||||
|
||||
{#if $probeContacted}
|
||||
<p>Probe contact detected!</p>
|
||||
{:else}
|
||||
<p>Waiting for probe contact...</p>
|
||||
{/if}
|
||||
{:else if step === "GetToolDiameter"}
|
||||
<label for="tool-diameter">Tool Diameter (mm)</label>
|
||||
<input id="tool-diameter" bind:value={toolDiameter} />
|
||||
{:else if step === "Probing"}
|
||||
<p>Probing in progress...</p>
|
||||
{:else if step === "ProbingFailed"}
|
||||
<p>Could not find the probe block during probing!</p>
|
||||
<Content id="simple-content" style="overflow: visible;">
|
||||
<div class="steps">
|
||||
<p><b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b></p>
|
||||
<ul>
|
||||
{#each steps as step}
|
||||
<li class:active={currentStep === step}>{stepLabels[step]}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="current-step">
|
||||
<p>
|
||||
Make sure the tip of the bit is about 1/4" (6mm) above the probe block,
|
||||
and try again.
|
||||
{#if currentStep === "CheckProbe"}
|
||||
Attach the probe magnet to the collet, then touch the probe block to
|
||||
the bit.
|
||||
{:else if currentStep === "BitDimensions"}
|
||||
<DimensionInput
|
||||
label="Cutter diameter"
|
||||
options={cutterDiameterOptions}
|
||||
bind:value={cutterDiameter}
|
||||
{metric}
|
||||
/>
|
||||
<DimensionInput
|
||||
label="Cutter length"
|
||||
options={cutterLengthOptions}
|
||||
bind:value={cutterLength}
|
||||
{metric}
|
||||
/>
|
||||
{:else if currentStep === "PlaceProbeBlock"}
|
||||
{#if probeType === "xyz"}
|
||||
Place the probe block face up, on the lower-left corner of your
|
||||
workpiece.
|
||||
{:else}
|
||||
Place the probe block face down, with the bit above the recess.
|
||||
{/if}
|
||||
{:else if currentStep === "Probe"}
|
||||
Probing in progress...
|
||||
{:else if currentStep === "Done"}
|
||||
{#if $probingFailed}
|
||||
Could not find the probe block during probing!
|
||||
|
||||
<p>
|
||||
Make sure the tip of the bit is less than {metric
|
||||
? "25mm"
|
||||
: "1 in"} above the probe block, and try again.
|
||||
</p>
|
||||
{:else}
|
||||
Don't forget to put away the probe!
|
||||
|
||||
{#if probeType === "xyz"}
|
||||
<p>The machine will now move to the XY origin.</p>
|
||||
|
||||
<p>Watch your hands!</p>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</p>
|
||||
{:else if step === "ProbingComplete"}
|
||||
<p>Don't forget to put away the probe!</p>
|
||||
<p>The machine will now move to the XY origin.</p>
|
||||
<p>Watch your hands!</p>
|
||||
{/if}
|
||||
</div>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
@@ -305,3 +389,87 @@
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
$primary: #0078e7;
|
||||
$very-dark: #555;
|
||||
$text: #777;
|
||||
$grey: #bbb;
|
||||
$light: #ddd;
|
||||
|
||||
:global {
|
||||
.mdc-dialog__content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.current-step {
|
||||
flex-grow: 1;
|
||||
|
||||
.mdc-text-field {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.steps {
|
||||
margin-right: 50px;
|
||||
|
||||
ul {
|
||||
margin: 0 auto;
|
||||
list-style-type: none;
|
||||
counter-reset: steps;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
padding: 0 0 12px 30px;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
color: $text;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 0.5px;
|
||||
content: "";
|
||||
border: 2px solid $text;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: 11px;
|
||||
width: 11px;
|
||||
text-align: center;
|
||||
line-height: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
top: 22px;
|
||||
bottom: 0;
|
||||
content: "";
|
||||
width: 0;
|
||||
border-left: 2px solid $text;
|
||||
}
|
||||
|
||||
&:last-of-type:before {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $primary;
|
||||
font-weight: bold;
|
||||
|
||||
&:after {
|
||||
border: 3px solid $primary;
|
||||
top: 2.5px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,4 +28,15 @@
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user