Initial commit

This commit is contained in:
2018-08-08 21:31:17 +08:00
commit 56434da37c
46 changed files with 9558 additions and 0 deletions

476
lib/aggregate.js Normal file
View File

@@ -0,0 +1,476 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
"use strict";
// THIS file should be identical with sql and history adapter's one
function initAggregate(options) {
//step; // 1 Step is 1 second
if (options.step === null) {
options.step = (options.end - options.start) / options.count;
}
// Limit 2000
if ((options.end - options.start) / options.step > options.limit) {
options.step = (options.end - options.start) / options.limit;
}
options.maxIndex = Math.ceil(((options.end - options.start) / options.step) - 1);
options.result = [];
options.averageCount = [];
options.aggregate = options.aggregate || 'minmax';
options.overallLength = 0;
// pre-fill the result with timestamps (add one before start and one after end)
for (var i = 0; i <= options.maxIndex + 2; i++){
options.result[i] = {
val: {ts: null, val: null},
max: {ts: null, val: null},
min: {ts: null, val: null},
start: {ts: null, val: null},
end: {ts: null, val: null}
};
if (options.aggregate === 'average') options.averageCount[i] = 0;
}
return options;
}
function aggregation(options, data) {
var index;
var preIndex;
for (var i = 0; i < data.length; i++) {
if (!data[i]) continue;
if (typeof data[i].ts !== 'number') data[i].ts = parseInt(data[i].ts, 10);
if (data[i].ts < 946681200000) data[i].ts *= 1000;
preIndex = Math.floor((data[i].ts - options.start) / options.step);
// store all border values
if (preIndex < 0) {
index = 0;
} else if (preIndex > options.maxIndex) {
index = options.maxIndex + 2;
} else {
index = preIndex + 1;
}
options.overallLength++;
if (!options.result[index]) {
console.error('Cannot find index ' + index);
continue;
}
if (options.aggregate === 'max') {
if (!options.result[index].val.ts) options.result[index].val.ts = Math.round(options.start + (((index - 1) + 0.5) * options.step));
if (options.result[index].val.val === null || options.result[index].val.val < data[i].val) options.result[index].val.val = data[i].val;
} else if (options.aggregate === 'min') {
if (!options.result[index].val.ts) options.result[index].val.ts = Math.round(options.start + (((index - 1) + 0.5) * options.step));
if (options.result[index].val.val === null || options.result[index].val.val > data[i].val) options.result[index].val.val = data[i].val;
} else if (options.aggregate === 'average') {
if (!options.result[index].val.ts) options.result[index].val.ts = Math.round(options.start + (((index - 1) + 0.5) * options.step));
options.result[index].val.val += parseFloat(data[i].val);
options.averageCount[index]++;
} else if (options.aggregate === 'total') {
if (!options.result[index].val.ts) options.result[index].val.ts = Math.round(options.start + (((index - 1) + 0.5) * options.step));
options.result[index].val.val += parseFloat(data[i].val);
} else if (options.aggregate === 'minmax') {
if (options.result[index].min.ts === null) {
options.result[index].min.ts = data[i].ts;
options.result[index].min.val = data[i].val;
options.result[index].max.ts = data[i].ts;
options.result[index].max.val = data[i].val;
options.result[index].start.ts = data[i].ts;
options.result[index].start.val = data[i].val;
options.result[index].end.ts = data[i].ts;
options.result[index].end.val = data[i].val;
} else {
if (data[i].val !== null) {
if (data[i].val > options.result[index].max.val) {
options.result[index].max.ts = data[i].ts;
options.result[index].max.val = data[i].val;
} else if (data[i].val < options.result[index].min.val) {
options.result[index].min.ts = data[i].ts;
options.result[index].min.val = data[i].val;
}
if (data[i].ts > options.result[index].end.ts) {
options.result[index].end.ts = data[i].ts;
options.result[index].end.val = data[i].val;
}
} else {
if (data[i].ts > options.result[index].end.ts) {
options.result[index].end.ts = data[i].ts;
options.result[index].end.val = null;
}
}
}
}
}
return {result: options.result, step: options.step, sourceLength: data.length} ;
}
function finishAggregation(options) {
if (options.aggregate === 'minmax') {
for (var ii = options.result.length - 1; ii >= 0; ii--) {
// no one value in this period
if (options.result[ii].start.ts === null) {
options.result.splice(ii, 1);
} else {
// just one value in this period: max == min == start == end
if (options.result[ii].start.ts === options.result[ii].end.ts) {
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
} else
if (options.result[ii].min.ts === options.result[ii].max.ts) {
// if just 2 values: start == min == max, end
if (options.result[ii].start.ts === options.result[ii].min.ts ||
options.result[ii].end.ts === options.result[ii].min.ts) {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
} // if just 3 values: start, min == max, end
else {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].max.ts,
val: options.result[ii].max.val
});
options.result.splice(ii + 2, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
}
} else
if (options.result[ii].start.ts === options.result[ii].max.ts) {
// just one value in this period: start == max, min == end
if (options.result[ii].min.ts === options.result[ii].end.ts) {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
} // start == max, min, end
else {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].min.ts,
val: options.result[ii].min.val
});
options.result.splice(ii + 2, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
}
} else
if (options.result[ii].end.ts === options.result[ii].max.ts) {
// just one value in this period: start == min, max == end
if (options.result[ii].min.ts === options.result[ii].start.ts) {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
} // start, min, max == end
else {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].min.ts,
val: options.result[ii].min.val
});
options.result.splice(ii + 2, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
}
} else
if (options.result[ii].start.ts === options.result[ii].min.ts ||
options.result[ii].end.ts === options.result[ii].min.ts) {
// just one value in this period: start == min, max, end
options.result.splice(ii + 1, 0, {
ts: options.result[ii].max.ts,
val: options.result[ii].max.val
});
options.result.splice(ii + 2, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
} else {
// just one value in this period: start == min, max, end
if (options.result[ii].max.ts > options.result[ii].min.ts) {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].min.ts,
val: options.result[ii].min.val
});
options.result.splice(ii + 2, 0, {
ts: options.result[ii].max.ts,
val: options.result[ii].max.val
});
} else {
options.result.splice(ii + 1, 0, {
ts: options.result[ii].max.ts,
val: options.result[ii].max.val
});
options.result.splice(ii + 2, 0, {
ts: options.result[ii].min.ts,
val: options.result[ii].min.val
});
}
options.result.splice(ii + 3, 0, {
ts: options.result[ii].end.ts,
val: options.result[ii].end.val
});
options.result[ii] = {
ts: options.result[ii].start.ts,
val: options.result[ii].start.val
};
}
}
}
} else if (options.aggregate === 'average') {
for (var k = options.result.length - 1; k >= 0; k--) {
if (options.result[k].val.ts) {
options.result[k] = {
ts: options.result[k].val.ts,
val: (options.result[k].val.val !== null) ? Math.round(options.result[k].val.val / options.averageCount[k] * 100) / 100 : null
};
} else {
// no one value in this interval
options.result.splice(k, 1);
}
}
} else {
for (var j = options.result.length - 1; j >= 0; j--) {
if (options.result[j].val.ts) {
options.result[j] = {
ts: options.result[j].val.ts,
val: options.result[j].val.val
};
} else {
// no one value in this interval
options.result.splice(j, 1);
}
}
}
beautify(options);
}
function beautify(options) {
var preFirstValue = null;
var postLastValue = null;
if (options.ignoreNull === 'true') options.ignoreNull = true; // include nulls and replace them with last value
if (options.ignoreNull === 'false') options.ignoreNull = false; // include nulls
if (options.ignoreNull === '0') options.ignoreNull = 0; // include nulls and replace them with 0
if (options.ignoreNull !== true && options.ignoreNull !== false && options.ignoreNull !== 0) options.ignoreNull = false;
// process null values, remove points outside the span and find first points after end and before start
for (var i = 0; i < options.result.length; i++) {
if (options.ignoreNull !== false) {
// if value is null
if (options.result[i].val === null) {
// null value must be replaced with last not null value
if (options.ignoreNull === true) {
// remove value
options.result.splice(i, 1);
i--;
continue;
} else {
// null value must be replaced with 0
options.result[i].val = options.ignoreNull;
}
}
}
// remove all not requested points
if (options.result[i].ts < options.start) {
preFirstValue = options.result[i].val !== null ? options.result[i] : null;
options.result.splice(i, 1);
i--;
continue;
}
postLastValue = options.result[i].val !== null ? options.result[i] : null;
if (options.result[i].ts > options.end) {
options.result.splice(i, options.result.length - i);
break;
}
}
// check start and stop
if (options.result.length && options.aggregate !== 'none') {
var firstTS = options.result[0].ts;
if (firstTS > options.start) {
if (preFirstValue) {
var firstY = options.result[0].val;
// if steps
if (options.aggregate === 'onchange' || !options.aggregate) {
if (preFirstValue.ts !== firstTS) {
options.result.unshift({ts: options.start, val: preFirstValue.val});
} else {
if (options.ignoreNull) {
options.result.unshift({ts: options.start, val: firstY});
}
}
} else {
if (preFirstValue.ts !== firstTS) {
if (firstY !== null) {
// interpolate
var y = preFirstValue.val + (firstY - preFirstValue.val) * (options.start - preFirstValue.ts) / (firstTS - preFirstValue.ts);
options.result.unshift({ts: options.start, val: y});
} else {
options.result.unshift({ts: options.start, val: null});
}
} else {
if (options.ignoreNull) {
options.result.unshift({ts: options.start, val: firstY});
}
}
}
} else {
if (options.ignoreNull) {
options.result.unshift({ts: options.start, val: options.result[0].val});
} else {
options.result.unshift({ts: options.start, val: null});
}
}
}
var lastTS = options.result[options.result.length - 1].ts;
if (lastTS < options.end) {
if (postLastValue) {
// if steps
if (options.aggregate === 'onchange' || !options.aggregate) {
// if more data following, draw line to the end of chart
if (postLastValue.ts !== lastTS) {
options.result.push({ts: options.end, val: postLastValue.val});
} else {
if (options.ignoreNull) {
options.result.push({ts: options.end, val: postLastValue.val});
}
}
} else {
if (postLastValue.ts !== lastTS) {
var lastY = options.result[options.result.length - 1].val;
if (lastY !== null) {
// make interpolation
var _y = lastY + (postLastValue.val - lastY) * (options.end - lastTS) / (postLastValue.ts - lastTS);
options.result.push({ts: options.end, val: _y});
} else {
options.result.push({ts: options.end, val: null});
}
} else {
if (options.ignoreNull) {
options.result.push({ts: options.end, val: postLastValue.val});
}
}
}
} else {
if (options.ignoreNull) {
var lastY = options.result[options.result.length - 1].val;
// if no more data, that means do not draw line
options.result.push({ts: options.end, val: lastY});
} else {
// if no more data, that means do not draw line
options.result.push({ts: options.end, val: null});
}
}
}
}
else if (options.aggregate === 'none') {
if ((options.count) && (options.result.length > options.count)) {
options.result = options.result.slice(0, options.count);
}
}
}
function sendResponse(adapter, msg, options, data, startTime) {
var aggregateData;
if (typeof data === 'string') {
adapter.log.error(data);
return adapter.sendTo(msg.from, msg.command, {
result: [],
step: 0,
error: data,
sessionId: options.sessionId
}, msg.callback);
}
if (options.count && !options.start && data.length > options.count) {
data.splice(0, data.length - options.count);
}
if (data[0]) {
options.start = options.start || data[0].ts;
if (!options.aggregate || options.aggregate === 'onchange' || options.aggregate === 'none') {
aggregateData = {result: data, step: 0, sourceLength: data.length};
// convert ack from 0/1 to false/true
if (options.ack && aggregateData.result) {
for (var i = 0; i < aggregateData.result.length; i++) {
aggregateData.result[i].ack = !!aggregateData.result[i].ack;
}
}
options.result = aggregateData.result;
beautify(options);
if ((options.aggregate === 'none') && (options.count) && (options.result.length > options.count)) {
options.result = options.result.slice(0, options.count);
}
aggregateData.result = options.result;
} else {
initAggregate(options);
aggregateData = aggregation(options, data);
finishAggregation(options);
aggregateData.result = options.result;
}
adapter.log.debug('Send: ' + aggregateData.result.length + ' of: ' + aggregateData.sourceLength + ' in: ' + (new Date().getTime() - startTime) + 'ms');
adapter.sendTo(msg.from, msg.command, {
result: aggregateData.result,
step: aggregateData.step,
error: null,
sessionId: options.sessionId
}, msg.callback);
} else {
adapter.log.info('No Data');
adapter.sendTo(msg.from, msg.command, {result: [], step: null, error: null, sessionId: options.sessionId}, msg.callback);
}
}
module.exports.sendResponse = sendResponse;
module.exports.initAggregate = initAggregate;
module.exports.aggregation = aggregation;
module.exports.beautify = beautify;
module.exports.finishAggregation = finishAggregation;

86
lib/mssql-client.js Normal file
View File

@@ -0,0 +1,86 @@
// Generated by CoffeeScript 1.10.0
(function() {
var ConnectionFactory, SQLClient, SQLClientPool, MSSQLClient, MSSQLClientPool, MSSQLConnectionFactory, mssql,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty,
slice = [].slice;
SQLClient = require('sql-client/lib/sql-client').SQLClient;
SQLClientPool = require('sql-client/lib/sql-client-pool').SQLClientPool;
ConnectionFactory = require('sql-client/lib/connection-factory').ConnectionFactory;
mssql = require('mssql');
MSSQLConnectionFactory = (function(superClass) {
extend(MSSQLConnectionFactory, superClass);
function MSSQLConnectionFactory() {
this.execute = bind(this.execute, this);
this.open_connection = bind(this.open_connection, this);
return MSSQLConnectionFactory.__super__.constructor.apply(this, arguments);
}
MSSQLConnectionFactory.prototype.open_connection = function(config, callback) {
var connection;
var pos = config.server.indexOf(':');
if (pos != -1) {
config.port = parseInt(config.server.substring(pos + 1), 10);
config.server = config.server.substring(0, pos);
}
return connection = new mssql.Connection(config, function (err) {
if (err != null) {
return callback(err);
} else {
return callback(null, connection);
}
}.bind(this));
};
MSSQLConnectionFactory.prototype.execute = function(connection, sql, bindvars, callback) {
var request = new mssql.Request(connection);
return request.query(sql, function(err, recordset) {
callback(err, recordset);
});
};
return MSSQLConnectionFactory;
})(ConnectionFactory);
MSSQLClient = (function(superClass) {
extend(MSSQLClient, superClass);
function MSSQLClient() {
var options;
options = 1 <= arguments.length ? slice.call(arguments, 0) : [];
MSSQLClient.__super__.constructor.apply(this, slice.call(options).concat([new MSSQLConnectionFactory()]));
}
return MSSQLClient;
})(SQLClient);
MSSQLClientPool = (function(superClass) {
extend(MSSQLClientPool, superClass);
function MSSQLClientPool() {
var options;
options = 1 <= arguments.length ? slice.call(arguments, 0) : [];
MSSQLClientPool.__super__.constructor.apply(this, slice.call(options).concat([new MSSQLConnectionFactory()]));
}
return MSSQLClientPool;
})(SQLClientPool);
exports.MSSQLConnectionFactory = MSSQLConnectionFactory;
exports.MSSQLClient = MSSQLClient;
exports.MSSQLClientPool = MSSQLClientPool;
}).call(this);

151
lib/mssql.js Normal file
View File

@@ -0,0 +1,151 @@
exports.init = function (dbname) {
return [
"CREATE DATABASE " + dbname + ";",
"CREATE TABLE " + dbname + ".dbo.sources (id INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1), name varchar(255));",
"CREATE TABLE " + dbname + ".dbo.datapoints (id INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1), name varchar(255), type INTEGER);",
"CREATE TABLE " + dbname + ".dbo.ts_number (id INTEGER, ts BIGINT, val REAL, ack BIT, _from INTEGER, q INTEGER);",
"CREATE TABLE " + dbname + ".dbo.ts_string (id INTEGER, ts BIGINT, val TEXT, ack BIT, _from INTEGER, q INTEGER);",
"CREATE TABLE " + dbname + ".dbo.ts_bool (id INTEGER, ts BIGINT, val BIT, ack BIT, _from INTEGER, q INTEGER);"
];
};
exports.destroy = function (dbname) {
return [
"DROP TABLE " + dbname + ".dbo.ts_number;",
"DROP TABLE " + dbname + ".dbo.ts_string;",
"DROP TABLE " + dbname + ".dbo.ts_bool;",
"DROP TABLE " + dbname + ".dbo.sources;",
"DROP TABLE " + dbname + ".dbo.datapoints;",
"DROP DATABASE " + dbname + ";",
"DBCC FREEPROCCACHE;"
];
};
exports.getFirstTs = function (dbname, db) {
return "SELECT id, MIN(ts) AS ts FROM " + dbname + ".dbo." + db + " GROUP BY id;";
};
exports.insert = function (dbname, index, state, from, db) {
if (state.val === null) state.val = 'NULL';
else if (db === "ts_bool") state.val = state.val ? 1 : 0;
else if (db === "ts_string") state.val = "'" + state.val.toString().replace(/'/g, '') + "'";
return "INSERT INTO " + dbname + ".dbo." + db + " (id, ts, val, ack, _from, q) VALUES(" + index + ", " + state.ts + ", " + state.val + ", " + (state.ack ? 1 : 0) + ", " + (from || 0) + ", " + (state.q || 0) + ");";
};
exports.retention = function (dbname, index, db, retention) {
var d = new Date();
d.setSeconds(-retention);
var query = "DELETE FROM " + dbname + ".dbo." + db + " WHERE";
query += " id=" + index;
query += " AND ts < " + d.getTime();
query += ";";
return query;
};
exports.getIdSelect = function (dbname, name) {
if (!name) {
return "SELECT id, type, name FROM " + dbname + ".dbo.datapoints;";
} else {
return "SELECT id, type, name FROM " + dbname + ".dbo.datapoints WHERE name='" + name + "';";
}
};
exports.getIdInsert = function (dbname, name, type) {
return "INSERT INTO " + dbname + ".dbo.datapoints (name, type) VALUES('" + name + "', " + type + ");";
};
exports.getIdUpdate = function (dbname, id, type) {
return "UPDATE " + dbname + ".dbo.datapoints SET type = " + type + " WHERE id = " + id + ";";
};
exports.getFromSelect = function (dbname, from) {
return "SELECT id FROM " + dbname + ".dbo.sources WHERE name='" + from + "';";
};
exports.getFromInsert = function (dbname, from) {
return "INSERT INTO " + dbname + ".dbo.sources (name) VALUES('" + from + "');";
};
exports.getHistory = function (dbname, db, options) {
var query = "SELECT ";
if (!options.start && options.count) {
query += " TOP " + options.count;
}
query += " ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", " + dbname + ".dbo.sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM " + dbname + ".dbo." + db;
if (options.from) {
query += " INNER JOIN " + dbname + ".dbo.sources ON " + dbname + ".dbo.sources.id=" + dbname + ".dbo." + db + "._from";
}
var where = "";
if (options.id) {
where += " " + dbname + ".dbo." + db + ".id=" + options.id;
}
if (options.end) {
where += (where ? " AND" : "") + " " + dbname + ".dbo." + db + ".ts < " + options.end;
}
if (options.start) {
where += (where ? " AND" : "") + " " + dbname + ".dbo." + db + ".ts >= " + options.start;
// add last value before start
var subQuery;
var subWhere;
subQuery = " SELECT TOP 1 ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", " + dbname + ".dbo.sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM " + dbname + ".dbo." + db;
if (options.from) {
subQuery += " INNER JOIN " + dbname + ".dbo.sources ON " + dbname + ".dbo.sources.id=" + dbname + ".dbo." + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " " + dbname + ".dbo." + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " " + dbname + ".dbo." + db + ".ts < " + options.start;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY " + dbname + ".dbo." + db + ".ts DESC";
where += " UNION SELECT * FROM (" + subQuery + ") a";
// add next value after end
subQuery = " SELECT TOP 1 ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", " + dbname + ".dbo.sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM " + dbname + ".dbo." + db;
if (options.from) {
subQuery += " INNER JOIN " + dbname + ".dbo.sources ON " + dbname + ".dbo.sources.id=" + dbname + ".dbo." + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " " + dbname + ".dbo." + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " " + dbname + ".dbo." + db + ".ts >= " + options.end;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY " + dbname + ".dbo." + db + ".ts ASC";
where += " UNION SELECT * FROM (" + subQuery + ") b";
}
if (where) query += " WHERE " + where;
query += " ORDER BY ts";
if (!options.start && options.count) {
query += " DESC";
} else {
query += " ASC";
}
query += ";";
return query;
};

146
lib/mysql.js Normal file
View File

@@ -0,0 +1,146 @@
exports.init = function (dbname) {
return [
"CREATE DATABASE `" + dbname + "` DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;",
"CREATE TABLE `" + dbname + "`.sources (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name TEXT);",
"CREATE TABLE `" + dbname + "`.datapoints (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name TEXT, type INTEGER);",
"CREATE TABLE `" + dbname + "`.ts_number (id INTEGER, ts BIGINT, val REAL, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));",
"CREATE TABLE `" + dbname + "`.ts_string (id INTEGER, ts BIGINT, val TEXT, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));",
"CREATE TABLE `" + dbname + "`.ts_bool (id INTEGER, ts BIGINT, val BOOLEAN, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));"
];
};
exports.destroy = function (dbname) {
return [
"DROP TABLE `" + dbname + "`.ts_number;",
"DROP TABLE `" + dbname + "`.ts_string;",
"DROP TABLE `" + dbname + "`.ts_bool;",
"DROP TABLE `" + dbname + "`.sources;",
"DROP TABLE `" + dbname + "`.datapoints;",
"DROP DATABASE `" + dbname + "`;"
];
};
exports.getFirstTs = function (dbname, db) {
return "SELECT id, MIN(ts) AS ts FROM `" + dbname + "`." + db + " GROUP BY id;";
};
exports.insert = function (dbname, index, state, from, db) {
if (state.val === null) state.val = 'NULL';
else if (db === "ts_string") state.val = "'" + state.val.toString().replace(/'/g, '') + "'";
return "INSERT INTO `" + dbname + "`." + db + " (id, ts, val, ack, _from, q) VALUES(" + index + ", " + state.ts + ", " + state.val + ", " + (state.ack ? 1 : 0) + ", " + (from || 0) + ", " + (state.q || 0) + ");";
};
exports.retention = function (dbname, index, db, retention) {
var d = new Date();
d.setSeconds(-retention);
var query = "DELETE FROM `" + dbname + "`." + db + " WHERE";
query += " id=" + index;
query += " AND ts < " + d.getTime();
query += ";";
return query;
};
exports.getIdSelect = function (dbname, name) {
if (!name) {
return "SELECT id, type, name FROM `" + dbname + "`.datapoints;";
} else {
return "SELECT id, type, name FROM `" + dbname + "`.datapoints WHERE name='" + name + "';";
}
};
exports.getIdInsert = function (dbname, name, type) {
return "INSERT INTO `" + dbname + "`.datapoints (name, type) VALUES('" + name + "', " + type + ");";
};
exports.getIdUpdate = function (dbname, id, type) {
return "UPDATE `" + dbname + "`.datapoints SET type = " + type + " WHERE id = " + id + ";";
};
exports.getFromSelect = function (dbname, from) {
return "SELECT id FROM `" + dbname + "`.sources WHERE name='" + from + "';";
};
exports.getFromInsert = function (dbname, from) {
return "INSERT INTO `" + dbname + "`.sources (name) VALUES('" + from + "');";
};
exports.getHistory = function (dbname, db, options) {
var query = "SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", `" + dbname + "`.sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM `" + dbname + "`." + db;
if (options.from) {
query += " INNER JOIN `" + dbname + "`.sources ON `" + dbname + "`.sources.id=`" + dbname + "`." + db + "._from";
}
var where = "";
if (options.id) {
where += " `" + dbname + "`." + db + ".id=" + options.id;
}
if (options.end) {
where += (where ? " AND" : "") + " `" + dbname + "`." + db + ".ts < " + options.end;
}
if (options.start) {
where += (where ? " AND" : "") + " `" + dbname + "`." + db + ".ts >= " + options.start;
var subQuery;
var subWhere;
subQuery = " SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", `" + dbname + "`.sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM `" + dbname + "`." + db;
if (options.from) {
subQuery += " INNER JOIN `" + dbname + "`.sources ON `" + dbname + "`.sources.id=`" + dbname + "`." + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " `" + dbname + "`." + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " `" + dbname + "`." + db + ".ts < " + options.start;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY `" + dbname + "`." + db + ".ts DESC LIMIT 1";
where += " UNION (" + subQuery + ")";
//add next value after end
subQuery = " SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", `" + dbname + "`.sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM `" + dbname + "`." + db;
if (options.from) {
subQuery += " INNER JOIN `" + dbname + "`.sources ON `" + dbname + "`.sources.id=`" + dbname + "`." + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " `" + dbname + "`." + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " `" + dbname + "`." + db + ".ts >= " + options.end;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY `" + dbname + "`." + db + ".ts ASC LIMIT 1";
where += " UNION (" + subQuery + ")";
}
if (where) query += " WHERE " + where;
query += " ORDER BY ts";
if (!options.start && options.count) {
query += " DESC LIMIT " + options.count;
} else {
query += " ASC";
}
query += ";";
return query;
};

158
lib/postgresql-client.js Normal file
View File

@@ -0,0 +1,158 @@
// Generated by CoffeeScript 1.10.0
(function() {
var ConnectionFactory, PostgreSQLClient, PostgreSQLClient2, PostgreSQLClientPool, PostgreSQLClientPool2, PostgreSQLConnectionFactory, PostgreSQLConnectionFactory2, SQLClient, SQLClientPool, pg,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty,
slice = [].slice;
SQLClient = require('sql-client/lib/sql-client').SQLClient;
SQLClientPool = require('sql-client/lib/sql-client-pool').SQLClientPool;
ConnectionFactory = require('sql-client/lib/connection-factory').ConnectionFactory;
pg = require('pg');
try {
if ((pg != null ? pg["native"] : void 0) != null) {
pg = pg["native"];
}
} catch(err) {
}
PostgreSQLConnectionFactory = (function(superClass) {
extend(PostgreSQLConnectionFactory, superClass);
function PostgreSQLConnectionFactory() {
this.pre_process_sql = bind(this.pre_process_sql, this);
this.open_connection = bind(this.open_connection, this);
return PostgreSQLConnectionFactory.__super__.constructor.apply(this, arguments);
}
PostgreSQLConnectionFactory.prototype.open_connection = function(connect_string, callback) {
var connection;
connection = new pg.Client(connect_string);
return connection.connect((function(_this) {
return function(err) {
return callback(err, connection);
};
})(this));
};
PostgreSQLConnectionFactory.prototype.pre_process_sql = function(sql, bindvars, callback) {
var index;
if ((sql != null) && (bindvars != null)) {
index = 1;
sql = sql.replace(/\?/g, (function() {
return '$' + index++;
}));
}
return callback(null, sql, bindvars);
};
return PostgreSQLConnectionFactory;
})(ConnectionFactory);
PostgreSQLClient = (function(superClass) {
extend(PostgreSQLClient, superClass);
function PostgreSQLClient() {
var options;
options = 1 <= arguments.length ? slice.call(arguments, 0) : [];
PostgreSQLClient.__super__.constructor.apply(this, slice.call(options).concat([new PostgreSQLConnectionFactory()]));
}
return PostgreSQLClient;
})(SQLClient);
PostgreSQLClientPool = (function(superClass) {
extend(PostgreSQLClientPool, superClass);
function PostgreSQLClientPool() {
var options;
options = 1 <= arguments.length ? slice.call(arguments, 0) : [];
PostgreSQLClientPool.__super__.constructor.apply(this, slice.call(options).concat([new PostgreSQLConnectionFactory()]));
}
return PostgreSQLClientPool;
})(SQLClientPool);
exports.PostgreSQLConnectionFactory = PostgreSQLConnectionFactory;
exports.PostgreSQLClient = PostgreSQLClient;
exports.PostgreSQLClientPool = PostgreSQLClientPool;
PostgreSQLConnectionFactory2 = (function(superClass) {
extend(PostgreSQLConnectionFactory2, superClass);
function PostgreSQLConnectionFactory2() {
this.close_connection = bind(this.close_connection, this);
this.open_connection = bind(this.open_connection, this);
return PostgreSQLConnectionFactory2.__super__.constructor.apply(this, arguments);
}
PostgreSQLConnectionFactory2.prototype.open_connection = function(connect_string, callback) {
return pg.connect(connect_string, (function(_this) {
return function(err, client, done_fn) {
var connection;
connection = client;
if (connection != null) {
connection._sqlclient_done = done_fn;
}
return callback(err, connection);
};
})(this));
};
PostgreSQLConnectionFactory2.prototype.close_connection = function(connection, callback) {
if ((connection != null ? connection._sqlclient_done : void 0) != null) {
connection._sqlclient_done();
return typeof callback === "function" ? callback(null) : void 0;
} else {
return PostgreSQLConnectionFactory2.__super__.close_connection.apply(this, arguments).close_connection(connection, callback);
}
};
return PostgreSQLConnectionFactory2;
})(PostgreSQLConnectionFactory);
PostgreSQLClient2 = (function(superClass) {
extend(PostgreSQLClient2, superClass);
function PostgreSQLClient2() {
var options;
options = 1 <= arguments.length ? slice.call(arguments, 0) : [];
PostgreSQLClient2.__super__.constructor.apply(this, slice.call(options).concat([new PostgreSQLConnectionFactory2()]));
}
return PostgreSQLClient2;
})(SQLClient);
PostgreSQLClientPool2 = (function(superClass) {
extend(PostgreSQLClientPool2, superClass);
function PostgreSQLClientPool2() {
var options;
options = 1 <= arguments.length ? slice.call(arguments, 0) : [];
PostgreSQLClientPool2.__super__.constructor.apply(this, slice.call(options).concat([new PostgreSQLConnectionFactory2()]));
}
return PostgreSQLClientPool2;
})(SQLClientPool);
exports.PostgreSQLConnectionFactory2 = PostgreSQLConnectionFactory2;
exports.PostgreSQLClient2 = PostgreSQLClient2;
exports.PostgreSQLClientPool2 = PostgreSQLClientPool2;
}).call(this);

145
lib/postgresql.js Normal file
View File

@@ -0,0 +1,145 @@
exports.init = function (dbname) {
return [
"CREATE TABLE sources (id SERIAL NOT NULL PRIMARY KEY, name TEXT);",
"CREATE TABLE datapoints (id SERIAL NOT NULL PRIMARY KEY, name TEXT, type INTEGER);",
"CREATE TABLE ts_number (id INTEGER NOT NULL, ts BIGINT, val REAL, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));",
"CREATE TABLE ts_string (id INTEGER NOT NULL, ts BIGINT, val TEXT, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));",
"CREATE TABLE ts_bool (id INTEGER NOT NULL, ts BIGINT, val BOOLEAN, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));"
];
};
exports.destroy = function (dbname) {
return [
"DROP TABLE ts_number;",
"DROP TABLE ts_string;",
"DROP TABLE ts_bool;",
"DROP TABLE sources;",
"DROP TABLE datapoints;"
];
};
exports.getFirstTs = function (dbname, db) {
return "SELECT id, MIN(ts) AS ts FROM " + db + " GROUP BY id;";
};
exports.insert = function (dbname, index, state, from, db) {
if (state.val === null) state.val = 'NULL';
else if (db === "ts_string") state.val = "'" + state.val.toString().replace(/'/g, '') + "'";
return "INSERT INTO " + db + " (id, ts, val, ack, _from, q) VALUES(" + index + ", " + state.ts + ", " + state.val + ", " + (!!state.ack) + ", " + (from || 0) + ", " + (state.q || 0) + ");";
};
exports.retention = function (dbname, index, db, retention) {
var d = new Date();
d.setSeconds(-retention);
var query = "DELETE FROM " + db + " WHERE";
query += " id=" + index;
query += " AND ts < " + d.getTime();
query += ";";
return query;
};
exports.getIdSelect = function (dbname, name) {
if (!name) {
return "SELECT id, type, name FROM datapoints;";
} else {
return "SELECT id, type, name FROM datapoints WHERE name='" + name + "';";
}
};
exports.getIdInsert = function (dbname, name, type) {
return "INSERT INTO datapoints (name, type) VALUES('" + name + "', " + type + ");";
};
exports.getIdUpdate = function (dbname, id, type) {
return "UPDATE datapoints SET type = " + type + " WHERE id = " + id + ";";
};
exports.getFromSelect = function (dbname, from) {
return "SELECT id FROM sources WHERE name='" + from + "';";
};
exports.getFromInsert = function (dbname, from) {
return "INSERT INTO sources (name) VALUES('" + from + "');";
};
exports.getHistory = function (dbname, db, options) {
var query = "SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", sources.name as from") : "") +
(options.q ? ", q" : "") + " FROM " + db;
if (options.from) {
query += " INNER JOIN sources ON sources.id=" + db + "._from";
}
var where = "";
if (options.id) {
where += " " + db + ".id=" + options.id;
}
if (options.end) {
where += (where ? " AND" : "") + " " + db + ".ts < " + options.end;
}
if (options.start) {
where += (where ? " AND" : "") + " " + db + ".ts >= " + options.start;
//add last value before start
var subQuery;
var subWhere;
subQuery = " SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", sources.name as from") : "") +
(options.q ? ", q" : "") + " FROM " + db;
if (options.from) {
subQuery += " INNER JOIN sources ON sources.id=" + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " " + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " " + db + ".ts < " + options.start;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY " + db + ".ts DESC LIMIT 1";
where += " UNION (" + subQuery + ")";
//add next value after end
subQuery = " SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", sources.name as from") : "") +
(options.q ? ", q" : "") + " FROM " + db;
if (options.from) {
subQuery += " INNER JOIN sources ON sources.id=" + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " " + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " " + db + ".ts >= " + options.end;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY " + db + ".ts ASC LIMIT 1";
where += " UNION (" + subQuery + ")";
}
if (where) query += " WHERE " + where;
query += " ORDER BY ts";
if (!options.start && options.count) {
query += " DESC LIMIT " + options.count;
} else {
query += " ASC";
}
query += ";";
return query;
};

149
lib/sqlite.js Normal file
View File

@@ -0,0 +1,149 @@
exports.init = function (dbname) {
return [
"CREATE TABLE sources (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT);",
"CREATE TABLE datapoints (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT, type INTEGER);",
"CREATE TABLE ts_number (id INTEGER, ts INTEGER, val REAL, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));",
"CREATE TABLE ts_string (id INTEGER, ts INTEGER, val TEXT, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));",
"CREATE TABLE ts_bool (id INTEGER, ts INTEGER, val BOOLEAN, ack BOOLEAN, _from INTEGER, q INTEGER, PRIMARY KEY(id, ts));"
];
};
exports.destroy = function (dbname) {
return [
"DROP TABLE ts_number;",
"DROP TABLE ts_string;",
"DROP TABLE ts_bool;",
"DROP TABLE sources;",
"DROP TABLE datapoints;"
];
};
exports.getFirstTs = function (dbname, db) {
return "SELECT id, MIN(ts) AS ts FROM " + db + " GROUP BY id;";
};
exports.insert = function (dbname, index, state, from, db) {
if (state.val === null) state.val = 'NULL';
else if (db === "ts_bool") state.val = state.val ? 1 : 0;
else if (db === "ts_string") state.val = "'" + state.val.toString().replace(/'/g, '') + "'";
return "INSERT INTO " + db + " (id, ts, val, ack, _from, q) VALUES(" + index + ", " + state.ts + ", " + state.val + ", " + (state.ack ? 1 : 0) + ", " + (from || 0) + ", " + (state.q || 0) + ");";
};
exports.retention = function (dbname, index, db, retention) {
var d = new Date();
d.setSeconds(-retention);
var query = "DELETE FROM " + db + " WHERE";
query += " id=" + index;
query += " AND ts < " + d.getTime();
query += ";";
return query;
};
exports.getIdSelect = function (dbname, name) {
if (!name) {
return "SELECT id, type, name FROM datapoints;";
} else {
return "SELECT id, type, name FROM datapoints WHERE name='" + name + "';";
}
};
exports.getIdInsert = function (dbname, name, type) {
return "INSERT INTO datapoints (name, type) VALUES('" + name + "', " + type + ");";
};
exports.getIdUpdate = function (dbname, id, type) {
return "UPDATE datapoints SET type = " + type + " WHERE id = " + id + ";";
};
exports.getFromSelect = function (dbname, from) {
if (!from) {
return "SELECT id, name FROM sources;";
} else {
return "SELECT id FROM sources WHERE name='" + from + "';";
}
};
exports.getFromInsert = function (dbname, from) {
return "INSERT INTO sources (name) VALUES('" + from + "');";
};
exports.getHistory = function (dbname, db, options) {
var query = "SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM " + db;
if (options.from) {
query += " INNER JOIN sources ON sources.id=" + db + "._from";
}
var where = "";
if (options.id) {
where += " " + db + ".id=" + options.id;
}
if (options.end) {
where += (where ? " AND" : "") + " " + db + ".ts < " + options.end;
}
if (options.start) {
where += (where ? " AND" : "") + " " + db + ".ts >= " + options.start;
//add last value before start
var subQuery;
var subWhere;
subQuery = " SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM " + db;
if (options.from) {
subQuery += " INNER JOIN sources ON sources.id=" + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " " + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " " + db + ".ts < " + options.start;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY " + db + ".ts DESC LIMIT 1";
where += " UNION SELECT * from (" + subQuery + ")";
//add next value after end
subQuery = " SELECT ts, val" +
(!options.id ? (", " + db + ".id as id") : "") +
(options.ack ? ", ack" : "") +
(options.from ? (", sources.name as 'from'") : "") +
(options.q ? ", q" : "") + " FROM " + db;
if (options.from) {
subQuery += " INNER JOIN sources ON sources.id=" + db + "._from";
}
subWhere = "";
if (options.id) {
subWhere += " " + db + ".id=" + options.id;
}
if (options.ignoreNull) {
//subWhere += (subWhere ? " AND" : "") + " val <> NULL";
}
subWhere += (subWhere ? " AND" : "") + " " + db + ".ts >= " + options.end;
if (subWhere) subQuery += " WHERE " + subWhere;
subQuery += " ORDER BY " + db + ".ts ASC LIMIT 1";
where += " UNION SELECT * from (" + subQuery + ") ";
}
if (where) query += " WHERE " + where;
query += " ORDER BY ts";
if (!options.start && options.count) {
query += " DESC LIMIT " + options.count;
} else {
query += " ASC";
}
query += ";";
return query;
};

83
lib/utils.js Normal file
View File

@@ -0,0 +1,83 @@
'use strict';
const fs = require('fs');
const path = require('path');
let controllerDir;
let appName;
/**
* returns application name
*
* The name of the application can be different and this function finds it out.
*
* @returns {string}
*/
function getAppName() {
const parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 2].split('.')[0];
}
/**
* looks for js-controller home folder
*
* @param {boolean} isInstall
* @returns {string}
*/
function getControllerDir(isInstall) {
// Find the js-controller location
const possibilities = [
'yunkong2.js-controller',
'yunkong2.js-controller',
];
/** @type {string} */
let controllerPath;
for (const pkg of possibilities) {
try {
const possiblePath = require.resolve(pkg);
if (fs.existsSync(possiblePath)) {
controllerPath = possiblePath;
break;
}
} catch (e) { /* not found */ }
}
if (controllerPath == null) {
if (!isInstall) {
console.log('Cannot find js-controller');
process.exit(10);
} else {
process.exit();
}
}
// we found the controller
return path.dirname(controllerPath);
}
/**
* reads controller base settings
*
* @alias getConfig
* @returns {object}
*/
function getConfig() {
let configPath;
if (fs.existsSync(
configPath = path.join(controllerDir, 'conf', appName + '.json')
)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
} else if (fs.existsSync(
configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
} else {
throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
}
}
appName = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));
exports.controllerDir = controllerDir;
exports.getConfig = getConfig;
exports.Adapter = adapter;
exports.appName = appName;