"use strict"; import show_help from '../help.mjs'; import { decode_payload } from '../ttn-app-server/DecodePayload.mjs'; import path from 'path'; import { unnormalise_lat, unnormalise_lng, } from '../../common/Normalisers.mjs' // HACK import awilix from 'awilix'; export default async function(c) { let { ansi: a, log: l, settings, DatasetFetcher } = c.cradle; // 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(c.cradle); process.exit(); break; case "--version": case "-v": console.log(program.version); break; case "--ai-unified": settings.ai.mode = "unified"; break; case "--ai-split": settings.ai.mode = "split"; break; // Add more arguments here } } // 3: Environment Variable Parsing // process.env.XYZ // 4: Run if(extras.length < 1) { console.error(`${a.fred}${a.hicol}Error: No subcommand specified.${a.reset}`); show_help(c.cradle); process.exit(); } l.log_e(`${a.fgreen}${a.hicol}*** LoRaWAN Signal Mapper ***${a.reset}`); switch(extras[0]) { case "ttn-app-server": l.log(`${a.fgreen}${a.hicol}Starting The Things Network application server${a.reset}`); let app_server = c.resolve("TTNAppServer"); await app_server.run(); break; case "decode-test": l.log(`${a.fgreen}${a.hicol}Decoding message${a.reset}`); if(process.argv.length < 4) { l.error("Error: No message specified. Specify the message to decode in single quotes directly after 'decode-test'."); process.exit(1); } l.log("Input: ", process.argv[3]); l.log("Output: ", decode_payload(process.argv[3])); break; case "process-data": l.log(`${a.fgreen}${a.hicol}Processing microSD card data${a.reset}`); if(process.argv.length < 4) { l.error("Error: No filename specified. Do so by specifying a filename after the 'process-data' subcommand."); process.exit(1); } let filepath = process.argv[3]; let data_processor = c.cradle.DataProcessor; await data_processor.process(filepath); l.log("Processing complete!"); break; case "train-ai": // Do ninja dynamic import // This avoid loading TensorFlow when it's not needed, as it causes // a crash if the AVX2 CPU instruction set isn't available for some // reason. Hrm. let AITrainer = (await import(path.join(c.cradle.root_dir, "train-ai/AITrainer.mjs"))).default; c.register({ AITrainer: awilix.asClass(AITrainer) }); l.log(`${a.fgreen}${a.hicol}Training AIs in ${settings.ai.mode} mode${a.reset}`); let ai_trainer = c.cradle.AITrainer; switch(settings.ai.mode) { case "split": await ai_trainer.train_all(); break; case "unified": await ai_trainer.train_unified(); break; } break; case "geojson-debug": let result = []; for(let next_item of DatasetFetcher.fetch_all(null, true)) { result.push({ type: "Feature", geometry: { type: "Point", coordinates: [ unnormalise_lng(next_item.input.longitude), unnormalise_lat(next_item.input.latitude) ] }, properties: { "marker-symbol": "circle", "marker-color": next_item.output[0] <= 0 ? "#dd0707" : "#04a104", gateway: next_item.ext.gateway, rssi: next_item.ext.rssi_raw } }) } console.log(JSON.stringify({ type: "FeatureCollection", features: result }, null, "\t")); break; default: l.error(`Error: Subcommand '${extras[0]}' not recognised.`); l.error(`Perhaps you mistyped it, or it hasn't been implemented yet?`); l.error(`Try invoking this program with --help to see a list of supported subcommands.`); process.exit(1); break; } }