Skip to content

Commit

Permalink
V2 (#29)
Browse files Browse the repository at this point in the history
* Many improvements

A axis
Jog distances change with units
toolpath displayer more stable
Arrows instead of Go
Display last 2 serial messages
Better formatting for different display sizes

* Fix inconsistent formatting in numpad code

No semantic changes

* Respect current WCS when setting positions

* Moved set and goto commands to calculator popup

That declutters the main page and reduces the number
of clicks necessary to perform the operation.

* Fullscreen, Enter in MDI text, message scrolling

* Layout improvements

Looks good at a lot of resolutions

* Merge changes from master

* Version 2.0.0

* Updated README.md to v2
  • Loading branch information
MitchBradley committed Nov 24, 2020
1 parent ca57820 commit 1d62392
Show file tree
Hide file tree
Showing 8 changed files with 909 additions and 618 deletions.
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ The full cncjs UI can still be used, perhaps on a different computer or in a dif

It works well with TinyG/g2core and GRBL. It has been tested a little with Marlin but not extensively. It has not been tested with Smoothieware - but Smoothie and GRBL are quite similar from a protocol standpoint so it is likely to work.

Rotary axes A/B/C are not supported from the GUI, but you can issue GCode commands for those axes manually via the MDI boxes.
Rotary axis A is supported from the GUI, but not B and C. You can issue GCode commands for those axes manually via the MDI boxes. The A axis GUI support includes a DRO, Set and GoTo functions, but not jog buttons. There is a Home A menu entry in the dropdown menu.


### Setup

Expand Down Expand Up @@ -64,23 +65,36 @@ You can still use the full cncjs UI by browsing 'http://*host*:8000'. You can us

### Usage

![cncjs-tablet 2](https://user-images.githubusercontent.com/4861133/33970662-4a8244b2-e018-11e7-92ab-5a379e3de461.PNG)

* **Start/Pause/Resume/Stop** are highlighted and colored according to the program state
* **Start** is green when it is possible to start running a program
* **Pause** is red when the program is running
* **Stop** is red when the program is running or paused
* **Resume** is green when the program is paused
* The **Inch** or **mm** button shows the currently-active units, and toggles them if clicked.
![cncjs-shopfloor-tablet](https://user-images.githubusercontent.com/4861133/100148429-d9b16b80-2e40-11eb-8bc0-7c80a1b27a9f.png)

From left to right, top to bottom, the screen elements are:
* The legend in the upper left hand corner shows the current controller state and sometimes error messages.
* There are two program control buttons that change according to the program state
* The left button is generalized "Go", while the right is generalized "Stop"
* The idea is that the left one is always the "accelerator" and the right the "brake"
* If no program is loaded, neither button is active (both are greyed out)
* If a program is loaded but not running, the left button is green/Start
* When a program is running, the right button is red, labeled Pause
* Then a program is paused, the left one is green/Resume and the right is red/Stop
* When a program is running, hitting the "brake" (right button) pauses exection. From there you can decide whether to Resume or Stop (go back to idle state).
* The number to the right of the control buttons is the number of program lines that have been executed.
* The number to the right of that is the program run time.
* The button in the upper right corner is a drop-down menu with miscellaneous functions that are used infrequently.
* "G54" shows the current work coordinate system - G54, G55, .. G59. To change the coordinate system manually you can enter e.g. G55 in an MDI box.
* The gray "DRO" boxes labeled X, Y, Z, A show the current work coordinate for that axis. Touching a DRO box brings up an RPN calculator in which you can enter or calculate a new value. Having entered the value, you can exit from the calculator with Cancel (does nothing), GoTo (rapid move to the new location) or Set (change the work coordinate to the new value).
* You can use the calculator to quickly perform custom jogs, as follows: When the calculator starts, the current location is pre-entered. If you type a number followed by + or -, the new number will be added to or subtracted from the current location. You can then hit GoTo.
* The **Inch** or **mm** button shows the currently-active units, and toggles them if clicked. Switching units also changes the jog increment selections.
* **X=** **Y=** **Z=** set the axis work coordinate to the value in the number box above
* **GoX** **GoY** **GoZ** rapid to the axis work coordinate to the value in the number box above
* **X=0** **Y=0** **Z=0** set the axis work coordinate to 0
* **GoX0** **GoY0** **GoZ0** rapid to work 0 for that axis
* **0.001** .. **5** set the jog increment.
* **X=0** **Y=0** **Z=0** **A=0** set the axis work coordinate to 0
* **->X0** **->Y0** **->Z0** **->A0" rapid to work 0 for that axis
* **0.001** .. **5** set the jog increment. The selection of increments changes according to the units (Inch or mm).
* The selector box between **Z+** and **Z-** shows the current jog increment, and, when clicked, permits the choice of some additional jog increments.
* **X-** **X+** **Y-** **Y+** **Z-** **Z+** jog by the current increment. You can jog continuously by selecting a large increment, starting the jog, then hitting **Stop** when it has gone far enough.
* **MDI** sends the GCode command entered in the box to its left. To resend that command, click **MDI** again. There are MDI blocks so you can have two different GCode commands "queued up" for easy execution.
* To load a GCode file from the cncjs server's watch directory, select it from the file selector at the lower left. Its GCode text will be displayed in the scrollable textarea to the right and the X-Y projection of its toolpath will be displayed in the image area to the right of that.
* **Rfrsh** refreshes the file selector list. That is useful if additional files are added to the watch directory. Another way to refresh the file list is to reload the page.
* **Load** reloads the GCode program from the currently selected file. That is useful if you edit the file from another computer and want to pick up the new version. To reload it directly from the file selector, you would have to first select a different file and then re-select the edited one (because of the way selector ".change" events work).
* If the file selector is blank and there is GCode text in the GCode display text area, the GCode is probably a macro loaded by a different cncjs session.
* The text box to the right of MDI shows the currently loaded GCode program. You can scroll it by swiping in the box. When a program is running, the currently executing line is highlighted. The highlighted line is sometimes wrong due to the fact that the CNCjs server filters out blank lines, so the line that the controller reports is sometimes a little off compared to the raw text in the file.
* The graphic window in the lower right shows the toolpath looking down from the top. Feedrate (cutting) moves are shown in blue, with rapid moves in green.There is a crosshair reticle at the origin, and the current tool position is shown as a pink circle. The numbers are the motion limits in X and Y.
* The scrollable Serial Messages box shows selected serial messages from the controller, filtering out very common protocol and flow control messages that are generally uninteresting. The messages that are shown include error and alarm messages, and responses to inquiries that you enter in MDI boxes.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cncjs-shopfloor-tablet",
"version": "1.0.4",
"version": "2.0.0",
"description": "A simplified UI for cncjs optimized for tablet computers in a production (shop floor) environment.",
"homepage": "https://github.com/cncjs/cncjs-shopfloor-tablet",
"author": "Mitch Bradley",
Expand Down
167 changes: 145 additions & 22 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ controller.on('serialport:open', function(options) {
// settings report, so interpreting the numbers from the first status
// report is ambiguous. Subsequent status reports are interpreted correctly.
// We work around that by deferring status reports until the settings report.
controller.writeln('$$');
// I commented this out because of https://github.com/cncjs/cncjs-shopfloor-tablet/issues/20
// controller.writeln('$$');
}

root.location = '#/workspace';
Expand Down Expand Up @@ -148,24 +149,44 @@ cnc.goAxis = function(axis, coordinate) {
}

cnc.moveAxis = function(axis, field) {
coordinate = document.getElementById(field).value;
cnc.goAxis(axis, coordinate)
cnc.goAxis(axis, document.getElementById(field).value)
}

cnc.currentAxisPNum = function() {
return 'P' + String(Number(modal.wcs.substring(1)) - 53);
}

cnc.setAxisByValue = function(axis, coordinate) {
cnc.click();
controller.command('gcode', 'G10 L20 ' + cnc.currentAxisPNum() + ' ' + axis + coordinate);
}
cnc.setAxis = function(axis, field) {
cnc.setAxisByValue(axis, document.getElementById(field).value);
}
cnc.MDIcmd = function(value) {
cnc.click();
coordinate = document.getElementById(field).value;
controller.command('gcode', 'G10 L20 P1 ' + axis + coordinate);
controller.command('gcode', value);
}

cnc.MDI = function(field) {
cnc.click();
mdicmd = document.getElementById(field).value;
controller.command('gcode', mdicmd);
cnc.MDIcmd(document.getElementById(field).value);
}

cnc.zeroAxis = function(axis) {
cnc.click();
controller.command('gcode', 'G10 L20 P1 ' + axis + '0');
cnc.setAxisByValue(axis, 0);
}

cnc.toggleFullscreen = function() {
var messages = document.getElementById('messages');

if (document.fullscreenElement) {
document.exitFullscreen();
messages.rows = 2;
} else {
document.documentElement.requestFullscreen();
messages.rows = 4;
}
messages.scrollTop = messages.scrollHeight;
}

cnc.toggleUnits = function() {
Expand All @@ -178,6 +199,12 @@ cnc.toggleUnits = function() {
// No need to fix the button label, as that will be done by the status watcher
}

cnc.btnSetDistance = function() {
cnc.click();
var distance = event.target.innerText;
$('[data-route="workspace"] select[data-name="select-distance"]').val(distance);
}

cnc.setDistance = function(distance) {
cnc.click();
$('[data-route="workspace"] select[data-name="select-distance"]').val(distance);
Expand Down Expand Up @@ -269,13 +296,20 @@ cnc.sendMove = function(cmd) {
fn && fn();
};

controller.on('serialport:read', function(data) {
echoData = function(data) {
var messages = $('[data-route="workspace"] [id="messages"]');
messages.text(messages.text() + "\n" + data);
messages[0].scrollTop = messages[0].scrollHeight;
}

controller.on('serialport:read', function(data) {
if (data.r) {
cnc.line++;
}
switch (cnc.controllerType) {
case 'Marlin':
if (data.startsWith('echo:')) {
echoData(data);
stateName = data.substring(5);
if (machineWorkflow == MACHINE_IDLE) {
machineWorkflow = MACHINE_STALL; // Disables Start button
Expand All @@ -284,17 +318,27 @@ controller.on('serialport:read', function(data) {
stateName = 'Idle';
machineWorkflow = MACHINE_IDLE;
} else if (data.startsWith('Error:')) {
echoData(data);
stateName = data;
}
cnc.updateView();
break;
case 'Smoothie':
case 'Grbl':
if (!data.startsWith('ok')) {
echoData(data);
}
if (data.startsWith('error:')) {
stateName = data;
}
cnc.updateView();
break;
case 'TinyG':
if (!data.startsWith('{"qr"')) {
echoData(data);
}

break;
}
});

Expand All @@ -303,7 +347,7 @@ controller.on('serialport:read', function(data) {
// We track the $13 value by watching for the Grbl:settings event and by
// watching for manual changes via serialport:write. Upon initial connection,
// we issue a settings request in serialport:open.
var grblReportingUnits; // initially undefined
var grblReportingUnits = 0; // initially undefined

controller.on('serialport:write', function(data) {
// var style = 'font-weight: bold; line-height: 20px; padding: 2px 4px; border: 1px solid; color: #00529B; background: #BDE5F8';
Expand Down Expand Up @@ -552,6 +596,9 @@ controller.on('TinyG:state', function(data) {
return;
}

if (sr.modal) {
Object.assign(modal, sr.modal);
}
mpos = sr.mpos;
wpos = sr.wpos;
var INIT = 0, READY = 1, ALARM = 2, STOP = 3, END = 4, RUN = 5,
Expand All @@ -569,14 +616,8 @@ controller.on('TinyG:state', function(data) {
} else {
machineWorkflow = MACHINE_STOP;
}
if (sr.modal) {
Object.assign(modal, sr.modal);
}
} else if ([READY, STOP].indexOf(machineState) >= 0) {
machineWorkflow = (machineState == STOP && workflowState == 'paused') ? MACHINE_HOLD : MACHINE_IDLE;
if (sr.modal) {
Object.assign(modal, sr.modal);
}
} else if ([RUN, CYCLE, HOMING, PROBE, JOG].indexOf(machineState) >= 0) {
machineWorkflow = MACHINE_RUN;
running = true;
Expand Down Expand Up @@ -681,6 +722,72 @@ cnc.doRightButton = function() {
}


cnc.setJogSelector = function(units) {
var selector = $('[data-route="workspace"] select[data-name="select-distance"]');
selector.empty();
if (units == "Inch") {
$('[data-route="workspace"] [id="jog00"]').text('0.001');
$('[data-route="workspace"] [id="jog01"]').text('0.01');
$('[data-route="workspace"] [id="jog02"]').text('0.1');
$('[data-route="workspace"] [id="jog03"]').text('1');
$('[data-route="workspace"] [id="jog10"]').text('0.003');
$('[data-route="workspace"] [id="jog11"]').text('0.03');
$('[data-route="workspace"] [id="jog12"]').text('0.3');
$('[data-route="workspace"] [id="jog13"]').text('3');
$('[data-route="workspace"] [id="jog20"]').text('0.005');
$('[data-route="workspace"] [id="jog21"]').text('0.05');
$('[data-route="workspace"] [id="jog22"]').text('0.5');
$('[data-route="workspace"] [id="jog23"]').text('5');
selector.append($("<option/>").text('0.00025'));
selector.append($("<option/>").text('0.0005'));
selector.append($("<option/>").text('0.001'));
selector.append($("<option/>").text('0.003'));
selector.append($("<option/>").text('0.005'));
selector.append($("<option/>").text('0.01'));
selector.append($("<option/>").text('0.03'));
selector.append($("<option/>").text('0.05'));
selector.append($("<option/>").text('0.1'));
selector.append($("<option/>").text('0.3'));
selector.append($("<option/>").text('0.5'));
selector.append($("<option/>").text('1'));
selector.append($("<option/>").text('3'));
selector.append($("<option/>").text('5'));
selector.append($("<option/>").text('10'));
selector.append($("<option/>").text('30'));
selector.val('1');
} else {
$('[data-route="workspace"] [id="jog00"]').text('0.01');
$('[data-route="workspace"] [id="jog01"]').text('0.1');
$('[data-route="workspace"] [id="jog02"]').text('1');
$('[data-route="workspace"] [id="jog03"]').text('10');
$('[data-route="workspace"] [id="jog10"]').text('0.03');
$('[data-route="workspace"] [id="jog11"]').text('0.3');
$('[data-route="workspace"] [id="jog12"]').text('3');
$('[data-route="workspace"] [id="jog13"]').text('30');
$('[data-route="workspace"] [id="jog20"]').text('0.05');
$('[data-route="workspace"] [id="jog21"]').text('0.5');
$('[data-route="workspace"] [id="jog22"]').text('5');
$('[data-route="workspace"] [id="jog23"]').text('50');
selector.append($("<option/>").text('0.005'));
selector.append($("<option/>").text('0.01'));
selector.append($("<option/>").text('0.03'));
selector.append($("<option/>").text('0.05'));
selector.append($("<option/>").text('0.1'));
selector.append($("<option/>").text('0.3'));
selector.append($("<option/>").text('0.5'));
selector.append($("<option/>").text('1'));
selector.append($("<option/>").text('3'));
selector.append($("<option/>").text('5'));
selector.append($("<option/>").text('10'));
selector.append($("<option/>").text('30'));
selector.append($("<option/>").text('50'));
selector.append($("<option/>").text('100'));
selector.append($("<option/>").text('300'));
selector.append($("<option/>").text('1000'));
selector.val('10');
}
}

cnc.updateView = function() {
// if (cnc.filename == '') {
// canStart = false;
Expand All @@ -693,7 +800,12 @@ cnc.updateView = function() {
$('[data-route="workspace"] .axis-position .btn').prop('disabled', cannotClick);
$('[data-route="workspace"] .axis-position .position').prop('disabled', cannotClick);

$('[data-route="workspace"] [id="units"]').text(modal.units == 'G21' ? 'mm' : 'Inch');
var newUnits = modal.units == 'G21' ? 'mm' : 'Inch';
if ($('[data-route="workspace"] [id="units"]').text() != newUnits) {
$('[data-route="workspace"] [id="units"]').text(newUnits);
cnc.setJogSelector(newUnits);
}
// $('[data-route="workspace"] [id="units"]').text(modal.units == 'G21' ? 'mm' : 'Inch');
$('[data-route="workspace"] [id="units"]').prop('disabled', cnc.controllerType == 'Marlin');

var green = '#86f686';
Expand Down Expand Up @@ -749,6 +861,8 @@ cnc.updateView = function() {
if (seconds < 10)
seconds = '0' + seconds;
runTime = minutes + ':' + seconds;
} else {
runTime = "0:00";
}
$('[data-route="workspace"] [id="runtime"]').text(runTime);

Expand Down Expand Up @@ -778,17 +892,20 @@ cnc.updateView = function() {
var dmpos = {
x: Number(mpos.x).toFixed(digits),
y: Number(mpos.y).toFixed(digits),
z: Number(mpos.z).toFixed(digits)
z: Number(mpos.z).toFixed(digits),
a: Number(mpos.a).toFixed(2)
};
var dwpos = {
x: Number(wpos.x).toFixed(digits),
y: Number(wpos.y).toFixed(digits),
z: Number(wpos.z).toFixed(digits)
z: Number(wpos.z).toFixed(digits),
a: Number(wpos.a).toFixed(2)
};

$('[data-route="workspace"] [id="wpos-x"]').prop('value', dwpos.x);
$('[data-route="workspace"] [id="wpos-y"]').prop('value', dwpos.y);
$('[data-route="workspace"] [id="wpos-z"]').prop('value', dwpos.z);
$('[data-route="workspace"] [id="wpos-a"]').prop('value', dwpos.a);
}

controller.on('gcode:load', function(name, gcode) {
Expand Down Expand Up @@ -862,7 +979,6 @@ cnc.runUserCommand = function(name) {
});
}


cnc.getFileList = function() {
jQuery.get("../api/watch/files", {token: cnc.token, path: watchPath}, function(data) {
var selector = $('[data-route="workspace"] select[data-name="select-file"]');
Expand Down Expand Up @@ -1073,6 +1189,13 @@ jogClick = function(name) {
// Reports whether a text input box has focus - see the next comment
cnc.inputFocused = false;

$('.mdifield').on('keyup', function(event){
if (event.key === 'Enter') {
cnc.MDIcmd(event.target.value);
event.target.blur();
}
});

$(document).on('keydown keyup', function(event){
// When we are in a modal input field like the MDI text boxes
// or the numeric entry boxes, disable keyboard jogging so those
Expand Down
Loading

0 comments on commit 1d62392

Please sign in to comment.