src/lib/db/upload.js
import { utimes } from 'fs';
import through from 'through2';
import DBWorker, { Job } from 'atvise-dbworker';
import { log, colors, PluginError } from 'gulp-util';
import plur from 'plur';
import Project from '../config/ProjectConfig';
import NodeId from '../model/NodeId';
function encodedValue(value, dataType) {
const stringValue = value.toString().trim();
if (dataType === 'Boolean') { return stringValue === 'true'; }
if (dataType === 'String') { return stringValue; }
if (dataType === 'NodeId') { return NodeId.fromString(stringValue); }
if (dataType === 'DateTime') { return new Date(value); }
// FIXME: For ints that are not set (value = {}) this inserts a value of 0
if (dataType.match(/^u?int/i)) { return parseInt(stringValue, 10) || 0; }
return value;
}
function encodedContents(file) {
const contents = file.contents;
const dataType = file.rc.dataType;
if (file.rc.array) {
return JSON.parse(contents).map(v => encodedValue(v, dataType));
}
return encodedValue(contents, dataType);
}
function forceValidMtime(session, file, stream, cb) {
session.read([file.nodeId], (err, nodes, results) => {
if (!err && results.length > 0) {
const mtime = Math.floor(results[0].serverTimestamp / 1000);
utimes(file.path, mtime, mtime, utimesErr => {
if (!utimesErr) {
file.stat.mtime = new Date(mtime);
stream.push(file);
}
cb(utimesErr);
});
} else {
cb(err);
}
});
}
function uploadFile(session, file, stream, worker, cb) {
try {
session.writeSingleNode(file.nodeId.toString(), {
dataType: file.rc.dataType,
value: encodedContents(file),
}, (err, status) => {
if (err) {
err.node = file.nodeId;
cb(err);
} else if (status > 0) {
log(colors.red.bold('Error'), 'writing to', colors.magenta(file.nodeId.value));
log(colors.yellow(status.description));
log(colors.yellow('Make sure this node is not opened in atvise builder'));
cb();
} else {
if (worker) {
// Force valid mtime
worker.addJob(new Job((...args) => forceValidMtime(...args), file, stream));
} else {
stream.push(file);
}
cb();
}
});
} catch (err) {
err.node = file.nodeId;
cb(err);
}
}
/**
* Uploads {@link AtviseFile}s from a stream to atvise-server.
* @param {Boolean} [validMtime=false] Pass `true` to force the file to have the same mtime as the
* db node. **Use with caution: forcing mtime to be in sync doubles upload time.**
* @return {Stream}
*/
export default function upload(validMtime) {
let files = 0;
const worker = new DBWorker(`opc.tcp://${Project.host}:${Project.port.opc}`);
return through.obj(function(file, enc, cb) {
worker.addJob(
new Job((...args) => uploadFile(...args), file, this, validMtime ? worker : false)
);
files++;
cb();
}, function(cb) {
if (files === 0) {
cb();
} else {
worker.once('complete', () => {
log('Uploaded', colors.cyan(files), `${plur('node', files)}.`);
cb();
});
worker.on('error', err => {
console.log('error uploading');
// FIXME: Better error
cb(new PluginError('upload', err));
});
worker.start();
}
});
}