Refactoring for SQLite

This commit is contained in:
Skylar Grant 2023-01-21 21:33:52 -05:00
parent 6ee01cc214
commit 299a8b2efa
9 changed files with 412 additions and 130 deletions

2
.vscode/launch.json vendored
View File

@ -11,7 +11,7 @@
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/websvr.js"
"program": "${workspaceFolder}/main.js"
}
]
}

View File

@ -16,10 +16,10 @@
// "description": "Log output to console"
// }
"Log if in Debug mode": {
"Hestia Debug Log": {
"scope": "javascript",
"prefix": "log",
"body": "if (config.debugMode) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: $1`);\n$0",
"body": "if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: $1`);$0",
"description": "Log output to console if in debug mode"
},
"Run only on Pi": {
@ -28,16 +28,16 @@
"body": "if (process.env.ONPI == 'true') {\n\t$1\n} else {\n\t$2\n}$0",
"description": "Run something only if the ONPI env var is set"
},
"Error": {
"Hestia Error": {
"scope": "javascript",
"prefix": "err",
"body": "console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: $1`);\n$0",
"body": "console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: $1`)$0",
"description": "Log an error to the console with timestamp"
},
"Log Always": {
"Hestia Log": {
"scope": "javascript",
"prefix": "log",
"body": "console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: $1`);\n$0",
"body": "console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: $1`);$0",
"description": "Log output to console always"
},
}

0
config.db Normal file
View File

91
main.js Normal file
View File

@ -0,0 +1,91 @@
const fn = require('./modules/functions.js').functions;
// TODO: Move these to config
// Physical Pin numbers for GPIO
const augerPin = 7; // Pin for controlling the relay for the pellet auger motor.
// Import the config file
var config = require('./templates/config.json');
// Database Functions
const dbfn = require('./modules/database.js');
dbfn.run(`UPDATE timestamps SET value = ${Date.now()} WHERE key = 'process_start'`).catch(err => console.error(`Error setting process start time: ${err}`));
fn.commands.refreshConfig().then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
config = res.config;
// Setup for use with the Pi's GPIO pins
switch (process.env.ONPI) {
case 'true':
console.log(`== Running on a Raspberry Pi.`);
var gpio = require('rpi-gpio');
fn.init(gpio).then((res) => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
main(gpio, db);
}).catch(rej => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Error during initialization: ${rej}`);
process.exit(1);
});
break;
case 'false':
console.log(`I: Not running on a Raspberry Pi.`);
var gpio = 'gpio';
fn.init(gpio).then(res => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
main(gpio);
}).catch(rej => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Error during initialization: ${rej}`);
process.exit(1);
});
break;
default:
console.log(`[${Date.now() - config.timestamps.procStart}] E: Problem with ENV file.`);
process.exit(1);
break;
}
}).catch(rej => {
console.error(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Problem refreshing the config: ${rej}`);
process.exit(1);
});
function main(gpio) {
// If the auger is enabled
if (config.status.auger == 1) {
// Run a cycle of the auger
fn.auger.cycle(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
fn.checkForQuit().then(n => {
fn.commands.refreshConfig().then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
config = res.config;
// Recursion ecursion cursion ursion rsion sion ion on n
main(gpio);
}).catch(rej => {
console.error(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Problem refreshing the config: ${rej}`);
// Recursion ecursion cursion ursion rsion sion ion on n
main(gpio);
});
});
}).catch(err => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`);
});
} else {
// If the auger is disabled
fn.commands.pause().then(res => {
fn.checkForQuit().then(n => {
fn.commands.refreshConfig().then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res.status}`);
config = res.config;
// Recursion ecursion cursion ursion rsion sion ion on n
main(gpio);
}).catch(rej => {
console.error(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Problem refreshing the config: ${rej}`);
// Recursion ecursion cursion ursion rsion sion ion on n
main(gpio);
});
});
}).catch(err => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`);
// Recursion ecursion cursion ursion rsion sion ion on n
main(gpio);
});
}
}

173
modules/_setupdb.js Normal file
View File

@ -0,0 +1,173 @@
const dbfn = require('../modules/database.js');
const sqlite3 = require('sqlite3');
// Connect to or create the database.
let db = new sqlite3.Database('../data/config.db', (err) => {
if (err) console.error("DB Connect: " + err);
console.log("Connected to database.");
});
// Create `status` table
/*
+ ----- + ------------- + ---- + --- + ------- + -------------- +
| Field | Type | Null | Key | Default | Extra |
+ ----- + ------------- + ---- + --- + ------- + -------------- +
| key | varchar(100) | No | | | |
| value | varchar(1000) | No | | | |
+ ----- + ------------- + ---- + --- + ------- + -------------- +
+ ------------------- +
| igniter |
| blower |
| auger |
| igniter_finished |
| shutdown_initiated |
| vacuum |
| proof_of_fire |
| shutdown_next_cycle |
+ ------------------- +
CREATE TABLE IF NOT EXISTS status (
key varchar(100) NOT NULL,
value varchar(1000) NOT NULL
);
*/
const createStatusTableQuery = "CREATE TABLE IF NOT EXISTS status (key varchar(100) NOT NULL,value varchar(1000) NOT NULL);";
dbfn.run(db, createStatusTableQuery).then(res => {
console.log(res.status);
const statusEntries = {
igniter: 0,
blower: 0,
auger: 0,
igniter_finished: false,
shutdown_initiated: 0,
vacuum: 0,
proof_of_fire: 0,
shutdown_next_cycle: 0
};
for ( key in statusEntries ){
const insertStatusEntryQuery = `INSERT INTO status (key, value) VALUES ("${key}", "${statusEntries[key]}")`;
dbfn.run(db, insertStatusEntryQuery).then(res => {
console.log(`${res.status}: ${res.data.lastID}: ${res.data.changes} changes`);
}).catch(err => console.error(err));
}
const selectAllStatusEntriesQuery = "SELECT * FROM status";
dbfn.all(db, selectAllStatusEntriesQuery).then(res => {
console.log(res.status);
res.rows.forEach(row => {
console.log(`${row.key} | ${row.value}`);
});
}).catch(err => console.error(err));
}).catch(err => {
console.error(err);
});
// Create `timestamps` table
/*
+ ----- + ------------- + ---- + --- + ------- + -------------- +
| Field | Type | Null | Key | Default | Extra |
+ ----- + ------------- + ---- + --- + ------- + -------------- +
| key | varchar(100) | No | | | |
| value | varchar(1000) | No | | | |
+ ----- + ------------- + ---- + --- + ------- + -------------- +
+ ------------- +
| process_start |
| blower_on |
| blower_off |
| igniter_on |
| igniter_off |
+ ------------- +
CREATE TABLE IF NOT EXISTS timestamps (
key varchar(100) NOT NULL,
value varchar(1000) NOT NULL
);
*/
const createTimestampsTableQuery = "CREATE TABLE IF NOT EXISTS timestamps (key varchar(100) NOT NULL,value varchar(1000) NOT NULL);";
dbfn.run(db, createTimestampsTableQuery).then(res => {
console.log(res.status);
const timestampsEntries = {
process_start: 0,
blower_on: 0,
blower_off: 0,
igniter_on: 0,
igniter_off: 0
};
for ( key in timestampsEntries ){
const insertTimestampsEntryQuery = `INSERT INTO timestamps (key, value) VALUES ("${key}", "${timestampsEntries[key]}")`;
dbfn.run(db, insertTimestampsEntryQuery).then(res => {
console.log(`${res.status}: ${res.data.lastID}: ${res.data.changes} changes`);
}).catch(err => console.error(err));
}
const selectAllTimestampsEntriesQuery = "SELECT * FROM timestamps";
dbfn.all(db, selectAllTimestampsEntriesQuery).then(res => {
console.log(res.status);
res.rows.forEach(row => {
console.log(`${row.key} | ${row.value}`);
});
}).catch(err => console.error(err));
}).catch(err => {
console.error(err);
});
// Create `intervals` table
/*
+ ----- + ------------- + ---- + --- + ------- + -------------- +
| Field | Type | Null | Key | Default | Extra |
+ ----- + ------------- + ---- + --- + ------- + -------------- +
| key | varchar(100) | No | | | |
| value | varchar(1000) | No | | | |
+ ----- + ------------- + ---- + --- + ------- + -------------- +
+ ------------- +
| auger_on |
| auger_off |
| pause |
| igniter_start |
| blower_stop |
+ ------------- +
CREATE TABLE IF NOT EXISTS intervals (
key varchar(100) NOT NULL,
value varchar(1000) NOT NULL
);
*/
const createIntervalsTableQuery = "CREATE TABLE IF NOT EXISTS intervals (key varchar(100) NOT NULL,value varchar(1000) NOT NULL);";
dbfn.run(db, createIntervalsTableQuery).then(res => {
console.log(res.status);
const intervalsEntries = {
process_start: 0,
blower_on: 0,
blower_off: 0,
igniter_on: 0,
igniter_off: 0
};
for ( key in intervalsEntries ){
const insertIntervalsEntryQuery = `INSERT INTO intervals (key, value) VALUES ("${key}", "${intervalsEntries[key]}")`;
dbfn.run(db, insertIntervalsEntryQuery).then(res => {
console.log(`${res.status}: ${res.data.lastID}: ${res.data.changes} changes`);
}).catch(err => console.error(err));
}
const selectAllIntervalsEntriesQuery = "SELECT * FROM intervals";
dbfn.all(db, selectAllIntervalsEntriesQuery).then(res => {
console.log(res.status);
res.rows.forEach(row => {
console.log(`${row.key} | ${row.value}`);
});
}).catch(err => console.error(err));
}).catch(err => {
console.error(err);
});
// Show the tables to confirm they were created properly:
dbfn.showTables(db).then(res => {
res.rows.forEach(row => {
console.log("Table: " + JSON.stringify(row));
});
}).catch(err => {
console.error(err);
});

View File

@ -1,23 +1,53 @@
// Get autocomplete
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./data/config.db', (err) => {
if (err) throw `E: DB Connection: ${err.message}`;
console.log(`I: Connected to the database.`);
});
module.exports = {
query(db) {
db.serialize(() => {
db.each(`SELECT PlaylistId as id,
Name as name
FROM playlists`, (err, row) => {
if (err) {
console.error(err.message);
}
console.log(row.id + "\t" + row.name);
});
});
db.close((err) => {
if (err) {
console.error(err.message);
}
console.log('Close the database connection.');
});
}
run(query) {
return new Promise((resolve, reject) => {
db.serialize(() => {
db.run(query, function(err) {
if (err) {
reject("Problem executing the query: " + err.message);
return;
}
resolve( { "status": "Query executed successfully: " + query, "data": this });
});
});
});
},
all(query) {
return new Promise((resolve, reject) => {
db.serialize(() => {
db.all(query, (err, rows) => {
if (err) {
reject("Problem executing the query: " + err.message);
return;
}
// [ { key: 'key_name', value: '0' }, { key: 'key_name', value: '0' } ]
let organizedRows = {};
rows.forEach(row => {
organizedRows[row.key] = row.value;
});
resolve({ "status": "Query executed successfully: " + query, "rows": organizedRows });
});
});
});
},
showTables() {
return new Promise((resolve, reject) => {
db.serialize(() => {
db.all("SELECT name FROM sqlite_master WHERE type='table'", (err, rows) => {
if (err) {
reject("Problem executing the query: " + err.message);
return;
}
resolve({ "status": "Tables retreived successfully", "rows": rows });
});
});
});
}
};

View File

@ -1,51 +1,18 @@
// TODOs: Add tests for PoF and Vacuum switches, add delays for shutting down blower, test logic for igniter
// TODO: Move these to config
// Physical Pin numbers for GPIO
const augerPin = 7; // Pin for controlling the relay for the pellet auger motor.
// Require the package for pulling version numbers
const package = require('./package.json');
// Import the config file
var config = require('./config.json');
config.timestamps.procStart = Date.now();
const package = require('../package.json');
// Database Functions
const dbfn = require('./database.js');
// Get environment variables
const dotenv = require('dotenv').config();
// Module for working with files
const fs = require('fs');
const { exec } = require('child_process');
var config = require('../templates/config.json');
const main = (gpio) => {
functions.commands.refreshConfig().then(res => {
// If the auger is enabled
if (config.status.auger == 1) {
// Run a cycle of the auger
functions.auger.cycle(gpio).then(res => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
// Recursion ecursion cursion ursion rsion sion ion on n
functions.checkForQuit().then(n => {
main(gpio);
});
}).catch(err => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`);
});
} else {
// If the auger is disabled
functions.commands.pause().then(res => {
functions.checkForQuit().then(n => {
main(gpio);
});
}).catch(err => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: ${err}`);
main(gpio);
});
}
}).catch(rej => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Problem refreshing the config file.`);
main(gpio);
});
}
// The functions we'll export to be used in other files
const functions = {
@ -60,7 +27,7 @@ const functions = {
on(gpio) {
return new Promise((resolve) => {
if (process.env.ONPI == 'true') {
gpio.write(augerPin, true, function(err) {
gpio.write(augerPin, true, function (err) {
if (err) throw err;
resolve('Auger turned on.');
});
@ -68,13 +35,13 @@ const functions = {
resolve('Simulated auger turned on.');
}
});
},
// Turns the auger off (pin 7 low)
off(gpio) {
return new Promise((resolve) => {
if (process.env.ONPI == 'true') {
gpio.write(augerPin, false, function(err) {
gpio.write(augerPin, false, function (err) {
if (err) throw err;
resolve('Auger turned off.');
@ -116,24 +83,24 @@ const functions = {
},
commands: {
// Prepare the stove for starting
startup () {
startup() {
// Basic startup just enables the auger
config.status.auger = 1;
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Auger enabled.`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Auger enabled.`);
return;
},
shutdown() {
// Basic shutdown only needs to disable the auger
config.status.auger = 0;
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Auger disabled.`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Auger disabled.`);
},
// Pauses the script for the time defined in env variables
pause() {
return new Promise((resolve) => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Pausing for ${config.intervals.pause}ms`);
functions.sleep(config.intervals.pause).then((res) => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Pause finished.`);
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Pausing for ${config.intervals.pause}ms`);
functions.sleep(config.intervals.pause).then((res) => {
if (process.env.DEBUG) console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Pause finished.`);
resolve();
});
});
@ -155,9 +122,9 @@ const functions = {
// Resolve the promise, letting the main script know we're done reloading the variables and the cycle can continue
resolve();
});
},
refreshConfig(newSettings) {
refreshConfig() {
return new Promise((resolve, reject) => {
// When the reload button is pressed, the call to this function will contain new config values
// {
@ -165,26 +132,76 @@ const functions = {
// augerOn: 1500,
// pause: 5000
// }
if (newSettings != undefined) {
config.intervals.augerOff = newSettings.augerOff;
config.intervals.augerOn = newSettings.augerOn;
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Intervals updated: (${newSettings.augerOn}/${newSettings.augerOff})`);
}
fs.writeFile('./config.json', JSON.stringify(config), (err) => {
if (err) reject(err);
resolve();
// if (newSettings != undefined) {
// config.intervals.augerOff = newSettings.augerOff;
// config.intervals.augerOn = newSettings.augerOn;
// console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Intervals updated: (${newSettings.augerOn}/${newSettings.augerOff})`);
// }
// fs.writeFile('./config.json', JSON.stringify(config), (err) => {
// if (err) reject(err);
// resolve();
// });
// Get status
const selectStatusQuery = "SELECT * FROM status";
dbfn.all(selectStatusQuery).then(res => {
console.log(JSON.stringify(res));
let { status } = config;
let { rows } = res;
status.auger = rows.auger;
status.blower = rows.blower;
status.igniter = rows.igniter;
status.igniterFinished = rows.igniter_finished;
status.pof = rows.proof_of_fire;
status.shutdownNextCycle = rows.shutdown_next_cycle;
status.vacuum = rows.vacuum;
// Get timestamps
const selectTimestampsQuery = "SELECT * FROM timestamps";
dbfn.all(selectTimestampsQuery).then(res => {
console.log(JSON.stringify(res));
let { timestamps } = config;
let { rows } = res;
timestamps.blowerOff = rows.blower_off;
timestamps.blowerOn = rows.blower_on;
timestamps.igniterOff = rows.igniter_off;
timestamps.igniterOn = rows.igniter_on;
timestamps.procStart = rows.process_start;
// Get intervals
const selectIntervalsQuery = "SELECT * FROM intervals";
dbfn.all(selectIntervalsQuery).then(res => {
console.log(JSON.stringify(res));
let { intervals } = config;
let { rows } = res;
intervals.augerOff = rows.auger_off;
intervals.augerOn = rows.auger_on;
intervals.blowerStop = rows.blower_stop;
intervals.igniterStart = rows.igniter_start;
intervals.pause = rows.pause;
resolve({ "status": "Refreshed the config", "config": config });
}).catch(err => {
reject(err);
return;
});
}).catch(err => {
reject(err);
return;
});
}).catch(err => {
reject(err);
return;
});
})
});
},
quit() {
functions.commands.shutdown();
functions.auger.off(gpio).then(res => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Exiting app...`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Exiting app...`);
process.exit(0);
}).catch(err => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Unable to shut off auger, rebooting Pi!`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] E: Unable to shut off auger, rebooting Pi!`);
exec('shutdown -r 0');
});
}
@ -207,7 +224,7 @@ const functions = {
fs.readFile('./templates/config.json', (err, data) => {
fs.writeFile('./config.json', data, (err) => {
if (err) throw err;
config = require('./config.json');
config = require('../config.json');
})
})
// TODO this boot splash needs updating
@ -243,7 +260,7 @@ const functions = {
},
checkForQuit() {
if (config.status.shutdownNextCycle == 1) {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: Exiting Process!`);
console.log(`[${(Date.now() - config.timestamps.procStart) / 1000}] I: Exiting Process!`);
process.exit();
}
return new Promise((resolve, reject) => {
@ -265,35 +282,7 @@ const functions = {
}
}
// Setup for use with the Pi's GPIO pins
switch (process.env.ONPI) {
case 'true':
console.log(`== Running on a Raspberry Pi.`);
var gpio = require('rpi-gpio');
functions.init(gpio).then((res) => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
main(gpio);
}).catch(rej => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Error during initialization: ${rej}`);
process.exit(1);
});
break;
case 'false':
console.log(`I: Not running on a Raspberry Pi.`);
var gpio = 'gpio';
functions.init(gpio).then(res => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] I: ${res}`);
main(gpio);
}).catch(rej => {
console.log(`[${(Date.now() - config.timestamps.procStart)/1000}] E: Error during initialization: ${rej}`);
process.exit(1);
});
break;
default:
console.log(`[${Date.now() - config.timestamps.procStart}] E: Problem with ENV file.`);
process.exit(1);
break;
}
// Export the above object, functions, as a module
module.exports = { functions };

View File

@ -1,7 +0,0 @@
const sqlite3 = require('sqlite3').verbose();
// Connect to or create the database.
let db = new sqlite3.Database('./config.db', (err) => {
if (err) console.error("DB Connect: " + err);
console.log("Connected to database.");
});

View File

@ -13,8 +13,14 @@ const http = require('http');
const server = http.createServer(app);
const fs = require('fs');
// const bodyParser = require('body-parser');
var config;
var config = require('./templates/config.json');
var fn;
const sqlite3 = require('sqlite3');
const db = new sqlite3.Database('./data/config.db', (err) => {
if (err) throw `E: DB Connection: ${err.message}`;
console.log(`I: Connected to the database.`);
});
// First thing is to copy the template config to main config file
fs.readFile('./templates/config.json', (err, data) => {
@ -22,7 +28,7 @@ fs.readFile('./templates/config.json', (err, data) => {
if (err) throw err;
console.log(`I: Config Template copied.`);
config = require('./config.json');
fn = require('./functions.js').functions;
fn = require('./modules/functions.js').functions;
server.listen(config.web.port, config.web.ip);
});
});