/* ; Project: Open Vehicle Monitor System ; Date: 14th March 2017 ; ; Changes: ; 1.0 Initial release ; ; (C) 2011 Michael Stegen / Stegen Electronics ; (C) 2011-2017 Mark Webb-Johnson ; (C) 2011 Sonny Chen @ EPRO/DX ; ; Permission is hereby granted, free of charge, to any person obtaining a copy ; of this software and associated documentation files (the "Software"), to deal ; in the Software without restriction, including without limitation the rights ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ; copies of the Software, and to permit persons to whom the Software is ; furnished to do so, subject to the following conditions: ; ; The above copyright notice and this permission notice shall be included in ; all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ; THE SOFTWARE. */ #include #include #include #include #include #include "ovms_utils.h" #include "ovms_config.h" #include "ovms_events.h" #include "metrics_standard.h" #include "ovms_version.h" /** * chargestate_code: convert legacy chargestate key to code */ std::string chargestate_code(const int key) { std::string code; switch (key) { case 1: code = "charging"; break; case 2: code = "topoff"; break; case 4: code = "done"; break; case 13: code = "prepare"; break; case 14: code = "timerwait"; break; case 15: code = "heating"; break; case 21: code = "stopped"; break; default: code = ""; } return code; } /** * chargestate_key: convert chargestate code to legacy key */ int chargestate_key(const std::string code) { int key; if (code == "charging") key = 1; else if (code == "topoff") key = 2; else if (code == "done") key = 4; else if (code == "prepare") key = 13; else if (code == "timerwait") key = 14; else if (code == "heating") key = 15; else if (code == "stopped") key = 21; else key = 0; return key; } /** * chargesubstate_code: convert legacy charge substate key to code */ std::string chargesubstate_code(const int key) { std::string code; switch (key) { case 0x01: code = "scheduledstop"; break; case 0x02: code = "scheduledstart"; break; case 0x03: code = "onrequest"; break; case 0x05: code = "timerwait"; break; case 0x07: code = "powerwait"; break; case 0x0d: code = "stopped"; break; case 0x0e: code = "interrupted"; break; default: code = ""; } return code; } /** * chargesubstate_key: convert charge substate code to legacy key */ int chargesubstate_key(const std::string code) { int key; if (code == "scheduledstop") key = 0x01; else if (code == "scheduledstart") key = 0x02; else if (code == "onrequest") key = 0x03; else if (code == "timerwait") key = 0x05; else if (code == "powerwait") key = 0x07; else if (code == "stopped") key = 0x0d; else if (code == "interrupted") key = 0x0e; else key = 0; return key; } /** * chargemode_code: convert legacy chargemode key to code */ std::string chargemode_code(const int key) { std::string code; switch (key) { case 0: code = "standard"; break; case 1: code = "storage"; break; case 3: code = "range"; break; case 4: code = "performance"; break; default: code = ""; } return code; } /** * chargemode_key: convert chargemode code to legacy key */ int chargemode_key(const std::string code) { int key; if (code == "standard") key = 0; else if (code == "storage") key = 1; else if (code == "range") key = 3; else if (code == "performance") key = 4; else key = -1; return key; } /** * mp_encode: encode string for MP transport; * - replace '\r\n' by '\r' * - replace '\n' by '\r' * - replace ',' by ';' */ std::string mp_encode(const std::string text) { std::string res; char lc = 0; res.reserve(text.length()); for (int i=0; i> 4; // high nibble nibble += '0'; if (nibble>'9') nibble += 39; *p = nibble; p++; nibble = byte & 0x0f; // low nibble nibble += '0'; if (nibble>'9') nibble += 39; *p = nibble; p++; return p; } /** * FormatHexDump: create/fill hexdump buffer including printable representation * Note: allocates buffer as necessary in *bufferp, caller must free. * Returns new remaining length */ size_t FormatHexDump(char** bufferp, const char* data, size_t rlength, size_t colsize /*=16*/) { const char *s = data; if (rlength>0) { if (!*bufferp) *bufferp = (char*) ExternalRamMalloc(colsize*4 + 4); // space for 16x3 + 2 + 16 + 1(\0) char *p = *bufferp; const char *os = s; for (int k=0;k colsize) rlength -= colsize; else rlength = 0; } return rlength; } /** * hexencode: encode a string of bytes into hexadecimal form */ std::string hexencode(const std::string value) { std::string encval; size_t len = value.length(); encval.reserve(len * 2); char buf[3] = {0}; for (size_t i = 0; i < len; ++i) { unsigned char c = value.at(i); HexByte(buf, c); encval += buf; } return encval; } /** * hexdecode: decode a hexadecimal encoded string of bytes * Returns empty string on error */ std::string hexdecode(const std::string encval) { std::string value; size_t len = encval.length(); if (len == 0) return value; if (encval.find_first_not_of("0123456789ABCDEFabcdef", 0) != std::string::npos || (len & 1)) return value; value.reserve(len / 2); char buf[3] = {0}; for (size_t i = 0; i < len; i += 2) { buf[0] = encval.at(i); buf[1] = encval.at(i + 1); unsigned char c = strtoul(buf, NULL, 16); value += c; } return value; } /** * pwgen: simple password generator * Note: take care to seed the pseudo random generator i.e. by * srand48(StdMetrics.ms_m_monotonic->AsInt() * StdMetrics.ms_m_freeram->AsInt()); */ std::string pwgen(int length) { const char cs1[] = "abcdefghijklmnopqrstuvwxyz"; const char cs2[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const char cs3[] = "!@#$%^&*()_-=+;:,.?"; std::string res; for (int i=0; i < length; i++) { double r = drand48(); if (r > 0.4) res.push_back((char)cs1[(int)(drand48()*(sizeof(cs1)-1))]); else if (r > 0.2) res.push_back((char)cs2[(int)(drand48()*(sizeof(cs2)-1))]); else res.push_back((char)cs3[(int)(drand48()*(sizeof(cs3)-1))]); } return res; } #ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY /** * TaskGetHandle: get task handle by name * (FreeRTOS xTaskGetHandle() is not available) */ TaskHandle_t TaskGetHandle(const char *name) { TaskHandle_t res = 0; TaskStatus_t* pxTaskStatusArray; volatile UBaseType_t uxArraySize, x; uxArraySize = uxTaskGetNumberOfTasks(); pxTaskStatusArray = (TaskStatus_t*)pvPortMalloc(uxArraySize * sizeof(TaskStatus_t)); if (pxTaskStatusArray != NULL) { uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, NULL); for (x = 0; x < uxArraySize; x++) { if (strcmp(pxTaskStatusArray[x].pcTaskName, name) == 0) { res = pxTaskStatusArray[x].xHandle; break; } } vPortFree( pxTaskStatusArray ); } return res; } #endif // CONFIG_FREERTOS_USE_TRACE_FACILITY /** * mkpath: mkdir -p * * Original: https://stackoverflow.com/a/12904145 */ int mkpath(std::string path, mode_t mode /*=0*/) { size_t pre = 0, pos; std::string dir; int mdret; if (!endsWith(path, '/')) { // force trailing / so we can handle everything in loop path.append("/"); } while ((pos = path.find_first_of('/', pre)) != std::string::npos) { dir = path.substr(0, pos++); pre = pos; if (dir.size() == 0) continue; // if leading / first time is 0 length if ((mdret = mkdir(dir.c_str(), mode)) && errno != EEXIST) return mdret; } return 0; } /** * rmtree: rmdir -r */ int rmtree(const std::string path) { DIR *dir = opendir(path.c_str()); if (!dir) return 0; struct dirent *dp; struct stat st; std::string sub; bool ok = true; while ((dp = readdir(dir)) != NULL) { sub = path + "/" + dp->d_name; if (stat(sub.c_str(), &st)) { ok = false; break; } if (S_ISDIR(st.st_mode)) ok = (rmtree(sub) == 0); else ok = (unlink(sub.c_str()) == 0); if (!ok) break; } closedir(dir); ok = (rmdir(path.c_str()) == 0); return ok ? 0 : -1; } /** * path_exists: check if filesystem path exists */ bool path_exists(const std::string path) { struct stat st; return (stat(path.c_str(), &st) == 0); } /** * load file into string: * - return value: 0 = ok / errno */ int load_file(const std::string &path, extram::string &content) { std::ifstream file(path, std::ios::in | std::ios::binary | std::ios::ate); if (file.is_open()) { auto size = file.tellg(); if (size > 0) { content.resize(size, '\0'); file.seekg(0); file.read(&content[0], size); } } if (file.fail()) return errno; else return 0; } /** * save file from string: * - creates missing directories automatically & signals system.vfs.file.changed * - return value: 0 = ok / errno */ int save_file(const std::string &path, extram::string &content) { // create path: size_t n = path.rfind('/'); if (n != 0 && n != std::string::npos) { std::string dir = path.substr(0, n); if (!path_exists(dir)) { if (mkpath(dir) != 0) return errno; } } // write file: std::ofstream file(path, std::ios::out | std::ios::binary | std::ios::trunc); if (file.is_open()) file.write(content.data(), content.size()); if (file.fail()) { return errno; } else { MyEvents.SignalEvent("system.vfs.file.changed", (void*)path.c_str(), path.size()+1); return 0; } } /** * mqtt_topic: convert dotted string (e.g. notification subtype) to MQTT topic * - replace '.' by '/' */ std::string mqtt_topic(const std::string text) { std::string buf; buf.reserve(text.size()); for (int i=0; i ( )" */ std::string get_user_agent() { std::string ua; ua = "ovms/"; ua.append(GetOVMSProduct()); ua.append(" ("); ua.append(MyConfig.GetParamValue("vehicle","id","")); ua.append(" "); ua.append(StandardMetrics.ms_m_version->AsString()); ua.append(")"); return ua; } /** * float2double: minimize precision errors on float→double conversion * Casting a float to double sets the additional precision bits to 0, resulting in * the double to have a significant offset from the rounded float; e.g. * 11.08 becomes 11.079999923706055. * (Is there a better implementation for this than sprintf/atof?) */ double float2double(float f) { char buf[16]; snprintf(buf, sizeof buf, "%g", f); return atof(buf); } /** * idtag: create object instance tag for registrations */ std::string idtag(const char* tag, void* instance) { std::ostringstream buf; buf << tag << "-" << instance; std::string res = buf.str(); return res; }