Skip to content

Commit

Permalink
feat: allow setting DTR or RTS line status upon opening a serial conn…
Browse files Browse the repository at this point in the history
…ection
  • Loading branch information
cheton committed Feb 24, 2023
1 parent 33e2ef7 commit 4691816
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 49 deletions.
12 changes: 9 additions & 3 deletions src/app/store/defaultState.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,15 @@ const defaultState = {
connection: {
type: 'serial',
serial: {
// Hardware flow control (RTS/CTS)
rtscts: false
}
// RTS/CTS flow control
rtscts: false,
controlFlag: {
// Set DTR line status (default to null)
dtr: null,
// Set RTS line status (default to null)
rts: null,
},
},
},
autoReconnect: true
},
Expand Down
108 changes: 101 additions & 7 deletions src/app/widgets/Connection/Connection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ class Connection extends PureComponent {
connection,
alertMessage
} = state;
const enableHardwareFlowControl = get(connection, 'serial.rtscts', false);
const controlFlag = get(connection, 'serial.controlFlag');
// Set or clear the DTR line immediately upon opening. No action will be taken if the state is not a boolean type.
const enableDTRControlFlag = (typeof controlFlag?.dtr === 'boolean');
// Set or clear the RTS line immediately upon opening. No action will be taken if the state is not a boolean type.
const enableRTSControlFlag = (typeof controlFlag?.rts === 'boolean');
const enableRTSCTSFlowControl = get(connection, 'serial.rtscts', false);
const canSelectControllers = (controller.loadedControllers.length > 1);
const hasGrblController = includes(controller.loadedControllers, GRBL);
const hasMarlinController = includes(controller.loadedControllers, MARLIN);
Expand All @@ -120,7 +125,6 @@ class Connection extends PureComponent {
const canChangeController = notLoading && notConnected;
const canChangePort = notLoading && notConnected;
const canChangeBaudrate = notLoading && notConnected && (!(this.isPortInUse(port)));
const canToggleHardwareFlowControl = notConnected;
const canOpenPort = port && baudrate && notConnecting && notConnected;
const canClosePort = connected;

Expand Down Expand Up @@ -272,17 +276,107 @@ class Connection extends PureComponent {
</div>
<div
className={cx('checkbox', {
'disabled': !canToggleHardwareFlowControl
'disabled': connected,
})}
>
<label>
<input
type="checkbox"
defaultChecked={enableHardwareFlowControl}
onChange={actions.toggleHardwareFlowControl}
disabled={!canToggleHardwareFlowControl}
defaultChecked={enableDTRControlFlag}
onChange={actions.toggleDTRControlFlag}
disabled={connected}
/>
{i18n._('Enable hardware flow control')}
{i18n._('Set DTR line status upon opening')}
</label>
</div>
{enableDTRControlFlag && (
<div style={{ marginLeft: 20 }}>
<div className="input-group input-group-xs">
<div className="input-group-btn">
<button
type="button"
className={cx(
'btn',
'btn-default',
{ 'btn-select': !!controlFlag.dtr }
)}
onClick={actions.setDTR}
>
{i18n._('SET')}
</button>
<button
type="button"
className={cx(
'btn',
'btn-default',
{ 'btn-select': !controlFlag.dtr }
)}
onClick={actions.clearDTR}
>
{i18n._('CLR')}
</button>
</div>
</div>
</div>
)}
<div
className={cx('checkbox', {
'disabled': connected,
})}
>
<label>
<input
type="checkbox"
defaultChecked={enableRTSControlFlag}
onChange={actions.toggleRTSControlFlag}
disabled={connected}
/>
{i18n._('Set RTS line status upon opening')}
</label>
</div>
{enableRTSControlFlag && (
<div style={{ marginLeft: 20 }}>
<div className="input-group input-group-xs">
<div className="input-group-btn">
<button
type="button"
className={cx(
'btn',
'btn-default',
{ 'btn-select': !!controlFlag.rts }
)}
onClick={actions.setRTS}
>
{i18n._('SET')}
</button>
<button
type="button"
className={cx(
'btn',
'btn-default',
{ 'btn-select': !controlFlag.rts }
)}
onClick={actions.clearRTS}
>
{i18n._('CLR')}
</button>
</div>
</div>
</div>
)}
<div
className={cx('checkbox', {
'disabled': connected,
})}
>
<label>
<input
type="checkbox"
defaultChecked={enableRTSCTSFlowControl}
onChange={actions.toggleRTSCTSFlowControl}
disabled={connected}
/>
{i18n._('Use RTS/CTS flow control')}
</label>
</div>
<div className="checkbox">
Expand Down
102 changes: 99 additions & 3 deletions src/app/widgets/Connection/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class ConnectionWidget extends PureComponent {
autoReconnect: checked
}));
},
toggleHardwareFlowControl: (event) => {
toggleRTSCTSFlowControl: (event) => {
const checked = event.target.checked;
this.setState(state => ({
connection: {
Expand All @@ -91,6 +91,92 @@ class ConnectionWidget extends PureComponent {
}
}));
},
toggleDTRControlFlag: (event) => {
const checked = event.target.checked;
this.setState(state => ({
connection: {
...state.connection,
serial: {
...state.connection.serial,
controlFlag: {
...state.connection.serial.controlFlag,
dtr: checked ? true : null,
},
}
}
}));
},
setDTR: () => {
this.setState(state => ({
connection: {
...state.connection,
serial: {
...state.connection.serial,
controlFlag: {
...state.connection.serial.controlFlag,
dtr: true,
},
},
},
}));
},
clearDTR: () => {
this.setState(state => ({
connection: {
...state.connection,
serial: {
...state.connection.serial,
controlFlag: {
...state.connection.serial.controlFlag,
dtr: false,
},
},
},
}));
},
toggleRTSControlFlag: (event) => {
const checked = event.target.checked;
this.setState(state => ({
connection: {
...state.connection,
serial: {
...state.connection.serial,
controlFlag: {
...state.connection.serial.controlFlag,
rts: checked ? true : null,
},
}
}
}));
},
setRTS: () => {
this.setState(state => ({
connection: {
...state.connection,
serial: {
...state.connection.serial,
controlFlag: {
...state.connection.serial.controlFlag,
rts: true,
},
},
},
}));
},
clearRTS: () => {
this.setState(state => ({
connection: {
...state.connection,
serial: {
...state.connection.serial,
controlFlag: {
...state.connection.serial.controlFlag,
rts: false,
},
},
},
}));
},
handleRefreshPorts: (event) => {
this.refreshPorts();
},
Expand Down Expand Up @@ -229,6 +315,8 @@ class ConnectionWidget extends PureComponent {
}
if (connection) {
this.config.set('connection.serial.rtscts', get(connection, 'serial.rtscts', false));
this.config.set('connection.serial.controlFlag.dtr', get(connection, 'serial.controlFlag.dtr', null));
this.config.set('connection.serial.controlFlag.rts', get(connection, 'serial.controlFlag.rts', null));
}
this.config.set('autoReconnect', autoReconnect);
}
Expand Down Expand Up @@ -264,7 +352,11 @@ class ConnectionWidget extends PureComponent {
baudrate: this.config.get('baudrate'),
connection: {
serial: {
rtscts: this.config.get('connection.serial.rtscts')
rtscts: this.config.get('connection.serial.rtscts'),
controlFlag: {
dtr: this.config.get('connection.serial.controlFlag.dtr'),
rts: this.config.get('connection.serial.controlFlag.rts'),
},
}
},
autoReconnect: this.config.get('autoReconnect'),
Expand Down Expand Up @@ -325,7 +417,11 @@ class ConnectionWidget extends PureComponent {
controller.openPort(port, {
controllerType: this.state.controllerType,
baudrate: baudrate,
rtscts: this.state.connection.serial.rtscts
rtscts: this.state.connection.serial.rtscts,
controlFlag: {
dtr: this.state.connection.serial.controlFlag.dtr,
rts: this.state.connection.serial.controlFlag.rts,
},
}, (err) => {
if (err) {
this.setState(state => ({
Expand Down
40 changes: 33 additions & 7 deletions src/server/controllers/Grbl/GrblController.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,13 @@ class GrblController {
}
this.engine = engine;

const { port, baudrate, rtscts } = { ...options };
const { port, baudrate, rtscts, controlFlag } = { ...options };
this.options = {
...this.options,
port: port,
baudrate: baudrate,
rtscts: rtscts
rtscts: rtscts,
controlFlag,
};

// Connection
Expand Down Expand Up @@ -193,9 +194,9 @@ class GrblController {
dataFilter: (line, context) => {
const originalLine = line;
/**
* line = 'G0X10 ; comment text'
* parts = ['G0X10 ', ' comment text', '']
*/
* line = 'G0X10 ; comment text'
* parts = ['G0X10 ', ' comment text', '']
*/
const parts = originalLine.split(/;(.*)/s); // `s` is the modifier for single-line mode
line = ensureString(parts[0]).trim();
context = this.populateContext(context);
Expand Down Expand Up @@ -890,7 +891,7 @@ class GrblController {
}

open(callback = noop) {
const { port, baudrate } = this.options;
const { port, baudrate, controlFlag } = this.options;

// Assertion check
if (this.isOpen()) {
Expand All @@ -902,14 +903,39 @@ class GrblController {
this.connection.on('close', this.connectionEventListener.close);
this.connection.on('error', this.connectionEventListener.error);

this.connection.open((err) => {
this.connection.open(async (err) => {
if (err) {
log.error(`Error opening serial port "${port}":`, err);
this.emit('serialport:error', { err: err, port: port });
callback(err); // notify error
return;
}

let setOptions = null;
try {
// Set DTR and RTS control flags if they exist
if (typeof controlFlag?.dtr === 'boolean') {
setOptions = {
...setOptions,
dtr: controlFlag?.dtr,
};
}
if (typeof controlFlag?.rts === 'boolean') {
setOptions = {
...setOptions,
rts: controlFlag?.rts,
};
}

if (setOptions) {
await delay(100);
await this.connection.port.set(setOptions);
await delay(100);
}
} catch (err) {
log.error('Failed to set control flags:', { err, port });
}

this.emit('serialport:open', {
port: port,
baudrate: baudrate,
Expand Down
Loading

0 comments on commit 4691816

Please sign in to comment.