From e3a667cb6923fabcdde99ef236ce58b0c4eccdc8 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Thu, 4 Jul 2019 17:18:38 +0100
Subject: [PATCH] Implement the ttn client connection, but it throws a syntax
 error.

---
 build                                    | 14 ++++++
 server/bootstrap/container.mjs           |  2 +
 server/package-lock.json                 | 62 +++++++++++++++++++++++-
 server/package.json                      | 55 +++++++++++----------
 server/settings.default.toml             | 24 +++++++++
 server/ttn-app-server/DecodePayload.mjs  | 17 +++++++
 server/ttn-app-server/MessageHandler.mjs | 15 ++++++
 server/ttn-app-server/TTNAppServer.mjs   | 21 +++++++-
 8 files changed, 181 insertions(+), 29 deletions(-)
 create mode 100644 server/ttn-app-server/DecodePayload.mjs
 create mode 100644 server/ttn-app-server/MessageHandler.mjs

diff --git a/build b/build
index 38c5ef4..8c8e717 100755
--- a/build
+++ b/build
@@ -36,6 +36,7 @@ if [[ "$#" -lt 1 ]]; then
 	echo -e "${CSECTION}Available actions${RS}";
 	echo -e "    ${CACTION}setup${RS}    - Perform initial setup";
 	echo -e "    ${CACTION}render${RS}   - Render the report";
+	echo -e "    ${CACTION}server${RS}   - Execute the Node.js server.";
 	echo -e "";
 	
 	exit 1;
@@ -96,9 +97,22 @@ task_setup() {
 	cd -;
 	task_end $?;
 	
+	task_begin "Installing server dependencies";
+	cd server;
+	npm install;
+	cd ..;
+	exit_code="${?}";
+	task_end ${exit_code};
+	
 	stage_end 0;
 }
 
+task_server() {
+	execute node --experimental-modules ./server/index.mjs $@;
+}
+
+###############################################################################
+
 task_render() {
 	_render-latex-pdf "Reports/Initial-Report/Initial-Report.tex";
 }
diff --git a/server/bootstrap/container.mjs b/server/bootstrap/container.mjs
index 28cc0c6..9e3aa03 100644
--- a/server/bootstrap/container.mjs
+++ b/server/bootstrap/container.mjs
@@ -5,6 +5,7 @@ import a from 'awilix';
 import Ansi from '../Helpers/Ansi.mjs';
 import Log from '../Helpers/Log.mjs';
 import TTNAppServer from '../ttn-app-server/TTNAppServer.mjs';
+import MessageHandler from '../ttn-app-server/MessageHandler.mjs';
 
 import settings from './settings.mjs';
 import database_init from '../bootstrap/database_init.mjs';
@@ -19,6 +20,7 @@ c.register({
 	log: a.asClass(Log).singleton(),
 	database: a.asFunction(database_init).singleton(),
 	TTNAppServer: a.asClass(TTNAppServer),
+	MessageHandler: a.asClass(MessageHandler)
 });
 
 c.loadModules("../Repos.SQLite/*.mjs", {
diff --git a/server/package-lock.json b/server/package-lock.json
index 12e3f72..4c9fba2 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -13,6 +13,7 @@
       "version": "5.4.0",
       "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-5.4.0.tgz",
       "integrity": "sha512-nzm7lJ7l3jBmGUbtkL8cdOMhPkN6Pw2IM+b0V7iIKba+YKiLrjkIy7vVLsBIVnd7+lgzBzrHsXZxCaFTcmw5Ow==",
+      "dev": true,
       "requires": {
         "@types/integer": "*"
       }
@@ -20,7 +21,8 @@
     "@types/integer": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@types/integer/-/integer-1.0.0.tgz",
-      "integrity": "sha512-3viiRKLoSP2Qr78nMoQjkDc0fan4BgmpOyV1+1gKjE8wWXo3QQ78WItO6f9WuBf3qe3ymDYhM65oqHTOZ0rFxw=="
+      "integrity": "sha512-3viiRKLoSP2Qr78nMoQjkDc0fan4BgmpOyV1+1gKjE8wWXo3QQ78WItO6f9WuBf3qe3ymDYhM65oqHTOZ0rFxw==",
+      "dev": true
     },
     "abbrev": {
       "version": "1.1.1",
@@ -1091,6 +1093,54 @@
       "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
       "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
     },
+    "manuh": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/manuh/-/manuh-1.4.3.tgz",
+      "integrity": "sha512-reSOx1qBvH23C7mtpkDw6afXPq4jIVnsWxR/3RGTLnMKNKMxNiznQir/U1FrY3SbPpPV10oP2VLqYZ6zN1relA==",
+      "requires": {
+        "debug": "^3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
+    "manuh-bridge": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/manuh-bridge/-/manuh-bridge-0.0.2.tgz",
+      "integrity": "sha512-KGZnNYWVXV7SPZlnP7Zo0df80kUhhgJdsgtMazPbSoEx7j8FK6HPcyoM0QNomeftBNkbcB+5Z8c4XQr2YajaSg==",
+      "requires": {
+        "debug": "^3.1.0",
+        "manuh": "^1.3.0",
+        "mqtt": "^2.18.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
     "minimatch": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -1468,6 +1518,16 @@
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
       "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
     },
+    "simple-mqtt-client": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/simple-mqtt-client/-/simple-mqtt-client-1.0.1.tgz",
+      "integrity": "sha512-46CbCaOD4+rfz+IbNlpnYyI3ZIhkDbw7o9/wWGGg3uhqv+RwxxziVNuqiUN1GUIU/EWed9jP2c3C852DSxxjVQ==",
+      "requires": {
+        "manuh": "^1.4.3",
+        "manuh-bridge": "^0.0.2",
+        "mqtt": "^2.18.8"
+      }
+    },
     "source-map": {
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
diff --git a/server/package.json b/server/package.json
index 77d724a..0c0f259 100644
--- a/server/package.json
+++ b/server/package.json
@@ -1,28 +1,31 @@
 {
-  "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",
-  "dependencies": {
-    "@iarna/toml": "^2.2.3",
-    "@types/better-sqlite3": "^5.4.0",
-    "awilix": "^4.2.2",
-    "better-sqlite3": "^5.4.0",
-    "ttn": "^2.3.2"
-  }
+	"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",
+	"dependencies": {
+		"@iarna/toml": "^2.2.3",
+		"awilix": "^4.2.2",
+		"better-sqlite3": "^5.4.0",
+		"simple-mqtt-client": "^1.0.1",
+		"ttn": "^2.3.2"
+	},
+	"devDependencies": {
+		"@types/better-sqlite3": "^5.4.0"
+	}
 }
diff --git a/server/settings.default.toml b/server/settings.default.toml
index fc7706d..35fd9b0 100644
--- a/server/settings.default.toml
+++ b/server/settings.default.toml
@@ -6,6 +6,7 @@ program_name = "LoRaWAN Signal Mapper"
 version = "v0.1"
 description = "assists in mapping LoRaWAN signal coverage"
 
+
 [database]
 ### Database settings ###
 
@@ -16,6 +17,29 @@ filename = "lorawan.sqlite"
 [database.options]
 
 
+[ttn]
+### The Things Network settings ###
+
+# The host to connect to via MQTT.
+# See https://www.thethingsnetwork.org/docs/applications/mqtt/api.html
+# and also "Application Overview -> Handler"
+host = "eu.thethings.network"
+# The port number to connect on.
+port = 8883
+# Whether to use TLS or not.
+tls = true
+
+# The id of The Things Network application to connect with.
+# Basically your application's name. Get this from the things network 
+# console - e.g. "lorawan-signal-mapping".
+app_id = "CHANGE_THIS"
+# The access key to connect to The Things Network with.
+# Get this from the TTN console too. Click on your application, scroll to the 
+# "access keys" section at the bottom of the page, and copy the value you see 
+# there.
+access_key = "CHANGE_THIS"
+
+
 [logging]
 # The format the date displayed when logging things should take.
 # Allowed values: relative (e.g like when a Linux machine boots), absolute (e.g. like Nginx server logs)
diff --git a/server/ttn-app-server/DecodePayload.mjs b/server/ttn-app-server/DecodePayload.mjs
new file mode 100644
index 0000000..ed6e935
--- /dev/null
+++ b/server/ttn-app-server/DecodePayload.mjs
@@ -0,0 +1,17 @@
+"use strict";
+
+const offset_id = 0;
+const offset_lat = 32 / 8;
+const offset_lng = (32 / 8) + (32 / 8);
+
+function decode_payload(payload_base64) {
+	let payload_buffer = Buffer.from(payload_base64, "base64");
+	
+	return {
+		id: payload_buffer.readUInt32LE(offset_id),
+		latitude: patload_buffer.readFloatLE(offset_lat),
+		longitude: patload_buffer.readFloatLE(offset_lng)
+	};
+}
+
+export { decode_payload };
diff --git a/server/ttn-app-server/MessageHandler.mjs b/server/ttn-app-server/MessageHandler.mjs
new file mode 100644
index 0000000..a8e5151
--- /dev/null
+++ b/server/ttn-app-server/MessageHandler.mjs
@@ -0,0 +1,15 @@
+"use strict";
+
+import { decode_payload } from './DecodePayload.mjs';
+
+class MessageHandler {
+	constructor() {
+		
+	}
+	
+	async handle(device_id, payload) {
+		console.log(arguments);
+	}
+}
+
+export default MessageHandler;
diff --git a/server/ttn-app-server/TTNAppServer.mjs b/server/ttn-app-server/TTNAppServer.mjs
index 2c73c07..534e3f5 100644
--- a/server/ttn-app-server/TTNAppServer.mjs
+++ b/server/ttn-app-server/TTNAppServer.mjs
@@ -1,15 +1,32 @@
 "use strict";
 
+import { data as ttn_data } from 'ttn';
+
 class TTNAppServer {
 	// Destructure the awilix container
-	constructor({ settings, ansi }) {
+	constructor({ settings, ansi, message_handler }) {
 		this.settings = settings;
 		/** @type {Ansi} */
 		this.a = ansi;
+		/** @type {MessageHandler} */
+		this.message_handler = message_handler;
 	}
 	
-	run() {
+	async start() {
+		if(this.settings.ttn.app_id == "CHANGE_THIS" || this.settings.ttn.access_key == "CHANGE_THIS") {
+			console.error(`${this.a.fred}${this.a.hicol}Error: No TTN app id specified. Try filling in the required values in settings.toml. If they don't exist yet, try using server/settings.default.toml as a reference.${this.a.reset}`);
+			return false;
+		}
+		this.ttn_client = await ttn_data(
+			this.settings.ttn.app_id,
+			this.settings.ttn.access_key
+		);
 		
+		this.ttn_client.on("uplink", this.handle_message.bind(this));
+	}
+	
+	async handle_message(device_id, payload) {
+		await this.message_handler.handle(device_id, payload);
 	}
 }
 
