mirror of
https://github.com/xodio/xod.git
synced 2026-03-13 12:16:54 +01:00
refactor(xod-arduino-builder, xod-cli, xod-client-electron): make all Promises in xod-arduino-builder returns unified object CmdResult and use it in packages-consumers
This commit is contained in:
@@ -37,6 +37,13 @@
|
||||
* @property {string} board
|
||||
* @property {string} package */
|
||||
|
||||
/** Command result
|
||||
* @typedef {Object} CmdResult
|
||||
* @property {boolean} success
|
||||
* @property {string} module
|
||||
* @property {string} message
|
||||
* @property {*} data */
|
||||
|
||||
/** Serial port object.
|
||||
* @typedef {Object} Port
|
||||
* @property {string} comName - The {@link Path} or identifier used to open the serial port.
|
||||
@@ -51,7 +58,12 @@ import mime from 'rest/interceptor/mime';
|
||||
import SerialPort from 'serialport';
|
||||
import shelljs from 'shelljs';
|
||||
|
||||
const catchRestError = err => Promise.reject(R.prop('error', err));
|
||||
import * as msg from './messages';
|
||||
|
||||
const success = R.curry((message, data) => ({ success: true, module: module.id, message, data }));
|
||||
const error = R.compose(R.assoc('success', false), success);
|
||||
|
||||
const unwrapCmdResult = R.prop('data');
|
||||
|
||||
/** A url of the [official Arduino package index]{@link http://downloads.arduino.cc/packages/package_index.json}.
|
||||
* @constant
|
||||
@@ -75,20 +87,22 @@ const CONFIG_PATH = path.resolve(path.dirname(module.filename), 'config.json');
|
||||
|
||||
/** Writes the provided configuration to {@link CONFIG_PATH} file.
|
||||
* @param {*} config
|
||||
* @return {Promise<void>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
const setConfig = config =>
|
||||
Promise.resolve()
|
||||
.then(() => fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2)))
|
||||
.catch(() => Promise.reject([module.id, 'could not set config']));
|
||||
.then(() => success(msg.CONFIG_SETTED, {}))
|
||||
.catch(err => Promise.reject(error(msg.CONFIG_SET_ERROR, err)));
|
||||
|
||||
/** Reads the configuration value from file at {@link CONFIG_PATH}.
|
||||
* @param {RamdaPath} [ramdaPath=[]] - Ramda path to configuration value.
|
||||
* @return {Promise<*>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
const getConfig = (ramdaPath = []) =>
|
||||
Promise.resolve()
|
||||
.then(() => JSON.parse(fs.readFileSync(CONFIG_PATH).toString()))
|
||||
.then(R.path(ramdaPath))
|
||||
.catch(() => Promise.reject([module.id, 'could not get config']));
|
||||
.then(success(msg.CONFIG_GETTED))
|
||||
.catch(err => Promise.reject(error(msg.CONFIG_GET_ERROR, err)));
|
||||
|
||||
/** Parses Arduino's `.txt` definition file.
|
||||
* @kind function
|
||||
@@ -107,59 +121,68 @@ const parseTxtConfig = R.compose(
|
||||
|
||||
/** Sets path to Arduino IDE executable.
|
||||
* @param {Path} arduinoIdePathExecutable - Path to Arduino IDE executable.
|
||||
* @return {Promise<void>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const setArduinoIdePathExecutable = arduinoIdePathExecutable =>
|
||||
getConfig()
|
||||
.then(unwrapCmdResult)
|
||||
.catch(() => Promise.resolve({}))
|
||||
.then(R.assocPath(ARDUINO_IDE_PATH_EXECUTABLE)(arduinoIdePathExecutable))
|
||||
.then(setConfig);
|
||||
|
||||
/** Sets path to Arduino IDE packages.
|
||||
* @param {Path} arduinoIdePathPackages - Path to Arduino IDE packages.
|
||||
* @return {Promise<void>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const setArduinoIdePathPackages = arduinoIdePathPackages =>
|
||||
getConfig()
|
||||
.then(unwrapCmdResult)
|
||||
.catch(() => Promise.resolve({}))
|
||||
.then(R.assocPath(ARDUINO_IDE_PATH_PACKAGES)(arduinoIdePathPackages))
|
||||
.then(setConfig);
|
||||
|
||||
/** Lists the raw [official Arduino package index]{@link http://downloads.arduino.cc/packages/package_index.json}.
|
||||
* @return {Promise<Object>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const listPackageIndex = () =>
|
||||
rest.wrap(mime).wrap(errorCode)({ path: ARDUINO_PACKAGE_INDEX_URL })
|
||||
.then(R.prop('entity'))
|
||||
.catch(catchRestError);
|
||||
.then(success(msg.INDEX_LIST_GETTED))
|
||||
.catch(err => Promise.reject(error(msg.INDEX_LIST_ERROR, err)));
|
||||
|
||||
/** Lists the processed [official Arduino package index]{@link http://downloads.arduino.cc/packages/package_index.json}, optimized for {@link PAV} selection.
|
||||
* @return {Promise<Map<string, PAV[]>>} */
|
||||
CmdResult.data contains Map<string, PAV[]>
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const listPAVs = () =>
|
||||
listPackageIndex().then(R.compose(
|
||||
R.groupBy(pav => `${pav.package}:${pav.architecture}`),
|
||||
R.unnest,
|
||||
R.map(({ name, platforms }) =>
|
||||
platforms.map(({ architecture, version }) => ({
|
||||
package: name,
|
||||
architecture,
|
||||
version,
|
||||
}))
|
||||
),
|
||||
R.prop('packages')
|
||||
)).catch(catchRestError);
|
||||
listPackageIndex()
|
||||
.then(unwrapCmdResult)
|
||||
.then(R.compose(
|
||||
R.groupBy(pav => `${pav.package}:${pav.architecture}`),
|
||||
R.unnest,
|
||||
R.map(({ name, platforms }) =>
|
||||
platforms.map(({ architecture, version }) => ({
|
||||
package: name,
|
||||
architecture,
|
||||
version,
|
||||
}))
|
||||
),
|
||||
R.prop('packages')
|
||||
))
|
||||
.then(success(msg.PAVS_LIST_GETTED))
|
||||
.catch(error(msg.PAVS_LIST_ERROR));
|
||||
|
||||
/** Installs the selected {@link PAV}.
|
||||
* @param {PAV} pav - Selected {@link PAV}.
|
||||
* @return {Promise<boolean>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const installPAV = pav =>
|
||||
getConfig(ARDUINO_IDE_PATH_EXECUTABLE)
|
||||
.then(unwrapCmdResult)
|
||||
.then(
|
||||
arduino => new Promise(
|
||||
(resolve, reject) => {
|
||||
shelljs.exec(
|
||||
`${arduino} --install-boards ${pav.package}:${pav.architecture}:${pav.version}`,
|
||||
(code) => {
|
||||
if (code === 255) { return resolve('This PAV is already installed.'); }
|
||||
if (code === 0) { return resolve(); }
|
||||
return reject([module.id, 'could not install boards']);
|
||||
(code, stdout, stderr) => {
|
||||
if (code === 255) { return resolve(success(msg.PAV_ALREADY_INSTALLED, stdout)); }
|
||||
if (code === 0) { return resolve(success(msg.PAV_INSTALLED, stdout)); }
|
||||
return reject(error(msg.PAV_INSTALL_ERROR, stderr));
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -168,35 +191,39 @@ export const installPAV = pav =>
|
||||
|
||||
/** Lists the boards supported by the selected {@link PAV}.
|
||||
* @param {PAV} pav - Selected {@link PAV}.
|
||||
* @return {Promise<Object>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const listPAVBoards = pav =>
|
||||
getConfig(ARDUINO_IDE_PATH_PACKAGES)
|
||||
.then(unwrapCmdResult)
|
||||
.then(packages => fs.readFileSync(path.resolve(packages, pav.package, 'hardware', pav.architecture, pav.version, 'boards.txt')).toString())
|
||||
.then(parseTxtConfig)
|
||||
.catch(() => Promise.reject([module.id, 'could not parse boards config']));
|
||||
.then(success(msg.BOARDS_LIST_GETTED))
|
||||
.catch(err => Promise.reject(error(msg.BOARDS_LIST_ERROR, err)));
|
||||
|
||||
/** Lists the available {@link Port}s.
|
||||
* @return {Promise<Port[]>} */
|
||||
CmdResult.data contains Port[]
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const listPorts = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
SerialPort.list((error, ports) => {
|
||||
if (error) reject(error);
|
||||
else resolve(ports);
|
||||
SerialPort.list((err, ports) => {
|
||||
if (err) reject(error(msg.PORTS_LIST_ERROR, err));
|
||||
else resolve(success(msg.PORTS_LIST_GETTED, ports));
|
||||
});
|
||||
});
|
||||
|
||||
/** Compiles the file for the selected {@link PAB}.
|
||||
* @param {PAB} pab - Package, architecture, board.
|
||||
* @param {Path} file - Path to the compilation source.
|
||||
* @return {Promise<boolean>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const verify = (pab, file) =>
|
||||
getConfig(ARDUINO_IDE_PATH_EXECUTABLE)
|
||||
.then(unwrapCmdResult)
|
||||
.then(arduino => new Promise((resolve, reject) =>
|
||||
shelljs.exec(
|
||||
`${arduino} --verify --board "${pab.package}:${pab.architecture}:${pab.board}" "${file}"`,
|
||||
(code, stdout, stderr) => {
|
||||
if (code === 0) { return resolve(stdout); }
|
||||
return reject(stderr);
|
||||
if (code === 0) { return resolve(success(msg.SKETCH_VERIFIED, stdout)); }
|
||||
return reject(error(msg.SKETCH_VERIFY_ERROR, stderr));
|
||||
}
|
||||
)
|
||||
))
|
||||
@@ -206,15 +233,16 @@ export const verify = (pab, file) =>
|
||||
* @param {PAB} pab - Package, architecture, board.
|
||||
* @param {Port#comName} port - Port.
|
||||
* @param {Path} file - Path to the compilation source.
|
||||
* @return {Promise<boolean>} */
|
||||
* @return {Promise<CmdResult>} */
|
||||
export const upload = (pab, port, file) =>
|
||||
getConfig(ARDUINO_IDE_PATH_EXECUTABLE)
|
||||
.then(unwrapCmdResult)
|
||||
.then(arduino => new Promise((resolve, reject) =>
|
||||
shelljs.exec(
|
||||
`${arduino} --upload --board "${pab.package}:${pab.architecture}:${pab.board}" --port "${port}" "${file}"`,
|
||||
(code, stdout, stderr) => {
|
||||
if (code === 0) { return resolve(stdout); }
|
||||
return reject(stderr);
|
||||
if (code === 0) { return resolve(success(msg.SKETCH_UPLOADED, stdout)); }
|
||||
return reject(error(msg.SKETCH_UPLOAD_ERROR, stderr));
|
||||
}
|
||||
)
|
||||
));
|
||||
|
||||
29
packages/xod-arduino-builder/src/messages.js
Normal file
29
packages/xod-arduino-builder/src/messages.js
Normal file
@@ -0,0 +1,29 @@
|
||||
export const CONFIG_SETTED = 'Config setted';
|
||||
export const CONFIG_SET_ERROR = 'Could not set config';
|
||||
|
||||
export const CONFIG_GETTED = 'Config getted';
|
||||
export const CONFIG_GET_ERROR = 'Could not get config';
|
||||
|
||||
export const REST_ERROR = 'Could not get REST';
|
||||
|
||||
export const PAVS_LIST_GETTED = 'List of PAVs getted';
|
||||
export const PAVS_LIST_ERROR = 'Could not get list of PAVs';
|
||||
|
||||
export const INDEX_LIST_GETTED = 'List of packages getted';
|
||||
export const INDEX_LIST_ERROR = 'Could not get list of packages';
|
||||
|
||||
export const BOARDS_LIST_GETTED = 'List of Boards getted';
|
||||
export const BOARDS_LIST_ERROR = 'Could not parse boards config';
|
||||
|
||||
export const PORTS_LIST_GETTED = 'List of ports getted';
|
||||
export const PORTS_LIST_ERROR = 'Could not get list of ports';
|
||||
|
||||
export const PAV_ALREADY_INSTALLED = 'This PAV is already installed';
|
||||
export const PAV_INSTALLED = 'PAV has been installed';
|
||||
export const PAV_INSTALL_ERROR = 'Could not install PAV';
|
||||
|
||||
export const SKETCH_VERIFIED = 'Sketch verified';
|
||||
export const SKETCH_UPLOADED = 'Sketch uploaded';
|
||||
|
||||
export const SKETCH_VERIFY_ERROR = 'Could not verify sketch';
|
||||
export const SKETCH_UPLOAD_ERROR = 'Could not upload sketch';
|
||||
@@ -2,9 +2,9 @@ import * as ab from 'xod-arduino-builder';
|
||||
import * as messages from './messages';
|
||||
|
||||
function run(promise, { success, error }) {
|
||||
promise.then((value) => {
|
||||
if (value) {
|
||||
messages.notice(JSON.stringify(value, null, 2));
|
||||
promise.then((response) => {
|
||||
if (response && response.data) {
|
||||
messages.notice(JSON.stringify(response.data, null, 2));
|
||||
}
|
||||
if (success) {
|
||||
messages.success(success);
|
||||
|
||||
@@ -74,6 +74,7 @@ export const checkArduinoIde = (updatePaths, success) => {
|
||||
const getPAV = pab => R.composeP(
|
||||
R.last,
|
||||
R.prop(`${pab.package}:${pab.architecture}`),
|
||||
R.prop('data'),
|
||||
xab.listPAVs
|
||||
)();
|
||||
|
||||
@@ -100,7 +101,7 @@ export const findPort = (pab, success) => {
|
||||
};
|
||||
|
||||
return xab.listPorts()
|
||||
.then(ports => R.compose(
|
||||
.then(R.compose(
|
||||
R.ifElse(
|
||||
R.isNil,
|
||||
() => Promise.reject(new Error('Could not find Arduino device on opened ports.')),
|
||||
@@ -109,8 +110,9 @@ export const findPort = (pab, success) => {
|
||||
R.propOr(null, 'comName'),
|
||||
R.find(
|
||||
R.propEq('vendorId', '0x2341') // TODO: Replace it with normal find function
|
||||
)
|
||||
)(ports))
|
||||
),
|
||||
R.prop('data')
|
||||
))
|
||||
.then((port) => { success(result); return port; })
|
||||
.catch(throwError(result));
|
||||
};
|
||||
@@ -126,7 +128,7 @@ export const doTranspileForArduino = ({ pab, project, patchId }, success) => {
|
||||
return Promise.resolve(project)
|
||||
.then(v2 => transpileForArduino(v2, patchId))
|
||||
.then(foldEither(
|
||||
err => Promise.reject(new Error(err)),
|
||||
err => Promise.reject(err),
|
||||
code => Promise.resolve(code)
|
||||
))
|
||||
.then((code) => { success(result); return code; })
|
||||
@@ -144,16 +146,21 @@ export const uploadToArduino = (pab, port, code, success) => {
|
||||
message: 'Code has been successfully uploaded.',
|
||||
percentage: 55,
|
||||
};
|
||||
const updateMessageByData = R.compose(
|
||||
R.assoc('message', R.__, result),
|
||||
R.prop('data')
|
||||
);
|
||||
const clearTmp = () => fs.unlinkSync(tmpPath);
|
||||
|
||||
return writeFile(tmpPath, code)
|
||||
.then(({ path }) => xab.upload(pab, port, path))
|
||||
.then((response) => { result.message = response; })
|
||||
.catch((errorMessage) => {
|
||||
clearTmp();
|
||||
return Promise.reject(new Error(`Can't build or upload project to Arduino.\n${errorMessage}`));
|
||||
})
|
||||
.then(() => success(result))
|
||||
.then(updateMessageByData)
|
||||
.catch(R.compose(
|
||||
err => Promise.reject(err),
|
||||
R.tap(clearTmp),
|
||||
updateMessageByData
|
||||
))
|
||||
.then(success)
|
||||
.then(() => clearTmp())
|
||||
.catch(throwError(result));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user