diff --git a/.github/workflows/divert_sim.yaml b/.github/workflows/divert_sim.yaml new file mode 100644 index 00000000..adaec9f4 --- /dev/null +++ b/.github/workflows/divert_sim.yaml @@ -0,0 +1,69 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Build OpenEVSE divert simulator + +permissions: + contents: write + +on: + push: + branches: + - master + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + path: OpenEVSE_WiFi + + - uses: actions/checkout@v3 + with: + repository: JeremyPoulter/MicroDebug + path: MicroDebug + + - uses: actions/checkout@v3 + with: + repository: JeremyPoulter/MicroTasks + path: MicroTasks + + - uses: actions/checkout@v3 + with: + repository: JeremyPoulter/StreamSpy + path: StreamSpy + + - uses: actions/checkout@v3 + with: + repository: JeremyPoulter/ConfigJson + path: ConfigJson + + - uses: actions/checkout@v3 + with: + repository: bblanchon/ArduinoJson + path: ArduinoJson + ref: v6.20.1 + + - uses: actions/checkout@v3 + with: + repository: JeremyPoulter/OpenEVSE_Lib + path: OpenEVSE_Lib + + - uses: actions/checkout@v3 + with: + repository: JeremyPoulter/EpoxyDuino + path: EpoxyDuino + + - uses: actions/checkout@v3 + with: + repository: JeremyPoulter/ESPAL + path: ESPAL + + - uses: ammaraskar/gcc-problem-matcher@master + + - name: Build the simulator + run: | + cd OpenEVSE_WiFi/divert_sim + make -j diff --git a/.gitignore b/.gitignore index f559f3e0..5ea8bf96 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,5 @@ lib/StreamSpy lib/MicroTasks *.bin +divert_sim/epoxyfsdata +divert_sim/epoxyeepromdata diff --git a/divert_sim/.vscode/launch.json b/divert_sim/.vscode/launch.json index faf32b14..713b444c 100644 --- a/divert_sim/.vscode/launch.json +++ b/divert_sim/.vscode/launch.json @@ -10,6 +10,7 @@ "request": "launch", "program": "${workspaceFolder}/divert_sim", "args": [ + "-c", "data/test_config.json", "<", "${workspaceFolder}/data/day1.csv" ], "stopAtEntry": false, @@ -100,4 +101,4 @@ "miDebuggerPath": "/usr/bin/gdb" } ] -} \ No newline at end of file +} diff --git a/divert_sim/EEPROM.h b/divert_sim/EEPROM.h new file mode 100644 index 00000000..4277c80f --- /dev/null +++ b/divert_sim/EEPROM.h @@ -0,0 +1,2 @@ +#include +#define EEPROM EpoxyEepromEspInstance diff --git a/divert_sim/FakeDuino/Arduino.h b/divert_sim/FakeDuino/Arduino.h index 35b1cbba..9265262f 100644 --- a/divert_sim/FakeDuino/Arduino.h +++ b/divert_sim/FakeDuino/Arduino.h @@ -36,4 +36,7 @@ extern void setup(void); extern void loop(void); +#define interrupts() +#define noInterrupts() + #endif \ No newline at end of file diff --git a/divert_sim/FakeDuino/FS.cpp b/divert_sim/FakeDuino/FS.cpp new file mode 100644 index 00000000..8d89b1c1 --- /dev/null +++ b/divert_sim/FakeDuino/FS.cpp @@ -0,0 +1,303 @@ +/* + FS.cpp - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "FS.h" +#include "FSImpl.h" + +using namespace fs; + +size_t File::write(uint8_t c) +{ + if (!*this) { + return 0; + } + + return _p->write(&c, 1); +} + +time_t File::getLastWrite() +{ + if (!*this) { + return 0; + } + + return _p->getLastWrite(); +} + +size_t File::write(const uint8_t *buf, size_t size) +{ + if (!*this) { + return 0; + } + + return _p->write(buf, size); +} + +int File::available() +{ + if (!*this) { + return false; + } + + return _p->size() - _p->position(); +} + +int File::read() +{ + if (!*this) { + return -1; + } + + uint8_t result; + if (_p->read(&result, 1) != 1) { + return -1; + } + + return result; +} + +size_t File::read(uint8_t* buf, size_t size) +{ + if (!*this) { + return -1; + } + + return _p->read(buf, size); +} + +int File::peek() +{ + if (!*this) { + return -1; + } + + size_t curPos = _p->position(); + int result = read(); + seek(curPos, SeekSet); + return result; +} + +void File::flush() +{ + if (!*this) { + return; + } + + _p->flush(); +} + +bool File::seek(uint32_t pos, SeekMode mode) +{ + if (!*this) { + return false; + } + + return _p->seek(pos, mode); +} + +size_t File::position() const +{ + if (!*this) { + return 0; + } + + return _p->position(); +} + +size_t File::size() const +{ + if (!*this) { + return 0; + } + + return _p->size(); +} + +bool File::setBufferSize(size_t size) +{ + if (!*this) { + return 0; + } + + return _p->setBufferSize(size); +} + +void File::close() +{ + if (_p) { + _p->close(); + _p = nullptr; + } +} + +File::operator bool() const +{ + return _p != nullptr && *_p != false; +} + +const char* File::path() const +{ + if (!*this) { + return nullptr; + } + + return _p->path(); +} + +const char* File::name() const +{ + if (!*this) { + return nullptr; + } + + return _p->name(); +} + +//to implement +boolean File::isDirectory(void) +{ + if (!*this) { + return false; + } + return _p->isDirectory(); +} + +File File::openNextFile(const char* mode) +{ + if (!*this) { + return File(); + } + return _p->openNextFile(mode); +} + +boolean File::seekDir(long position){ + if(!_p){ + return false; + } + return _p->seekDir(position); +} + +String File::getNextFileName(void) +{ + if (!_p) { + return ""; + } + return _p->getNextFileName(); + +} + +void File::rewindDirectory(void) +{ + if (!*this) { + return; + } + _p->rewindDirectory(); +} + +File FS::open(const String& path, const char* mode, const bool create) +{ + return open(path.c_str(), mode, create); +} + +File FS::open(const char* path, const char* mode, const bool create) +{ + if (!_impl) { + return File(); + } + + return File(_impl->open(path, mode, create)); +} + +bool FS::exists(const char* path) +{ + if (!_impl) { + return false; + } + return _impl->exists(path); +} + +bool FS::exists(const String& path) +{ + return exists(path.c_str()); +} + +bool FS::remove(const char* path) +{ + if (!_impl) { + return false; + } + return _impl->remove(path); +} + +bool FS::remove(const String& path) +{ + return remove(path.c_str()); +} + +bool FS::rename(const char* pathFrom, const char* pathTo) +{ + if (!_impl) { + return false; + } + return _impl->rename(pathFrom, pathTo); +} + +bool FS::rename(const String& pathFrom, const String& pathTo) +{ + return rename(pathFrom.c_str(), pathTo.c_str()); +} + + +bool FS::mkdir(const char *path) +{ + if (!_impl) { + return false; + } + return _impl->mkdir(path); +} + +bool FS::mkdir(const String &path) +{ + return mkdir(path.c_str()); +} + +bool FS::rmdir(const char *path) +{ + if (!_impl) { + return false; + } + return _impl->rmdir(path); +} + +bool FS::rmdir(const String &path) +{ + return rmdir(path.c_str()); +} + + +void FSImpl::mountpoint(const char * mp) +{ + _mountpoint = mp; +} + +const char * FSImpl::mountpoint() +{ + return _mountpoint; +} diff --git a/divert_sim/FakeDuino/FS.h b/divert_sim/FakeDuino/FS.h new file mode 100644 index 00000000..95e6f265 --- /dev/null +++ b/divert_sim/FakeDuino/FS.h @@ -0,0 +1,129 @@ +/* + FS.h - file system wrapper + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FS_H +#define FS_H + +#include +#include + +namespace fs +{ + +#define FILE_READ "r" +#define FILE_WRITE "w" +#define FILE_APPEND "a" + +class File; + +class FileImpl; +typedef std::shared_ptr FileImplPtr; +class FSImpl; +typedef std::shared_ptr FSImplPtr; + +enum SeekMode { + SeekSet = 0, + SeekCur = 1, + SeekEnd = 2 +}; + +class File : public Stream +{ +public: + File(FileImplPtr p = FileImplPtr()) : _p(p) { + _timeout = 0; + } + + size_t write(uint8_t) override; + size_t write(const uint8_t *buf, size_t size) override; + int available() override; + int read() override; + int peek() override; + void flush() override; + size_t read(uint8_t* buf, size_t size); + size_t readBytes(char *buffer, size_t length) + { + return read((uint8_t*)buffer, length); + } + + bool seek(uint32_t pos, SeekMode mode); + bool seek(uint32_t pos) + { + return seek(pos, SeekSet); + } + size_t position() const; + size_t size() const; + bool setBufferSize(size_t size); + void close(); + operator bool() const; + time_t getLastWrite(); + const char* path() const; + const char* name() const; + + boolean isDirectory(void); + boolean seekDir(long position); + File openNextFile(const char* mode = FILE_READ); + String getNextFileName(void); + void rewindDirectory(void); + +protected: + FileImplPtr _p; +}; + +class FS +{ +public: + FS(FSImplPtr impl) : _impl(impl) { } + + File open(const char* path, const char* mode = FILE_READ, const bool create = false); + File open(const String& path, const char* mode = FILE_READ, const bool create = false); + + bool exists(const char* path); + bool exists(const String& path); + + bool remove(const char* path); + bool remove(const String& path); + + bool rename(const char* pathFrom, const char* pathTo); + bool rename(const String& pathFrom, const String& pathTo); + + bool mkdir(const char *path); + bool mkdir(const String &path); + + bool rmdir(const char *path); + bool rmdir(const String &path); + + +protected: + FSImplPtr _impl; +}; + +} // namespace fs + +#ifndef FS_NO_GLOBALS +using fs::FS; +using fs::File; +using fs::SeekMode; +using fs::SeekSet; +using fs::SeekCur; +using fs::SeekEnd; +#endif //FS_NO_GLOBALS + +#endif //FS_H diff --git a/divert_sim/FakeDuino/FSImpl.h b/divert_sim/FakeDuino/FSImpl.h new file mode 100644 index 00000000..263ab191 --- /dev/null +++ b/divert_sim/FakeDuino/FSImpl.h @@ -0,0 +1,71 @@ +/* + FSImpl.h - base file system interface + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef FSIMPL_H +#define FSIMPL_H + +#include +#include + +namespace fs +{ + +class FileImpl +{ +public: + virtual ~FileImpl() { } + virtual size_t write(const uint8_t *buf, size_t size) = 0; + virtual size_t read(uint8_t* buf, size_t size) = 0; + virtual void flush() = 0; + virtual bool seek(uint32_t pos, SeekMode mode) = 0; + virtual size_t position() const = 0; + virtual size_t size() const = 0; + virtual bool setBufferSize(size_t size) = 0; + virtual void close() = 0; + virtual time_t getLastWrite() = 0; + virtual const char* path() const = 0; + virtual const char* name() const = 0; + virtual boolean isDirectory(void) = 0; + virtual FileImplPtr openNextFile(const char* mode) = 0; + virtual boolean seekDir(long position); + virtual String getNextFileName(void); + virtual void rewindDirectory(void) = 0; + virtual operator bool() = 0; +}; + +class FSImpl +{ +protected: + const char * _mountpoint; +public: + FSImpl() : _mountpoint(NULL) { } + virtual ~FSImpl() { } + virtual FileImplPtr open(const char* path, const char* mode, const bool create) = 0; + virtual bool exists(const char* path) = 0; + virtual bool rename(const char* pathFrom, const char* pathTo) = 0; + virtual bool remove(const char* path) = 0; + virtual bool mkdir(const char *path) = 0; + virtual bool rmdir(const char *path) = 0; + void mountpoint(const char *); + const char * mountpoint(); +}; + +} // namespace fs + +#endif //FSIMPL_H diff --git a/divert_sim/FakeDuino/LittleFS.cpp b/divert_sim/FakeDuino/LittleFS.cpp new file mode 100644 index 00000000..2755f601 --- /dev/null +++ b/divert_sim/FakeDuino/LittleFS.cpp @@ -0,0 +1,56 @@ +#include "vfs_api.h" + +#include + +using namespace fs; + +class LittleFSImpl : public VFSImpl +{ +public: + LittleFSImpl(); + virtual ~LittleFSImpl() { } + virtual bool exists(const char* path); +}; + +LittleFSImpl::LittleFSImpl() +{ +} + +bool LittleFSImpl::exists(const char* path) +{ + File f = open(path, "r",false); + return (f == true); +} + + +LittleFSFS::LittleFSFS() : FS(FSImplPtr(new LittleFSImpl())) +{ + +} + +LittleFSFS::~LittleFSFS() +{ + +} + +bool LittleFSFS::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFiles, const char * partitionLabel) +{ + return true; +} + +void LittleFSFS::end() +{ + +} + +bool LittleFSFS::format() +{ + return true; +} + +size_t LittleFSFS::usedBytes() +{ + return 0; +} + +LittleFSFS LittleFS; diff --git a/divert_sim/FakeDuino/LittleFS.h b/divert_sim/FakeDuino/LittleFS.h new file mode 100644 index 00000000..5d714aaf --- /dev/null +++ b/divert_sim/FakeDuino/LittleFS.h @@ -0,0 +1,38 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _LITTLEFS_H_ +#define _LITTLEFS_H_ + +#include "FS.h" + +namespace fs +{ + +class LittleFSFS : public FS +{ +public: + LittleFSFS(); + ~LittleFSFS(); + bool begin(bool formatOnFail=false, const char * basePath="/littlefs", uint8_t maxOpenFiles=10, const char * partitionLabel="spiffs"); + bool format(); + size_t totalBytes(); + size_t usedBytes(); + void end(); +}; + +} + +extern fs::LittleFSFS LittleFS; + +#endif diff --git a/divert_sim/FakeDuino/WProgram.h b/divert_sim/FakeDuino/WProgram.h new file mode 100644 index 00000000..c071f332 --- /dev/null +++ b/divert_sim/FakeDuino/WProgram.h @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/divert_sim/FakeDuino/WString.cpp b/divert_sim/FakeDuino/WString.cpp index 263edfa7..b02bb7b0 100644 --- a/divert_sim/FakeDuino/WString.cpp +++ b/divert_sim/FakeDuino/WString.cpp @@ -26,7 +26,7 @@ /*********************************************/ char *dtostrf (double val, signed char width, unsigned char prec, char *sout) { - //asm(".global _printf_float"); // Commented out, not available with emscripten. + //asm(".global _printf_float"); // Commented out, not available with emscripten. char fmt[20]; sprintf(fmt, "%%%d.%df", width, prec); @@ -309,10 +309,10 @@ void String::move(String &rhs) String & String::operator = (const String &rhs) { if (this == &rhs) return *this; - + if (rhs.buffer) copy(rhs.buffer, rhs.len); else invalidate(); - + return *this; } @@ -334,7 +334,7 @@ String & String::operator = (const char *cstr) { if (cstr) copy(cstr, strlen(cstr)); else invalidate(); - + return *this; } @@ -551,7 +551,7 @@ unsigned char String::equalsIgnoreCase( const String &s2 ) const const char *p2 = s2.buffer; while (*p1) { if (tolower(*p1++) != tolower(*p2++)) return 0; - } + } return 1; } @@ -582,7 +582,7 @@ char String::charAt(unsigned int loc) const return operator[](loc); } -void String::setCharAt(unsigned int loc, char c) +void String::setCharAt(unsigned int loc, char c) { if (loc < len) buffer[loc] = c; } @@ -691,7 +691,7 @@ String String::substring(unsigned int left, unsigned int right) const if (left >= len) return out; if (right > len) right = len; char temp = buffer[right]; // save the replaced character - buffer[right] = '\0'; + buffer[right] = '\0'; out = buffer + left; // pointer arithmetic buffer[right] = temp; //restore character return out; @@ -811,4 +811,36 @@ float String::toFloat(void) const { if (buffer) return float(atof(buffer)); return 0; -} \ No newline at end of file +} + +size_t String::write(unsigned char c) +{ + return write((const unsigned char *) &c, 1); +} + +size_t String::write(const unsigned char* s, size_t n) +{ + if (n == 0) return 0; + if (len + n > capacity && !changeBuffer(len + n)) return 0; + memcpy(buffer + len, s, n); + len += n; + buffer[len] = 0; + return n; +} + +int String::read() +{ + if (pos >= len) return -1; + return buffer[pos++]; +} + +size_t String::readBytes(char* buffer, size_t length) +{ + if (pos >= len) return 0; + size_t toRead = length; + if (pos + length > len) toRead = len - pos; + memcpy(buffer, this->buffer + pos, toRead); + pos += toRead; + return toRead; +} + diff --git a/divert_sim/FakeDuino/WString.h b/divert_sim/FakeDuino/WString.h index 134ae118..a559e0eb 100644 --- a/divert_sim/FakeDuino/WString.h +++ b/divert_sim/FakeDuino/WString.h @@ -80,7 +80,7 @@ class String inline unsigned int length(void) const {return len;} // creates a copy of the assigned value. if the value is null or - // invalid, or if the memory allocation fails, the string will be + // invalid, or if the memory allocation fails, the string will be // marked as invalid ("if (s)" will be false). String & operator = (const String &rhs); String & operator = (const char *cstr); @@ -90,10 +90,10 @@ class String #endif // concatenate (works w/ built-in types) - + // returns true on success, false on failure (in which case, the string - // is left unchanged). if the argument is null or invalid, the - // concatenation is considered unsucessful. + // is left unchanged). if the argument is null or invalid, the + // concatenation is considered unsucessful. unsigned char concat(const String &str); unsigned char concat(const char *cstr); unsigned char concat(char c); @@ -105,7 +105,7 @@ class String unsigned char concat(float num); unsigned char concat(double num); unsigned char concat(const __FlashStringHelper * str); - + // if there's not enough memory for the concatenated value, the string // will be left unchanged (but this isn't signalled in any way) String & operator += (const String &rhs) {concat(rhs); return (*this);} @@ -184,10 +184,16 @@ class String long toInt(void) const; float toFloat(void) const; + size_t write(unsigned char c); + size_t write(const unsigned char* s, size_t n); + int read(); + size_t readBytes(char* buffer, size_t length); + protected: char *buffer; // the actual char array unsigned int capacity; // the array length minus one (for the '\0') unsigned int len; // the String length (not counting the '\0') + unsigned int pos; // the current position for read/write protected: void init(void); void invalidate(void); diff --git a/divert_sim/FakeDuino/vfs_api.cpp b/divert_sim/FakeDuino/vfs_api.cpp new file mode 100644 index 00000000..ef2dd2db --- /dev/null +++ b/divert_sim/FakeDuino/vfs_api.cpp @@ -0,0 +1,570 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "vfs_api.h" + +#include + +using namespace fs; + +#define DEFAULT_FILE_BUFFER_SIZE 4096 + +#define log_e printf + + +const char * pathToFileName(const char * path) +{ + size_t i = 0; + size_t pos = 0; + char * p = (char *)path; + while(*p){ + i++; + if(*p == '/' || *p == '\\'){ + pos = i; + } + p++; + } + return path+pos; +} + + +FileImplPtr VFSImpl::open(const char* fpath, const char* mode, const bool create) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return FileImplPtr(); + } + + if(!fpath || fpath[0] != '/') { + log_e("%s does not start with /", fpath); + return FileImplPtr(); + } + + char * temp = (char *)malloc(strlen(fpath)+strlen(_mountpoint)+2); + if(!temp) { + log_e("malloc failed"); + return FileImplPtr(); + } + + strcpy(temp, _mountpoint); + strcat(temp, fpath); + + struct stat st; + //file found + if(!stat(temp, &st)) { + free(temp); + if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) { + return std::make_shared(this, fpath, mode); + } + log_e("%s has wrong mode 0x%08X", fpath, st.st_mode); + return FileImplPtr(); + } + + //try to open this as directory (might be mount point) + DIR * d = opendir(temp); + if(d) { + closedir(d); + free(temp); + return std::make_shared(this, fpath, mode); + } + + //file not found but mode permits file creation without folder creation + if((mode && mode[0] != 'r') && (!create)){ + free(temp); + return std::make_shared(this, fpath, mode); + } + + ////file not found but mode permits file creation and folder creation + if((mode && mode[0] != 'r') && create){ + + char *token; + char *folder = (char *)malloc(strlen(fpath)); + + int start_index = 0; + int end_index = 0; + + token = strchr((char *)fpath+1,'/'); + end_index = (token-fpath); + + while (token != NULL) + { + memcpy(folder,fpath + start_index, end_index-start_index); + folder[end_index-start_index] = '\0'; + + if(!VFSImpl::mkdir(folder)) + { + log_e("Creating folder: %s failed!",folder); + return FileImplPtr(); + } + + token=strchr(token+1,'/'); + if(token != NULL) + { + end_index = (token-fpath); + memset(folder, 0, strlen(folder)); + } + + } + + free(folder); + free(temp); + return std::make_shared(this, fpath, mode); + + } + + log_e("%s does not exist, no permits for creation", temp); + free(temp); + return FileImplPtr(); +} + +bool VFSImpl::exists(const char* fpath) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + VFSFileImpl f(this, fpath, "r"); + if(f) { + f.close(); + return true; + } + return false; +} + +bool VFSImpl::rename(const char* pathFrom, const char* pathTo) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + if(!pathFrom || pathFrom[0] != '/' || !pathTo || pathTo[0] != '/') { + log_e("bad arguments"); + return false; + } + if(!exists(pathFrom)) { + log_e("%s does not exists", pathFrom); + return false; + } + size_t mountpointLen = strlen(_mountpoint); + char * temp1 = (char *)malloc(strlen(pathFrom)+mountpointLen+1); + if(!temp1) { + log_e("malloc failed"); + return false; + } + char * temp2 = (char *)malloc(strlen(pathTo)+mountpointLen+1); + if(!temp2) { + free(temp1); + log_e("malloc failed"); + return false; + } + + strcpy(temp1, _mountpoint); + strcat(temp1, pathFrom); + + strcpy(temp2, _mountpoint); + strcat(temp2, pathTo); + + auto rc = ::rename(temp1, temp2); + free(temp1); + free(temp2); + return rc == 0; +} + +bool VFSImpl::remove(const char* fpath) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + if(!fpath || fpath[0] != '/') { + log_e("bad arguments"); + return false; + } + + VFSFileImpl f(this, fpath, "r"); + if(!f || f.isDirectory()) { + if(f) { + f.close(); + } + log_e("%s does not exists or is directory", fpath); + return false; + } + f.close(); + + char * temp = (char *)malloc(strlen(fpath)+strlen(_mountpoint)+1); + if(!temp) { + log_e("malloc failed"); + return false; + } + + strcpy(temp, _mountpoint); + strcat(temp, fpath); + + auto rc = unlink(temp); + free(temp); + return rc == 0; +} + +bool VFSImpl::mkdir(const char *fpath) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + VFSFileImpl f(this, fpath, "r"); + if(f && f.isDirectory()) { + f.close(); + //log_w("%s already exists", fpath); + return true; + } else if(f) { + f.close(); + log_e("%s is a file", fpath); + return false; + } + + char * temp = (char *)malloc(strlen(fpath)+strlen(_mountpoint)+1); + if(!temp) { + log_e("malloc failed"); + return false; + } + + strcpy(temp, _mountpoint); + strcat(temp, fpath); + + auto rc = ::mkdir(temp, ACCESSPERMS); + free(temp); + return rc == 0; +} + +bool VFSImpl::rmdir(const char *fpath) +{ + if(!_mountpoint) { + log_e("File system is not mounted"); + return false; + } + + if (strcmp(_mountpoint, "/spiffs") == 0) { + log_e("rmdir is unnecessary in SPIFFS"); + return false; + } + + VFSFileImpl f(this, fpath, "r"); + if(!f || !f.isDirectory()) { + if(f) { + f.close(); + } + log_e("%s does not exists or is a file", fpath); + return false; + } + f.close(); + + char * temp = (char *)malloc(strlen(fpath)+strlen(_mountpoint)+1); + if(!temp) { + log_e("malloc failed"); + return false; + } + + strcpy(temp, _mountpoint); + strcat(temp, fpath); + + auto rc = ::rmdir(temp); + free(temp); + return rc == 0; +} + + + + +VFSFileImpl::VFSFileImpl(VFSImpl* fs, const char* fpath, const char* mode) + : _fs(fs) + , _f(NULL) + , _d(NULL) + , _path(NULL) + , _isDirectory(false) + , _written(false) +{ + char * temp = (char *)malloc(strlen(fpath)+strlen(_fs->_mountpoint)+1); + if(!temp) { + return; + } + + strcpy(temp, _fs->_mountpoint); + strcat(temp, fpath); + + _path = strdup(fpath); + if(!_path) { + log_e("strdup(%s) failed", fpath); + free(temp); + return; + } + + if(!stat(temp, &_stat)) { + //file found + if (S_ISREG(_stat.st_mode)) { + _isDirectory = false; + _f = fopen(temp, mode); + if(!_f) { + log_e("fopen(%s) failed", temp); + } + if(_f && (_stat.st_blksize == 0)) + { + setvbuf(_f,NULL,_IOFBF,DEFAULT_FILE_BUFFER_SIZE); + } + } else if(S_ISDIR(_stat.st_mode)) { + _isDirectory = true; + _d = opendir(temp); + if(!_d) { + log_e("opendir(%s) failed", temp); + } + } else { + log_e("Unknown type 0x%08X for file %s", _stat.st_mode, temp); + } + } else { + //file not found + if(!mode || mode[0] == 'r') { + //try to open as directory + _d = opendir(temp); + if(_d) { + _isDirectory = true; + } else { + _isDirectory = false; + //log_w("stat(%s) failed", temp); + } + } else { + //lets create this new file + _isDirectory = false; + _f = fopen(temp, mode); + if(!_f) { + log_e("fopen(%s) failed", temp); + } + if(_f && (_stat.st_blksize == 0)) + { + setvbuf(_f,NULL,_IOFBF,DEFAULT_FILE_BUFFER_SIZE); + } + } + } + free(temp); +} + +VFSFileImpl::~VFSFileImpl() +{ + close(); +} + +void VFSFileImpl::close() +{ + if(_path) { + free(_path); + _path = NULL; + } + if(_isDirectory && _d) { + closedir(_d); + _d = NULL; + _isDirectory = false; + } else if(_f) { + fclose(_f); + _f = NULL; + } +} + +VFSFileImpl::operator bool() +{ + return (_isDirectory && _d != NULL) || _f != NULL; +} + +time_t VFSFileImpl::getLastWrite() { + _getStat() ; + return _stat.st_mtime; +} + +void VFSFileImpl::_getStat() const +{ + if(!_path) { + return; + } + char * temp = (char *)malloc(strlen(_path)+strlen(_fs->_mountpoint)+1); + if(!temp) { + return; + } + + strcpy(temp, _fs->_mountpoint); + strcat(temp, _path); + + if(!stat(temp, &_stat)) { + _written = false; + } + free(temp); +} + +size_t VFSFileImpl::write(const uint8_t *buf, size_t size) +{ + if(_isDirectory || !_f || !buf || !size) { + return 0; + } + _written = true; + return fwrite(buf, 1, size, _f); +} + +size_t VFSFileImpl::read(uint8_t* buf, size_t size) +{ + if(_isDirectory || !_f || !buf || !size) { + return 0; + } + + return fread(buf, 1, size, _f); +} + +void VFSFileImpl::flush() +{ + if(_isDirectory || !_f) { + return; + } + fflush(_f); + // workaround for https://github.com/espressif/arduino-esp32/issues/1293 + fsync(fileno(_f)); +} + +bool VFSFileImpl::seek(uint32_t pos, SeekMode mode) +{ + if(_isDirectory || !_f) { + return false; + } + auto rc = fseek(_f, pos, mode); + return rc == 0; +} + +size_t VFSFileImpl::position() const +{ + if(_isDirectory || !_f) { + return 0; + } + return ftell(_f); +} + +size_t VFSFileImpl::size() const +{ + if(_isDirectory || !_f) { + return 0; + } + if (_written) { + _getStat(); + } + return _stat.st_size; +} + +/* +* Change size of files internal buffer used for read / write operations. +* Need to be called right after opening file before any other operation! +*/ +bool VFSFileImpl::setBufferSize(size_t size) +{ + if(_isDirectory || !_f) { + return 0; + } + int res = setvbuf(_f,NULL,_IOFBF,size); + return res == 0; +} + +const char* VFSFileImpl::path() const +{ + return (const char*) _path; +} + +const char* VFSFileImpl::name() const +{ + return pathToFileName(path()); +} + +//to implement +boolean VFSFileImpl::isDirectory(void) +{ + return _isDirectory; +} + +FileImplPtr VFSFileImpl::openNextFile(const char* mode) +{ + if(!_isDirectory || !_d) { + return FileImplPtr(); + } + struct dirent *file = readdir(_d); + if(file == NULL) { + return FileImplPtr(); + } + if(file->d_type != DT_REG && file->d_type != DT_DIR) { + return openNextFile(mode); + } + + size_t pathLen = strlen(_path); + size_t fileNameLen = strlen(file->d_name); + char * name = (char *)malloc(pathLen+fileNameLen+2); + + if(name == NULL) { + return FileImplPtr(); + } + + strcpy(name, _path); + + if ((file->d_name[0] != '/') && (_path[pathLen - 1] != '/')) + { + strcat(name, "/"); + } + + strcat(name, file->d_name); + + FileImplPtr fileImplPtr = std::make_shared(_fs, name, mode); + free(name); + return fileImplPtr; +} + +boolean VFSFileImpl::seekDir(long position){ + if(!_d){ + return false; + } + seekdir(_d, position); + return true; +} + + +String VFSFileImpl::getNextFileName() +{ + if (!_isDirectory || !_d) { + return ""; + } + struct dirent *file = readdir(_d); + if (file == NULL) { + return ""; + } + if (file->d_type != DT_REG && file->d_type != DT_DIR) { + return ""; + } + String fname = String(file->d_name); + String name = String(_path); + if (!fname.startsWith("/") && !name.endsWith("/")) { + name += "/"; + } + name += fname; + return name; +} + +void VFSFileImpl::rewindDirectory(void) +{ + if(!_isDirectory || !_d) { + return; + } + rewinddir(_d); +} diff --git a/divert_sim/FakeDuino/vfs_api.h b/divert_sim/FakeDuino/vfs_api.h new file mode 100644 index 00000000..fe475bd3 --- /dev/null +++ b/divert_sim/FakeDuino/vfs_api.h @@ -0,0 +1,81 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef vfs_api_h +#define vfs_api_h + +#include "FS.h" +#include "FSImpl.h" + +extern "C" { +#include +#include +#include +} + +using namespace fs; + +class VFSFileImpl; + +class VFSImpl : public FSImpl +{ + +protected: + friend class VFSFileImpl; + +public: + FileImplPtr open(const char* path, const char* mode, const bool create) override; + bool exists(const char* path) override; + bool rename(const char* pathFrom, const char* pathTo) override; + bool remove(const char* path) override; + bool mkdir(const char *path) override; + bool rmdir(const char *path) override; +}; + +class VFSFileImpl : public FileImpl +{ +protected: + VFSImpl* _fs; + FILE * _f; + DIR * _d; + char * _path; + bool _isDirectory; + mutable struct stat _stat; + mutable bool _written; + + void _getStat() const; + +public: + VFSFileImpl(VFSImpl* fs, const char* path, const char* mode); + ~VFSFileImpl() override; + size_t write(const uint8_t *buf, size_t size) override; + size_t read(uint8_t* buf, size_t size) override; + void flush() override; + bool seek(uint32_t pos, SeekMode mode) override; + size_t position() const override; + size_t size() const override; + bool setBufferSize(size_t size); + void close() override; + const char* path() const override; + const char* name() const override; + time_t getLastWrite() override; + boolean isDirectory(void) override; + boolean seekDir(long position) override; + String getNextFileName(void) override; + FileImplPtr openNextFile(const char* mode) override; + void rewindDirectory(void) override; + operator bool(); +}; + +#endif diff --git a/divert_sim/LittleFS.h b/divert_sim/LittleFS.h new file mode 100644 index 00000000..6c773c98 --- /dev/null +++ b/divert_sim/LittleFS.h @@ -0,0 +1,2 @@ +#include "EpoxyFS.h" +#define LittleFS fs::EpoxyFS diff --git a/divert_sim/Makefile b/divert_sim/Makefile index 0621465e..59533a02 100644 --- a/divert_sim/Makefile +++ b/divert_sim/Makefile @@ -1,18 +1,128 @@ CPP := g++ -BASE_ENV = openevse_huzzah32_idf -CPPFLAGS := -I. -I FakeDuino -I ../src -I../.pio/libdeps/$(BASE_ENV)/ArduinoJson/src -I../.pio/libdeps/$(BASE_ENV)/OpenEVSE/src -ggdb -DDIVERT_SIM -DRAPI_PORT=Console -#-DENABLE_DEBUG -DDEBUG_PORT=Console +EPOXY_DUINO_DIR := ../../EpoxyDuino +EPOXY_CORE_PATH ?= $(EPOXY_DUINO_DIR)/cores/epoxy +EPOXY_LIB_DIR ?= $(EPOXY_DUINO_DIR)/libraries + +#BASE_ENV = openevse_wifi_v1 +#ARDUINO_LIB_DIR := ../.pio/libdeps/$(BASE_ENV) +ARDUINO_LIB_DIR := ../.. + +CPPFLAGS := \ + -I . \ + -I ../include \ + -I ../src \ + -I $(EPOXY_CORE_PATH) \ + -I $(ARDUINO_LIB_DIR)/MicroDebug/src \ + -I $(ARDUINO_LIB_DIR)/MicroTasks/include \ + -I $(ARDUINO_LIB_DIR)/StreamSpy/src \ + -I $(ARDUINO_LIB_DIR)/ConfigJson/src \ + -I $(ARDUINO_LIB_DIR)/ArduinoJson/src \ + -I $(ARDUINO_LIB_DIR)/OpenEVSE_Lib/src \ + -I $(ARDUINO_LIB_DIR)/ESPAL/src \ + -I $(EPOXY_LIB_DIR)/EpoxyFS/src \ + -I $(EPOXY_LIB_DIR)/EpoxyEepromEsp/src \ + -ggdb \ + -D DIVERT_SIM \ + -D ARDUINO=100 \ + -D UNIX_HOST_DUINO \ + -D EPOXY_DUINO \ + -D EPOXY_CORE_ESP8266 \ + -D ENERGY_METER_STORE_STATE=0 \ + -D ARDUINOJSON_ENABLE_PROGMEM=0 \ + -D ENABLE_CONFIG_V1_IMPORT=0 \ + -D ENABLE_CONFIG_CHANGE_NOTIFICATION=0 +# \ +# -D ENABLE_DEBUG \ +# -D ENABLE_DEBUG_DIVERT \ +# -D ENABLE_DEBUG_EVSE_MAN \ +# -D ENABLE_DEBUG_EVSE_MONITOR LDFLAGS := -pthread TARGETS:= divert_sim MAINS := $(addsuffix .o, $(TARGETS) ) #OBJ := kbd.o command.o display.o $(MAINS) #DEPS := defs.h command.h -OBJ := divert.o WString.o RapiSender.o Console.o Print.o Core.o $(MAINS) + +MICRO_TASKS_OBJ := \ + MicroTasks.o \ + MicroTasksTask.o \ + MicroTasksEvent.o \ + MicroTasksEventListener.o \ + MicroTasksAlarm.o \ + MicroTasksNode.o \ + MicroTasksList.o + +OPENEVSE_LIB_OBJ := \ + openevse.o \ + RapiSender.o + +CONFIG_JSON_OBJ := \ + ConfigJson.o + +STREAM_SPY_OBJ := \ + StreamSpy.o \ + +OPENEVSE_WIFI_OBJ := \ + divert.o \ + current_shaper.o \ + evse_man.o \ + evse_monitor.o \ + energy_meter.o \ + event_log.o \ + debug.o \ + manual.o \ + app_config.o + +ARDUINO_OBJ := \ + avr_stdlib.o \ + Arduino.o \ + base64.o \ + Esp.o \ + IPAddress.o \ + Print.o \ + SPI.o \ + StdioSerial.o \ + Stream.o \ + Wire.o \ + WMath.o \ + WString.o + +EPOXY_FS_OBJ := \ + EpoxyFS.o \ + FS.o \ + FSImpl.o + +EPOXY_EEPROM_OBJ := \ + EpoxyEepromEsp.o + +ESPAL_OBJ := \ + espal.o + +OBJ := \ + $(MICRO_TASKS_OBJ) \ + $(CONFIG_JSON_OBJ) \ + $(STREAM_SPY_OBJ) \ + $(ESPAL_OBJ) \ + $(OPENEVSE_LIB_OBJ) \ + $(OPENEVSE_WIFI_OBJ) \ + $(EPOXY_FS_OBJ) \ + $(EPOXY_EEPROM_OBJ) \ + $(ARDUINO_OBJ) \ + $(MAINS) DEPS := -VPATH := .:../src:FakeDuino +VPATH := \ + . \ + ../src \ + $(EPOXY_CORE_PATH) \ + $(ARDUINO_LIB_DIR)/OpenEVSE_Lib/src \ + $(ARDUINO_LIB_DIR)/MicroTasks/src \ + $(ARDUINO_LIB_DIR)/ConfigJson/src \ + $(ARDUINO_LIB_DIR)/StreamSpy/src \ + $(ARDUINO_LIB_DIR)/ESPAL/src \ + $(EPOXY_LIB_DIR)/EpoxyFS/src \ + $(EPOXY_LIB_DIR)/EpoxyEepromEsp/src .PHONY: all clean diff --git a/divert_sim/Makefile.FakeDuino b/divert_sim/Makefile.FakeDuino new file mode 100644 index 00000000..e286f6b1 --- /dev/null +++ b/divert_sim/Makefile.FakeDuino @@ -0,0 +1,93 @@ +CPP := g++ + +BASE_ENV = openevse_wifi_v1 + +CPPFLAGS := \ + -I . \ + -I ../include \ + -I ../src \ + -I FakeDuino \ + -I "../.pio/libdeps/$(BASE_ENV)/Micro Debug/src" \ + -I ../.pio/libdeps/$(BASE_ENV)/MicroTasks/include \ + -I ../.pio/libdeps/$(BASE_ENV)/StreamSpy/src \ + -I ../.pio/libdeps/$(BASE_ENV)/ArduinoJson/src \ + -I ../.pio/libdeps/$(BASE_ENV)/OpenEVSE/src \ + -ggdb \ + -D DIVERT_SIM \ + -D ARDUINO \ + -D RAPI_PORT=Console \ + -D ENERGY_METER_STORE_STATE=0 \ + -D ARDUINOJSON_ENABLE_PROGMEM=0 + # -D ENABLE_DEBUG + # -D DEBUG_PORT=Console +LDFLAGS := -pthread + +TARGETS:= divert_sim +MAINS := $(addsuffix .o, $(TARGETS) ) +#OBJ := kbd.o command.o display.o $(MAINS) +#DEPS := defs.h command.h + +MICRO_TASKS_OBJ := \ + MicroTasks.o \ + MicroTasksTask.o \ + MicroTasksEvent.o \ + MicroTasksEventListener.o \ + MicroTasksAlarm.o \ + MicroTasksNode.o \ + MicroTasksList.o + +OPENEVSE_LIB_OBJ := \ + openevse.o \ + RapiSender.o + +STREAM_SPY_OBJ := \ + StreamSpy.o \ + +OPENEVSE_WIFI_OBJ := \ + divert.o \ + current_shaper.o \ + evse_man.o \ + evse_monitor.o \ + energy_meter.o \ + event_log.o \ + debug.o \ + manual.o + +ARDUINO_OBJ := \ + WString.o \ + Console.o \ + Stream.o \ + Print.o \ + Core.o \ + vfs_api.o \ + FS.o \ + LittleFS.o + +OBJ := \ + $(MICRO_TASKS_OBJ) \ + $(STREAM_SPY_OBJ) \ + $(OPENEVSE_LIB_OBJ) \ + $(OPENEVSE_WIFI_OBJ) \ + $(ARDUINO_OBJ) \ + $(MAINS) +DEPS := +VPATH := \ + . \ + ../src \ + FakeDuino \ + ../.pio/libdeps/$(BASE_ENV)/OpenEVSE/src \ + ../.pio/libdeps/$(BASE_ENV)/MicroTasks/src \ + ../.pio/libdeps/$(BASE_ENV)/StreamSpy/src + +.PHONY: all clean + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) $(OBJ) + +$(OBJ): %.o : %.cpp $(DEPS) + $(CPP) -c -o $@ $< $(CPPFLAGS) + +$(TARGETS): % : $(filter-out $(MAINS), $(OBJ)) %.o + $(CPP) -o $@ $(LIBS) $^ $(CPPFLAGS) $(LDFLAGS) diff --git a/divert_sim/RapiSender.cpp b/divert_sim/RapiSender.cpp index de0d2ee2..5f6d9ce4 100644 --- a/divert_sim/RapiSender.cpp +++ b/divert_sim/RapiSender.cpp @@ -8,7 +8,9 @@ #ifdef ENABLE_DEBUG #define DBG #endif -#define DBUGF + +extern long pilot; +extern long state; static CommandItem commandQueueItems[RAPI_MAX_COMMANDS]; @@ -57,15 +59,24 @@ void RapiSender::_commandComplete(int result) } void -RapiSender::sendCmd(const char *cmdstr, RapiCommandCompleteHandler callback, unsigned long timeout) { +RapiSender::sendCmd(const char *cmdstr, RapiCommandCompleteHandler callback, unsigned long timeout) +{ + String cmd = cmdstr; + return sendCmd(cmd, callback, timeout); } void -RapiSender::sendCmd(String &cmdstr, RapiCommandCompleteHandler callback, unsigned long timeout) { +RapiSender::sendCmd(String &cmdstr, RapiCommandCompleteHandler callback, unsigned long timeout) +{ + int ret = sendCmdSync(cmdstr, timeout); + callback(ret); } void -RapiSender::sendCmd(const __FlashStringHelper *cmdstr, RapiCommandCompleteHandler callback, unsigned long timeout) { +RapiSender::sendCmd(const __FlashStringHelper *cmdstr, RapiCommandCompleteHandler callback, unsigned long timeout) +{ + String cmd = cmdstr; + return sendCmd(cmd, callback, timeout); } int @@ -77,62 +88,170 @@ RapiSender::sendCmdSync(const char *cmdstr, unsigned long timeout) { int RapiSender::sendCmdSync(String &cmd, unsigned long timeout) { + DBUGVAR(cmd); + static char ok[] = "$OK"; static char zero[] = "0"; - static char buf1[16]; + static char buf1[32]; - if(cmd == "$GE") - { - sprintf(buf1, "%ld", pilot); - _tokens[0] = ok; - _tokens[1] = buf1; - _tokens[2] = zero; - _tokenCnt = 3; - } - else if(cmd.startsWith("$SC")) - { - sscanf(cmd.c_str(), "$SC %ld V", &pilot); - _tokens[0] = ok; - _tokenCnt = 1; - } - else if(cmd == "$GD") - { - _tokens[0] = ok; - _tokens[1] = zero; - _tokens[2] = zero; - _tokens[3] = zero; - _tokens[4] = zero; - _tokenCnt = 5; - } - else if(cmd == "$FE") - { - state = OPENEVSE_STATE_CHARGING; - _tokens[0] = ok; - _tokenCnt = 1; - } - else if(cmd == "$FD") + switch (cmd[1]) { - state = OPENEVSE_STATE_DISABLED; - _tokens[0] = ok; - _tokenCnt = 1; - } - else if(cmd == "$FS") - { - state = OPENEVSE_STATE_SLEEPING; - _tokens[0] = ok; - _tokenCnt = 1; - } - else if(cmd == "$GG") - { - _tokens[0] = ok; - _tokens[1] = zero; - _tokenCnt = 2; - } - else - { - DBUGF("Unhandled command: %s", cmd.c_str()); + case 'G': + switch(cmd[2]) + { + case 'E': + { + sprintf(buf1, "%ld", pilot); + _tokens[0] = ok; + _tokens[1] = buf1; + _tokens[2] = zero; + _tokenCnt = 3; + } break; + + case 'D': + { + _tokens[0] = ok; + _tokens[1] = zero; + _tokens[2] = zero; + _tokens[3] = zero; + _tokens[4] = zero; + _tokenCnt = 5; + } break; + case 'G': + { + _tokens[0] = ok; + _tokens[1] = zero; + _tokenCnt = 2; + } break; + case 'V': + { + _tokens[0] = ok; + _tokens[1] = "1.2.3"; + _tokens[2] = "1.2.3"; + _tokenCnt = 3; + } break; + case 'F': + { + _tokens[0] = ok; + _tokens[1] = zero; + _tokens[2] = zero; + _tokens[3] = zero; + _tokenCnt = 4; + } break; + case 'C': + { + sprintf(buf1, "%ld", pilot); + _tokens[0] = ok; + _tokens[1] = "6"; + _tokens[2] = "32"; + _tokens[3] = buf1; + _tokens[4] = "32"; + _tokenCnt = 5; + } break; + case 'A': + { + _tokens[0] = ok; + _tokens[1] = "220"; + _tokens[2] = zero; + _tokenCnt = 3; + } break; + case 'I': + { + _tokens[0] = ok; + _tokens[1] = "Y57414FF020F0C"; + _tokenCnt = 2; + } break; + case 'S': + { + char *ptr = buf1; + + _tokens[0] = ok; + _tokens[1] = ptr; + ptr += sprintf(ptr, "%ld", state) + 1; + _tokens[2] = zero; + _tokens[3] = ptr; + ptr += sprintf(ptr, "%ld", state) + 1; // Should not reflect the Sleep/Disabled state + _tokens[4] = zero; + _tokenCnt = 5; + } break; + case 'P': + { + _tokens[0] = ok; + _tokens[1] = "200"; + _tokens[2] = "-2560"; + _tokens[3] = "-2560"; + _tokenCnt = 4; + } break; + case 'U': + { + _tokens[0] = ok; + _tokens[1] = zero; + _tokens[2] = zero; + _tokenCnt = 3; + } break; + + default: + DBUGF("Unhandled get command: %s", cmd.c_str()); + return RAPI_RESPONSE_NK; + } break; + + case 'S': + switch(cmd[2]) + { + case 'C': + { + sscanf(cmd.c_str(), "$SC %ld V", &pilot); + _tokens[0] = ok; + _tokenCnt = 1; + } break; + case 'Y': + { + _tokens[0] = ok; + _tokenCnt = 1; + } break; + case 'B': + { + _tokens[0] = ok; + _tokenCnt = 1; + } break; + + default: + DBUGF("Unhandled set command: %s", cmd.c_str()); + return RAPI_RESPONSE_NK; + } break; + + case 'F': + switch(cmd[2]) + { + case 'E': + { + state = OPENEVSE_STATE_CHARGING; + _tokens[0] = ok; + _tokenCnt = 1; + } break; + case 'D': + { + state = OPENEVSE_STATE_DISABLED; + _tokens[0] = ok; + _tokenCnt = 1; + } break; + case 'S': + { + state = OPENEVSE_STATE_SLEEPING; + _tokens[0] = ok; + _tokenCnt = 1; + } break; + + default: + DBUGF("Unhandled function command: %s", cmd.c_str()); + return RAPI_RESPONSE_NK; + } break; + + default: + break; } + return RAPI_RESPONSE_OK; } diff --git a/divert_sim/data/test_config.json b/divert_sim/data/test_config.json new file mode 100644 index 00000000..eed0ef54 --- /dev/null +++ b/divert_sim/data/test_config.json @@ -0,0 +1,6 @@ +{ + "divert_PV_ratio": 0.5, + "divert_attack_smoothing_factor": 0.4, + "divert_decay_smoothing_factor": 0.05, + "divert_min_charge_time": 600 +} diff --git a/divert_sim/divert_sim.cpp b/divert_sim/divert_sim.cpp index fd9d9fd9..795f6f9d 100644 --- a/divert_sim/divert_sim.cpp +++ b/divert_sim/divert_sim.cpp @@ -2,28 +2,32 @@ #include #include -#ifndef RAPI_PORT -#define RAPI_PORT Console -#endif - -#include "Console.h" +#include "StdioSerial.h" #include "RapiSender.h" #include "openevse.h" #include "divert.h" #include "event.h" +#include "event_log.h" +#include "manual.h" #include "parser.hpp" #include "cxxopts.hpp" +#include +#include + using namespace aria::csv; -RapiSender rapiSender(&RAPI_PORT); +EventLog eventLog; +EvseManager evse(RAPI_PORT, eventLog); +DivertTask divert(evse); +ManualOverride manual(evse); long pilot = 32; // OpenEVSE Pilot Setting -long state = OPENEVSE_STATE_SLEEPING; // OpenEVSE State -String mqtt_solar = ""; -String mqtt_grid_ie = ""; -uint32_t flags; +long state = OPENEVSE_STATE_CONNECTED; // OpenEVSE State +double voltage = 240; // Voltage from OpenEVSE or MQTT + +extern double smoothed_available_current; int date_col = 0; int grid_ie_col = -1; @@ -34,13 +38,6 @@ time_t simulated_time = 0; bool kw = false; -extern double smoothed_available_current; -double divert_attack_smoothing_factor = 0.4; -double divert_decay_smoothing_factor = 0.05; -double divert_PV_ratio = 0.5; -uint32_t divert_min_charge_time = (10 * 60); -double voltage = 240; // Voltage from OpenEVSE or MQTT - time_t parse_date(const char *dateStr) { int y = 2020, M = 1, d = 1, h = 0, m = 0, s = 0; @@ -99,6 +96,7 @@ int main(int argc, char** argv) { int voltage_arg = -1; std::string sep = ","; + std::string config; cxxopts::Options options(argv[0], " - example command line options"); options @@ -111,8 +109,7 @@ int main(int argc, char** argv) ("d,date", "The date column", cxxopts::value(date_col), "N") ("s,solar", "The solar column", cxxopts::value(solar_col), "N") ("g,gridie", "The Grid IE column", cxxopts::value(grid_ie_col), "N") - ("attack", "The attack factor for the smoothing", cxxopts::value(divert_attack_smoothing_factor)) - ("decay", "The decay factor for the smoothing", cxxopts::value(divert_decay_smoothing_factor)) + ("c,config", "Config options, either a file name or JSON", cxxopts::value(config)) ("v,voltage", "The Voltage column if < 50, else the fixed voltage", cxxopts::value(voltage_arg), "N") ("kw", "values are KW") ("sep", "Field separator", cxxopts::value(sep)); @@ -125,6 +122,22 @@ int main(int argc, char** argv) exit(0); } + fs::EpoxyFS.begin(); + config_reset(); + + // If config is set and not a JSON string, assume it is a file name + if(config.length() > 0 && config[0] != '{') + { + std::ifstream t(config); + std::stringstream buffer; + buffer << t.rdbuf(); + config = buffer.str(); + } + // If we have some JSON load it + if(config.length() > 0 && config[0] == '{') { + config_deserialize(config.c_str()); + } + kw = result.count("kw") > 0; mqtt_solar = grid_ie_col >= 0 ? "" : "yes"; @@ -141,7 +154,15 @@ int main(int argc, char** argv) solar = 0; grid_ie = 0; - divertmode_update(DIVERT_MODE_ECO); + evse.begin(); + divert.begin(); + + // Initialise the EVSE Manager + while (!evse.isConnected()) { + MicroTask.update(); + } + + divert.setMode(DivertMode::Eco); CsvParser parser(std::cin); parser.delimiter(sep.c_str()[0]); @@ -171,7 +192,8 @@ int main(int argc, char** argv) col++; } - divert_update_state(); + divert.update_state(); + MicroTask.update(); tm tm; gmtime_r(&simulated_time, &tm); @@ -183,7 +205,7 @@ int main(int argc, char** argv) int ev_watt = ev_pilot * voltage; int min_ev_watt = 6 * voltage; - double smoothed = smoothed_available_current * voltage; + double smoothed = divert.smoothedAvailableCurrent() * voltage; std::cout << buffer << "," << solar << "," << grid_ie << "," << ev_pilot << "," << ev_watt << "," << min_ev_watt << "," << state << "," << smoothed << std::endl; } @@ -200,3 +222,7 @@ void event_send(String event) void event_send(JsonDocument &event) { } + +void emoncms_publish(JsonDocument &data) +{ +} diff --git a/divert_sim/runall.sh b/divert_sim/runall.sh index b88eb905..d71412a5 100644 --- a/divert_sim/runall.sh +++ b/divert_sim/runall.sh @@ -11,6 +11,6 @@ mkdir -p output ./divert_sim -g 2 < data/day2_grid_ie.csv > output/day2_grid_ie.csv ./divert_sim -g 2 < data/day3_grid_ie.csv > output/day3_grid_ie.csv ./divert_sim -v 2 < data/solar-vrms.csv > output/solar-vrms.csv -./divert_sim --sep \; --kw --decay 0.4 < data/Energy_and_Power_Day_2020-03-22.csv > output/Energy_and_Power_Day_2020-03-22.csv -./divert_sim --sep \; --kw --decay 0.4 < data/Energy_and_Power_Day_2020-03-31.csv > output/Energy_and_Power_Day_2020-03-31.csv -./divert_sim --sep \; --kw --decay 0.4 < data/Energy_and_Power_Day_2020-04-01.csv > output/Energy_and_Power_Day_2020-04-01.csv +./divert_sim --sep \; --kw --config '{"divert_decay_smoothing_factor":0.4}' < data/Energy_and_Power_Day_2020-03-22.csv > output/Energy_and_Power_Day_2020-03-22.csv +./divert_sim --sep \; --kw --config '{"divert_decay_smoothing_factor":0.4}' < data/Energy_and_Power_Day_2020-03-31.csv > output/Energy_and_Power_Day_2020-03-31.csv +./divert_sim --sep \; --kw --config '{"divert_decay_smoothing_factor":0.4}' < data/Energy_and_Power_Day_2020-04-01.csv > output/Energy_and_Power_Day_2020-04-01.csv diff --git a/src/app_config.cpp b/src/app_config.cpp index b42907cb..b2330440 100644 --- a/src/app_config.cpp +++ b/src/app_config.cpp @@ -1,5 +1,16 @@ #include "emonesp.h" #include "espal.h" + +#include +#include // Save config settings +#include +#include + +#include "app_config.h" +#include "app_config_mqtt.h" +#include "app_config_mode.h" + +#if ENABLE_CONFIG_CHANGE_NOTIFICATION #include "divert.h" #include "net_manager.h" #include "mqtt.h" @@ -10,15 +21,7 @@ #include "LedManagerTask.h" #include "current_shaper.h" #include "limit.h" - -#include "app_config.h" -#include "app_config_mqtt.h" -#include "app_config_mode.h" - -#include -#include // Save config settings -#include -#include +#endif #define EEPROM_SIZE 4096 @@ -253,9 +256,14 @@ config_load_settings() user_config.onChanged(config_changed); factory_config.load(false); - if(!user_config.load(true)) { + if(!user_config.load(true)) + { +#if ENABLE_CONFIG_V1_IMPORT DBUGF("No JSON config found, trying v1 settings"); config_load_v1_settings(); +#else + DBUGF("No JSON config found, using defaults"); +#endif } } @@ -263,6 +271,7 @@ void config_changed(String name) { DBUGF("%s changed", name.c_str()); +#if ENABLE_CONFIG_CHANGE_NOTIFICATION if(name == "time_zone") { timeManager.setTimeZone(time_zone); } else if(name == "flags") { @@ -319,6 +328,7 @@ void config_changed(String name) } else if(name == "sntp_enabled") { timeManager.setSntpEnabled(config_sntp_enabled()); } +#endif } void config_commit(bool factory) diff --git a/src/app_config.h b/src/app_config.h index 7fa149af..5d80b422 100644 --- a/src/app_config.h +++ b/src/app_config.h @@ -4,6 +4,14 @@ #include #include +#ifndef ENABLE_CONFIG_V1_IMPORT +#define ENABLE_CONFIG_V1_IMPORT 1 +#endif + +#ifndef ENABLE_CONFIG_CHANGE_NOTIFICATION +#define ENABLE_CONFIG_CHANGE_NOTIFICATION 1 +#endif + // ------------------------------------------------------------------- // Load and save the OpenEVSE WiFi config. // @@ -194,7 +202,9 @@ extern String ohm; // Load saved settings // ------------------------------------------------------------------- extern void config_load_settings(); +#if ENABLE_CONFIG_V1_IMPORT extern void config_load_v1_settings(); +#endif // ------------------------------------------------------------------- // Reset the config back to defaults diff --git a/src/debug.cpp b/src/debug.cpp index 537f723a..803f617e 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -5,6 +5,8 @@ #define DEBUG_PORT Serial #elif defined(ESP8266) #define DEBUG_PORT Serial1 +#elif defined(DIVERT_SIM) +#define DEBUG_PORT Serial #else #error Platform not supported #endif @@ -15,6 +17,8 @@ #define RAPI_PORT Serial1 #elif defined(ESP8266) #define RAPI_PORT Serial +#elif defined(DIVERT_SIM) +#define RAPI_PORT Serial #else #error Platform not supported #endif diff --git a/src/divert.cpp b/src/divert.cpp index 1e8666a8..f8b24fd5 100644 --- a/src/divert.cpp +++ b/src/divert.cpp @@ -8,14 +8,9 @@ #include #include "emonesp.h" -#include "input.h" -#include "app_config.h" -#include "RapiSender.h" -#include "mqtt.h" -#include "event.h" -#include "openevse.h" #include "divert.h" #include "emoncms.h" +#include "event.h" #include diff --git a/src/divert.h b/src/divert.h index f21f48a5..1677b41b 100644 --- a/src/divert.h +++ b/src/divert.h @@ -79,6 +79,14 @@ class DivertTask : public MicroTasks::Task return _charge_rate; } + double availableCurrent() { + return _available_current; + } + + double smoothedAvailableCurrent() { + return _smoothed_available_current; + } + // Set charge rate depending on charge mode and solarPV output void update_state(); diff --git a/src/emonesp.h b/src/emonesp.h index 84d27ef7..faf9113d 100644 --- a/src/emonesp.h +++ b/src/emonesp.h @@ -163,9 +163,6 @@ #define FORMAT_LITTLEFS_IF_FAILED true #endif // !FORMAT_LITTLEFS_IF_FAILED -// Just because it is irritating to be all caps.... -#define LittleFS LITTLEFS - #ifndef SCHEDULE_PATH #define SCHEDULE_PATH "/schedule.json" #endif // !SCHEDULE_PATH diff --git a/src/energy_meter.cpp b/src/energy_meter.cpp index 52780f71..6b1bfe0c 100644 --- a/src/energy_meter.cpp +++ b/src/energy_meter.cpp @@ -1,6 +1,9 @@ #if defined(ENABLE_DEBUG) && !defined(ENABLE_DEBUG_ENERGY_METER) #undef ENABLE_DEBUG #endif + +#include + #include "energy_meter.h" #include "evse_monitor.h" @@ -424,7 +427,7 @@ bool EnergyMeter::load() bool EnergyMeter::write(EnergyMeterData &data) { DBUGLN("Energy Meter: Saving data"); - File file = LittleFS.open(ENERGY_METER_FILE, "w", true); + File file = LittleFS.open(ENERGY_METER_FILE, "w"); if (!file) { file.close(); @@ -498,4 +501,4 @@ void EnergyMeter::increment_switch_counter() event_send(doc); } _switch_state = _monitor->isActive(); -}; \ No newline at end of file +}; diff --git a/src/energy_meter.h b/src/energy_meter.h index 43f68c39..d9cc1631 100644 --- a/src/energy_meter.h +++ b/src/energy_meter.h @@ -4,13 +4,14 @@ #include #include #include "emonesp.h" -#include +#include #include "app_config.h" #define MAX_INTERVAL 10000 #define EVENT_INTERVAL 5000 #define ROTATE_INTERVAL 6000 #define SAVE_INTERVAL 5 * 60 * 1000 // save to flash each 5 minutes while charging + #ifndef ENERGY_METER_FILE #define ENERGY_METER_FILE "/emeter.json" #endif @@ -123,4 +124,4 @@ class EnergyMeter }; }; -#endif // _ENERGY_METER_H \ No newline at end of file +#endif // _ENERGY_METER_H diff --git a/src/event_log.cpp b/src/event_log.cpp index 13aa5619..890635a5 100644 --- a/src/event_log.cpp +++ b/src/event_log.cpp @@ -2,7 +2,7 @@ #undef ENABLE_DEBUG #endif -#include +#include #include #include "debug.h" diff --git a/src/main.cpp b/src/main.cpp index e7ebf171..b9b2f217 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,7 +29,7 @@ #include // local OTA update from Arduino IDE #include #include -#include +#include #include "emonesp.h" #include "app_config.h" diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 4db227da..9a56b70f 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include uint32_t Scheduler::Event::_next_id = 1;