From 0eeeb06a80a8038865a11e48d0443a3e49955c80 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Mon, 20 May 2019 12:18:25 +0100
Subject: [PATCH] Setup initial server-side application structure

---
 Helpers/Ansi.mjs                | 78 +++++++++++++++++++++++++++++++++
 help.mjs                        | 26 +++++++++++
 index.mjs                       | 68 ++++++++++++++++++++++++++++
 package.json                    | 21 +++++++++
 ttn-app-server/TTNAppServer.mjs | 14 ++++++
 5 files changed, 207 insertions(+)
 create mode 100644 Helpers/Ansi.mjs
 create mode 100644 help.mjs
 create mode 100755 index.mjs
 create mode 100644 package.json
 create mode 100644 ttn-app-server/TTNAppServer.mjs

diff --git a/Helpers/Ansi.mjs b/Helpers/Ansi.mjs
new file mode 100644
index 0000000..7fb8d79
--- /dev/null
+++ b/Helpers/Ansi.mjs
@@ -0,0 +1,78 @@
+"use strict";
+
+/**
+ * Generates various VT100 ANSI escape sequences.
+ * Ported from C#.
+ * @licence MPL-2.0 <https://www.mozilla.org/en-US/MPL/2.0/>
+ * @source https://gist.github.com/a4edd3204a03f4eedb79785751efb0f3#file-ansi-cs
+ * @author Starbeamrainbowlabs
+ * GitHub: @sbrl | Twitter: @SBRLabs | Reddit: u/Starbeamrainbowlabs
+ ***** Changelog *****
+ * 27th March 2019:
+ *  - Initial public release
+ */
+class Ansi {
+	constructor() {
+		/**
+		 * Whether we should *actually* emit ANSI escape codes or not.
+		 * Useful when we want to output to a log file, for example
+		 * @type {Boolean}
+		 */
+		this.enabled = true;
+        
+		this.escape_codes();
+	}
+	
+	escape_codes() {
+		// Solution on how to output ANSI escape codes in C# from here:
+		// https://www.jerriepelser.com/blog/using-ansi-color-codes-in-net-console-apps
+		this.reset = this.enabled ? "\u001b[0m" : "";
+		this.hicol = this.enabled ? "\u001b[1m" : "";
+		this.locol = this.enabled ? "\u001b[2m" : "";
+		this.underline = this.enabled ? "\u001b[4m" : "";
+		this.inverse = this.enabled ? "\u001b[7m" : "";
+		this.fblack = this.enabled ? "\u001b[30m" : "";
+		this.fred = this.enabled ? "\u001b[31m" : "";
+		this.fgreen = this.enabled ? "\u001b[32m" : "";
+		this.fyellow = this.enabled ? "\u001b[33m" : "";
+		this.fblue = this.enabled ? "\u001b[34m" : "";
+		this.fmagenta = this.enabled ? "\u001b[35m" : "";
+		this.fcyan = this.enabled ? "\u001b[36m" : "";
+		this.fwhite = this.enabled ? "\u001b[37m" : "";
+		this.bblack = this.enabled ? "\u001b[40m" : "";
+		this.bred = this.enabled ? "\u001b[41m" : "";
+		this.bgreen = this.enabled ? "\u001b[42m" : "";
+		this.byellow = this.enabled ? "\u001b[43m" : "";
+		this.bblue = this.enabled ? "\u001b[44m" : "";
+		this.bmagenta = this.enabled ? "\u001b[45m" : "";
+		this.bcyan = this.enabled ? "\u001b[46m" : "";
+		this.bwhite = this.enabled ? "\u001b[47m" : "";
+	}
+	
+	// Thanks to http://ascii-table.com/ansi-escape-sequences.php for the following ANSI escape sequences
+	up(lines = 1) {
+		return this.enabled ? `\u001b[${lines}A` : "";
+	}
+	down(lines = 1) {
+		return this.enabled ? `\u001b[${lines}B` : "";
+	}
+	right(lines = 1) {
+		return this.enabled ? `\u001b[${lines}C` : "";
+	}
+	left(lines = 1) {
+		return this.enabled ? `\u001b[${lines}D` : "";
+	}
+	
+	jump_to(x, y) {
+		return this.enabled ? `\u001b[${y};${x}H` : "";
+	}
+	
+	cursor_pos_save() {
+		return this.enabled ? `\u001b[s` : "";
+	}
+	cursor_pos_restore() {
+		return this.enabled ? `\u001b[u` : "";
+	}
+}
+
+export default Ansi;
diff --git a/help.mjs b/help.mjs
new file mode 100644
index 0000000..7848b07
--- /dev/null
+++ b/help.mjs
@@ -0,0 +1,26 @@
+import path from 'path';
+
+export default function(settings) {
+	let ansi = settings.ansi;
+	console.log(`${settings.program_name}, ${settings.version}
+${ansi.locol}By Starbeamrainbowlabs${ansi.reset}
+
+${ansi.hicol}This program ${settings.description}.${ansi.reset}
+
+${ansi.fblue}${ansi.hicol}Usage:${ansi.reset}
+    node --experimental-modules ${path.relative(process.cwd(), process.argv[1])} {subcommand} {options}
+
+${ansi.fblue}${ansi.hicol}Subcommands:${ansi.reset}
+    ${ansi.fyellow}ttn-app-server${ansi.reset}    Starts the thing network application server
+    ${ansi.fyellow}process-data${ansi.reset}      Consolidates collected data from the IoT device and the TTN app server
+    ${ansi.fyellow}train-ai${ansi.reset}          Trains the AI on the consolidated data
+    ${ansi.fyellow}serve-map${ansi.reset}         Serves the final output map using the trained AI
+
+${ansi.fblue}${ansi.hicol}Options:${ansi.reset}
+    ${ansi.fyellow}-h	--help		${ansi.reset}Show this message
+    ${ansi.fyellow}-v	--version	${ansi.reset}Show the version of this program
+
+${ansi.fblue}${ansi.hicol}Environment Variables:${ansi.reset}
+    (none yet)
+`);
+};
diff --git a/index.mjs b/index.mjs
new file mode 100755
index 0000000..c037683
--- /dev/null
+++ b/index.mjs
@@ -0,0 +1,68 @@
+#!/usr/bin/env node
+
+// Requires Ansi.mjs, which can be found here: https://gist.github.com/8c0bb5e172438b6e62dd48587cfeba84#file-ansi-mjs
+
+import Ansi from './Helpers/Ansi.mjs';
+
+import show_help from './help.mjs';
+import TTNAppServer from './ttn-app-server/TTNAppServer.mjs';
+
+// 1: Setup
+const ansi = new Ansi();
+
+const settings = {
+	program_name: "LoRaWAN Signal Mapper",
+	version: "v0.1",
+	description: "assists in mapping LoRaWAN signal coverage.",
+	ansi
+};
+
+// 2: CLI Argument Parsing
+let args = process.argv.slice(2); // Slice out the node binary name and the filename
+let extras = [];
+for(let i = 0; i < args.length; i++) {
+	if(!args[i].startsWith("-")) {
+		extras.push(args[i]);
+		continue;
+	}
+	
+	switch(args[i]) {
+		case "--help":
+		case "-h":
+			show_help(settings);
+			process.exit();
+			break;
+		
+		case "--version":
+		case "-v":
+			console.log(program.version);
+			break;
+		
+		// Add more arguments here
+	}
+}
+
+// 3: Environment Variable Parsing
+
+// process.env.XYZ
+
+// 4: Run
+if(extras.length < 1) {
+	console.error(`${ansi.fred}${ansi.hicol}Error: No subcommand specified.${ansi.reset}`);
+	show_help(settings);
+	process.exit();
+}
+switch(extras[0]) {
+	case "ttn-app-server":
+		let app_server = new TTNAppServer(settings);
+		app_server.run();
+		break;
+	
+	default:
+		console.error(`${ansi.fred}${ansi.hicol}Error: Subcommand '${extras[0]}' not recognised.${ansi.reset}`);
+		console.error( `${ansi.fred}Perhaps you mistyped it, or it hasn't been implemented yet?`);
+		process.exit(1);
+		break;
+}
+
+// 5: Cleanup
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..80a2cfc
--- /dev/null
+++ b/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "lorawan-signal-mapping",
+  "version": "0.0.1",
+  "description": "A LoRaWAN signal mapping system. Comprised of several distinct parts.",
+  "main": "index.mjs",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://git.starbeamrainbowlabs.com/sbrl/Msc-Summer-Project/"
+  },
+  "keywords": [
+    "lora",
+    "ttn",
+    "arduino",
+    "mapping"
+  ],
+  "author": "Starbeamrainbowlabs",
+  "license": "MPL-2.0"
+}
diff --git a/ttn-app-server/TTNAppServer.mjs b/ttn-app-server/TTNAppServer.mjs
new file mode 100644
index 0000000..300c84a
--- /dev/null
+++ b/ttn-app-server/TTNAppServer.mjs
@@ -0,0 +1,14 @@
+"use strict";
+
+class TTNAppServer {
+	constructor(in_settings) {
+		this.settings = in_settings;
+		this.ansi = this.settings.ansi;
+	}
+	
+	run() {
+		
+	}
+}
+
+export default TTNAppServer;
