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

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
node_modules
.idea
tmp
admin/i18n/flat.txt
admin/i18n/*/flat.txt
iob_npm.done
package-lock.json

9
.npmignore Normal file
View File

@@ -0,0 +1,9 @@
gulpfile.js
tasks
tmp
test
.travis.yml
appveyor.yml
admin/i18n
iob_npm.done
package-lock.json

34
.travis.yml Normal file
View File

@@ -0,0 +1,34 @@
os:
- linux
- osx
language: node_js
node_js:
- '4'
- '6'
- '8'
- '10'
services:
- mysql
- postgresql
before_install:
- 'if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export export CXX=g++-4.8; fi'
before_script:
- export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1)
- 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm@5; fi'
- npm -v
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi'
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install mariadb && mysql.server start; fi'
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then rm -rf /usr/local/var/postgres; fi'
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then initdb /usr/local/var/postgres; fi'
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pg_ctl -D /usr/local/var/postgres start; fi'
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sleep 5; fi'
- 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then createuser -s postgres; fi'
- sleep 15
- npm install winston@2.3.1
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2018 bluefox <dogafox@gmail.com>, Apollon77
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

506
README.md Normal file
View File

@@ -0,0 +1,506 @@
![Logo](admin/sql.png)
# yunkong2.sql
==================================
[![NPM version](http://img.shields.io/npm/v/yunkong2.sql.svg)](https://www.npmjs.com/package/yunkong2.sql)
[![Downloads](https://img.shields.io/npm/dm/yunkong2.sql.svg)](https://www.npmjs.com/package/yunkong2.sql)
[![Tests](https://travis-ci.org/yunkong2/yunkong2.sql.svg?branch=master)](https://travis-ci.org/yunkong2/yunkong2.sql)
[![NPM](https://nodei.co/npm/yunkong2.sql.png?downloads=true)](https://nodei.co/npm/yunkong2.sql/)
This adapter saves state history into SQL DB.
Supports PostgreSQL, mysql, Microsoft SQL Server and sqlite.
You can leave port 0 if default port is desired.
### MS-SQL:
Use ```localhost\instance``` for host and check that TCP/IP connections are enabled.
https://msdn.microsoft.com/en-us/library/bb909712(v=vs.90).aspx
### SQLite:
is "file"-DB and cannot manage too many events. If you have a big amount of data use real DB, like PostgreSQL and co.
SQLite DB must not be installed extra. It is just a file on disk, but to install it you require build tools on your system. For linux, just write:
```
sudo apt-get install build-essential
```
For windows:
```
c:\>npm install --global --production windows-build-tools
```
And then reinstall the adapter, e.g:
```
cd /opt/yunkong2
yunkong2 stop sql
npm install yunkong2.sql --production
yunkong2 start sql
```
### MySQL:
You can install mysql on linux systems:
```
apt-get install mysql-server mysql-client
mysql -uroot -p
CREATE USER 'yunkong2'@'%' IDENTIFIED BY 'yunkong2';
GRANT ALL PRIVILEGES ON * . * TO 'yunkong2'@'%';
FLUSH PRIVILEGES;
```
If required edit */etc/mysql/my.cnf* to set bind to IP-Address for remote connect.
**Warning**: yunkong2 user is "admin". If required give limited rights to yunkong2 user.
## Structure of the DBs
Default Database name is "yunkong2", but it can be changed in configuration.
### Sources
This table is a list of adapter's instances, that wrote the entries. (state.from)
| DB | Name in query |
|------------|----------------------|
| MS-SQL | yunkong2.dbo.sources |
| MySQL | yunkong2.sources |
| PostgreSQL | sources |
| SQLite | sources |
Structure:
| Field | Type | Description |
|-------|--------------------------------------------|-------------------------------------------|
| id | INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1) | unique ID |
| name | varchar(255) / TEXT | instance of adapter, that wrote the entry |
*Note:* MS-SQL uses varchar(255), and others use TEXT
### Datapoints
This table is a list of datapoints. (IDs)
| DB | Name in query |
|------------|-------------------------|
| MS-SQL | yunkong2.dbo.datapoints |
| MySQL | yunkong2.datapoints |
| PostgreSQL | datapoints |
| SQLite | datapoints |
Structure:
| Field | Type | Description |
|-------|--------------------------------------------|-------------------------------------------------|
| id | INTEGER NOT NULL PRIMARY KEY IDENTITY(1,1) | unique ID |
| name | varchar(255) / TEXT | ID of variable, e.g. hm-rpc.0.JEQ283747.1.STATE |
| type | INTEGER | 0 - number, 1 - string, 2 - boolean |
*Note:* MS-SQL uses varchar(255), and others use TEXT
### Numbers
Values for states with type "number". **ts** means "time series".
| DB | Name in query |
|------------|-------------------------|
| MS-SQL | yunkong2.dbo.ts_number |
| MySQL | yunkong2.ts_number |
| PostgreSQL | ts_number |
| SQLite | ts_number |
Structure:
| Field | Type | Description |
|--------|--------------------------------------------|-------------------------------------------------|
| id | INTEGER | ID of state from "Datapoints" table |
| ts | BIGINT / INTEGER | Time in ms till epoch. Can be converted to time with "new Date(ts)" |
| val | REAL | Value |
| ack | BIT/BOOLEAN | Is acknowledged: 0 - not ack, 1 - ack |
| _from | INTEGER | ID of source from "Sources" table |
| q | INTEGER | Quality as number. You can find description [here](https://git.spacen.net/yunkong2/yunkong2/blob/master/doc/SCHEMA.md#states) |
*Note:* MS-SQL uses BIT, and others use BOOLEAN. SQLite uses for ts INTEGER and all others BIGINT.
### Strings
Values for states with type "string".
| DB | Name in query |
|------------|-------------------------|
| MS-SQL | yunkong2.dbo.ts_string |
| MySQL | yunkong2.ts_string |
| PostgreSQL | ts_string |
| SQLite | ts_string |
Structure:
| Field | Type | Description |
|--------|--------------------------------------------|-------------------------------------------------|
| id | INTEGER | ID of state from "Datapoints" table |
| ts | BIGINT | Time in ms till epoch. Can be converted to time with "new Date(ts)" |
| val | TEXT | Value |
| ack | BIT/BOOLEAN | Is acknowledged: 0 - not ack, 1 - ack |
| _from | INTEGER | ID of source from "Sources" table |
| q | INTEGER | Quality as number. You can find description [here](https://git.spacen.net/yunkong2/yunkong2/blob/master/doc/SCHEMA.md#states) |
*Note:* MS-SQL uses BIT, and others use BOOLEAN. SQLite uses for ts INTEGER and all others BIGINT.
### Booleans
Values for states with type "boolean".
| DB | Name in query |
|------------|-------------------------|
| MS-SQL | yunkong2.dbo.ts_bool |
| MySQL | yunkong2.ts_bool |
| PostgreSQL | ts_bool |
| SQLite | ts_bool |
Structure:
| Field | Type | Description |
|--------|--------------------------------------------|-------------------------------------------------|
| id | INTEGER | ID of state from "Datapoints" table |
| ts | BIGINT | Time in ms till epoch. Can be converted to time with "new Date(ts)" |
| val | BIT/BOOLEAN | Value |
| ack | BIT/BOOLEAN | Is acknowledged: 0 - not ack, 1 - ack |
| _from | INTEGER | ID of source from "Sources" table |
| q | INTEGER | Quality as number. You can find description [here](https://git.spacen.net/yunkong2/yunkong2/blob/master/doc/SCHEMA.md#states) |
*Note:* MS-SQL uses BIT, and others use BOOLEAN. SQLite uses for ts INTEGER and all others BIGINT.
## Custom queries
The user can execute custom queries on tables from javascript adapter:
```
sendTo('sql.0', 'query', 'SELECT * FROM datapoints', function (result) {
if (result.error) {
console.error(result.error);
} else {
// show result
console.log('Rows: ' + JSON.stringify(result.result));
}
});
```
Or get entries for the last hour for ID=system.adapter.admin.0.memRss
```
sendTo('sql.0', 'query', 'SELECT id FROM datapoints WHERE name="system.adapter.admin.0.memRss"', function (result) {
if (result.error) {
console.error(result.error);
} else {
// show result
console.log('Rows: ' + JSON.stringify(result.result));
var now = new Date();
now.setHours(-1);
sendTo('sql.0', 'query', 'SELECT * FROM ts_number WHERE ts >= ' + now.getTime() + ' AND id=' + result.result[0].id, function (result) {
console.log('Rows: ' + JSON.stringify(result.result));
});
}
});
```
## storeState
If you want to write other data into the InfluxDB you can use the build in system function **storeState**.
This function can also be used to convert data from other History adapters like History or SQL.
The given ids are not checked against the yunkong2 database and do not need to be set up there, but can only be accessed directly.
The Message can have one of the following three formats:
* one ID and one state object
* one ID and array of state objects
* array of multiple IDs with state objects
## Get history
Additional to custom queries, you can use build in system function **getHistory**:
```
var end = new Date().getTime();
sendTo('sql.0', 'getHistory', {
id: 'system.adapter.admin.0.memRss',
options: {
start: end - 3600000,
end: end,
aggregate: 'minmax' // or 'none' to get raw values
}
}, function (result) {
for (var i = 0; i < result.result.length; i++) {
console.log(result.result[i].id + ' ' + new Date(result.result[i].ts).toISOString());
}
});
```
## History Logging Management via Javascript
The adapter supports enabling and disabling of history logging via JavaScript and also retrieving the list of enabled datapoints with their settings.
### enable
The message requires to have the "id" of the datapoint.Additionally optional "options" to define the datapoint specific settings:
```
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memRss',
options: {
changesOnly: true,
debounce: 0,
retention: 31536000,
maxLength: 3,
changesMinDelta: 0.5,
aliasId: ""
}
}, function (result) {
if (result.error) {
console.log(result.error);
}
if (result.success) {
//successfull enabled
}
});
```
### disable
The message requires to have the "id" of the datapoint.
```
sendTo('sql.0', 'disableHistory', {
id: 'system.adapter.sql.0.memRss',
}, function (result) {
if (result.error) {
console.log(result.error);
}
if (result.success) {
//successfull enabled
}
});
```
### get List
The message has no parameters.
```
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
//result is object like:
{
"system.adapter.sql.0.memRss": {
"changesOnly":true,
"debounce":0,
"retention":31536000,
"maxLength":3,
"changesMinDelta":0.5,
"enabled":true,
"changesRelogInterval":0,
"aliasId": ""
}
...
}
});
```
## Connection Settings
- **DB Type**: Type of the SQL DB: MySQL, PostgreSQL, MS-SQL or SQLite3
- **Host**: IP address or host name with SQL Server
- **Port**: Port of SQL Server (leave blank if not sure)
- **Database name**: Database name. Default yunkong2
- **User**: User name for SQL. Must exists in the DB.
- **Password**: Password for SQL.
- **Password confirm**: Just repeat password here.
- **Encrypt**: Some DBs support encryption.
- **Round real to**: Number of digits after comma.
- **Allow parallel requests**: Allow simultaneous SQL requests to DB.
## Default Settings
- **De-bounce interval**: Do not store values often than this interval.
- **Log unchanged values any**: Write additionally the values every X seconds.
- **Minimum difference from last value to log**: Minimum interval between two values.
- **Storage retention**: How long the values will be stored in DB.
## Changelog
## 1.9.0 (2018-06-19)
* (Apollon77) Add option to log datapoints as other ID (alias) to easier migrate devices and such
## 1.8.0 (2018-04-29)
* (Apollon77) Update sqlite3, nodejs 10 compatible
* (BuZZy1337) Admin fix
## 1.7.4 (2018-04-15)
* (Apollon77) Fix getHistory
## 1.7.3 (2018-03-28)
* (Apollon77) Respect 'keep forever' setting for retention from datapoint configuration
## 1.7.2 (2018-03-24)
* (Apollon77) Disable to write NULLs for SQLite
## 1.7.1 (2018-02-10)
* (Apollon77) Make option to write NULL values on start/stop boundaries configurable
## 1.6.9 (2018-02-07)
* (bondrogeen) Admin3 Fixes
* (Apollon77) optimize relog feature and other things
## 1.6.7 (2018-01-31)
* (Bluefox) Admin3 Fixes
* (Apollon77) Relog and null log fixes
## 1.6.2 (2018-01-30)
* (Apollon77) Admin3 Fixes
## 1.6.0 (2018-01-14)
* (bluefox) Ready for Admin3
## 1.5.8 (2017-10-05)
* (Apollon77) fix relog value feature
## 1.5.7 (2017-08-10)
* (bluefox) add "save last value" option
## 1.5.6 (2017-08-02)
* (Apollon77) fix behaviour of log interval to always log the current value
## 1.5.4 (2017-06-12)
* (Apollon77) fix dependency to other library
## 1.5.3 (2017-04-07)
* (Apollon77) fix in datatype conversions
### 1.5.0 (2017-03-02)
* (Apollon77) Add option to define storage datatype per datapoint inclusing converting the value if needed
### 1.4.6 (2017-02-25)
* (Apollon77) Fix typo with PostgrSQL
### 1.4.5 (2017-02-18)
* (Apollon77) Small fix again for older configurations
* (Apollon77) fix for DBConverter Analyze function
### 1.4.3 (2017-02-11)
* (Apollon77) Small fix for older configurations
### 1.4.2 (2017-01-16)
* (bluefox) Fix handling of float values in Adapter config and Datapoint config.
### 1.4.1
* (Apollon77) Rollback to sql-client 0.7 to get rid of the mmagic dependecy that brings problems on older systems
### 1.4.0 (2016-12-02)
* (Apollon77) Add messages enableHistory/disableHistory
* (Apollon77) add support to log changes only if value differs a minimum value for numbers
### 1.3.4 (2016-11)
* (Apollon77) Allow database names with '-' for MySQL
### 1.3.3 (2016-11)
* (Apollon77) Update dependecies
### 1.3.2 (2016-11-21)
* (bluefox) Fix insert of string with '
### 1.3.0 (2016-10-29)
* (Apollon77) add option to re-log unchanged values to make it easier for visualization
### 1.2.1 (2016-08-30)
* (bluefox) Fix selector for SQL objects
### 1.2.0 (2016-08-30)
* (bluefox) сompatible only with new admin
### 1.0.10 (2016-08-27)
* (bluefox) change name of object from "history" to "custom"
### 1.0.10 (2016-07-31)
* (bluefox) fix multi requests if sqlite
### 1.0.9 (2016-06-14)
* (bluefox) allow settings for parallel requests
### 1.0.7 (2016-05-31)
* (bluefox) draw line to the end if ignore null
### 1.0.6 (2016-05-30)
* (bluefox) allow setup DB name for mysql and mssql
### 1.0.5 (2016-05-29)
* (bluefox) switch max and min with each other
### 1.0.4 (2016-05-29)
* (bluefox) check retention of data if set "never"
### 1.0.3 (2016-05-28)
* (bluefox) try to calculate old timestamps
### 1.0.2 (2016-05-24)
* (bluefox) fix error with io-package
### 1.0.1 (2016-05-24)
* (bluefox) fix error with SQLite
### 1.0.0 (2016-05-20)
* (bluefox) change default aggregation name
### 0.3.3 (2016-05-18)
* (bluefox) fix postgres
### 0.3.2 (2016-05-13)
* (bluefox) queue select if IDs and FROMs queries for sqlite
### 0.3.1 (2016-05-12)
* (bluefox) queue delete queries too for sqlite
### 0.3.0 (2016-05-08)
* (bluefox) support of custom queries
* (bluefox) only one request simultaneously for sqlite
* (bluefox) add tests (primitive and only sql)
### 0.2.0 (2016-04-30)
* (bluefox) support of milliseconds
* (bluefox) fix sqlite
### 0.1.4 (2016-04-25)
* (bluefox) fix deletion of old entries
### 0.1.3 (2016-03-08)
* (bluefox) do not print errors twice
### 0.1.2 (2015-12-22)
* (bluefox) fix MS-SQL port settings
### 0.1.1 (2015-12-19)
* (bluefox) fix error with double entries
### 0.1.0 (2015-12-14)
* (bluefox) support of strings
### 0.0.3 (2015-12-06)
* (smiling_Jack) Add demo Data ( todo: faster insert to db )
* (smiling_Jack) change aggregation (now same as history Adapter)
* (bluefox) bug fixing
### 0.0.2 (2015-12-06)
* (bluefox) allow only 1 client for SQLite
### 0.0.1 (2015-11-19)
* (bluefox) initial commit
## License
The MIT License (MIT)
Copyright (c) 2015-2018 bluefox <dogafox@gmail.com>, Apollon77
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

0
admin/.gitignore vendored Normal file
View File

169
admin/custom.html Normal file
View File

@@ -0,0 +1,169 @@
<script type="text/x-yunkong2" data-template-name="sql">
<div class="form-row">
<table style="font-size: 12px">
<tr>
<td class="translate">enabled</td>
<td><input type="checkbox" data-field="enabled" data-default="false"></td>
<!-- this field is mandatory, just to find out if to include this settings or not -->
<td style="width: 40px">&nbsp;</td>
<td class="translate">log changes only</td>
<td><input type="checkbox" data-field="changesOnly" data-default="true"></td>
<td class="translate">De-bounce interval(ms)</td>
<td><input type="number" data-field="debounce" min="500" max="86400000" data-default="10000"></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="translate">log changes interval(s)</td>
<td><input type="number" data-field="changesRelogInterval" min="0" max="86400000" data-default="0"></td>
<td class="translate">0 = disable</td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="translate">log changes minimal delta</td>
<td><input type="number" step="any" data-field="changesMinDelta" min="0" max="86400000" data-default="0"></td>
<td class="translate">0 = disable delta check</td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="translate">Store as</td>
<td>
<select data-field="storageType" data-default="">
<option value="" class="translate">Automatic</option>
<option value="String" class="translate">String</option>
<option value="Number" class="translate">Number</option>
<option value="Boolean" class="translate">Boolean</option>
</select>
</td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="translate">Storage retention</td>
<td>
<select data-field="retention" data-default="31536000">
<option value="0" class="translate">keep forever</option>
<option value="63072000" class="translate">2 years</option>
<option value="31536000" class="translate">1 year</option>
<option value="15811200" class="translate">6 months</option>
<option value="7948800" class="translate">3 months</option>
<option value="2678400" class="translate">1 months</option>
<option value="1209600" class="translate">2 weeks</option>
<option value="604800" class="translate">1 week</option>
<option value="432000" class="translate">5 days</option>
<option value="259200" class="translate">3 days</option>
<option value="86400" class="translate">1 day</option>
</select>
</td>
<td></td>
<td></td>
</tr>
<tr>
<td class="translate">aliasId</td>
<td colspan=2><input type="value" data-field="aliasId" data-default="" maxlength="30" style="width: 100px"></td>
<td></td>
<td colspan=3 class="translate">Alias Info</td>
<td></td>
</tr>
</table>
</div>
</script>
<!-- translations -->
<script type="text/javascript">
systemDictionary = $.extend(systemDictionary, {
"enabled": {"en": "enabled", "de": "Aktiviert", "ru": "активно"},
"De-bounce interval(ms)": {"en": "De-bounce interval(ms)", "de": "Entprellzeit(ms)", "ru": "Минимальный интервал(ms)"},
"log changes interval(s)": {
"en": "Log unchanged values any(s)",
"de": "Gleiche Werte aufzeichnen(s)",
"ru": "Запись неизменённых значений каждые (сек)"
},
"log changes minimal delta": {
"en": "Minimum difference from last value to log",
"de": "Minimale Abweichung vom letzten Wert für Aufzeichnung",
"ru": "Минимальная разница с последним записанным значением"
},
"0 = disable delta check": {
"en": "0 = disable delta check",
"de": "0 = Abweichungsprüfung deaktivieren",
"ru": "0 = не проверять минимальное изменение"
},
"log changes only": {"en": "log by changes", "de": "Bei Änderungen aufzeichnen", "ru": "При изменениях"},
"0 = disable": {
"en": "0 = disable",
"de": "0 = Deaktivieren",
"ru": "0 = не активно"
},
"maximum datapoint count in RAM": {
"en": "maximum datapoint count in RAM",
"de": "maximale Anzahl Datenpunkte in RAM",
"ru": "макс. кол-во значений в RAM памяти"
},
"Store as": {"en": "Store as", "de": "Speichern als", "ru": "Store as"},
"Automatic": {"en": "Automatic", "de": "Automatisch", "ru": "Automatic"},
"String": {"en": "String", "de": "String", "ru": "String"},
"Number": {"en": "Number", "de": "Number", "ru": "Number"},
"Boolean": {"en": "Boolean", "de": "Boolean", "ru": "Boolean"},
"Storage retention": {"en": "Storage retention", "de": "Storage Vorhaltezeit", "ru": "Сохранять в базе файле"},
"keep forever": {"en": "keep forever", "de": "keine automatische Löschung", "ru": "хранить вечно"},
"2 years": {"en": "2 years", "de": "2 Jahre", "ru": "2 года"},
"1 year": {"en": "1 year", "de": "1 Jahr", "ru": "1 год"},
"6 months": {"en": "6 months", "de": "6 Monate", "ru": "6 месяцев"},
"3 months": {"en": "3 months", "de": "3 Monate", "ru": "3 месяца"},
"1 months": {"en": "1 months", "de": "1 Monat", "ru": "1 месяц"},
"2 weeks": {"en": "2 weeks", "de": "2 Wochen", "ru": "2 недели"},
"1 week": {"en": "1 week", "de": "1 Woche", "ru": "1 неделя"},
"5 days": {"en": "5 days", "de": "5 Tage", "ru": "5 дней"},
"3 days": {"en": "3 days", "de": "3 Tage", "ru": "3 дня"},
"1 day": {"en": "1 day", "de": "1 Tag", "ru": "1 день"},
"aliasId": {
"en": "Alias-ID"
},
"Alias Info": {
"en": "if not empty data will be logged as this ID in data files"
}
}
);
// There are two ways how to predefine default settings:
// - with attribute "data-default" (content independent)
// - with function in global variable "defaults". Function name is equal with adapter name.
// as input function receives object with all information concerning it
if (typeof defaults !== 'undefined') {
defaults.sql = function (obj, instanceObj) {
if (obj && obj.common && obj.common.type !== 'number') {
return {
enabled: false,
changesOnly: false,
debounce: 0,
retention: instanceObj.native.retention,
changesRelogInterval: instanceObj.native.changesRelogInterval,
changesMinDelta: 0,
storageType: '',
aliasId: ''
};
}
return {
enabled: false,
changesOnly: true,
debounce: instanceObj.native.debounce,
retention: instanceObj.native.retention,
changesRelogInterval: instanceObj.native.changesRelogInterval,
changesMinDelta: instanceObj.native.changesMinDelta,
storageType: '',
aliasId: ''
};
}
}
</script>

151
admin/custom_m.html Normal file
View File

@@ -0,0 +1,151 @@
<script type="text/x-yunkong2" data-template-name="sql">
<div class="row">
<div class="col s4">
<input type="checkbox" data-field="enabled" data-default="false"/>
<!-- this field is mandatory, just to find out if to include this settings or not</span-->
<span class="translate">enabled</span>
</div>
<div class="col s4">
<input type="checkbox" data-field="changesOnly" data-default="true"/>
<span class="translate">log changes only</span>
</div>
<div class="col s4">
<input type="number" data-field="debounce" min="500" max="86400000" data-default="10000" />
<label class="translate">De-bounce interval(ms)</label>
</div>
</div>
<div class="row">
<div class="col s4">
</div>
<div class="col s4">
<input type="number" data-field="changesRelogInterval" min="0" max="86400000" data-default="0" />
<label class="translate">log changes interval(s) (0 = disable)</label>
</div>
<div class="col s4">
<input type="number" data-field="changesMinDelta" min="0" max="86400000" data-default="0" />
<label class="translate">log changes minimal delta (0 = disable delta check)</label>
</div>
</div>
<div class="row">
<div class="col s4">
</div>
<div class="col s4">
<select data-field="storageType" data-default="">
<option value="" class="translate">Automatic</option>
<option value="String" class="translate">String</option>
<option value="Number" class="translate">Number</option>
<option value="Boolean" class="translate">Boolean</option>
</select>
<label class="translate">Store as</label>
</div>
<div class="col s4">
<select data-field="retention" data-default="31536000">
<option value="0" class="translate">keep forever</option>
<option value="63072000" class="translate">2 years</option>
<option value="31536000" class="translate">1 year</option>
<option value="15811200" class="translate">6 months</option>
<option value="7948800" class="translate">3 months</option>
<option value="2678400" class="translate">1 months</option>
<option value="1209600" class="translate">2 weeks</option>
<option value="604800" class="translate">1 week</option>
<option value="432000" class="translate">5 days</option>
<option value="259200" class="translate">3 days</option>
<option value="86400" class="translate">1 day</option>
</select>
<label class="translate">Storage retention</label>
</div>
</div>
<div class="row">
<div class="col s8">
<input class="value" data-field="aliasId" data-default="" type="text" maxlength="255" />
<label class="translate">aliasId</label>
</div>
<div class="col s4">
<label class="translate">Alias Info</label>
</div>
</div>
</script>
<!-- translations -->
<script type="text/javascript">
systemDictionary = $.extend(systemDictionary, {
"1 day": { "en": "1 day", "de": "1 Tag", "ru": "1 день", "pt": "1 dia", "nl": "1 dag", "fr": "Un jour", "it": "1 giorno", "es": "1 día", "pl": "1 dzień"},
"1 months": { "en": "1 months", "de": "1 Monat", "ru": "1 месяц", "pt": "1 mês", "nl": "1 maanden", "fr": "1 mois", "it": "1 mese", "es": "1 mes", "pl": "1 miesiąc"},
"1 week": { "en": "1 week", "de": "1 Woche", "ru": "1 неделя", "pt": "1 semana", "nl": "1 week", "fr": "1 semaine", "it": "1 settimana", "es": "1 semana", "pl": "1 tydzień"},
"1 year": { "en": "1 year", "de": "1 Jahr", "ru": "1 год", "pt": "1 ano", "nl": "1 jaar", "fr": "1 an", "it": "1 anno", "es": "1 año", "pl": "1 rok"},
"2 weeks": { "en": "2 weeks", "de": "2 Wochen", "ru": "2 недели", "pt": "2 semanas", "nl": "2 weken", "fr": "2 semaines", "it": "2 settimane", "es": "2 semanas", "pl": "2 tygodnie"},
"2 years": { "en": "2 years", "de": "2 Jahre", "ru": "2 года", "pt": "2 anos", "nl": "2 jaar", "fr": "2 ans", "it": "2 anni", "es": "2 años", "pl": "2 lata"},
"3 days": { "en": "3 days", "de": "3 Tage", "ru": "3 дня", "pt": "3 dias", "nl": "3 dagen", "fr": "3 jours", "it": "3 giorni", "es": "3 días", "pl": "3 dni"},
"3 months": { "en": "3 months", "de": "3 Monate", "ru": "3 месяца", "pt": "3 meses", "nl": "3 maanden", "fr": "3 mois", "it": "3 mesi", "es": "3 meses", "pl": "3 miesiące"},
"5 days": { "en": "5 days", "de": "5 Tage", "ru": "5 дней", "pt": "5 dias", "nl": "5 dagen", "fr": "5 jours", "it": "5 giorni", "es": "5 dias", "pl": "5 dni"},
"6 months": { "en": "6 months", "de": "6 Monate", "ru": "6 месяцев", "pt": "6 meses", "nl": "6 maanden", "fr": "6 mois", "it": "6 mesi", "es": "6 meses", "pl": "6 miesięcy"},
"Store as": { "en": "Store as", "de": "Speichern als", "ru": "Сохранить как", "pt": "Armazenar como", "nl": "Opslaan als", "fr": "Magasin comme", "it": "Conservare come", "es": "Przechowuj jako", "pl": "Przechowuj jako"},
"Automatic": { "en": "Automatic", "de": "Automatisch", "ru": "автоматическая", "pt": "Automático", "nl": "automatisch", "fr": "Automatique", "it": "Automatico", "es": "Automatycznyy", "pl": "Automatyczny"},
"String": { "en": "String", "de": "Zeichenfolge", "ru": "строка", "pt": "Corda", "nl": "Draad", "fr": "Chaîne", "it": "Stringa", "es": "Strunowy", "pl": "Strunowy"},
"Number": { "en": "Number", "de": "Nummer", "ru": "Число", "pt": "Número", "nl": "Aantal", "fr": "Nombre", "it": "Numero", "es": "Numer", "pl": "Numer"},
"Boolean": { "en": "Boolean", "de": "Boolesch", "ru": "логический", "pt": "boleano", "nl": "Boolean", "fr": "Booléen", "it": "booleano", "es": "Booleano", "pl": "Boolean"},
"De-bounce interval(ms)": { "en": "De-bounce interval(ms)", "de": "Entprellzeit(ms)", "ru": "Минимальный интервал(ms)", "pt": "Intervalo de desvio (ms)", "nl": "De-bounce interval (ms)", "fr": "Intervalle de rebond (ms)", "it": "Intervallo di rimbalzo (ms)", "es": "Intervalo de rebote (ms)", "pl": "Interwał odbicia (ms)"},
"Storage retention": { "en": "Storage retention", "de": "Storage Vorhaltezeit", "ru": "Сохранять в файле", "pt": "Retenção de armazenamento", "nl": "Opslag retentie", "fr": "Conservation du stockage", "it": "Conservazione di memoria", "es": "Retención de almacenamiento", "pl": "Przechowywanie danych"},
"enabled": { "en": "enabled", "de": "Aktiviert", "ru": "активно", "pt": "ativado", "nl": "ingeschakeld", "fr": "activée", "it": "abilitato", "es": "habilitado", "pl": "włączony"},
"keep forever": { "en": "keep forever", "de": "keine automatische Löschung", "ru": "хранить вечно", "pt": "mantenha para sempre", "nl": "blijf voor altijd", "fr": "garde pour toujours", "it": "tienilo per sempre", "es": "mantener para siempre", "pl": "zachowaj na zawsze"},
"log changes interval(s) (0 = disable)": { "en": "Log values any(s)<br>(0 = disable)", "de": "Nach Intervall aufzeichnen(s)<br>(0 = Deaktivieren)", "ru": "Запись значений каждые (сек)<br>(0 = не активно)", "pt": "Intervalo (s) de alterações no log (s)<br>(0 = desativado)", "nl": "log wijzigt interval (s)<br>(0 = uitschakelen)", "fr": "log changements intervalle (s)<br>(0 = désactiver)", "it": "log changes interval (s)<br>(0 = disabilita)", "es": "Log valores any (s) <br> (0 = desactivar)", "pl": "Wartości logów any (s) <br> (0 = wyłączone)"},
"log changes minimal delta (0 = disable delta check)": {"en": "Minimum difference from last value to log<br>(0 = disable delta check)", "de": "Minimale Abweichung vom letzten Wert für Aufzeichnung<br>(0 = Abweichungsprüfung deaktivieren)", "ru": "Минимальная разница с последним записанным значением<br>(0 = не проверять минимальное изменение)", "pt": "log altera delta mínimo<br>(0 = desabilita o checkout delta)", "nl": "log veranderingen minimale delta<br>(0 = deltablokkering uitschakelen)", "fr": "log change delta minimal<br>(0 = désactiver le contrôle delta)", "it": "registro cambia delta minimo<br>(0 = disabilita il controllo delta)", "es": "Diferencia mínima entre el último valor y el log <br> (0 = desactivar la verificación delta)", "pl": "Minimalna różnica od ostatniej wartości do log <br> (0 = wyłącz kontrolę delta)"},
"log changes only": { "en": "log changes only", "de": "Änderungen aufzeichnen", "ru": "Только изменения", "pt": "somente mudanças de log", "nl": "alleen logboekwijzigingen", "fr": "modifications du journal uniquement", "it": "registra solo le modifiche", "es": "solo cambios de registro", "pl": "tylko zmiany w dzienniku"},
"maximum datapoint count in RAM": { "en": "maximum datapoint count in RAM", "de": "maximale Anzahl Datenpunkte in RAM", "ru": "макс. кол-во значений в RAM памяти", "pt": "contagem máxima do datapoint na RAM", "nl": "maximum aantal datapunten in RAM", "fr": "nombre de points de données maximum en RAM", "it": "massimo conteggio dei punti di accesso nella RAM", "es": "recuento máximo de datos en RAM", "pl": "maksymalna liczba punktów w pamięci RAM"},
"aliasId": {
"en": "Alias-ID",
"de": "Alias-ID",
"ru": "Псевдоним ИД",
"pt": "Alias-ID",
"nl": "Alias-ID",
"fr": "Alias-ID",
"it": "Alias-ID",
"es": "Alias-ID",
"pl": "Alias-ID"
},
"Alias Info": {
"en": "if Alias-ID is set, the data will be logged as this ID in database",
"de": "Wenn Alias-ID gesetzt ist, werden die Daten als diese ID in der Datenbank protokolliert",
"ru": "если установлен Alias-ID, данные будут регистрироваться как этот идентификатор в базе данных",
"pt": "Se o Alias-ID for definido, os dados serão registrados como esse ID no banco de dados",
"nl": "als Alias-ID is ingesteld, worden de gegevens als deze ID geregistreerd in de database",
"fr": "Si Alias-ID est défini, les données seront enregistrées comme cet ID dans la base de données",
"it": "se Alias-ID è impostato, i dati verranno registrati come questo ID nel database",
"es": "si se establece Alias-ID, los datos se registrarán como esta ID en la base de datos",
"pl": "jeśli ustawiony jest Alias-ID, dane będą rejestrowane jako identyfikator w bazie danych"
}
});
// There are two ways how to predefine default settings:
// - with attribute "data-default" (content independent)
// - with function in global variable "defaults". Function name is equal with adapter name.
// as input function receives object with all information concerning it
if (typeof defaults !== 'undefined') {
defaults.sql = function (obj, instanceObj) {
if (obj && obj.common && obj.common.type !== 'number') {
return {
enabled: false,
changesOnly: false,
debounce: 0,
maxLength: 10,
retention: instanceObj.native.retention,
changesRelogInterval: instanceObj.native.changesRelogInterval,
changesMinDelta: 0,
storageType: '',
aliasId: ''
};
} else {
return {
enabled: false,
changesOnly: true,
debounce: instanceObj.native.debounce,
maxLength: instanceObj.native.maxLength,
retention: instanceObj.native.retention,
changesRelogInterval: instanceObj.native.changesRelogInterval,
changesMinDelta: instanceObj.native.changesMinDelta,
storageType: '',
aliasId: ''
};
}
}
}
</script>

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "History-Adapter Einstellungen",
"note": "History Einstellungen mussen für jeden State einzeln gemacht werden auf der Lasche \"States\" und nur wenn adapter aktiv ist",
"Default settings:": "Default Einstellungen für Zuständen",
"Start or enable adapter first": "Adapter muss zuerst aktiviert werden",
"debounce": "Entprellzeit(ms)",
"log changes interval(s)": "Gleiche Werte aufzeichnen(s)",
"log changes minimal delta": "Minimale Abweichung vom letzten Wert für Aufzeichnung",
"0 = disable delta check": "0 = Abweichungsprüfung deaktivieren",
"0 = disable": "0 = Deaktivieren",
"DB settings": "DB Einstellungen",
"Default settings": "Default Einstellungen",
"retention": "Storage Vorhaltezeit",
"keep forever": "keine automatische Löschung",
"2 years": "2 Jahre",
"1 year": "1 Jahr",
"6 months": "6 Monate",
"3 months": "3 Monate",
"1 months": "1 Monat",
"2 weeks": "2 Wochen",
"1 week": "1 Woche",
"5 days": "5 Tage",
"3 days": "3 Tage",
"1 day": "1 Tag",
"SQL history adapter settings": "SQL history adapter Einstellungen",
"DB settings:": "DB Einstellungen",
"DB Type": "DB Typ",
"Host": "Host",
"DB Name": "Datenbankname",
"Port": "Port",
"User": "Login",
"Password": "Kennwort",
"Password confirm": "Kennwort-Wiederholung",
"File for sqlite:": "Datei für sqlite",
"Encrypt:": "Verschlüsseln",
"Test connection": "Verbindung testen",
"requestInterval": "Intervall zwischen DB Anfragen(ms)",
"Round real to:": "Aufrunden auf: ",
"Password confirmation is not equal with password!": "Kennwort-Bestätigung ist nicht gleich mit dem Kennwort!",
"Input path with the file name.": "Z.B. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Sind Sie sicher? ALLE Daten werden geöscht.",
"Reset DB": "Alle Daten in DB löschen",
"Ok": "Ok",
"Allow parallel requests:": "Parallelanfragen erlauben",
"Write NULL values on start/stop boundaries:": "Schreibe NULL-Werte an Start- / Stop-Grenzen:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "History adapter settings",
"note": "History settings must be done for every state individually on the tab \"States\" and only if adapter is active",
"Default settings:": "Default settings",
"Start or enable adapter first": "Start or enable adapter first",
"debounce": "De-bounce interval(ms)",
"log changes interval(s)": "Log unchanged values any(s)",
"log changes minimal delta": "Minimum difference from last value to log",
"0 = disable delta check": "0 = disable delta check",
"0 = disable": "0 = disable",
"DB settings": "DB settings",
"Default settings": "Default settings",
"retention": "Storage retention",
"keep forever": "keep forever",
"2 years": "2 years",
"1 year": "1 year",
"6 months": "6 months",
"3 months": "3 months",
"1 months": "1 months",
"2 weeks": "2 weeks",
"1 week": "1 week",
"5 days": "5 days",
"3 days": "3 days",
"1 day": "1 day",
"SQL history adapter settings": "SQL history adapter settings",
"DB settings:": "DB settings",
"DB Type": "DB Type",
"Host": "Host",
"DB Name": "Database name",
"Port": "Port",
"User": "User",
"Password": "Password",
"Password confirm": "Password confirm",
"File for sqlite:": "File for sqlite",
"Encrypt:": "Encrypt",
"Test connection": "Test connection",
"requestInterval": "Pause between requests(ms)",
"Round real to:": "Round real to",
"Password confirmation is not equal with password!": "Password confirmation is not equal with password!",
"Input path with the file name.": "E.g. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Are you sure? All data will be dropped.",
"Reset DB": "Reset DB",
"Ok": "Ok",
"Allow parallel requests:": "Allow parallel requests",
"Write NULL values on start/stop boundaries:": "Write NULL values on start/stop boundaries:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "Configuración del adaptador de historial",
"note": "La configuración del historial se debe realizar para cada estado individualmente en la pestaña \"Estados\" y solo si el adaptador está activo",
"Default settings:": "Configuración por defecto",
"Start or enable adapter first": "Comience o habilite el adaptador primero",
"debounce": "Intervalo de rebote (ms)",
"log changes interval(s)": "Registre los valores sin modificar cualquier (s)",
"log changes minimal delta": "Diferencia mínima desde el último valor para registrar",
"0 = disable delta check": "0 = deshabilitar la verificación delta",
"0 = disable": "0 = desactivar",
"DB settings": "Configuración de DB",
"Default settings": "Configuración por defecto",
"retention": "Retención de almacenamiento",
"keep forever": "mantener para siempre",
"2 years": "2 años",
"1 year": "1 año",
"6 months": "6 meses",
"3 months": "3 meses",
"1 months": "1 mes",
"2 weeks": "2 semanas",
"1 week": "1 semana",
"5 days": "5 dias",
"3 days": "3 días",
"1 day": "1 día",
"SQL history adapter settings": "Configuración del adaptador de historial SQL",
"DB settings:": "Configuración de DB",
"DB Type": "Tipo de DB",
"Host": "Anfitrión",
"DB Name": "Nombre de la base de datos",
"Port": "Puerto",
"User": "Usuario",
"Password": "Contraseña",
"Password confirm": "Contraseña confirmada",
"File for sqlite:": "Archivo para sqlite",
"Encrypt:": "Encriptar",
"Test connection": "Conexión de prueba",
"requestInterval": "Pausa entre solicitudes (ms)",
"Round real to:": "Redondo real para",
"Password confirmation is not equal with password!": "La confirmación de la contraseña no es igual a la contraseña!",
"Input path with the file name.": "P.ej. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "¿Estás seguro? Todos los datos serán eliminados.",
"Reset DB": "Restablecer DB",
"Ok": "De acuerdo",
"Allow parallel requests:": "Permitir solicitudes paralelas",
"Write NULL values on start/stop boundaries:": "Escribe valores NULL en los límites de inicio / finalización:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "Paramètres de l'adaptateur d'historique",
"note": "Les paramètres d'historique doivent être définis pour chaque état individuellement dans l'onglet \"Etats\" et uniquement si l'adaptateur est actif",
"Default settings:": "Paramètres par défaut",
"Start or enable adapter first": "Démarrer ou activer l'adaptateur en premier",
"debounce": "Intervalle de rebond (ms)",
"log changes interval(s)": "Consigner les valeurs inchangées any (s)",
"log changes minimal delta": "Différence minimale de la dernière valeur à la notation",
"0 = disable delta check": "0 = désactiver le contrôle delta",
"0 = disable": "0 = désactiver",
"DB settings": "Paramètres DB",
"Default settings": "Paramètres par défaut",
"retention": "Rétention de stockage",
"keep forever": "garde pour toujours",
"2 years": "2 ans",
"1 year": "1 an",
"6 months": "6 mois",
"3 months": "3 mois",
"1 months": "1 mois",
"2 weeks": "2 semaines",
"1 week": "1 semaine",
"5 days": "5 jours",
"3 days": "3 jours",
"1 day": "Un jour",
"SQL history adapter settings": "Paramètres de l'adaptateur d'historique SQL",
"DB settings:": "Paramètres de base de données",
"DB Type": "Type de DB",
"Host": "Hôte",
"DB Name": "Nom de la base de données",
"Port": "Port",
"User": "Utilisateur",
"Password": "Mot de passe",
"Password confirm": "Confirmer le mot de passe",
"File for sqlite:": "Fichier pour sqlite",
"Encrypt:": "Crypter",
"Test connection": "Test de connexion",
"requestInterval": "Pause entre les demandes (ms)",
"Round real to:": "Rond réel à",
"Password confirmation is not equal with password!": "La confirmation du mot de passe n'est pas égale au mot de passe!",
"Input path with the file name.": "Par exemple. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Êtes-vous sûr? Toutes les données seront supprimées.",
"Reset DB": "Reset DB",
"Ok": "D'accord",
"Allow parallel requests:": "Autoriser les demandes parallèles",
"Write NULL values on start/stop boundaries:": "Écrire des valeurs NULL sur les limites de démarrage / arrêt:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "Impostazioni della scheda Cronologia",
"note": "Le impostazioni della cronologia devono essere eseguite singolarmente per ogni stato nella scheda \"Stati\" e solo se l'adattatore è attivo",
"Default settings:": "Impostazioni predefinite",
"Start or enable adapter first": "Inizia o abilita prima l'adattatore",
"debounce": "Intervallo di rimbalzo (ms)",
"log changes interval(s)": "Registra valori invariati nessuno (i)",
"log changes minimal delta": "Differenza minima dall'ultimo valore da registrare",
"0 = disable delta check": "0 = disabilita il controllo delta",
"0 = disable": "0 = disabilita",
"DB settings": "Impostazioni DB",
"Default settings": "Impostazioni predefinite",
"retention": "Conservazione dello spazio",
"keep forever": "tienilo per sempre",
"2 years": "2 anni",
"1 year": "1 anno",
"6 months": "6 mesi",
"3 months": "3 mesi",
"1 months": "1 mese",
"2 weeks": "2 settimane",
"1 week": "1 settimana",
"5 days": "5 giorni",
"3 days": "3 giorni",
"1 day": "1 giorno",
"SQL history adapter settings": "Impostazioni della scheda cronologia SQL",
"DB settings:": "Impostazioni DB",
"DB Type": "Tipo di DB",
"Host": "Ospite",
"DB Name": "Nome del database",
"Port": "Porta",
"User": "Utente",
"Password": "Parola d'ordine",
"Password confirm": "Conferma la password",
"File for sqlite:": "File per sqlite",
"Encrypt:": "Encrypt",
"Test connection": "Connessione di prova",
"requestInterval": "Pausa tra le richieste (ms)",
"Round real to:": "Round reale a",
"Password confirmation is not equal with password!": "La conferma della password non è uguale alla password!",
"Input path with the file name.": "Per esempio. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Sei sicuro? Tutti i dati verranno eliminati.",
"Reset DB": "Ripristina DB",
"Ok": "Ok",
"Allow parallel requests:": "Consenti richieste parallele",
"Write NULL values on start/stop boundaries:": "Scrivi valori NULL sui limiti di avvio / arresto:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "Geschiedenis adapter instellingen",
"note": "Geschiedenisinstellingen moeten voor elke afzonderlijke status worden uitgevoerd op het tabblad \"Staten\" en alleen als de adapter actief is",
"Default settings:": "Standaard instellingen",
"Start or enable adapter first": "Start of schakel eerst de adapter in",
"debounce": "De-bounce interval (ms)",
"log changes interval(s)": "Log onveranderde waarden voor elke",
"log changes minimal delta": "Minimaal verschil van laatste waarde tot log",
"0 = disable delta check": "0 = delta-controle uitschakelen",
"0 = disable": "0 = uitschakelen",
"DB settings": "DB-instellingen",
"Default settings": "Standaard instellingen",
"retention": "Bewaring van opslag",
"keep forever": "blijf voor altijd",
"2 years": "2 jaar",
"1 year": "1 jaar",
"6 months": "6 maanden",
"3 months": "3 maanden",
"1 months": "1 maanden",
"2 weeks": "2 weken",
"1 week": "1 week",
"5 days": "5 dagen",
"3 days": "3 dagen",
"1 day": "1 dag",
"SQL history adapter settings": "Instellingen voor SQL-geschiedenisadapter",
"DB settings:": "DB-instellingen",
"DB Type": "DB Type",
"Host": "Host",
"DB Name": "Database naam",
"Port": "Haven",
"User": "Gebruiker",
"Password": "Wachtwoord",
"Password confirm": "Wachtwoord bevestigen",
"File for sqlite:": "Bestand voor sqlite",
"Encrypt:": "versleutelen",
"Test connection": "Test verbinding",
"requestInterval": "Pauzeren tussen verzoeken (ms)",
"Round real to:": "Rond echt naar",
"Password confirmation is not equal with password!": "Wachtwoordbevestiging is niet gelijk aan wachtwoord!",
"Input path with the file name.": "Bijv. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Weet je het zeker? Alle gegevens worden verwijderd.",
"Reset DB": "Stel DB opnieuw in",
"Ok": "OK",
"Allow parallel requests:": "Sta parallelle verzoeken toe",
"Write NULL values on start/stop boundaries:": "Schrijf NULL-waarden op start / stop-grenzen:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "Ustawienia adaptera historii",
"note": "Ustawienia historii muszą być wykonane dla każdego stanu indywidualnie na karcie \"Stany\" i tylko wtedy, gdy adapter jest aktywny",
"Default settings:": "Ustawienia domyślne",
"Start or enable adapter first": "Najpierw włącz lub włącz adapter",
"debounce": "Interwał odbicia (ms)",
"log changes interval(s)": "Zaloguj niezmienione wartości dowolne (s)",
"log changes minimal delta": "Minimalna różnica od ostatniej wartości do dziennika",
"0 = disable delta check": "0 = wyłącz kontrolę delta",
"0 = disable": "0 = wyłącz",
"DB settings": "Ustawienia DB",
"Default settings": "Ustawienia domyślne",
"retention": "Zatrzymanie przechowywania",
"keep forever": "zachowaj na zawsze",
"2 years": "2 lata",
"1 year": "1 rok",
"6 months": "6 miesięcy",
"3 months": "3 miesiące",
"1 months": "1 miesiąc",
"2 weeks": "2 tygodnie",
"1 week": "1 tydzień",
"5 days": "5 dni",
"3 days": "3 dni",
"1 day": "1 dzień",
"SQL history adapter settings": "Ustawienia adaptera historii SQL",
"DB settings:": "Ustawienia DB",
"DB Type": "Typ DB",
"Host": "Gospodarz",
"DB Name": "Nazwa bazy danych",
"Port": "Port",
"User": "Użytkownik",
"Password": "Hasło",
"Password confirm": "Potwierdź hasło",
"File for sqlite:": "Plik dla sqlite",
"Encrypt:": "Szyfruj",
"Test connection": "Testuj połączenie",
"requestInterval": "Wstrzymaj między żądaniami (ms)",
"Round real to:": "Runda prawdziwa do",
"Password confirmation is not equal with password!": "Potwierdzenie hasła nie jest równe hasłu!",
"Input path with the file name.": "Na przykład. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Jesteś pewny? Wszystkie dane zostaną usunięte.",
"Reset DB": "Zresetuj DB",
"Ok": "Ok",
"Allow parallel requests:": "Pozwól na równoległe żądania",
"Write NULL values on start/stop boundaries:": "Napisz wartości NULL na granicach start / stop:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "Configurações do adaptador de histórico",
"note": "As configurações de histórico devem ser feitas para cada estado individual na guia \"Estados\" e somente se o adaptador estiver ativo",
"Default settings:": "Configurações padrão",
"Start or enable adapter first": "Inicie ou ative o adaptador primeiro",
"debounce": "Intervalo de desvio (ms)",
"log changes interval(s)": "Registre valores inalterados qualquer (s)",
"log changes minimal delta": "Diferença mínima do último valor ao log",
"0 = disable delta check": "0 = desativar o cheque delta",
"0 = disable": "0 = desativar",
"DB settings": "Configurações de banco de dados",
"Default settings": "Configurações padrão",
"retention": "Retenção de armazenamento",
"keep forever": "mantenha para sempre",
"2 years": "2 anos",
"1 year": "1 ano",
"6 months": "6 meses",
"3 months": "3 meses",
"1 months": "1 mês",
"2 weeks": "2 semanas",
"1 week": "1 semana",
"5 days": "5 dias",
"3 days": "3 dias",
"1 day": "1 dia",
"SQL history adapter settings": "Configurações do adaptador de histórico SQL",
"DB settings:": "Configurações de banco de dados",
"DB Type": "DB Type",
"Host": "Hospedeiro",
"DB Name": "Nome do banco de dados",
"Port": "Porta",
"User": "Do utilizador",
"Password": "Senha",
"Password confirm": "Confirmação de senha",
"File for sqlite:": "Arquivo para sqlite",
"Encrypt:": "Criptografar",
"Test connection": "Conexão de teste",
"requestInterval": "Pausa entre solicitações (ms)",
"Round real to:": "Round real to",
"Password confirmation is not equal with password!": "A confirmação da senha não é igual com a senha!",
"Input path with the file name.": "Por exemplo. /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Você tem certeza? Todos os dados serão descartados.",
"Reset DB": "Redefinir DB",
"Ok": "Está bem",
"Allow parallel requests:": "Permitir pedidos paralelos",
"Write NULL values on start/stop boundaries:": "Escreva valores NULL nos limites de início / parada:"
}

View File

@@ -0,0 +1,46 @@
{
"History adapter settings": "Настройки профайлера",
"note": "Настройки профайлера делаются для каждого состояния отдельно в закладке \"Состояния\" и только, когда драйвер активирован",
"Default settings:": "Настройки по умолчанию для состояний",
"Start or enable adapter first": "Сначала надо активировать драйвер SQL",
"debounce": "Минимальный интервал(ms)",
"log changes interval(s)": "Запись неизменённых значений каждые (сек)",
"log changes minimal delta": "Минимальная разница с последним записанным значением",
"0 = disable delta check": "0 = не проверять минимальное изменение",
"0 = disable": "0 = не активно",
"DB settings": "Настройки соединения с DB",
"Default settings": "Настройки по умолчанию",
"retention": "Время хранения в базе",
"keep forever": "хранить вечно",
"2 years": "2 года",
"1 year": "1 год",
"6 months": "6 месяцев",
"3 months": "3 месяца",
"1 months": "1 месяц",
"2 weeks": "2 недели",
"1 week": "1 неделя",
"5 days": "5 дней",
"3 days": "3 дня",
"1 day": "1 день",
"SQL history adapter settings": "Настройки профайлера в SQL базу данных",
"DB settings:": "DB settings",
"DB Type": "DB Type",
"Host": "Хост",
"DB Name": "Имя базы данных",
"Port": "Порт",
"User": "Пользователь",
"Password": "Пароль",
"Password confirm": "Подтверждение пароля",
"File for sqlite:": "Файл для sqlite",
"Encrypt:": "Шифрование",
"Test connection": "Проверить соединение",
"requestInterval": "Пауза между запросами к базе",
"Round real to:": "Округлять до",
"Password confirmation is not equal with password!": "Подтверждение пароля не совпадает с паролем!",
"Input path with the file name.": "Например /opt/sqlite/data.db",
"Are you sure? All data will be dropped.": "Вы уверенны? Все данные в базе будут удалены.",
"Reset DB": "Сбросить DB",
"Ok": "Ok",
"Allow parallel requests:": "Разрешить параллельные запросы к базе",
"Write NULL values on start/stop boundaries:": "Запись значений NULL на границах начала / остановки:"
}

276
admin/index.html Normal file
View File

@@ -0,0 +1,276 @@
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!-- these 4 files always have to be included -->
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
<script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
<!-- these three files have to be always included -->
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<script type="text/javascript" src="words.js"></script>
<!-- you have to define 2 functions in the global scope: -->
<script type="text/javascript">
var convertComma = null;
function setValue(id, value, onChange) {
var $value = $('#' + id + '.value');
if ($value.attr('type') === 'checkbox') {
$value.prop('checked', value).change(function () {
onChange();
});
} else {
$value.val(value).change(function() {
onChange();
}).keyup(function() {
// Check that only numbers entered
if ($(this).hasClass('number')) {
var val = $(this).val();
if (val) {
var newVal = '';
for (var i = 0; i < val.length; i++) {
if ((val[i] >= '0' && val[i] <= '9') || val[i] === '-' || val[i] === '+' || val[i] === '.' || val[i] === ',') {
if (val[i] === '.' && convertComma === true) val[i] = ',';
if (val[i] === ',' && convertComma === false) val[i] = '.';
newVal += val[i];
}
}
if (val != newVal) {
$(this).val(newVal);
}
}
}
$(this).trigger('change');
});
}
}
function load(settings, onChange) {
$('#tabs').tabs();
// works only with newest admin adapter
if (typeof systemConfig !== 'undefined') {
convertComma = systemConfig.common.isFloatComma;
}
if (settings.writeNulls === undefined) settings.writeNulls = true;
for (var key in settings) {
if (settings.hasOwnProperty(key)) {
setValue(key, settings[key], onChange);
}
}
$('#passwordConfirm').val(settings.password);
$('#test').button().click(function () {
getIsAdapterAlive(function (isAlive) {
if (!isAlive) {
showMessage(_('Start or enable adapter first'));
} else {
$('#test').button('option', 'disabled', true);
sendTo(null, 'test', {
config: {
dbtype: $('#dbtype').val(),
port: $('#port').val(),
host: $('#host').val(),
user: $('#user').val(),
fileName: $('#fileName').val(),
password: $('#password').val()
}
}, function (response) {
$('#test').button('option', 'disabled', false);
showMessage(response.error || 'OK');
});
}
});
});
$('#reset').button().click(function () {
getIsAdapterAlive(function (isAlive) {
if (!isAlive) {
showMessage(_('Start or enable adapter first'));
} else {
if (confirm(_('Are you sure? All data will be dropped.'))) {
$('#reset').button('option', 'disabled', true);
sendTo(null, 'destroy', null, function (response) {
$('#reset').button('option', 'disabled', false);
showMessage(response.error || _('Adapter will be restarted'));
});
}
}
});
});
$('#dbtype').change(function () {
if ($(this).val() === 'sqlite') {
$('.baby-sql').show();
$('.real-sql').hide();
$('.ms-sql').hide();
} else if ($(this).val() === 'mssql') {
$('.baby-sql').hide();
$('.real-sql').show();
$('.ms-sql').show();
} else {
$('.baby-sql').hide();
$('.ms-sql').hide();
$('.real-sql').show();
}
}).trigger('change');
onChange(false);
}
function save(callback) {
var settings = {};
$('.value').each(function () {
var $this = $(this);
var id = $this.attr('id');
if ($this.attr('type') === 'checkbox') {
settings[id] = $this.prop('checked');
} else {
settings[id] = $this.val();
}
});
if (($('#dbtype').val() !== 'sqlite') && ($('#passwordConfirm').val() !== $('#password').val())) {
showMessage(_('Password confirmation is not equal with password!'));
return;
}
callback(settings);
}
</script>
</head>
<body>
<div id="adapter-container">
<table><tr>
<td><img src="sql.png"></td>
<td style="padding-top: 20px;padding-left: 10px"><h3 class="translate">SQL history adapter settings</h3></td>
</tr></table>
<div id="tabs">
<ul>
<li><a href="#tabs-1" class="translate">DB settings</a></li>
<li><a href="#tabs-2" class="translate">Default settings</a></li>
</ul>
<div id="tabs-1">
<table>
<tr>
<td><label class="translate" for="dbtype">DB Type</label></td>
<td><select id="dbtype" class="value">
<option value="mysql">MySQL</option>
<option value="postgresql">PostgreSQL</option>
<option value="sqlite">SQLite3</option>
<option value="mssql">MS-SQL</option>
</select></td>
<td></td>
</tr>
<tr class="real-sql">
<td><label class="translate" for="host">Host</label></td>
<td><input id="host" class="value"/></td>
<td></td>
</tr>
<tr class="real-sql">
<td><label class="translate" for="port">Port</label></td>
<td><input id="port" class="value"/></td>
<td></td>
</tr>
<tr class="ms-sql real-sql">
<td><label class="translate" for="dbname">DB Name</label></td>
<td><input id="dbname" class="value"/></td>
<td></td>
</tr>
<tr class="real-sql">
<td><label class="translate" for="user">User</label></td>
<td><input id="user" class="value"/></td>
<td></td>
</tr>
<tr class="real-sql">
<td><label class="translate" for="password">Password</label></td>
<td><input id="password" type="password" class="value"/></td>
<td></td>
</tr>
<tr class="real-sql">
<td><label class="translate" for="passwordConfirm">Password confirm</label></td>
<td><input id="passwordConfirm" type="password"/></td>
<td></td>
</tr>
<tr class="baby-sql">
<td><label class="translate" for="fileName">File for sqlite:</label></td>
<td><input id="fileName" class="value"/></td>
<td class="translate">Input path with the file name.</td>
</tr>
<tr class="baby-sql">
<td><label class="translate" for="requestInterval">requestInterval</label></td>
<td><input id="requestInterval" class="value"/></td>
<td></td>
</tr>
<tr class="ms-sql real-sql">
<td><label class="translate" for="encrypt">Encrypt:</label></td>
<td><input id="encrypt" type="checkbox" class="value"/></td>
<td></td>
</tr>
<tr class="ms-sql real-sql">
<td><label class="translate" for="round">Round real to:</label></td>
<td><input id="round" class="value"/></td>
<td></td>
</tr>
<tr class="ms-sql real-sql">
<td><label class="translate" for="multiRequests">Allow parallel requests:</label></td>
<td><input id="multiRequests" type="checkbox" class="value"/></td>
<td></td>
</tr>
<tr>
<td><label class="translate" for="writeNulls">Write NULL values on start/stop boundaries:</label></td>
<td><input id="writeNulls" type="checkbox" class="value"/></td>
<td></td>
</tr>
</table>
<br>
<button id="test" class="translate">Test connection</button>
<button id="reset" class="translate">Reset DB</button>
</div>
<div id="tabs-2">
<table>
<tr><td colspan="3"><h4 class="translate">Default settings:</h4></td></tr>
<tr>
<td><label class="translate" for="debounce">debounce</label></td>
<td><input id="debounce" size="5" class="value number"/></td>
<td></td>
</tr>
<tr>
<td><label class="translate" for="changesRelogInterval">log changes interval(s)</label></td>
<td><input id="changesRelogInterval" size="5" class="value number"/></td>
<td class="translate">0 = disable</td>
</tr>
<tr>
<td><label class="translate" for="changesMinDelta">log changes minimal delta</label></td>
<td><input id="changesMinDelta" size="5" class="value number"/></td>
<td class="translate">0 = disable delta check</td>
</tr>
<tr>
<td><label class="translate" for="retention">retention</label></td>
<td><select id="retention" class="value">
<option value="0" class="translate">keep forever</option>
<option value="63072000" class="translate">2 years</option>
<option value="31536000" class="translate">1 year</option>
<option value="15811200" class="translate">6 months</option>
<option value="7948800" class="translate">3 months</option>
<option value="2678400" class="translate">1 months</option>
<option value="1209600" class="translate">2 weeks</option>
<option value="604800" class="translate">1 week</option>
<option value="432000" class="translate">5 days</option>
<option value="259200" class="translate">3 days</option>
<option value="86400" class="translate">1 day</option>
</select></td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>

296
admin/index_m.html Normal file
View File

@@ -0,0 +1,296 @@
<html>
<head>
<meta charset="UTF-8">
<!-- Materialze style -->
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<link rel="stylesheet" type="text/css" href="../../lib/css/materialize.css">
<script type="text/javascript" src="../../lib/js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../lib/js/materialize.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<script type="text/javascript" src="words.js"></script>
<script type="text/javascript">
var convertComma = null;
function setValue(id, value, onChange) {
var $value = $('#' + id + '.value');
if ($value.attr('type') === 'checkbox') {
$value.prop('checked', value).change(function () {
onChange();
});
} else {
$value.val(value).change(function() {
onChange();
}).keyup(function() {
// Check that only numbers entered
if ($(this).hasClass('number')) {
var val = $(this).val();
if (val) {
var newVal = '';
for (var i = 0; i < val.length; i++) {
if ((val[i] >= '0' && val[i] <= '9') || val[i] === '-' || val[i] === '+' || val[i] === '.' || val[i] === ',') {
if (val[i] === '.' && convertComma === true) val[i] = ',';
if (val[i] === ',' && convertComma === false) val[i] = '.';
newVal += val[i];
}
}
if (val != newVal) {
$(this).val(newVal);
}
}
}
$(this).trigger('change');
});
}
}
function load(settings, onChange) {
// works only with newest admin adapter
if (typeof systemConfig !== 'undefined') {
convertComma = systemConfig.common.isFloatComma;
}
if (settings.writeNulls === undefined) settings.writeNulls = true;
for (var key in settings) {
if (settings.hasOwnProperty(key)) {
setValue(key, settings[key], onChange);
}
}
$('#passwordConfirm').val(settings.password);
$('#test').on('click', function () {
getIsAdapterAlive(function (isAlive) {
if (!isAlive) {
showToast(null, _('Start or enable adapter first'));
} else {
$('#test').addClass('disabled');
sendTo(null, 'test', {
config: {
dbtype: $('#dbtype').val(),
port: $('#port').val(),
host: $('#host').val(),
user: $('#user').val(),
fileName: $('#fileName').val(),
password: $('#password').val()
}
}, function (response) {
$('#test').removeClass('disabled');
if (response.error) {
showError(response.error);
} else {
showMessage('OK');
}
});
}
});
});
$('#reset').on('click', function () {
getIsAdapterAlive(function (isAlive) {
if (!isAlive) {
showToast(null, _('Start or enable adapter first'));
} else {
if (confirm(_('Are you sure? All data will be dropped.'))) {
$('#reset').addClass('disabled');
sendTo(null, 'destroy', null, function (response) {
$('#reset').removeClass('disabled');
if (response.error) {
showError(response.error);
} else {
showToast(null, _('Adapter will be restarted'));
}
});
}
}
});
});
$('#dbtype').on('change', function () {
if ($(this).val() === 'sqlite') {
$('.baby-sql').show();
$('.real-sql').hide();
$('.ms-sql').hide();
} else if ($(this).val() === 'mssql') {
$('.baby-sql').hide();
$('.real-sql').show();
$('.ms-sql').show();
} else {
$('.baby-sql').hide();
$('.ms-sql').hide();
$('.real-sql').show();
}
}).trigger('change');
onChange(false);
if (M) M.updateTextFields();
}
function save(callback) {
var settings = {};
$('.value').each(function () {
var $this = $(this);
var id = $this.attr('id');
if ($this.attr('type') === 'checkbox') {
settings[id] = $this.prop('checked');
} else {
settings[id] = $this.val();
}
});
if (($('#dbtype').val() !== 'sqlite') && ($('#passwordConfirm').val() !== $('#password').val())) {
showMessage(_('Password confirmation is not equal with password!'));
return;
}
callback(settings);
}
</script>
<style>
.adapter-container>div {
margin-bottom: 0 !important;
}
.page {
height: calc(100% - 50px) !important;
}
.marg{
margin-top: 3em;
}
</style>
</head>
<body>
<div class="m adapter-container">
<div class="row">
<div class="col s12">
<ul class="tabs">
<li class="tab col s3"><a href="#tab-main" class="translate active">DB settings</a></li>
<li class="tab col s3"><a href="#tab-default" class="translate">Default settings</a></li>
</ul>
</div>
<div id="tab-main" class="col s12 page">
<div class="row">
<div class="col s6">
<img src="sql.png" class="logo">
</div>
</div>
<div class="row">
<div class="input-field col s12 m4">
<select id="dbtype" class="value">
<option value="mysql">MySQL</option>
<option value="postgresql">PostgreSQL</option>
<option value="sqlite">SQLite3</option>
<option value="mssql">MS-SQL</option>
</select>
<label class="translate" for="dbtype">DB Type</label>
</div>
</div>
<div class="row">
<div class="input-field real-sql col s12 m3 l4">
<input id="host" type="text" class="value" />
<label class="translate" for="host">Host</label>
</div>
<div class="input-field real-sql col s12 m3 l4 ">
<input id="port" type="number" min="1" max="65565" class="value" />
<label class="translate" for="port">Port</label>
</div>
<div class="input-field ms-sql real-sql col s12 m6 l4">
<input id="dbname" type="text" class="value" />
<label class="translate" for="dbname">DB Name</label>
</div>
</div>
<div class="row real-sql">
<div class="input-field col s12 m6 l4">
<input id="user" type="text" class="value" />
<label class="translate" for="user">User</label>
</div>
<div class="input-field col s12 m3 l4">
<input id="password" type="password" class="value" />
<label class="translate" for="password">Password</label>
</div>
<div class="input-field col s12 m3 l4">
<input id="passwordConfirm" type="password" />
<label class="translate" for="passwordConfirm">Password confirm</label>
</div>
</div>
<div class="row baby-sql">
<div class="input-field col s12 m6 l4">
<input id="fileName" type="text" class="value" />
<label for="fileName" class="translate">File for sqlite:</label>
<span class="translate">Input path with the file name.</span>
</div>
<div class="input-field col s12 m6 l4">
<input id="requestInterval" type="number" class="value" />
<label class="translate" for="requestInterval">requestInterval</label>
</div>
</div>
<div class="row ms-sql real-sql">
<div class="input-field col s12 m4">
<input id="encrypt" type="checkbox" class="value" />
<label class="translate" for="encrypt">Encrypt:</label>
</div>
<div class="input-field col s12 m4">
<input id="round" type="text" class="value" />
<label class="translate" for="round">Round real to:</label>
</div>
<div class="input-field col s12 m4">
<input id="multiRequests" type="checkbox" class="value" />
<label class="translate" for="multiRequests">Allow parallel requests:</label>
</div>
</div>
<div class="row">
<div class="input-field col s12 m4">
<input id="writeNulls" type="checkbox" class="value" />
<label class="translate" for="writeNulls">Write NULL values on start/stop boundaries:</label>
</div>
</div>
<div class="row">
<div class="col s12">
<a id="test" class="waves-effect waves-light btn"><i class="material-icons left">wifi_tethering</i><span class="translate">Test connection</span></a>
<a id="reset" class="waves-effect waves-light btn"><i class="material-icons left">report</i><span class="translate">Reset DB</span></a>
</div>
</div>
</div>
<div id="tab-default" class="col s12 page">
<div class="row marg">
<div class="input-field col s12 m6 l4">
<input id="debounce" size="5" class="value" type="number" />
<label class="translate" for="debounce">debounce</label>
</div>
<div class="input-field col s12 m6 l4">
<input id="changesRelogInterval" size="5" class="value" type="number" />
<label for="changesRelogInterval" class="translate">log changes interval(s)</label>
(<span class="translate">0 = disable</span>)
</div>
</div>
<div class="row">
<div class="input-field col s12 m6 l4">
<input id="changesMinDelta" type="number" size="5" class="value" />
<label for="changesMinDelta" class="translate">log changes minimal delta</label>
(<span class="translate">0 = disable delta check</span>)
</div>
<div class="input-field col s12 m6 l4">
<select id="retention" class="value">
<option value="0" class="translate">keep forever</option>
<option value="63072000" class="translate">2 years</option>
<option value="31536000" class="translate">1 year</option>
<option value="15811200" class="translate">6 months</option>
<option value="7948800" class="translate">3 months</option>
<option value="2678400" class="translate">1 months</option>
<option value="1209600" class="translate">2 weeks</option>
<option value="604800" class="translate">1 week</option>
<option value="432000" class="translate">5 days</option>
<option value="259200" class="translate">3 days</option>
<option value="86400" class="translate">1 day</option>
</select>
<label class="translate" for="retention">retention</label>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

BIN
admin/sql.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

60
admin/words.js Normal file
View File

@@ -0,0 +1,60 @@
// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n
/*global systemDictionary:true */
'use strict';
systemDictionary = {
"History adapter settings": { "en": "History adapter settings", "de": "History-Adapter Einstellungen", "ru": "Настройки профайлера", "pt": "Configurações do adaptador de histórico", "nl": "Geschiedenis adapter instellingen", "fr": "Paramètres de l'adaptateur d'historique", "it": "Impostazioni della scheda Cronologia", "es": "Configuración del adaptador de historial", "pl": "Ustawienia adaptera historii"},
"note": { "en": "History settings must be done for every state individually on the tab \"States\" and only if adapter is active", "de": "History Einstellungen mussen für jeden State einzeln gemacht werden auf der Lasche \"States\" und nur wenn adapter aktiv ist", "ru": "Настройки профайлера делаются для каждого состояния отдельно в закладке \"Состояния\" и только, когда драйвер активирован", "pt": "As configurações de histórico devem ser feitas para cada estado individual na guia \"Estados\" e somente se o adaptador estiver ativo", "nl": "Geschiedenisinstellingen moeten voor elke afzonderlijke status worden uitgevoerd op het tabblad \"Staten\" en alleen als de adapter actief is", "fr": "Les paramètres d'historique doivent être définis pour chaque état individuellement dans l'onglet \"Etats\" et uniquement si l'adaptateur est actif", "it": "Le impostazioni della cronologia devono essere eseguite singolarmente per ogni stato nella scheda \"Stati\" e solo se l'adattatore è attivo", "es": "La configuración del historial se debe realizar para cada estado individualmente en la pestaña \"Estados\" y solo si el adaptador está activo", "pl": "Ustawienia historii muszą być wykonane dla każdego stanu indywidualnie na karcie \"Stany\" i tylko wtedy, gdy adapter jest aktywny"},
"Default settings:": { "en": "Default settings", "de": "Default Einstellungen für Zuständen", "ru": "Настройки по умолчанию для состояний", "pt": "Configurações padrão", "nl": "Standaard instellingen", "fr": "Paramètres par défaut", "it": "Impostazioni predefinite", "es": "Configuración por defecto", "pl": "Ustawienia domyślne"},
"Start or enable adapter first": { "en": "Start or enable adapter first", "de": "Adapter muss zuerst aktiviert werden", "ru": "Сначала надо активировать драйвер SQL", "pt": "Inicie ou ative o adaptador primeiro", "nl": "Start of schakel eerst de adapter in", "fr": "Démarrer ou activer l'adaptateur en premier", "it": "Inizia o abilita prima l'adattatore", "es": "Comience o habilite el adaptador primero", "pl": "Najpierw włącz lub włącz adapter"},
"debounce": { "en": "De-bounce interval(ms)", "de": "Entprellzeit(ms)", "ru": "Минимальный интервал(ms)", "pt": "Intervalo de desvio (ms)", "nl": "De-bounce interval (ms)", "fr": "Intervalle de rebond (ms)", "it": "Intervallo di rimbalzo (ms)", "es": "Intervalo de rebote (ms)", "pl": "Interwał odbicia (ms)"},
"log changes interval(s)": { "en": "Log unchanged values any(s)", "de": "Gleiche Werte aufzeichnen(s)", "ru": "Запись неизменённых значений каждые (сек)", "pt": "Registre valores inalterados qualquer (s)", "nl": "Log onveranderde waarden voor elke", "fr": "Consigner les valeurs inchangées any (s)", "it": "Registra valori invariati nessuno (i)", "es": "Registre los valores sin modificar cualquier (s)", "pl": "Zaloguj niezmienione wartości dowolne (s)"},
"log changes minimal delta": { "en": "Minimum difference from last value to log", "de": "Minimale Abweichung vom letzten Wert für Aufzeichnung", "ru": "Минимальная разница с последним записанным значением", "pt": "Diferença mínima do último valor ao log", "nl": "Minimaal verschil van laatste waarde tot log", "fr": "Différence minimale de la dernière valeur à la notation", "it": "Differenza minima dall'ultimo valore da registrare", "es": "Diferencia mínima desde el último valor para registrar", "pl": "Minimalna różnica od ostatniej wartości do dziennika"},
"0 = disable delta check": { "en": "0 = disable delta check", "de": "0 = Abweichungsprüfung deaktivieren", "ru": "0 = не проверять минимальное изменение", "pt": "0 = desativar o cheque delta", "nl": "0 = delta-controle uitschakelen", "fr": "0 = désactiver le contrôle delta", "it": "0 = disabilita il controllo delta", "es": "0 = deshabilitar la verificación delta", "pl": "0 = wyłącz kontrolę delta"},
"0 = disable": { "en": "0 = disable", "de": "0 = Deaktivieren", "ru": "0 = не активно", "pt": "0 = desativar", "nl": "0 = uitschakelen", "fr": "0 = désactiver", "it": "0 = disabilita", "es": "0 = desactivar", "pl": "0 = wyłącz"},
"DB settings": { "en": "DB settings", "de": "DB Einstellungen", "ru": "Настройки соединения с DB", "pt": "Configurações de banco de dados", "nl": "DB-instellingen", "fr": "Paramètres DB", "it": "Impostazioni DB", "es": "Configuración de DB", "pl": "Ustawienia DB"},
"Default settings": { "en": "Default settings", "de": "Default Einstellungen", "ru": "Настройки по умолчанию", "pt": "Configurações padrão", "nl": "Standaard instellingen", "fr": "Paramètres par défaut", "it": "Impostazioni predefinite", "es": "Configuración por defecto", "pl": "Ustawienia domyślne"},
"retention": { "en": "Storage retention", "de": "Storage Vorhaltezeit", "ru": "Время хранения в базе", "pt": "Retenção de armazenamento", "nl": "Bewaring van opslag", "fr": "Rétention de stockage", "it": "Conservazione dello spazio", "es": "Retención de almacenamiento", "pl": "Zatrzymanie przechowywania"},
"keep forever": { "en": "keep forever", "de": "keine automatische Löschung", "ru": "хранить вечно", "pt": "mantenha para sempre", "nl": "blijf voor altijd", "fr": "garde pour toujours", "it": "tienilo per sempre", "es": "mantener para siempre", "pl": "zachowaj na zawsze"},
"2 years": { "en": "2 years", "de": "2 Jahre", "ru": "2 года", "pt": "2 anos", "nl": "2 jaar", "fr": "2 ans", "it": "2 anni", "es": "2 años", "pl": "2 lata"},
"1 year": { "en": "1 year", "de": "1 Jahr", "ru": "1 год", "pt": "1 ano", "nl": "1 jaar", "fr": "1 an", "it": "1 anno", "es": "1 año", "pl": "1 rok"},
"6 months": { "en": "6 months", "de": "6 Monate", "ru": "6 месяцев", "pt": "6 meses", "nl": "6 maanden", "fr": "6 mois", "it": "6 mesi", "es": "6 meses", "pl": "6 miesięcy"},
"3 months": { "en": "3 months", "de": "3 Monate", "ru": "3 месяца", "pt": "3 meses", "nl": "3 maanden", "fr": "3 mois", "it": "3 mesi", "es": "3 meses", "pl": "3 miesiące"},
"1 months": { "en": "1 months", "de": "1 Monat", "ru": "1 месяц", "pt": "1 mês", "nl": "1 maanden", "fr": "1 mois", "it": "1 mese", "es": "1 mes", "pl": "1 miesiąc"},
"2 weeks": { "en": "2 weeks", "de": "2 Wochen", "ru": "2 недели", "pt": "2 semanas", "nl": "2 weken", "fr": "2 semaines", "it": "2 settimane", "es": "2 semanas", "pl": "2 tygodnie"},
"1 week": { "en": "1 week", "de": "1 Woche", "ru": "1 неделя", "pt": "1 semana", "nl": "1 week", "fr": "1 semaine", "it": "1 settimana", "es": "1 semana", "pl": "1 tydzień"},
"5 days": { "en": "5 days", "de": "5 Tage", "ru": "5 дней", "pt": "5 dias", "nl": "5 dagen", "fr": "5 jours", "it": "5 giorni", "es": "5 dias", "pl": "5 dni"},
"3 days": { "en": "3 days", "de": "3 Tage", "ru": "3 дня", "pt": "3 dias", "nl": "3 dagen", "fr": "3 jours", "it": "3 giorni", "es": "3 días", "pl": "3 dni"},
"1 day": { "en": "1 day", "de": "1 Tag", "ru": "1 день", "pt": "1 dia", "nl": "1 dag", "fr": "Un jour", "it": "1 giorno", "es": "1 día", "pl": "1 dzień"},
"SQL history adapter settings": { "en": "SQL history adapter settings", "de": "SQL history adapter Einstellungen", "ru": "Настройки профайлера в SQL базу данных", "pt": "Configurações do adaptador de histórico SQL", "nl": "Instellingen voor SQL-geschiedenisadapter", "fr": "Paramètres de l'adaptateur d'historique SQL", "it": "Impostazioni della scheda cronologia SQL", "es": "Configuración del adaptador de historial SQL", "pl": "Ustawienia adaptera historii SQL"},
"DB settings:": { "en": "DB settings", "de": "DB Einstellungen", "ru": "DB settings", "pt": "Configurações de banco de dados", "nl": "DB-instellingen", "fr": "Paramètres de base de données", "it": "Impostazioni DB", "es": "Configuración de DB", "pl": "Ustawienia DB"},
"DB Type": { "en": "DB Type", "de": "DB Typ", "ru": "DB Type", "pt": "DB Type", "nl": "DB Type", "fr": "Type de DB", "it": "Tipo di DB", "es": "Tipo de DB", "pl": "Typ DB"},
"Host": { "en": "Host", "de": "Host", "ru": "Хост", "pt": "Hospedeiro", "nl": "Host", "fr": "Hôte", "it": "Ospite", "es": "Anfitrión", "pl": "Gospodarz"},
"DB Name": { "en": "Database name", "de": "Datenbankname", "ru": "Имя базы данных", "pt": "Nome do banco de dados", "nl": "Database naam", "fr": "Nom de la base de données", "it": "Nome del database", "es": "Nombre de la base de datos", "pl": "Nazwa bazy danych"},
"Port": { "en": "Port", "de": "Port", "ru": "Порт", "pt": "Porta", "nl": "Haven", "fr": "Port", "it": "Porta", "es": "Puerto", "pl": "Port"},
"User": { "en": "User", "de": "Login", "ru": "Пользователь", "pt": "Do utilizador", "nl": "Gebruiker", "fr": "Utilisateur", "it": "Utente", "es": "Usuario", "pl": "Użytkownik"},
"Password": { "en": "Password", "de": "Kennwort", "ru": "Пароль", "pt": "Senha", "nl": "Wachtwoord", "fr": "Mot de passe", "it": "Parola d'ordine", "es": "Contraseña", "pl": "Hasło"},
"Password confirm": { "en": "Password confirm", "de": "Kennwort-Wiederholung", "ru": "Подтверждение пароля", "pt": "Confirmação de senha", "nl": "Wachtwoord bevestigen", "fr": "Confirmer le mot de passe", "it": "Conferma la password", "es": "Contraseña confirmada", "pl": "Potwierdź hasło"},
"File for sqlite:": { "en": "File for sqlite", "de": "Datei für sqlite", "ru": "Файл для sqlite", "pt": "Arquivo para sqlite", "nl": "Bestand voor sqlite", "fr": "Fichier pour sqlite", "it": "File per sqlite", "es": "Archivo para sqlite", "pl": "Plik dla sqlite"},
"Encrypt:": { "en": "Encrypt", "de": "Verschlüsseln", "ru": "Шифрование", "pt": "Criptografar", "nl": "versleutelen", "fr": "Crypter", "it": "Encrypt", "es": "Encriptar", "pl": "Szyfruj"},
"Test connection": { "en": "Test connection", "de": "Verbindung testen", "ru": "Проверить соединение", "pt": "Conexão de teste", "nl": "Test verbinding", "fr": "Test de connexion", "it": "Connessione di prova", "es": "Conexión de prueba", "pl": "Testuj połączenie"},
"requestInterval": { "en": "Pause between requests(ms)", "de": "Intervall zwischen DB Anfragen(ms)", "ru": "Пауза между запросами к базе", "pt": "Pausa entre solicitações (ms)", "nl": "Pauzeren tussen verzoeken (ms)", "fr": "Pause entre les demandes (ms)", "it": "Pausa tra le richieste (ms)", "es": "Pausa entre solicitudes (ms)", "pl": "Wstrzymaj między żądaniami (ms)"},
"Round real to:": { "en": "Round real to", "de": "Aufrunden auf: ", "ru": "Округлять до", "pt": "Round real to", "nl": "Rond echt naar", "fr": "Rond réel à", "it": "Round reale a", "es": "Redondo real para", "pl": "Runda prawdziwa do"},
"Password confirmation is not equal with password!": {"en": "Password confirmation is not equal with password!", "de": "Kennwort-Bestätigung ist nicht gleich mit dem Kennwort!", "ru": "Подтверждение пароля не совпадает с паролем!", "pt": "A confirmação da senha não é igual com a senha!", "nl": "Wachtwoordbevestiging is niet gelijk aan wachtwoord!", "fr": "La confirmation du mot de passe n'est pas égale au mot de passe!", "it": "La conferma della password non è uguale alla password!", "es": "La confirmación de la contraseña no es igual a la contraseña!", "pl": "Potwierdzenie hasła nie jest równe hasłu!"},
"Input path with the file name.": { "en": "E.g. /opt/sqlite/data.db", "de": "Z.B. /opt/sqlite/data.db", "ru": "Например /opt/sqlite/data.db", "pt": "Por exemplo. /opt/sqlite/data.db", "nl": "Bijv. /opt/sqlite/data.db", "fr": "Par exemple. /opt/sqlite/data.db", "it": "Per esempio. /opt/sqlite/data.db", "es": "P.ej. /opt/sqlite/data.db", "pl": "Na przykład. /opt/sqlite/data.db"},
"Are you sure? All data will be dropped.": { "en": "Are you sure? All data will be dropped.", "de": "Sind Sie sicher? ALLE Daten werden geöscht.", "ru": "Вы уверенны? Все данные в базе будут удалены.", "pt": "Você tem certeza? Todos os dados serão descartados.", "nl": "Weet je het zeker? Alle gegevens worden verwijderd.", "fr": "Êtes-vous sûr? Toutes les données seront supprimées.", "it": "Sei sicuro? Tutti i dati verranno eliminati.", "es": "¿Estás seguro? Todos los datos serán eliminados.", "pl": "Jesteś pewny? Wszystkie dane zostaną usunięte."},
"Reset DB": { "en": "Reset DB", "de": "Alle Daten in DB löschen", "ru": "Сбросить DB", "pt": "Redefinir DB", "nl": "Stel DB opnieuw in", "fr": "Reset DB", "it": "Ripristina DB", "es": "Restablecer DB", "pl": "Zresetuj DB"},
"Ok": { "en": "Ok", "de": "Ok", "ru": "Ok", "pt": "Está bem", "nl": "OK", "fr": "D'accord", "it": "Ok", "es": "De acuerdo", "pl": "Ok"},
"Allow parallel requests:": { "en": "Allow parallel requests", "de": "Parallelanfragen erlauben", "ru": "Разрешить параллельные запросы к базе", "pt": "Permitir pedidos paralelos", "nl": "Sta parallelle verzoeken toe", "fr": "Autoriser les demandes parallèles", "it": "Consenti richieste parallele", "es": "Permitir solicitudes paralelas", "pl": "Pozwól na równoległe żądania"},
"Write NULL values on start/stop boundaries:": {
"en": "Write NULL values on start/stop boundaries:",
"de": "Schreibe NULL-Werte an Start- / Stop-Grenzen:",
"ru": "Запись значений NULL на границах начала / остановки:",
"pt": "Escreva valores NULL nos limites de início / parada:",
"nl": "Schrijf NULL-waarden op start / stop-grenzen:",
"fr": "Écrire des valeurs NULL sur les limites de démarrage / arrêt:",
"it": "Scrivi valori NULL sui limiti di avvio / arresto:",
"es": "Escribe valores NULL en los límites de inicio / finalización:",
"pl": "Napisz wartości NULL na granicach start / stop:"
}
};

31
appveyor.yml Normal file
View File

@@ -0,0 +1,31 @@
version: 'test-{build}'
os: Visual Studio 2013
environment:
matrix:
- nodejs_version: '4'
- nodejs_version: '6'
- nodejs_version: '8'
- nodejs_version: '10'
platform:
- x86
- x64
clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%'
services:
- mssql2014
- mysql
- postgresql
install:
- ps: 'Install-Product node $env:nodejs_version $env:platform'
- ps: '$NpmVersion = (npm -v).Substring(0,1)'
- ps: 'if($NpmVersion -eq 5) { npm install -g npm@5 }'
- ps: npm --version
- npm install
- npm install winston@2.3.1
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
test_script:
- echo %cd%
- node --version
- npm --version
- ps: Start-Sleep -s 15
- npm test
build: 'off'

401
gulpfile.js Normal file
View File

@@ -0,0 +1,401 @@
'use strict';
var gulp = require('gulp');
var fs = require('fs');
var pkg = require('./package.json');
var iopackage = require('./io-package.json');
var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
/*var appName = getAppName();
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 1].split('.')[0].toLowerCase();
}
*/
const fileName = 'words.js';
var languages = {
en: {},
de: {},
ru: {},
pt: {},
nl: {},
fr: {},
it: {},
es: {},
pl: {}
};
function lang2data(lang, isFlat) {
var str = isFlat ? '' : '{\n';
var count = 0;
for (var w in lang) {
if (lang.hasOwnProperty(w)) {
count++;
if (isFlat) {
str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n';
} else {
var key = ' "' + w.replace(/"/g, '\\"') + '": ';
str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
}
}
}
if (!count) return isFlat ? '' : '{\n}';
if (isFlat) {
return str;
} else {
return str.substring(0, str.length - 2) + '\n}';
}
}
function readWordJs(src) {
try {
var words;
if (fs.existsSync(src + 'js/' + fileName)) {
words = fs.readFileSync(src + 'js/' + fileName).toString();
} else {
words = fs.readFileSync(src + fileName).toString();
}
var lines = words.split(/\r\n|\r|\n/g);
var i = 0;
while (!lines[i].match(/^systemDictionary = {/)) {
i++;
}
lines.splice(0, i);
// remove last empty lines
i = lines.length - 1;
while (!lines[i]) {
i--;
}
if (i < lines.length - 1) {
lines.splice(i + 1);
}
lines[0] = lines[0].replace('systemDictionary = ', '');
lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}');
words = lines.join('\n');
var resultFunc = new Function('return ' + words + ';');
return resultFunc();
} catch (e) {
return null;
}
}
function padRight(text, totalLength) {
return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : '');
}
function writeWordJs(data, src) {
var text = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n';
text += '/*global systemDictionary:true */\n';
text += '\'use strict\';\n\n';
text += 'systemDictionary = {\n';
for (var word in data) {
if (data.hasOwnProperty(word)) {
text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50);
var line = '';
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' ';
}
}
if (line) {
line = line.trim();
line = line.substring(0, line.length - 1);
}
text += line + '},\n';
}
}
text = text.replace(/},\n$/, '}\n');
text += '};';
if (fs.existsSync(src + 'js/' + fileName)) {
fs.writeFileSync(src + 'js/' + fileName, text);
} else {
fs.writeFileSync(src + '' + fileName, text);
}
}
const EMPTY = '';
function words2languages(src) {
var langs = Object.assign({}, languages);
var data = readWordJs(src);
if (data) {
for (var word in data) {
if (data.hasOwnProperty(word)) {
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang];
// pre-fill all other languages
for (var j in langs) {
if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY;
}
}
}
}
}
}
if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/');
}
for (var l in langs) {
if (!langs.hasOwnProperty(l)) continue;
var keys = Object.keys(langs[l]);
//keys.sort();
var obj = {};
for (var k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]];
}
if (!fs.existsSync(src + 'i18n/' + l)) {
fs.mkdirSync(src + 'i18n/' + l);
}
fs.writeFileSync(src + 'i18n/' + l + '/translations.json', lang2data(obj));
}
} else {
console.error('Cannot read or parse ' + fileName);
}
}
function words2languagesFlat(src) {
var langs = Object.assign({}, languages);
var data = readWordJs(src);
if (data) {
for (var word in data) {
if (data.hasOwnProperty(word)) {
for (var lang in data[word]) {
if (data[word].hasOwnProperty(lang)) {
langs[lang][word] = data[word][lang];
// pre-fill all other languages
for (var j in langs) {
if (langs.hasOwnProperty(j)) {
langs[j][word] = langs[j][word] || EMPTY;
}
}
}
}
}
}
var keys = Object.keys(langs.en);
//keys.sort();
for (var l in langs) {
if (!langs.hasOwnProperty(l)) continue;
var obj = {};
for (var k = 0; k < keys.length; k++) {
obj[keys[k]] = langs[l][keys[k]];
}
langs[l] = obj;
}
if (!fs.existsSync(src + 'i18n/')) {
fs.mkdirSync(src + 'i18n/');
}
for (var ll in langs) {
if (!langs.hasOwnProperty(ll)) continue;
if (!fs.existsSync(src + 'i18n/' + ll)) {
fs.mkdirSync(src + 'i18n/' + ll);
}
fs.writeFileSync(src + 'i18n/' + ll + '/flat.txt', lang2data(langs[ll], langs.en));
}
fs.writeFileSync(src + 'i18n/flat.txt', keys.join('\n'));
} else {
console.error('Cannot read or parse ' + fileName);
}
}
function languagesFlat2words(src) {
var dirs = fs.readdirSync(src + 'i18n/');
var langs = {};
var bigOne = {};
var order = Object.keys(languages);
dirs.sort(function (a, b) {
var posA = order.indexOf(a);
var posB = order.indexOf(b);
if (posA === -1 && posB === -1) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
} else if (posA === -1) {
return -1;
} else if (posB === -1) {
return 1;
} else {
if (posA > posB) return 1;
if (posA < posB) return -1;
return 0;
}
});
var keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n');
for (var l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l];
var values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n');
langs[lang] = {};
keys.forEach(function (word, i) {
langs[lang][word] = values[i].replace(/<\/ i>/g, '</i>').replace(/<\/ b>/g, '</b>').replace(/<\/ span>/g, '</span>').replace(/% s/g, ' %s');
});
var words = langs[lang];
for (var word in words) {
if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) {
bigOne[word][lang] = words[word];
}
}
}
}
// read actual words.js
var aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt'];
if (aWords) {
// Merge words together
for (var w in aWords) {
if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w);
bigOne[w] = aWords[w]
}
dirs.forEach(function (lang) {
if (temporaryIgnore.indexOf(lang) !== -1) return;
if (!bigOne[w][lang]) {
console.warn('Missing "' + lang + '": ' + w);
}
});
}
}
}
writeWordJs(bigOne, src);
}
function languages2words(src) {
var dirs = fs.readdirSync(src + 'i18n/');
var langs = {};
var bigOne = {};
var order = Object.keys(languages);
dirs.sort(function (a, b) {
var posA = order.indexOf(a);
var posB = order.indexOf(b);
if (posA === -1 && posB === -1) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
} else if (posA === -1) {
return -1;
} else if (posB === -1) {
return 1;
} else {
if (posA > posB) return 1;
if (posA < posB) return -1;
return 0;
}
});
for (var l = 0; l < dirs.length; l++) {
if (dirs[l] === 'flat.txt') continue;
var lang = dirs[l];
langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString();
langs[lang] = JSON.parse(langs[lang]);
var words = langs[lang];
for (var word in words) {
if (words.hasOwnProperty(word)) {
bigOne[word] = bigOne[word] || {};
if (words[word] !== EMPTY) {
bigOne[word][lang] = words[word];
}
}
}
}
// read actual words.js
var aWords = readWordJs();
var temporaryIgnore = ['pt', 'fr', 'nl', 'it'];
if (aWords) {
// Merge words together
for (var w in aWords) {
if (aWords.hasOwnProperty(w)) {
if (!bigOne[w]) {
console.warn('Take from actual words.js: ' + w);
bigOne[w] = aWords[w]
}
dirs.forEach(function (lang) {
if (temporaryIgnore.indexOf(lang) !== -1) return;
if (!bigOne[w][lang]) {
console.warn('Missing "' + lang + '": ' + w);
}
});
}
}
}
writeWordJs(bigOne, src);
}
gulp.task('adminWords2languages', function (done) {
words2languages('./admin/');
done();
});
gulp.task('adminWords2languagesFlat', function (done) {
words2languagesFlat('./admin/');
done();
});
gulp.task('adminLanguagesFlat2words', function (done) {
languagesFlat2words('./admin/');
done();
});
gulp.task('adminLanguages2words', function (done) {
languages2words('./admin/');
done();
});
gulp.task('updatePackages', function (done) {
iopackage.common.version = pkg.version;
iopackage.common.news = iopackage.common.news || {};
if (!iopackage.common.news[pkg.version]) {
var news = iopackage.common.news;
var newNews = {};
newNews[pkg.version] = {
en: 'news',
de: 'neues',
ru: 'новое'
};
iopackage.common.news = Object.assign(newNews, news);
}
fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4));
done();
});
gulp.task('updateReadme', function (done) {
var readme = fs.readFileSync('README.md').toString();
var pos = readme.indexOf('## Changelog\n');
if (pos !== -1) {
var readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
var readmeEnd = readme.substring(pos + '## Changelog\n'.length);
if (readme.indexOf(version) === -1) {
var timestamp = new Date();
var date = timestamp.getFullYear() + '-' +
('0' + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' +
('0' + (timestamp.getDate()).toString(10)).slice(-2);
var news = '';
if (iopackage.common.news && iopackage.common.news[pkg.version]) {
news += '* ' + iopackage.common.news[pkg.version].en;
}
fs.writeFileSync('README.md', readmeStart + '### ' + version + ' (' + date + ')\n' + (news ? news + '\n\n' : '\n') + readmeEnd);
}
}
done();
});
gulp.task('default', ['updatePackages', 'updateReadme']);

95
io-package.json Normal file
View File

@@ -0,0 +1,95 @@
{
"common": {
"name": "sql",
"title": "SQL History",
"titleLang": {
"en": "SQL logging"
},
"desc": {
"en": "Logging of states into SQL DB"
},
"version": "1.9.2",
"mode": "daemon",
"platform": "Node.js",
"loglevel": "info",
"messagebox": true,
"subscribe": "messagebox",
"keywords": ["charts", "sql", "logging", "graphs", "archive"],
"preserveSettings": "custom",
"supportCustoms": true,
"getHistory": true,
"enabled": true,
"stopBeforeUpdate": true,
"materialize": true,
"authors": [
"bluefox <dogafox@gmail.com>",
"Apollon77 <ingo@fischer-ka.de>"
],
"license": "MIT",
"readme": "https://git.spacen.net/yunkong2/yunkong2.sql/blob/master/README.md",
"icon": "sql.png",
"extIcon": "https://git.spacen.net/yunkong2/yunkong2.sql/raw/master/admin/sql.png",
"type": "storage",
"dependencies": [{"js-controller": ">=0.12.0","admin": ">=1.6.10"}],
"supportStopInstance": 2000,
"config":{
"minWidth": 570,
"width ": 435,
"minHeight": 200,
"height": 540
}
},
"native": {
"connLink": "",
"debounce": 1000,
"retention": 31536000,
"host": "localhost",
"port": 0,
"user": "",
"password": "",
"dbtype": "sqlite",
"fileName": "sqlite.db",
"requestInterval": 0,
"encrypt": false,
"round": 4,
"dbname": "yunkong2",
"multiRequests": true,
"changesRelogInterval": 0,
"changesMinDelta": 0,
"writeNulls": true
},
"instanceObjects": [
{
"_id": "info",
"type": "channel",
"common": {
"name": "Information"
},
"native": {}
},
{
"_id": "info.connection",
"type": "state",
"common": {
"role": "indicator.connected",
"name": "If connected to DB",
"type": "boolean",
"read": true,
"write": false,
"def": false
},
"native": {}
}
],
"objects": [
{
"_id": "_design/custom",
"language": "javascript",
"views": {
"state": {
"map": "function(doc) { if (doc.type==='state' && (doc.common.custom || doc.common.history)) emit(doc._id, doc.common.custom || doc.common.history) }"
}
}
}
]
}

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;

2070
main.js Normal file

File diff suppressed because it is too large Load Diff

48
package.json Normal file
View File

@@ -0,0 +1,48 @@
{
"name": "yunkong2.sql",
"description": "Log state sql in a two-stages process (first to Redis, then to CouchDB)",
"version": "1.9.2",
"author": "bluefox <dogafox@gmail.com>",
"contributors": [
"bluefox <dogafox@gmail.com>",
"Apollon77"
],
"homepage": "https://git.spacen.net/yunkong2/yunkong2.sql",
"repository": {
"type": "git",
"url": "https://git.spacen.net/yunkong2/yunkong2.sql"
},
"licenses": [
{
"type": "MIT",
"url": "https://git.spacen.net/yunkong2/yunkong2.sql/blob/master/LICENSE"
}
],
"keywords": [
"yunkong2",
"log data",
"home automation"
],
"optionalDependencies": {
"mysql": "^2.15.0",
"pg": "^6.1.5",
"sqlite3": "^4.0.1",
"mssql": "^3.3.0"
},
"dependencies": {
"sql-client": "0.7.0"
},
"devDependencies": {
"gulp": "^3.9.1",
"mocha": "^4.1.0",
"chai": "^4.1.2"
},
"bugs": {
"url": "https://git.spacen.net/yunkong2/yunkong2.sql/issues"
},
"main": "main.js",
"scripts": {
"test": "node node_modules/mocha/bin/mocha --exit"
},
"license": "MIT"
}

17
tasks/jscs.js Normal file
View File

@@ -0,0 +1,17 @@
var srcDir = __dirname + "/../";
module.exports = {
all: {
src: [
srcDir + "*.js",
srcDir + "lib/*.js",
srcDir + "adapter/example/*.js",
srcDir + "tasks/**/*.js",
srcDir + "www/**/*.js",
'!' + srcDir + "www/lib/**/*.js",
'!' + srcDir + 'node_modules/**/*.js',
'!' + srcDir + 'adapter/*/node_modules/**/*.js'
],
options: require('./jscsRules.js')
}
};

36
tasks/jscsRules.js Normal file
View File

@@ -0,0 +1,36 @@
module.exports = {
force: true,
"requireCurlyBraces": ["else", "for", "while", "do", "try", "catch"], /*"if",*/
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"requireSpaceBeforeBlockStatements": true,
"requireParenthesesAroundIIFE": true,
"disallowSpacesInFunctionDeclaration": {"beforeOpeningRoundBrace": true},
"disallowSpacesInNamedFunctionExpression": {"beforeOpeningRoundBrace": true},
"requireSpacesInFunctionExpression": {"beforeOpeningCurlyBrace": true},
"requireSpacesInAnonymousFunctionExpression": {"beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true},
"requireSpacesInNamedFunctionExpression": {"beforeOpeningCurlyBrace": true},
"requireSpacesInFunctionDeclaration": {"beforeOpeningCurlyBrace": true},
"disallowMultipleVarDecl": true,
"requireBlocksOnNewline": true,
"disallowEmptyBlocks": true,
"disallowSpacesInsideObjectBrackets": true,
"disallowSpacesInsideArrayBrackets": true,
"disallowSpaceAfterObjectKeys": true,
"disallowSpacesInsideParentheses": true,
"requireCommaBeforeLineBreak": true,
//"requireAlignedObjectValues": "all",
"requireOperatorBeforeLineBreak": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
// "disallowLeftStickedOperators": ["?", "+", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
// "requireRightStickedOperators": ["!"],
// "requireSpaceAfterBinaryOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
//"disallowSpaceAfterBinaryOperators": [","],
"disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
"requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
"requireSpaceAfterBinaryOperators": ["?", ">", ",", ">=", "<=", "<", "+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
//"validateIndentation": 4,
//"validateQuoteMarks": { "mark": "\"", "escape": true },
"disallowMixedSpacesAndTabs": true,
"disallowKeywordsOnNewLine": ["else", "catch"]
};

17
tasks/jshint.js Normal file
View File

@@ -0,0 +1,17 @@
var srcDir = __dirname + "/../";
module.exports = {
options: {
force: true
},
all: [
srcDir + "*.js",
srcDir + "lib/*.js",
srcDir + "adapter/example/*.js",
srcDir + "tasks/**/*.js",
srcDir + "www/**/*.js",
'!' + srcDir + "www/lib/**/*.js",
'!' + srcDir + 'node_modules/**/*.js',
'!' + srcDir + 'adapter/*/node_modules/**/*.js'
]
};

728
test/lib/setup.js Normal file
View File

@@ -0,0 +1,728 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
// check if tmp directory exists
var fs = require('fs');
var path = require('path');
var child_process = require('child_process');
var rootDir = path.normalize(__dirname + '/../../');
var pkg = require(rootDir + 'package.json');
var debug = typeof v8debug === 'object';
pkg.main = pkg.main || 'main.js';
var adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/');
adapterName = adapterName[adapterName.length - 2];
var adapterStarted = false;
function getAppName() {
var parts = __dirname.replace(/\\/g, '/').split('/');
return parts[parts.length - 3].split('.')[0];
}
var appName = getAppName().toLowerCase();
var objects;
var states;
var pid = null;
function copyFileSync(source, target) {
var targetFile = target;
//if target is a directory a new file with the same name will be created
if (fs.existsSync(target)) {
if ( fs.lstatSync( target ).isDirectory() ) {
targetFile = path.join(target, path.basename(source));
}
}
try {
fs.writeFileSync(targetFile, fs.readFileSync(source));
}
catch (err) {
console.log("file copy error: " +source +" -> " + targetFile + " (error ignored)");
}
}
function copyFolderRecursiveSync(source, target, ignore) {
var files = [];
var base = path.basename(source);
if (base === adapterName) {
base = pkg.name;
}
//check if folder needs to be created or integrated
var targetFolder = path.join(target, base);
if (!fs.existsSync(targetFolder)) {
fs.mkdirSync(targetFolder);
}
//copy
if (fs.lstatSync(source).isDirectory()) {
files = fs.readdirSync(source);
files.forEach(function (file) {
if (ignore && ignore.indexOf(file) !== -1) {
return;
}
var curSource = path.join(source, file);
var curTarget = path.join(targetFolder, file);
if (fs.lstatSync(curSource).isDirectory()) {
// ignore grunt files
if (file.indexOf('grunt') !== -1) return;
if (file === 'chai') return;
if (file === 'mocha') return;
copyFolderRecursiveSync(curSource, targetFolder, ignore);
} else {
copyFileSync(curSource, curTarget);
}
});
}
}
if (!fs.existsSync(rootDir + 'tmp')) {
fs.mkdirSync(rootDir + 'tmp');
}
function storeOriginalFiles() {
console.log('Store original files...');
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) {
objects['system.adapter.admin.0'].common.enabled = false;
}
if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) {
objects['system.adapter.admin.1'].common.enabled = false;
}
fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects));
try {
f = fs.readFileSync(dataDir + 'states.json');
fs.writeFileSync(dataDir + 'states.json.original', f);
}
catch (err) {
console.log('no states.json found - ignore');
}
}
function restoreOriginalFiles() {
console.log('restoreOriginalFiles...');
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var f = fs.readFileSync(dataDir + 'objects.json.original');
fs.writeFileSync(dataDir + 'objects.json', f);
try {
f = fs.readFileSync(dataDir + 'states.json.original');
fs.writeFileSync(dataDir + 'states.json', f);
}
catch (err) {
console.log('no states.json.original found - ignore');
}
}
function checkIsAdapterInstalled(cb, counter, customName) {
customName = customName || pkg.name.split('.').pop();
counter = counter || 0;
var dataDir = rootDir + 'tmp/' + appName + '-data/';
console.log('checkIsAdapterInstalled...');
try {
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.' + customName + '.0']) {
console.log('checkIsAdapterInstalled: ready!');
setTimeout(function () {
if (cb) cb();
}, 100);
return;
} else {
console.warn('checkIsAdapterInstalled: still not ready');
}
} catch (err) {
}
if (counter > 20) {
console.error('checkIsAdapterInstalled: Cannot install!');
if (cb) cb('Cannot install');
} else {
console.log('checkIsAdapterInstalled: wait...');
setTimeout(function() {
checkIsAdapterInstalled(cb, counter + 1);
}, 1000);
}
}
function checkIsControllerInstalled(cb, counter) {
counter = counter || 0;
var dataDir = rootDir + 'tmp/' + appName + '-data/';
console.log('checkIsControllerInstalled...');
try {
var f = fs.readFileSync(dataDir + 'objects.json');
var objects = JSON.parse(f.toString());
if (objects['system.adapter.admin.0']) {
console.log('checkIsControllerInstalled: installed!');
setTimeout(function () {
if (cb) cb();
}, 100);
return;
}
} catch (err) {
}
if (counter > 20) {
console.log('checkIsControllerInstalled: Cannot install!');
if (cb) cb('Cannot install');
} else {
console.log('checkIsControllerInstalled: wait...');
setTimeout(function() {
checkIsControllerInstalled(cb, counter + 1);
}, 1000);
}
}
function installAdapter(customName, cb) {
if (typeof customName === 'function') {
cb = customName;
customName = null;
}
customName = customName || pkg.name.split('.').pop();
console.log('Install adapter...');
var startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js';
// make first install
if (debug) {
child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2]
});
checkIsAdapterInstalled(function (error) {
if (error) console.error(error);
console.log('Adapter installed.');
if (cb) cb();
});
} else {
// add controller
var _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2, 'ipc']
});
waitForEnd(_pid, function () {
checkIsAdapterInstalled(function (error) {
if (error) console.error(error);
console.log('Adapter installed.');
if (cb) cb();
});
});
}
}
function waitForEnd(_pid, cb) {
if (!_pid) {
cb(-1, -1);
return;
}
_pid.on('exit', function (code, signal) {
if (_pid) {
_pid = null;
cb(code, signal);
}
});
_pid.on('close', function (code, signal) {
if (_pid) {
_pid = null;
cb(code, signal);
}
});
}
function installJsController(cb) {
console.log('installJsController...');
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') ||
!fs.existsSync(rootDir + 'tmp/' + appName + '-data')) {
// try to detect appName.js-controller in node_modules/appName.js-controller
// travis CI installs js-controller into node_modules
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) {
console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"');
// copy all
// stop controller
console.log('Stop controller if running...');
var _pid;
if (debug) {
// start controller
_pid = child_process.exec('node ' + appName + '.js stop', {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
_pid = child_process.fork(appName + '.js', ['stop'], {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(_pid, function () {
// copy all files into
if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp');
if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules');
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){
console.log('Copy js-controller...');
copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/');
}
console.log('Setup js-controller...');
var __pid;
if (debug) {
// start controller
_pid = child_process.exec('node ' + appName + '.js setup first --console', {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
__pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(__pid, function () {
checkIsControllerInstalled(function () {
// change ports for object and state DBs
var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
config.objects.port = 19001;
config.states.port = 19000;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
console.log('Setup finished.');
copyAdapterToController();
installAdapter(function () {
storeOriginalFiles();
if (cb) cb(true);
});
});
});
});
} else {
// check if port 9000 is free, else admin adapter will be added to running instance
var client = new require('net').Socket();
client.connect(9000, '127.0.0.1', function() {
console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.');
process.exit(0);
});
setTimeout(function () {
client.destroy();
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) {
console.log('installJsController: no js-controller => install from git');
child_process.execSync('npm install https://git.spacen.net/' + appName + '/' + appName + '.js-controller/tarball/master --prefix ./ --production', {
cwd: rootDir + 'tmp/',
stdio: [0, 1, 2]
});
} else {
console.log('Setup js-controller...');
var __pid;
if (debug) {
// start controller
child_process.exec('node ' + appName + '.js setup first', {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2]
});
} else {
child_process.fork(appName + '.js', ['setup', 'first'], {
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
}
// let npm install admin and run setup
checkIsControllerInstalled(function () {
var _pid;
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) {
_pid = child_process.fork(appName + '.js', ['stop'], {
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
stdio: [0, 1, 2, 'ipc']
});
}
waitForEnd(_pid, function () {
// change ports for object and state DBs
var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
config.objects.port = 19001;
config.states.port = 19000;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
copyAdapterToController();
installAdapter(function () {
storeOriginalFiles();
if (cb) cb(true);
});
});
});
}, 1000);
}
} else {
setTimeout(function () {
console.log('installJsController: js-controller installed');
if (cb) cb(false);
}, 0);
}
}
function copyAdapterToController() {
console.log('Copy adapter...');
// Copy adapter to tmp/node_modules/appName.adapter
copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']);
console.log('Adapter copied.');
}
function clearControllerLog() {
var dirPath = rootDir + 'tmp/log';
var files;
try {
if (fs.existsSync(dirPath)) {
console.log('Clear controller log...');
files = fs.readdirSync(dirPath);
} else {
console.log('Create controller log directory...');
files = [];
fs.mkdirSync(dirPath);
}
} catch(e) {
console.error('Cannot read "' + dirPath + '"');
return;
}
if (files.length > 0) {
try {
for (var i = 0; i < files.length; i++) {
var filePath = dirPath + '/' + files[i];
fs.unlinkSync(filePath);
}
console.log('Controller log cleared');
} catch (err) {
console.error('cannot clear log: ' + err);
}
}
}
function clearDB() {
var dirPath = rootDir + 'tmp/yunkong2-data/sqlite';
var files;
try {
if (fs.existsSync(dirPath)) {
console.log('Clear sqlite DB...');
files = fs.readdirSync(dirPath);
} else {
console.log('Create controller log directory...');
files = [];
fs.mkdirSync(dirPath);
}
} catch(e) {
console.error('Cannot read "' + dirPath + '"');
return;
}
if (files.length > 0) {
try {
for (var i = 0; i < files.length; i++) {
var filePath = dirPath + '/' + files[i];
fs.unlinkSync(filePath);
}
console.log('Clear sqlite DB');
} catch (err) {
console.error('cannot clear DB: ' + err);
}
}
}
function setupController(cb) {
installJsController(function (isInited) {
clearControllerLog();
clearDB();
if (!isInited) {
restoreOriginalFiles();
copyAdapterToController();
}
// read system.config object
var dataDir = rootDir + 'tmp/' + appName + '-data/';
var objs;
try {
objs = fs.readFileSync(dataDir + 'objects.json');
objs = JSON.parse(objs);
}
catch (e) {
console.log('ERROR reading/parsing system configuration. Ignore');
objs = {'system.config': {}};
}
if (!objs || !objs['system.config']) {
objs = {'system.config': {}};
}
if (cb) cb(objs['system.config']);
});
}
function startAdapter(objects, states, callback) {
if (adapterStarted) {
console.log('Adapter already started ...');
if (callback) callback(objects, states);
return;
}
adapterStarted = true;
console.log('startAdapter...');
if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) {
try {
if (debug) {
// start controller
pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2]
});
} else {
// start controller
pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], {
cwd: rootDir + 'tmp',
stdio: [0, 1, 2, 'ipc']
});
}
} catch (error) {
console.error(JSON.stringify(error));
}
} else {
console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main);
}
if (callback) callback(objects, states);
}
function startController(isStartAdapter, onObjectChange, onStateChange, callback) {
if (typeof isStartAdapter === 'function') {
callback = onStateChange;
onStateChange = onObjectChange;
onObjectChange = isStartAdapter;
isStartAdapter = true;
}
if (onStateChange === undefined) {
callback = onObjectChange;
onObjectChange = undefined;
}
if (pid) {
console.error('Controller is already started!');
} else {
console.log('startController...');
adapterStarted = false;
var isObjectConnected;
var isStatesConnected;
var Objects = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/objects/objectsInMemServer');
objects = new Objects({
connection: {
"type" : "file",
"host" : "127.0.0.1",
"port" : 19001,
"user" : "",
"pass" : "",
"noFileCache": false,
"connectTimeout": 2000
},
logger: {
silly: function (msg) {
console.log(msg);
},
debug: function (msg) {
console.log(msg);
},
info: function (msg) {
console.log(msg);
},
warn: function (msg) {
console.warn(msg);
},
error: function (msg) {
console.error(msg);
}
},
connected: function () {
isObjectConnected = true;
if (isStatesConnected) {
console.log('startController: started!');
if (isStartAdapter) {
startAdapter(objects, states, callback);
} else {
if (callback) {
callback(objects, states);
callback = null;
}
}
}
},
change: onObjectChange
});
// Just open in memory DB itself
var States = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/states/statesInMemServer');
states = new States({
connection: {
type: 'file',
host: '127.0.0.1',
port: 19000,
options: {
auth_pass: null,
retry_max_delay: 15000
}
},
logger: {
silly: function (msg) {
console.log(msg);
},
debug: function (msg) {
console.log(msg);
},
info: function (msg) {
console.log(msg);
},
warn: function (msg) {
console.log(msg);
},
error: function (msg) {
console.log(msg);
}
},
connected: function () {
isStatesConnected = true;
if (isObjectConnected) {
console.log('startController: started!!');
if (isStartAdapter) {
startAdapter(objects, states, callback);
} else {
if (callback) {
callback(objects, states);
callback = null;
}
}
}
},
change: onStateChange
});
}
}
function stopAdapter(cb) {
if (!pid) {
console.error('Controller is not running!');
if (cb) {
setTimeout(function () {
cb(false);
}, 0);
}
} else {
adapterStarted = false;
pid.on('exit', function (code, signal) {
if (pid) {
console.log('child process terminated due to receipt of signal ' + signal);
if (cb) cb();
pid = null;
}
});
pid.on('close', function (code, signal) {
if (pid) {
if (cb) cb();
pid = null;
}
});
pid.kill('SIGTERM');
}
}
function _stopController() {
if (objects) {
objects.destroy();
objects = null;
}
if (states) {
states.destroy();
states = null;
}
}
function stopController(cb) {
var timeout;
if (objects) {
console.log('Set system.adapter.' + pkg.name + '.0');
objects.setObject('system.adapter.' + pkg.name + '.0', {
common:{
enabled: false
}
});
}
stopAdapter(function () {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
_stopController();
if (cb) {
cb(true);
cb = null;
}
});
timeout = setTimeout(function () {
timeout = null;
console.log('child process NOT terminated');
_stopController();
if (cb) {
cb(false);
cb = null;
}
pid = null;
}, 5000);
}
// Setup the adapter
function setAdapterConfig(common, native, instance) {
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
if (common) objects[id].common = common;
if (native) objects[id].native = native;
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects));
}
// Read config of the adapter
function getAdapterConfig(instance) {
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
return objects[id];
}
if (typeof module !== undefined && module.parent) {
module.exports.getAdapterConfig = getAdapterConfig;
module.exports.setAdapterConfig = setAdapterConfig;
module.exports.startController = startController;
module.exports.stopController = stopController;
module.exports.setupController = setupController;
module.exports.stopAdapter = stopAdapter;
module.exports.startAdapter = startAdapter;
module.exports.installAdapter = installAdapter;
module.exports.appName = appName;
module.exports.adapterName = adapterName;
module.exports.adapterStarted = adapterStarted;
}

397
test/testMSSQL.js Normal file
View File

@@ -0,0 +1,397 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
var now = new Date().getTime();
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error('MSSQL: ' + err);
if (state && state.val) {
cb && cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error('MSSQL: ' + err);
if (value === null && !state) {
cb && cb();
} else
if (state && (value === undefined || state.val === value)) {
cb && cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test MSSQL', function() {
before('Test MSSQL: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
console.log('Started in TRAVIS: ' + (process.env.TRAVIS && process.env.TRAVIS==='true'));
console.log('Started in APPVEYOR: ' + (process.env.APPVEYOR && process.env.APPVEYOR==='True'));
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
console.log('MSSQL testing only available in Appveyor on Windows, ignore test run (APPVEYOR:' + JSON.stringify(process.env.APPVEYOR) + ', TRAVIS:' + JSON.stringify(process.env.TRAVIS) + ')');
_done();
return;
}
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.dbtype = 'mssql';
config.native.user = 'sa';
config.native.password = 'Password12!';
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
objects.setObject('sql.0.memRss', {
common: {
type: 'number',
role: 'state',
custom: {
"sql.0": {
enabled: true,
changesOnly: true,
debounce: 0,
retention: 31536000,
maxLength: 3,
changesMinDelta: 0.5
}
}
},
type: 'state'
}, _done);
});
});
});
it('Test MSSQL: Check if adapter started', function (done) {
this.timeout(60000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
checkConnectionOfAdapter(function () {
now = new Date().getTime();
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
setTimeout(function () {
sendTo('sql.0', 'enableHistory', {
id: 'sql.0.memRss',
options: {
changesOnly: true,
debounce: 0,
retention: 31536000,
changesMinDelta: 0.5,
storageType: 'Number'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memHeapTotal',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'String'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.uptime',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'Boolean'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 20000);
});
});
});
}, 10000);
});
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Enable', function (done) {
this.timeout(20000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(3);
expect(result['sql.0.memRss'].enabled).to.be.true;
setTimeout(function () {
done();
}, 15000);
});
});
it('Test MSSQL: Write values into DB', function (done) {
this.timeout(10000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
this.timeout(10000);
states.setState('sql.0.memRss', {val: 2, ts: now - 20000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: true, ts: now - 10000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2, ts: now - 5000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.2, ts: now - 4000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: '2.5', ts: now - 3000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 'Test', ts: now - 500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(done, 5000);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
});
it('Test MSSQL: Read values from DB using query', function (done) {
this.timeout(10000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
sendTo('sql.0', 'query', "SELECT id FROM yunkong2.dbo.datapoints WHERE name='sql.0.memRss'", function (result) {
sendTo('sql.0', 'query', 'SELECT * FROM yunkong2.dbo.ts_number WHERE id=' + result.result[0].id, function (result) {
console.log('MSSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
setTimeout(function () {
done();
}, 3000);
});
});
});
it('Test MSSQL: Read values from DB using GetHistory', function (done) {
this.timeout(10000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 30000,
limit: 50,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log('MSSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 15000,
end: now,
limit: 2,
count: 2,
aggregate: 'none'
}
}, function (result) {
console.log('MSSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
done();
});
});
});
it('Test ' + adapterShortName + ': Check Datapoint Types', function (done) {
this.timeout(5000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
sendTo('sql.0', 'query', "SELECT name, type FROM yunkong2.dbo.datapoints", function (result) {
console.log('MSSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.least(3);
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].name === 'sql.0.memRss') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.memHeapTotal') {
expect(result.result[i].type).to.be.equal(1);
}
else if (result.result[i].name === 'system.adapter.sql.0.uptime') {
expect(result.result[i].type).to.be.equal(2);
}
}
setTimeout(function () {
done();
}, 3000);
});
});
it('Test ' + adapterShortName + ': Disable Datapoint again', function (done) {
this.timeout(5000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
sendTo('sql.0', 'disableHistory', {
id: 'sql.0.memRss',
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
setTimeout(done, 2000);
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Disable', function (done) {
this.timeout(5000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(2);
done();
});
});
after('Test MSSQL: Stop js-controller', function (done) {
this.timeout(6000);
if (!(process.env.APPVEYOR && process.env.APPVEYOR==='True')) {
done();
return;
}
setup.stopController(function (normalTerminated) {
console.log('MSSQL: Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

374
test/testMySQL.js Normal file
View File

@@ -0,0 +1,374 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
var now = new Date().getTime();
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error('MySQL: ' + err);
if (state && state.val) {
cb && cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error('MySQL: ' + err);
if (value === null && !state) {
cb && cb();
} else
if (state && (value === undefined || state.val === value)) {
cb && cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test MySQL', function() {
before('Test MySQL: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.dbtype = 'mysql';
config.native.user = 'root';
if (process.env.APPVEYOR && process.env.APPVEYOR==='True') {
config.native.password = 'Password12!';
}
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
objects.setObject('sql.0.memRss', {
common: {
type: 'number',
role: 'state',
custom: {
"sql.0": {
enabled: true,
changesOnly: true,
debounce: 0,
retention: 31536000,
maxLength: 3,
changesMinDelta: 0.5
}
}
},
type: 'state'
}, _done);
});
});
});
it('Test MySQL: Check if adapter started', function (done) {
this.timeout(90000);
checkConnectionOfAdapter(function () {
now = new Date().getTime();
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
setTimeout(function () {
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memHeapTotal',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'String'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.alive',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'Boolean'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.uptime',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 70000);
});
});
});
}, 10000);
});
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Enable', function (done) {
this.timeout(20000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(4);
expect(result['sql.0.memRss'].enabled).to.be.true;
setTimeout(function () {
done();
}, 15000);
});
});
it('Test MySQL: Write values into DB', function (done) {
this.timeout(10000);
states.setState('sql.0.memRss', {val: 2, ts: now - 20000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: true, ts: now - 10000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2, ts: now - 5000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.2, ts: now - 4000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.3, ts: now - 3500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: '2.5', ts: now - 3000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 'Test', ts: now - 500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(done, 5000);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
});
it('Test MySQL: Read values from DB using query', function (done) {
this.timeout(10000);
sendTo('sql.0', 'query', 'SELECT id FROM yunkong2.datapoints WHERE name="sql.0.memRss"', function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
sendTo('sql.0', 'query', 'SELECT * FROM yunkong2.ts_number WHERE id=' + result.result[0].id, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
var found22 = false;
var found23 = false;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
if (result.result[i].val === 2.2) found22 = true;
if (result.result[i].val === 2.3) found23 = true;
}
expect(found).to.be.equal(6);
expect(found22).to.be.false;
expect(found23).to.be.true;
setTimeout(function () {
done();
}, 3000);
});
});
});
it('Test MySQL: Read values from DB using GetHistory', function (done) {
this.timeout(10000);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 30000,
limit: 50,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 15000,
end: now,
limit: 2,
count: 2,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
done();
});
});
});
it('Test ' + adapterShortName + ': Check Datapoint Types', function (done) {
this.timeout(5000);
sendTo('sql.0', 'query', "SELECT id, name, type FROM yunkong2.datapoints", function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.least(3);
var uptime_id = null;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].name === 'sql.0.memRss') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.memHeapTotal') {
expect(result.result[i].type).to.be.equal(1);
}
else if (result.result[i].name === 'system.adapter.sql.0.alive') {
expect(result.result[i].type).to.be.equal(2);
}
else if (result.result[i].name === 'system.adapter.sql.0.uptime') {
expect(result.result[i].type).to.be.equal(0);
uptime_id = result.result[i].id;
expect(uptime_id).to.be.not.null;
}
}
sendTo('sql.0', 'query', "UPDATE yunkong2.datapoints SET type=NULL WHERE id=" + uptime_id, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.affectedRows).to.be.equal(1);
done();
});
});
});
it('Test ' + adapterShortName + ': Disable Datapoint again', function (done) {
this.timeout(5000);
sendTo('sql.0', 'disableHistory', {
id: 'sql.0.memRss',
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
setTimeout(done, 2000);
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Disable', function (done) {
this.timeout(5000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(3);
done();
});
});
after('Test MySQL: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('MySQL: Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

482
test/testMySQLDash.js Normal file
View File

@@ -0,0 +1,482 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
var now = new Date().getTime();
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error('MySQL-with-dash: ' + err);
if (state && state.val) {
cb && cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error('MySQL-with-dash: ' + err);
if (value === null && !state) {
cb && cb();
} else
if (state && (value === undefined || state.val === value)) {
cb && cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test MySQL-with-dash', function() {
before('Test MySQL-with-dash: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.dbtype = 'mysql';
config.native.user = 'root';
config.native.dbname = 'io-broker';
if (process.env.APPVEYOR && process.env.APPVEYOR==='True') {
config.native.password = 'Password12!';
}
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
objects.setObject('sql.0.memRss', {
common: {
type: 'number',
role: 'state',
custom: {
"sql.0": {
enabled: true,
changesOnly: true,
debounce: 0,
retention: 31536000,
maxLength: 3,
changesMinDelta: 0.5
}
}
},
type: 'state'
}, _done);
});
});
});
it('Test MySQL-with-dash: Check if adapter started', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function () {
now = new Date().getTime();
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
setTimeout(function () {
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memHeapTotal',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.uptime',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.alive',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
objects.setObject('sql.0.testValue2', {
common: {
type: 'number',
role: 'state'
},
type: 'state'
},
function () {
sendTo('sql.0', 'enableHistory', {
id: 'sql.0.testValue2',
options: {
changesOnly: true,
debounce: 0,
retention: 31536000,
maxLength: 3,
changesMinDelta: 0.5,
aliasId: 'sql.0.testValue2-alias'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 2000);
});
});
});
});
});
}, 10000);
});
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Enable', function (done) {
this.timeout(20000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(5);
expect(result['sql.0.memRss'].enabled).to.be.true;
setTimeout(function () {
done();
}, 15000);
});
});
it('Test MySQL-with-dash: Write values into DB', function (done) {
this.timeout(10000);
states.setState('sql.0.memRss', {val: 2, ts: now - 20000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: true, ts: now - 10000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2, ts: now - 5000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.2, ts: now - 4000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: '2.5', ts: now - 3000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 'Test', ts: now - 500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.testValue2', {val: 1, ts: now - 2000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.testValue2', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(done, 5000);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
});
it('Test MySQL-with-dash: Read values from DB using query', function (done) {
this.timeout(10000);
sendTo('sql.0', 'query', 'SELECT id FROM `io-broker`.datapoints WHERE name="sql.0.memRss"', function (result) {
console.log('MySQL-with-dash: ' + JSON.stringify(result.result, null, 2));
sendTo('sql.0', 'query', 'SELECT * FROM `io-broker`.ts_number WHERE id=' + result.result[0].id, function (result) {
console.log('MySQL-with-dash: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
setTimeout(function () {
done();
}, 3000);
});
});
});
it('Test MySQL-with-dash: Read values from DB using GetHistory', function (done) {
this.timeout(10000);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 30000,
limit: 50,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL-with-dash: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 15000,
end: now,
limit: 2,
count: 2,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL-with-dash: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
done();
});
});
});
it('Test ' + adapterShortName + ': Check Datapoint Types', function (done) {
this.timeout(5000);
sendTo('sql.0', 'query', "SELECT name, type FROM `io-broker`.datapoints", function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.least(3);
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].name === 'sql.0.memRss') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.memHeapTotal') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.alive') {
expect(result.result[i].type).to.be.equal(2);
}
else if (result.result[i].name === 'system.adapter.sql.0.uptime') {
expect(result.result[i].type).to.be.equal(0);
}
}
setTimeout(function () {
done();
}, 3000);
});
});
it('Test ' + adapterShortName + ': Read values from DB using GetHistory for aliased testValue2', function (done) {
this.timeout(25000);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.testValue2',
options: {
start: now - 5000,
end: now,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log(JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.testValue2-alias',
options: {
start: now - 5000,
end: now,
count: 50,
aggregate: 'none'
}
}, function (result2) {
console.log(JSON.stringify(result2.result, null, 2));
expect(result2.result.length).to.be.equal(2);
for (var i = 0; i < result2.result.length; i++) {
expect(result2.result[i].val).to.be.equal(result.result[i].val);
}
done();
});
});
});
it('Test ' + adapterShortName + ': Remove Alias-ID', function (done) {
this.timeout(5000);
sendTo('sql.0', 'enableHistory', {
id: 'sql.0.testValue2',
options: {
aliasId: ''
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 2000);
});
});
it('Test ' + adapterShortName + ': Add Alias-ID again', function (done) {
this.timeout(5000);
sendTo('sql.0', 'enableHistory', {
id: 'sql.0.testValue2',
options: {
aliasId: 'this.is.a.test-value'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 2000);
});
});
it('Test ' + adapterShortName + ': Change Alias-ID', function (done) {
this.timeout(5000);
sendTo('sql.0', 'enableHistory', {
id: 'sql.0.testValue2',
options: {
aliasId: 'this.is.another.test-value'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 2000);
});
});
it('Test ' + adapterShortName + ': Disable Datapoint again', function (done) {
this.timeout(5000);
sendTo('sql.0', 'disableHistory', {
id: 'sql.0.memRss',
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
setTimeout(done, 2000);
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Disable', function (done) {
this.timeout(5000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(4);
done();
});
});
after('Test MySQL-with-dash: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('MySQL-with-dash: Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

368
test/testMySQLExisting.js Normal file
View File

@@ -0,0 +1,368 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
var now = new Date().getTime();
var now2;
if (!now2) now2 = new Date().getTime();
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error('MySQL: ' + err);
if (state && state.val) {
cb && cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error('MySQL: ' + err);
if (value === null && !state) {
cb && cb();
} else
if (state && (value === undefined || state.val === value)) {
cb && cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test MySQL Existing', function() {
before('Test MySQL Existing: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.dbtype = 'mysql';
config.native.user = 'root';
if (process.env.APPVEYOR && process.env.APPVEYOR==='True') {
config.native.password = 'Password12!';
}
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
_done();
});
});
});
it('Test MySQL Existing: Check if adapter started', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function () {
now = new Date().getTime();
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
setTimeout(function() {
sendTo('sql.0', 'enableHistory', {
id: 'sql.0.memRss',
options: {
changesOnly: true,
debounce: 0,
retention: 31536000,
changesMinDelta: 0.5,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memHeapTotal',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'Number'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.alive',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.uptime',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 20000);
});
});
});
});
}, 10000);
});
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Enable', function (done) {
this.timeout(20000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(4);
expect(result['sql.0.memRss'].enabled).to.be.true;
setTimeout(function () {
done();
}, 15000);
});
});
it('Test MySQL Existing: Write values into DB', function (done) {
this.timeout(10000);
states.setState('sql.0.memRss', {val: 2, ts: now - 20000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: true, ts: now - 10000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2, ts: now - 5000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.2, ts: now - 4000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.3, ts: now - 3500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: '2.5', ts: now - 3000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 'Test', ts: now - 500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(done, 5000);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
});
it('Test MySQL Existing: Read values from DB using query', function (done) {
this.timeout(10000);
sendTo('sql.0', 'query', 'SELECT id FROM yunkong2.datapoints WHERE name="sql.0.memRss"', function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
sendTo('sql.0', 'query', 'SELECT * FROM yunkong2.ts_number WHERE id=' + result.result[0].id, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
var found22 = false;
var found23 = false;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
if (result.result[i].val === 2.2) found22 = true;
if (result.result[i].val === 2.3) found23 = true;
}
expect(found).to.be.equal(12);
expect(found22).to.be.false;
expect(found23).to.be.true;
setTimeout(function () {
done();
}, 3000);
});
});
});
it('Test MySQL Existing: Read values from DB using GetHistory', function (done) {
this.timeout(10000);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now2 - 30000,
limit: 50,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(12);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now2 - 15000,
end: now,
limit: 2,
count: 2,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
done();
});
});
});
it('Test ' + adapterShortName + ': Check Datapoint Types', function (done) {
this.timeout(5000);
sendTo('sql.0', 'query', "SELECT name, type FROM yunkong2.datapoints", function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.least(3);
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].name === 'sql.0.memRss') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.memHeapTotal') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.alive') {
expect(result.result[i].type).to.be.equal(2);
}
else if (result.result[i].name === 'system.adapter.sql.0.uptime') {
expect(result.result[i].type).to.be.equal(0);
}
}
setTimeout(function () {
done();
}, 3000);
});
});
it('Test ' + adapterShortName + ': Disable Datapoint again', function (done) {
this.timeout(5000);
sendTo('sql.0', 'disableHistory', {
id: 'sql.0.memRss',
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
setTimeout(done, 2000);
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Disable', function (done) {
this.timeout(5000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(3);
done();
});
});
after('Test MySQL Existing: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('MySQL: Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

View File

@@ -0,0 +1,369 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
var now = new Date().getTime();
var now2;
if (!now2) now2 = new Date().getTime();
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error('MySQL: ' + err);
if (state && state.val) {
cb && cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error('MySQL: ' + err);
if (value === null && !state) {
cb && cb();
} else
if (state && (value === undefined || state.val === value)) {
cb && cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test MySQL Existing No Nulls', function() {
before('Test MySQL Existing No Nulls: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.writeNulls = false;
config.native.dbtype = 'mysql';
config.native.user = 'root';
if (process.env.APPVEYOR && process.env.APPVEYOR==='True') {
config.native.password = 'Password12!';
}
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
_done();
});
});
});
it('Test MySQL Existing No Nulls: Check if adapter started', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function () {
now = new Date().getTime();
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
setTimeout(function() {
sendTo('sql.0', 'enableHistory', {
id: 'sql.0.memRss',
options: {
changesOnly: true,
debounce: 0,
retention: 31536000,
changesMinDelta: 0.5,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memHeapTotal',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'Number'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.alive',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.uptime',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 20000);
});
});
});
});
}, 10000);
});
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Enable', function (done) {
this.timeout(20000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(4);
expect(result['sql.0.memRss'].enabled).to.be.true;
setTimeout(function () {
done();
}, 15000);
});
});
it('Test MySQL Existing No Nulls: Write values into DB', function (done) {
this.timeout(10000);
states.setState('sql.0.memRss', {val: 2, ts: now - 20000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: true, ts: now - 10000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2, ts: now - 5000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.2, ts: now - 4000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.3, ts: now - 3500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: '2.5', ts: now - 3000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 'Test', ts: now - 500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(done, 5000);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
});
it('Test MySQL Existing No Nulls: Read values from DB using query', function (done) {
this.timeout(10000);
sendTo('sql.0', 'query', 'SELECT id FROM yunkong2.datapoints WHERE name="sql.0.memRss"', function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
sendTo('sql.0', 'query', 'SELECT * FROM yunkong2.ts_number WHERE id=' + result.result[0].id, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
var found22 = false;
var found23 = false;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
if (result.result[i].val === 2.2) found22 = true;
if (result.result[i].val === 2.3) found23 = true;
}
expect(found).to.be.equal(18);
expect(found22).to.be.false;
expect(found23).to.be.true;
setTimeout(function () {
done();
}, 3000);
});
});
});
it('Test MySQL Existing No Nulls: Read values from DB using GetHistory', function (done) {
this.timeout(10000);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now2 - 30000,
limit: 50,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(18);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now2 - 15000,
end: now,
limit: 2,
count: 2,
aggregate: 'none'
}
}, function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
done();
});
});
});
it('Test ' + adapterShortName + ': Check Datapoint Types', function (done) {
this.timeout(5000);
sendTo('sql.0', 'query', "SELECT name, type FROM yunkong2.datapoints", function (result) {
console.log('MySQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.least(3);
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].name === 'sql.0.memRss') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.memHeapTotal') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.alive') {
expect(result.result[i].type).to.be.equal(2);
}
else if (result.result[i].name === 'system.adapter.sql.0.uptime') {
expect(result.result[i].type).to.be.equal(0);
}
}
setTimeout(function () {
done();
}, 3000);
});
});
it('Test ' + adapterShortName + ': Disable Datapoint again', function (done) {
this.timeout(5000);
sendTo('sql.0', 'disableHistory', {
id: 'sql.0.memRss',
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
setTimeout(done, 2000);
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Disable', function (done) {
this.timeout(5000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(3);
done();
});
});
after('Test MySQL Existing No Nulls: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('MySQL: Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

91
test/testPackageFiles.js Normal file
View File

@@ -0,0 +1,91 @@
/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs = require('fs');
describe('Test package.json and io-package.json', function() {
it('Test package files', function (done) {
console.log();
var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
var ioPackage = JSON.parse(fileContentIOPackage);
var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
var npmPackage = JSON.parse(fileContentNPMPackage);
expect(ioPackage).to.be.an('object');
expect(npmPackage).to.be.an('object');
expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;
expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);
if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
console.log();
}
expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;
if (ioPackage.common.name.indexOf('template') !== 0) {
if (Array.isArray(ioPackage.common.authors)) {
expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
if (ioPackage.common.authors.length === 1) {
expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
}
}
else {
expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
}
}
else {
console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
console.log();
}
expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
console.log('WARNING: titleLang is not existing in io-package.json. Please add');
console.log();
}
if (
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
ioPackage.common.title.indexOf('adapter') !== -1 ||
ioPackage.common.title.indexOf('Adapter') !== -1
) {
console.log('WARNING: title contains Adapter or yunkong2. It is clear anyway, that it is adapter for yunkong2.');
console.log();
}
if (ioPackage.common.name.indexOf('vis-') !== 0) {
if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
console.log('WARNING: Admin3 support is missing! Please add it');
console.log();
}
if (ioPackage.common.materialize) {
expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
}
}
var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
if (fileContentReadme.indexOf('## Changelog') === -1) {
console.log('Warning: The README.md should have a section ## Changelog');
console.log();
}
expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
if (!licenseFileExists) {
console.log('Warning: The License should also exist as LICENSE file');
console.log();
}
if (fileContentReadme.indexOf('## License') === -1) {
console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
console.log();
}
done();
});
});

342
test/testPostgreSQL.js Normal file
View File

@@ -0,0 +1,342 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
var now = new Date().getTime();
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error('PostgreSQL: ' + err);
if (state && state.val) {
cb && cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error('PostgreSQL: ' + err);
if (value === null && !state) {
cb && cb();
} else
if (state && (value === undefined || state.val === value)) {
cb && cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test PostgreSQL', function() {
before('Test PostgreSQL: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.dbtype = 'postgresql';
config.native.user = 'postgres';
if (process.env.APPVEYOR && process.env.APPVEYOR==='True') {
config.native.password = 'Password12!';
}
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
objects.setObject('sql.0.memRss', {
common: {
type: 'number',
role: 'state',
custom: {
"sql.0": {
enabled: true,
changesOnly: true,
debounce: 0,
retention: 31536000,
maxLength: 3,
changesMinDelta: 0.5
}
}
},
type: 'state'
}, _done);
});
});
});
it('Test PostgreSQL: Check if adapter started', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function () {
now = new Date().getTime();
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
setTimeout(function () {
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memHeapTotal',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'String'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.uptime',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'Boolean'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 20000);
});
});
}, 10000);
});
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Enable', function (done) {
this.timeout(20000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(3);
expect(result['sql.0.memRss'].enabled).to.be.true;
setTimeout(function () {
done();
}, 15000);
});
});
it('Test PostgreSQL: Write values into DB', function (done) {
this.timeout(10000);
this.timeout(10000);
states.setState('sql.0.memRss', {val: 2, ts: now - 20000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: true, ts: now - 10000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2, ts: now - 5000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.2, ts: now - 4000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: '2.5', ts: now - 3000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 'Test', ts: now - 500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(done, 5000);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
});
it('Test PostgreSQL: Read values from DB using query', function (done) {
this.timeout(10000);
sendTo('sql.0', 'query', "SELECT id FROM datapoints WHERE name='sql.0.memRss'", function (result) {
sendTo('sql.0', 'query', 'SELECT * FROM ts_number WHERE id=' + result.result[0].id, function (result) {
console.log('PostgreSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
setTimeout(function () {
done();
}, 3000);
});
});
});
it('Test PostgreSQL: Read values from DB using GetHistory', function (done) {
this.timeout(10000);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 30000,
limit: 50,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log('PostgreSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 15000,
end: now,
limit: 2,
count: 2,
aggregate: 'none'
}
}, function (result) {
console.log('PostgreSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
done();
});
});
});
it('Test ' + adapterShortName + ': Check Datapoint Types', function (done) {
this.timeout(5000);
sendTo('sql.0', 'query', "SELECT name, type FROM datapoints", function (result) {
console.log('PostgreSQL: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.least(3);
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].name === 'sql.0.memRss') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.memHeapTotal') {
expect(result.result[i].type).to.be.equal(1);
}
else if (result.result[i].name === 'system.adapter.sql.0.uptime') {
expect(result.result[i].type).to.be.equal(2);
}
}
setTimeout(function () {
done();
}, 3000);
});
});
it('Test ' + adapterShortName + ': Disable Datapoint again', function (done) {
this.timeout(5000);
sendTo('sql.0', 'disableHistory', {
id: 'sql.0.memRss',
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
setTimeout(done, 2000);
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Disable', function (done) {
this.timeout(5000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(2);
done();
});
});
after('Test PostgreSQL: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('PostgreSQL: Adapter normal terminated: ' + normalTerminated);
done();
});
});
});

355
test/testSQLite.js Normal file
View File

@@ -0,0 +1,355 @@
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
/*jshint expr: true*/
var expect = require('chai').expect;
var setup = require(__dirname + '/lib/setup');
var objects = null;
var states = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;
var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);
var now = new Date().getTime();
function checkConnectionOfAdapter(cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check connection');
return;
}
states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
if (err) console.error('SQLite:' + err);
if (state && state.val) {
cb && cb();
} else {
setTimeout(function () {
checkConnectionOfAdapter(cb, counter + 1);
}, 1000);
}
});
}
function checkValueOfState(id, value, cb, counter) {
counter = counter || 0;
if (counter > 20) {
cb && cb('Cannot check value Of State ' + id);
return;
}
states.getState(id, function (err, state) {
if (err) console.error('SQLite:' + err);
if (value === null && !state) {
cb && cb();
} else
if (state && (value === undefined || state.val === value)) {
cb && cb();
} else {
setTimeout(function () {
checkValueOfState(id, value, cb, counter + 1);
}, 500);
}
});
}
function sendTo(target, command, message, callback) {
onStateChanged = function (id, state) {
if (id === 'messagebox.system.adapter.test.0') {
callback(state.message);
}
};
states.pushMessage('system.adapter.' + target, {
command: command,
message: message,
from: 'system.adapter.test.0',
callback: {
message: message,
id: sendToID++,
ack: false,
time: (new Date()).getTime()
}
});
}
describe('Test SQLite', function() {
before('Test SQLite: Start js-controller', function (_done) {
this.timeout(600000); // because of first install from npm
setup.adapterStarted = false;
setup.setupController(function () {
var config = setup.getAdapterConfig();
// enable adapter
config.common.enabled = true;
config.common.loglevel = 'debug';
config.native.dbtype = 'sqlite';
setup.setAdapterConfig(config.common, config.native);
setup.startController(true, function(id, obj) {}, function (id, state) {
if (onStateChanged) onStateChanged(id, state);
},
function (_objects, _states) {
objects = _objects;
states = _states;
objects.setObject('sql.0.memRss', {
common: {
type: 'number',
role: 'state',
custom: {
"sql.0": {
enabled: true,
changesOnly: true,
debounce: 0,
retention: 31536000,
maxLength: 3,
changesMinDelta: 0.5
}
}
},
type: 'state'
}, _done);
});
});
});
it('Test SQLite: Check if adapter started', function (done) {
this.timeout(60000);
checkConnectionOfAdapter(function () {
now = new Date().getTime();
objects.setObject('system.adapter.test.0', {
common: {
},
type: 'instance'
},
function () {
states.subscribeMessage('system.adapter.test.0');
setTimeout(function () {
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.memHeapTotal',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'String'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.alive',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: 'Boolean'
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
sendTo('sql.0', 'enableHistory', {
id: 'system.adapter.sql.0.uptime',
options: {
changesOnly: false,
debounce: 0,
retention: 31536000,
storageType: false
}
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
// wait till adapter receives the new settings
setTimeout(function () {
done();
}, 20000);
});
});
});
}, 10000);
});
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Enable', function (done) {
this.timeout(20000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(4);
expect(result['sql.0.memRss'].enabled).to.be.true;
setTimeout(function () {
done();
}, 15000);
});
});
it('Test SQLite: Write values into DB', function (done) {
this.timeout(10000);
this.timeout(10000);
states.setState('sql.0.memRss', {val: 2, ts: now - 20000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: true, ts: now - 10000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2, ts: now - 5000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 2.2, ts: now - 4000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: '2.5', ts: now - 3000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 3, ts: now - 1000}, function (err) {
if (err) {
console.log(err);
}
setTimeout(function () {
states.setState('sql.0.memRss', {val: 'Test', ts: now - 500}, function (err) {
if (err) {
console.log(err);
}
setTimeout(done, 5000);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
}, 100);
});
});
it('Test SQLite: Read values from DB using query', function (done) {
this.timeout(10000);
sendTo('sql.0', 'query', 'SELECT id FROM datapoints WHERE name="sql.0.memRss"', function (result) {
sendTo('sql.0', 'query', 'SELECT * FROM ts_number WHERE id=' + result.result[0].id, function (result) {
console.log('SQLite:' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
setTimeout(function () {
done();
}, 3000);
});
});
});
it('Test SQLite: Read values from DB using GetHistory', function (done) {
this.timeout(20000);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 30000,
limit: 50,
count: 50,
aggregate: 'none'
}
}, function (result) {
console.log('SQLite:' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.at.least(5);
var found = 0;
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].val >= 1 && result.result[i].val <= 3) found ++;
}
expect(found).to.be.equal(6);
sendTo('sql.0', 'getHistory', {
id: 'sql.0.memRss',
options: {
start: now - 15000,
end: now,
limit: 2,
count: 2,
aggregate: 'none'
}
}, function (result) {
console.log('SQLite:' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.be.equal(2);
done();
});
});
});
it('Test ' + adapterShortName + ': Check Datapoint Types', function (done) {
this.timeout(125000);
setTimeout(function() {
sendTo('sql.0', 'query', "SELECT name, type FROM datapoints", function (result) {
console.log('SQLite: ' + JSON.stringify(result.result, null, 2));
expect(result.result.length).to.least(3);
for (var i = 0; i < result.result.length; i++) {
if (result.result[i].name === 'sql.0.memRss') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.memHeapTotal') {
expect(result.result[i].type).to.be.equal(1);
}
else if (result.result[i].name === 'system.adapter.sql.0.uptime') {
expect(result.result[i].type).to.be.equal(0);
}
else if (result.result[i].name === 'system.adapter.sql.0.alive') {
expect(result.result[i].type).to.be.equal(2);
}
}
setTimeout(function () {
done();
}, 3000);
});
}, 121000);
});
it('Test ' + adapterShortName + ': Disable Datapoint again', function (done) {
this.timeout(5000);
sendTo('sql.0', 'disableHistory', {
id: 'sql.0.memRss',
}, function (result) {
expect(result.error).to.be.undefined;
expect(result.success).to.be.true;
setTimeout(done, 2000);
});
});
it('Test ' + adapterShortName + ': Check Enabled Points after Disable', function (done) {
this.timeout(5000);
sendTo('sql.0', 'getEnabledDPs', {}, function (result) {
console.log(JSON.stringify(result));
expect(Object.keys(result).length).to.be.equal(3);
done();
});
});
after('Test SQLite: Stop js-controller', function (done) {
this.timeout(6000);
setup.stopController(function (normalTerminated) {
console.log('SQLite: Adapter normal terminated: ' + normalTerminated);
done();
});
});
});