diff --git a/.vscode/launch.json b/.vscode/launch.json index 8fdb5a0..c52eeff 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "skipFiles": [ "/**" ], - "program": "${workspaceFolder}/websvr.js" + "program": "${workspaceFolder}/main.js" } ] } \ No newline at end of file diff --git a/.vscode/pssnippets.code-snippets b/.vscode/pssnippets.code-snippets index 757f347..0f66f97 100644 --- a/.vscode/pssnippets.code-snippets +++ b/.vscode/pssnippets.code-snippets @@ -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" }, } \ No newline at end of file diff --git a/config.db b/config.db new file mode 100644 index 0000000..e69de29 diff --git a/main.js b/main.js new file mode 100644 index 0000000..0f9c5f0 --- /dev/null +++ b/main.js @@ -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); + }); + } +} \ No newline at end of file diff --git a/modules/_setupdb.js b/modules/_setupdb.js new file mode 100644 index 0000000..fc566ef --- /dev/null +++ b/modules/_setupdb.js @@ -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); +}); \ No newline at end of file diff --git a/modules/database.js b/modules/database.js index 7b76141..4e82071 100644 --- a/modules/database.js +++ b/modules/database.js @@ -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 }); + }); + }); + }); + } }; \ No newline at end of file diff --git a/functions.js b/modules/functions.js similarity index 68% rename from functions.js rename to modules/functions.js index 048df1e..1abc04a 100644 --- a/functions.js +++ b/modules/functions.js @@ -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 }; \ No newline at end of file diff --git a/setupdb.js b/setupdb.js deleted file mode 100644 index 9efbde1..0000000 --- a/setupdb.js +++ /dev/null @@ -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."); -}); - diff --git a/websvr.js b/websvr.js index 35e764f..80d1bc0 100644 --- a/websvr.js +++ b/websvr.js @@ -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); }); });