/* * Copyright (C) 2015 by Multi-Tech Systems * * This file is part of lora-query. * * lora-query is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * lora-query is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lora-query. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Version.h" #define INET_ADDR "127.0.0.1" #define INET_PORT 6677 #define MAX_RECEIVED_BYTES 2000 #define TIMEOUT 100 /* By default 100 msec */ int opt_get_stats = 0; int opt_get_nodelist = 0; int opt_get_json = 0; int opt_stats_reset = 0; int timeout = TIMEOUT; const char* cmd_stats = "stats"; const char* cmd_stats_reset = "stats reset"; const char* cmd_nodelist = "node list"; const std::string lora_network_stats_json("/var/tmp/lora_network_stats.json"); const std::string lora_network_nodelist("/var/tmp/lora_network_nodelist"); const std::string lora_network_nodelist_json("/var/tmp/lora_network_nodelist.json"); std::stringstream receiveStream; const int nodeSize = 15; const std::string NODE_ADDR("nodeAddr"); const std::string NODE_DEV_EUI("devEui"); const std::string NODE_JOINED("joined"); const std::string NODE_SEQ_NUM("seqNum"); const std::string NODE_PKTS_UP("pktsUp"); const std::string NODE_PKTS_DOWN("pktsDown"); const std::string NODE_PKTS_1ST("pkts1st"); const std::string NODE_PKTS_2ND("pkts2nd"); const std::string NODE_DROPPED("dropped"); const std::string NODE_RSSI_MIN("rssiMin"); const std::string NODE_RSSI_MAX("rssiMax"); const std::string NODE_RSSI_AVR("rssiAvg"); const std::string NODE_SNR_MIX("snrMin"); const std::string NODE_SNR_MAX("snrMax"); const std::string NODE_SNR_AVR("snrAvg"); void getloraData(const char *command); void printStats(void); void printNodeList(void); void saveToFile(const std::string& fileName, const std::string& buffer); void parseOptions(int argc, char** argv); void printHelp(const std::string& sApp); std::string trim(std::string& str); int main(int argc, char**argv) { parseOptions(argc, argv); if (opt_stats_reset) { getloraData(cmd_stats_reset); } if (opt_get_stats) { getloraData(cmd_stats); printStats(); } if (opt_get_nodelist) { getloraData(cmd_nodelist); if (opt_get_json) { printNodeList(); } else { if (receiveStream.str().empty() ) { return 0; } saveToFile(lora_network_nodelist, receiveStream.str()); std::cout << receiveStream.str(); } } return 0; } void getloraData(const char *command) { int sockfd; struct sockaddr_in servaddr; int receiveBytes = 0; char receiveMessage[MAX_RECEIVED_BYTES]; struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; receiveStream.str(""); receiveStream.clear(); if (NULL == command) { printError("Command is null\n"); return; } sockfd=socket(AF_INET,SOCK_DGRAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr=inet_addr(INET_ADDR); servaddr.sin_port=htons(INET_PORT); memset(receiveMessage, 0, MAX_RECEIVED_BYTES); if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { printError("setsockopt error\n"); return; } sendto(sockfd, command, strlen(command), 0, (struct sockaddr *)&servaddr, sizeof(servaddr)); while(1) { receiveBytes = recvfrom(sockfd, receiveMessage, MAX_RECEIVED_BYTES, 0, NULL, NULL); if(receiveBytes < 0) { /*printf("timeout\n");*/ break; } receiveStream << std::string(receiveMessage, receiveBytes); } } void printStats() { Json::Reader reader; Json::Value stats; if (receiveStream.str().empty() ) { return; } if (!reader.parse(receiveStream.str(), stats), false) { printError("Error parsing JSON: [%s]", receiveStream.str().c_str()); } // Replace underscores keys with camel-case. if (stats.isObject()) { Json::Value::Members keys = stats.getMemberNames(); std::string newKey; for (Json::Value::Members::iterator it = keys.begin(); it != keys.end(); ++it) { const std::string& key = *it; newKey.clear(); for (size_t i = 0; i < key.length() ; ++i) { if (('_' == key[i]) || ('-' == key[i])) { if ((i + 1) < key.length()) { newKey.push_back(std::toupper(key[++i])); } } else { newKey.push_back(key[i]); } } stats[newKey] = stats[key]; stats.removeMember(key); } } saveToFile(lora_network_stats_json, stats.toStyledString()); std::cout << stats.toStyledString(); } void printNodeList() { std::string line; std::vector parts; Json::Value nodeList = Json::ValueType::arrayValue; if (receiveStream.str().empty() ) { return; } getline(receiveStream, line); while (1) { getline(receiveStream, line); if (line.size() == 0) break; line = trim(line); parts = MTS::Text::split(line, " "); if (nodeSize == parts.size()) { Json::Value jNode(Json::objectValue); jNode[NODE_ADDR] = parts[0]; jNode[NODE_DEV_EUI] = parts[1]; jNode[NODE_JOINED] = parts[2]; jNode[NODE_SEQ_NUM] = atoi(parts[3].c_str()); jNode[NODE_PKTS_UP] = atoi(parts[4].c_str()); jNode[NODE_PKTS_DOWN] = atoi(parts[5].c_str()); jNode[NODE_PKTS_1ST] = atoi(parts[6].c_str()); jNode[NODE_PKTS_2ND] = atoi(parts[7].c_str()); jNode[NODE_DROPPED] = atoi(parts[8].c_str()); jNode[NODE_RSSI_MIN] = atoi(parts[9].c_str()); jNode[NODE_RSSI_MAX] = atoi(parts[10].c_str()); jNode[NODE_RSSI_AVR] = atoi(parts[11].c_str()); jNode[NODE_SNR_MIX] = atoi(parts[12].c_str()); jNode[NODE_SNR_MAX] = atoi(parts[13].c_str()); jNode[NODE_SNR_AVR] = atoi(parts[14].c_str()); nodeList.append(jNode); } else { printError("Incorrect Node Data! parts.size() = [%d]\n", parts.size()); } } saveToFile(lora_network_nodelist_json, nodeList.toStyledString()); std::cout << nodeList.toStyledString(); } void saveToFile(const std::string& fileName, const std::string& buffer) { std::ofstream outFile; outFile.open(fileName); outFile << buffer; outFile.close(); } void parseOptions(int argc, char** argv) { if(argc == 1) { printHelp(argv[0]); exit(0); } const char* short_options = "hvsirnjt:"; const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, {"stats", no_argument, 0, 's'}, {"stats-reset", no_argument, 0, 'r'}, {"node-list", no_argument, 0, 'n'}, {"json", no_argument, 0, 'j'}, {"timeout", required_argument, 0, 't'}, {0, 0, 0, 0} }; int rez; int option_index; while ((rez=getopt_long(argc, argv, short_options, long_options, &option_index))!=-1) { switch(rez){ case 's': { ++opt_get_stats; break; }; case 'r': { ++opt_stats_reset; break; }; case 'n': { ++opt_get_nodelist; break; }; case 'j': { ++opt_get_json; break; }; case 't': { timeout = atoi(optarg); break; } case 'v': printf("Version: %s\n", Version::version.c_str()); exit(0); break; case 'h': case '?': default: { printHelp(argv[0]); exit(0); break; }; }; }; } void printHelp(const std::string& sApp) { printf("Usage: %s [-t timeout] [-s] [-n]\n", sApp.c_str()); printf("\tSimple UDP client utility to pull info from LoRa Network server\n"); printf("\t--timeout (t) : UDP recv timeout, default: 100 (msecs)\n"); printf("\t--stats (s) : get LoRa Network server statistics\n"); printf("\t--stats-reset (r) : reset LoRa Network server statistics\n"); printf("\t--node-list (n) : get Node List\n"); printf("\t--json (j) : data in json format\n"); printf("\t--help (?) : returns this message\n"); printf("\t--version (v) : print version\n"); } std::string trim(std::string& str) { str.erase(std::unique(str.begin(), str.end(), [](char a, char b) { return a == ' ' && b == ' '; } ), str.end() ); return str; }