summaryrefslogtreecommitdiff
path: root/src/Device/Device.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Device/Device.cpp')
-rw-r--r--src/Device/Device.cpp422
1 files changed, 422 insertions, 0 deletions
diff --git a/src/Device/Device.cpp b/src/Device/Device.cpp
new file mode 100644
index 0000000..573e10b
--- /dev/null
+++ b/src/Device/Device.cpp
@@ -0,0 +1,422 @@
+/**********************************************************************
+* COPYRIGHT 2020 MULTI-TECH SYSTEMS, INC.
+*
+* ALL RIGHTS RESERVED BY AND FOR THE EXCLUSIVE BENEFIT OF
+* MULTI-TECH SYSTEMS, INC.
+*
+* MULTI-TECH SYSTEMS, INC. - CONFIDENTIAL AND PROPRIETARY
+* INFORMATION AND/OR TRADE SECRET.
+*
+* NOTICE: ALL CODE, PROGRAM, INFORMATION, SCRIPT, INSTRUCTION,
+* DATA, AND COMMENT HEREIN IS AND SHALL REMAIN THE CONFIDENTIAL
+* INFORMATION AND PROPERTY OF MULTI-TECH SYSTEMS, INC.
+* USE AND DISCLOSURE THEREOF, EXCEPT AS STRICTLY AUTHORIZED IN A
+* WRITTEN AGREEMENT SIGNED BY MULTI-TECH SYSTEMS, INC. IS PROHIBITED.
+*
+***********************************************************************/
+
+#include "Device.h"
+
+const std::vector<std::string> Device::apIdentifiers = {"ap1", "ap2", "lora", "lora-2", "slot1", "slot2"};
+const std::regex Device::apFilters("(modalias)|(power)(.*)|(subsystem)|(uevent)");
+const std::regex Device::lora15Filters("(MTAC-LORA-)(.*)|(MTCAP-LORA-)(.*)|(MTLGA-)(.*)|(MTCDT3-)(.*)");
+const std::regex Device::loraG16Filters("(MTAC-LORA-G16)(.*)");
+const std::regex Device::loraG64Filters("(MTAC-LORA-G64)(.*)");
+const std::regex Device::gpiobFilters("(MTAC-GPIOB)|(MMTAC-GPIOI)");
+const std::regex Device::mfserFilters("(MTAC-MFSER-DTE)|(MTAC-MFSER-DCE)|(MTR-)(.*)");
+const std::regex Device::serialModeFilter("(.*)(serial-mode)");
+const std::regex Device::storeFilters("(.*)(mac-)(.*)|(.*)(-id)|(uuid)|(.*)(/eui)|(.*)(/cdone)|(.*)(hw-version)|(imei)|(capability)(.*)|(radio-reset-backoff-seconds)|(modalias)|(power)|((subsystem)(.*))|(uevent)|(board-temperature)|(reset)|(led3)|(led-ls)|(usbhd-ps-oc)|(.*)(adc[0-9])|(.*)(din[0-9])|(gpi[0-9])|(gpi[0-9][0-9])");
+const std::regex Device::showFilters("(modalias)|(subsystem)|(uevent)");
+
+Device::Device() {
+ isRoot = !getuid();
+}
+
+void Device::exitHandler(int code) {
+ if (code != 0) {
+ logError("exiting with " + std::to_string(code));
+ }
+ exit(code);
+}
+
+bool Device::fileExists(std::string file) {
+ struct stat buffer;
+ return (stat (file.c_str(), &buffer) == 0) ? true : false;
+}
+
+mode_t Device::fileType(std::string file) {
+ struct stat buf;
+ stat (file.c_str(), &buf);
+ return buf.st_mode & S_IFMT;
+}
+
+void Device::getSystemTreeJson(const char * dir_name) {
+ std::string fullPath = SYSFS_PLATFORM + std::string(dir_name);
+ DIR * d = opendir (fullPath.c_str());
+
+ if (!d) {
+ logError("Cannot open directory " + fullPath);
+ exitHandler(99);
+ }
+ while (1) {
+ struct dirent * entry;
+ const char * d_name;
+ entry = readdir (d); // Gets subsequent entries from "d"
+ if (!entry) { // If there are no more entries, exit
+ break;
+ }
+ d_name = entry->d_name; // Get file name
+ if (!(entry->d_type & DT_DIR)) {
+ std::string fileData;
+ MTS::System::readFile(fullPath + "/" + std::string(d_name), fileData);
+ fileData = MTS::Text::trim(fileData);
+ if (strlen(dir_name) > 0) {
+ if (std::binary_search(apIdentifiers.begin(), apIdentifiers.end(), dir_name) && !regex_match(d_name, apFilters)) {
+ if (accessoryCard.IsNull()) {
+ accessoryCard.SetObject();
+ } else if (accessoryCard.HasMember(d_name)) {
+ accessoryCards.PushBack(accessoryCard, accessoryCardsAlloc);
+ accessoryCard.SetObject();
+ }
+ if (strcmp(d_name, "product-id") == 0) {
+ if (regex_match(fileData, lora15Filters)) {
+ capabilityList["lora"] = true;
+ AccessoryCardLora15 mSPI(fileData, dir_name);
+ mSPI.getFPGAVersion();
+ accessoryCard.AddMember("loraFpgaVersion", mSPI.getFPGAVersion(), accessoryCardsAlloc);
+ } else if (regex_match(fileData, loraG16Filters)) {
+ capabilityList["lora"] = true;
+ std::string fpgaVersion;
+ MTS::System::cmd(LORA_2_1_FPGA_VERSION, fpgaVersion);
+ accessoryCard.AddMember("loraFpgaVersion", std::stoi(fpgaVersion), accessoryCardsAlloc);
+ } else if (regex_match(fileData, loraG64Filters)) {
+ capabilityList["lora"] = true;
+ std::string fpgaVersion;
+ std::string fpgaVersion2;
+ MTS::System::cmd(LORA_2_1_FPGA_VERSION, fpgaVersion);
+ MTS::System::cmd(LORA_2_1_EXT_FPGA_VERSION, fpgaVersion2);
+ accessoryCard.AddMember("loraFpgaVersion", std::stoi(fpgaVersion), accessoryCardsAlloc);
+ accessoryCard.AddMember("loraFpgaVersion2", std::stoi(fpgaVersion2), accessoryCardsAlloc);
+ } else if (regex_match(fileData, gpiobFilters)) {
+ capabilityList["adc"] = true;
+ capabilityList["din"] = true;
+ capabilityList["dout"] = true;
+ capabilityList["gpio"] = true;
+ } else if (regex_match(fileData, mfserFilters)) {
+ capabilityList["rs232"] = true;
+ capabilityList["rs422"] = true;
+ capabilityList["rs485"] = true;
+ capabilityList["serial"] = true;
+ if (!fileExists("/dev/ext_serial") && strlen(dir_name) > 0) {
+ std::string temp;
+ MTS::System::cmd("ln -s /dev/ttyAP" + std::string(dir_name + strlen(dir_name) - 1) + " /dev/ext_serial", temp);
+ }
+ }
+ }
+ accessoryCard.AddMember(rapidjson::Value().SetString(toCamelCase(d_name).c_str(), accessoryCardsAlloc), rapidjson::Value().SetString(fileData.c_str(), accessoryCardsAlloc), accessoryCardsAlloc);
+ } else if (strcmp (dir_name, "capability") == 0 && fileData == "1") {
+ capabilityList[toCamelCase(d_name)] = true;
+ }
+ } else if ((entry->d_type != DT_LNK)){
+ if (deviceInfoList.count(toCamelCase(d_name)) > 0) {
+ deviceInfoList[toCamelCase(d_name)] = fileData;
+ } else if (strcmp(d_name, "hw-version") == 0) {
+ deviceInfoList["hardwareVersion"] = fileData;
+ } else if (strcmp(d_name, "mac-eth") == 0) {
+ deviceInfoList["macAddress"] = fileData;
+ }
+ }
+ }
+ if (entry->d_type & DT_DIR) { /* Check that the directory is not "d" or d's parent. */
+ if (strcmp (d_name, "..") != 0 && strcmp (d_name, ".") != 0) {
+ std::string path = fullPath + "/" + std::string(d_name);
+ if (path.length() >= PATH_MAX) {
+ logError("Path length has got too long.\n");
+ exitHandler(99);
+ }
+ getSystemTreeJson(d_name); /* Recursively call with the new path */
+ }
+ }
+ }
+ if (closedir (d)) { /* After going through all the entries, close the directory. */
+ logError("Could not close " + std::string(fullPath));
+ exitHandler(errno);
+ }
+}
+
+void Device::init() {
+ if (!isRoot) {
+ logError("Must be root to generate device_info.json");
+ exitHandler(99);
+ }
+ load();
+ writeJson();
+ exitHandler(0);
+}
+
+void Device::json() {
+ if (!isRoot) {
+ logError("Must be root to generate json");
+ exitHandler(99);
+ }
+ load();
+ printJson();
+ exitHandler(0);
+}
+
+void Device::load() {
+ deviceInfo.SetObject();
+ capabilities.SetObject();
+ accessoryCards.SetArray();
+ getSystemTreeJson("");
+ if (!accessoryCard.IsNull()) {
+ accessoryCards.PushBack(accessoryCard, accessoryCardsAlloc);
+ }
+ mapFileToCapability();
+ mapFirmware();
+ for (const auto capability : capabilityList) {
+ capabilities.AddMember(rapidjson::Value().SetString(capability.first.c_str(),
+ capability.first.length(), accessoryCardsAlloc), capability.second, accessoryCardsAlloc);
+ }
+ for (const auto device : deviceInfoList) {
+ deviceInfo.AddMember(rapidjson::Value().SetString(device.first.c_str(),
+ device.first.length(), alloc), rapidjson::Value().SetString(device.second.c_str(),
+ device.second.length(), alloc), alloc);
+ }
+
+ deviceInfo.AddMember("capabilities", capabilities, alloc);
+ deviceInfo.AddMember("accessoryCards", accessoryCards, alloc);
+}
+
+void Device::logError(std::string info) {
+ printf("error: %s\n", info.c_str());
+}
+
+void Device::logInfo(std::string info) {
+ if (verbose) {
+ printf("info: %s\n", info.c_str());
+ }
+}
+
+void Device::mapFileToCapability() {
+ if (fileType("/dev/modem_at0") == S_IFLNK && fileType("/dev/modem_at1") == S_IFLNK) {
+ capabilityList["cell"] = true;
+ }
+ if (fileType("/dev/modem_at0") == S_IFLNK) {
+ capabilityList["cell"] = true;
+ }
+ if (fileType("/dev/ext_serial") == S_IFCHR) {
+ capabilityList["externalSerialPort"] = true;
+ }
+ if (fileType("/opt/node-red") == S_IFDIR) {
+ capabilityList["nodeRed"] = true;
+ }
+ if (fileType("/opt/lora/lora-network-server") == S_IFREG) {
+ capabilityList["loraNetworkServer"] = true;
+ }
+}
+
+void Device::mapFirmware() {
+ std::ifstream file(FIRMWARE_FILE);
+ if (!file.is_open()){
+ return;
+ }
+ std::string line;
+ while (std::getline(file, line)){
+ if (line.find(FIRMWARE_VERSION) != std::string::npos){
+ deviceInfoList["firmware"] = line.substr(line.find(" ") + 1);
+ } else if (line.find(FIRMWARE_DATE) != std::string::npos){
+ deviceInfoList["firmwareDate"] = line.substr(line.find(" ") + 1);
+ }
+ }
+}
+
+void Device::printDir(const std::string dir_name, std::vector<std::string> &results) {
+ std::string fullPath = SYSFS_PLATFORM + std::string(dir_name);
+ DIR * d = opendir(fullPath.c_str());
+ if (!d) {
+ logError("Cannot open directory " + std::string(fullPath));
+ exitHandler(99);
+ }
+ while (1) {
+ struct dirent * entry = readdir (d);
+ if (!entry) {
+ break;
+ }
+ const char * d_name = entry->d_name;
+ if (!(entry->d_type & DT_DIR)) {
+ std::string result;
+ if (dir_name.size() > 0) {
+ result = (dir_name + "/");
+ }
+ result.append(d_name);
+ results.push_back(result);
+ }
+ if ((entry->d_type & DT_DIR) && strcmp (d_name, "..") != 0 && strcmp (d_name, ".") != 0) {
+ std::string path = fullPath + "/" + std::string(d_name);
+ if (path.length() >= PATH_MAX) {
+ logError("Path length has got too long.\n");
+ exitHandler(99);
+ }
+ printDir(d_name, results);
+ }
+ }
+ if (closedir (d)) { /* After going through all the entries, close the directory. */
+ logError("Could not close " + std::string(fullPath));
+ exitHandler(errno);
+ }
+}
+
+void Device::printJson() {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ deviceInfo.Accept(writer);
+ std::cout << buffer.GetString();
+}
+
+void Device::printVersion (std::string name) {
+ printf("%s %s\nCopyright (C) 2019 by Multi-Tech Systems\nThis program is free software; you may redistribute it under the terms of\nthe GNU General Public License version 2 or (at your option) any later version.\nThis program has absolutely no warranty.\n",name.c_str(), Version::version.c_str());
+}
+
+void Device::printUsage(std::string program) {
+ std::vector<std::string> showResults;
+ printf("Usage: %s [ OPTIONS ] OBJECT [--] [ ARGUMENTS ]\n", program.c_str());
+ printf("where OBJECT := {\n");
+ printf(" init |\n");
+ printf(" show SHOW-NAME |\n");
+ printf(" store STORE-NAME |\n");
+ printf(" json |\n");
+ printf(" }\n");
+ printf("\n");
+ printf(" SHOW-NAME := {\n");
+ printDir("", showResults);
+ sort(showResults.begin(), showResults.end()); // Unix file tree is not sorted
+
+ for (std::string showResult: showResults) {
+ if (!regex_match(showResult, showFilters))
+ printf (" %s\n", showResult.c_str());
+ }
+ printf(" }\n");
+ printf("\n");
+ printf(" STORE-NAME := {\n");
+ for (std::string showResult: showResults) {
+ if (showResult == "radio-reset") {
+ printf (" %s { 0 }\n", showResult.c_str());
+ } else if (showResult == "reset-monitor") {
+ printf (" %s { pid short-signal long-signal [extra-long-signal] }\n", showResult.c_str());
+ } else if (showResult == "reset-monitor-intervals") {
+ printf (" %s { short-interval long-interval }\n", showResult.c_str());
+ } else if (regex_match(showResult, serialModeFilter)) {
+ printf (" %s { loopback | rs232 | rs485-half | rs422-485-full }\n", showResult.c_str());
+ } else if (!regex_match(showResult, storeFilters)) {
+ printf (" %s BOOLEAN\n", showResult.c_str());
+ }
+ }
+ printf(" OPTIONS := {\n");
+ printf(" --verbose\n");
+ printf(" }\n");
+ printf("\n");
+ printf(" BOOLEAN := { OFF | ON }\n");
+ printf(" OFF := 0\n");
+ printf(" ON := 1\n");
+ printf("\n");
+ exitHandler(1);
+}
+
+void Device::show(std::string name) {
+ std::string fileData;
+ int32_t code = MTS::System::readFile(SYSFS_PLATFORM + name, fileData);
+ if (code == 0) {
+ printf("%s",fileData.c_str());
+ exitHandler(0);
+ } else {
+ logError("cat: can't open " + std::string(SYSFS_PLATFORM) + name + ": No such file or directory");
+ exitHandler(99);
+ }
+
+}
+
+void Device::showTrigger(std::string name) {
+ std::string fileData;
+ int32_t code = MTS::System::readFile(LEDS_GPIO_DIR + name + "/trigger", fileData);
+ if (code == 0) {
+ printf("%s",fileData.c_str());
+ exitHandler(0);
+ } else {
+ logError("can't not open '" + std::string(LEDS_GPIO_DIR) + name + "/trigger': No such file or directory");
+ exitHandler(99);
+ }
+}
+
+void Device::store(std::string name, std::string value) {
+ if (!isRoot) {
+ logError("Must be root to store to " + name);
+ exitHandler(99);
+ }
+ logInfo("setting " + name + " to " + value);
+ std::ofstream fileToWrite(SYSFS_PLATFORM + name);
+ if (!fileToWrite.bad()) {
+ fileToWrite << value;
+ fileToWrite.close();
+ exitHandler(0);
+ } else {
+ logError("can't not open '" + std::string(SYSFS_PLATFORM) + name + "': No such file or directory");
+ exitHandler(99);
+ }
+
+}
+
+void Device::storeTrigger(std::string name, std::string value) {
+ if (!isRoot) {
+ logError("Must be root to storeTrigger to " + name + "/trigger");
+ exitHandler(99);
+ }
+ logInfo("setting " + name + " to " + value);
+ std::ofstream fileToWrite(LEDS_GPIO_DIR + name + "/trigger");
+ if (!fileToWrite.bad()) {
+ fileToWrite << value;
+ fileToWrite.close();
+ exitHandler(0);
+ } else {
+ logError("can't not open '" + std::string(LEDS_GPIO_DIR) + name + "/trigger': No such file or directory");
+ exitHandler(99);
+ }
+
+}
+
+std::string Device::toCamelCase(const char * d_name) {
+ std::string camelString = strdup(d_name);
+ std::string tempString = "";
+ for (size_t x = 0; x < camelString.length(); x++){
+ if (camelString[x] == '-' || camelString[x] == '_'){
+ tempString = camelString.substr(x + 1, 1);
+ transform(tempString.begin(), tempString.end(), tempString.begin(), toupper);
+ camelString.erase(x, 2);
+ camelString.insert(x, tempString);
+ }
+ }
+ return camelString;
+}
+
+void Device::Verbose(bool val) {
+ verbose = val;
+}
+
+bool Device::Verbose() {
+ return verbose;
+}
+
+void Device::writeJson() {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ deviceInfo.Accept(writer);
+ std::string json (buffer.GetString(), buffer.GetSize());
+ std::ofstream of ("/var/run/config/device_info.json");
+ of << json;
+ if (!of.good()) {
+ logError("Can't write to /var/run/config/device_info.json");
+ exitHandler(99);
+ } else {
+ exitHandler(0);
+ }
+}