diff --git a/divert_sim/.vscode/launch.json b/divert_sim/.vscode/launch.json index 4e14e1519..3d4521cba 100644 --- a/divert_sim/.vscode/launch.json +++ b/divert_sim/.vscode/launch.json @@ -4,6 +4,32 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "divert_sim: data_shaper.csv", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/divert_sim", + "args": [ + "-c", "data/test_shapper.json", + "-l", "1", + "--sep", "\\;", + "<", "${workspaceFolder}/data/data_shaper.csv" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "build", + "miDebuggerPath": "/usr/bin/gdb" + }, { "name": "divert_sim: day1.csv", "type": "cppdbg", @@ -11,6 +37,7 @@ "program": "${workspaceFolder}/divert_sim", "args": [ "-c", "data/test_config.json", + "-s", "1", "<", "${workspaceFolder}/data/day1.csv" ], "stopAtEntry": false, @@ -58,6 +85,7 @@ "request": "launch", "program": "${workspaceFolder}/divert_sim", "args": [ + "-s", "1", "-v", "2", "<", "${workspaceFolder}/data/solar-vrms.csv" ], @@ -82,6 +110,7 @@ "request": "launch", "program": "${workspaceFolder}/divert_sim", "args": [ + "-s", "1", "--sep", "\\;", "--kw", "<", "${workspaceFolder}/data/Energy_and_Power_Day_2020-03-22.csv" ], diff --git a/divert_sim/Makefile b/divert_sim/Makefile index c356cfab5..adb4471c6 100644 --- a/divert_sim/Makefile +++ b/divert_sim/Makefile @@ -32,10 +32,9 @@ CPPFLAGS := \ -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_CONFIG_CHANGE_NOTIFICATION=0 \ # -D ENABLE_DEBUG \ -# -D SERIAL_OUTFD=STDERR_FILENO +# -D ENABLE_DEBUG_MICRO_TASK \ # -D ENABLE_DEBUG_DIVERT \ # -D ENABLE_DEBUG_INPUT_FILTER \ # -D ENABLE_DEBUG_EVSE_MAN \ diff --git a/divert_sim/data/test_shapper.json b/divert_sim/data/test_shapper.json new file mode 100644 index 000000000..f1d78ddbd --- /dev/null +++ b/divert_sim/data/test_shapper.json @@ -0,0 +1,3 @@ +{ + "current_shaper_max_pwr": 3000 +} diff --git a/divert_sim/divert_sim.cpp b/divert_sim/divert_sim.cpp index 9befd5e63..137345b5b 100644 --- a/divert_sim/divert_sim.cpp +++ b/divert_sim/divert_sim.cpp @@ -9,6 +9,7 @@ #include "event.h" #include "event_log.h" #include "manual.h" +#include "current_shaper.h" #include "parser.hpp" #include "cxxopts.hpp" @@ -33,8 +34,9 @@ extern double smoothed_available_current; int date_col = 0; int grid_ie_col = -1; -int solar_col = 1; -int voltage_col = 1; +int solar_col = -1; +int voltage_col = -1; +int live_pwr_col = -1; time_t simulated_time = 0; time_t last_time = 0; @@ -48,17 +50,19 @@ time_t parse_date(const char *dateStr) if(6 != sscanf(dateStr, "%d-%d-%dT%d:%d:%dZ", &y, &M, &d, &h, &m, &s)) { if(6 != sscanf(dateStr, "%d-%d-%dT%d:%d:%d+00:00", &y, &M, &d, &h, &m, &s)) { if(6 != sscanf(dateStr, "%d-%d-%d %d:%d:%d", &y, &M, &d, &h, &m, &s)) { - if(3 != sscanf(dateStr, "%d:%d %s", &h, &m, ampm)) { - if(1 == sscanf(dateStr, "%d", &s)) { - return s; - } - } else { - y = 2020; M = 1; d = 1; s = 0; - if(12 == h) { - h -= 12; - } - if('P' == ampm[0]) { - h += 12; + if(6 != sscanf(dateStr, "%d/%d/%d %d:%d:%d", &d, &M, &y, &h, &m, &s)) { + if(3 != sscanf(dateStr, "%d:%d %s", &h, &m, ampm)) { + if(1 == sscanf(dateStr, "%d", &s)) { + return s; + } + } else { + y = 2020; M = 1; d = 1; s = 0; + if(12 == h) { + h -= 12; + } + if('P' == ampm[0]) { + h += 12; + } } } } @@ -112,6 +116,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") + ("l,livepwr", "The live power column", cxxopts::value(live_pwr_col), "N") ("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") @@ -128,6 +133,8 @@ int main(int argc, char** argv) exit(0); } + EpoxyTest::set_millis(millis()); + fs::EpoxyFS.begin(); if(result.count("config-load") > 0) { config_load_settings(); @@ -163,7 +170,7 @@ int main(int argc, char** argv) kw = result.count("kw") > 0; divert_type = grid_ie_col >= 0 ? 1 : 0; - + if(voltage_arg >= 0) { if(voltage_arg < 50) { voltage_col = voltage_arg; @@ -177,19 +184,25 @@ int main(int argc, char** argv) evse.begin(); divert.begin(); + shaper.begin(evse); // Initialise the EVSE Manager while (!evse.isConnected()) { MicroTask.update(); } - divert.setMode(DivertMode::Eco); + if(solar_col >= 0 || grid_ie_col >= 0) { + divert.setMode(DivertMode::Eco); + } + if(live_pwr_col >= 0) { + shaper.setState(true); + } CsvParser parser(std::cin); parser.delimiter(sep.c_str()[0]); int row_number = 0; - std::cout << "Date,Solar,Grid IE,Pilot,Charge Power,Min Charge Power,State,Smoothed Available" << std::endl; + std::cout << "Date,Solar,Grid IE,Pilot,Charge Power,Min Charge Power,State,Smoothed Available,Live Power,Smoother Live Power,Shapper Max Power" << std::endl; for (auto& row : parser) { try @@ -206,6 +219,8 @@ int main(int argc, char** argv) grid_ie = get_watt(val.c_str()); } else if (solar_col == col) { solar = get_watt(val.c_str()); + } else if (live_pwr_col == col) { + shaper.setLivePwr(get_watt(val.c_str())); } else if (voltage_col == col) { voltage = stoi(field); } @@ -218,6 +233,7 @@ int main(int argc, char** argv) int delta = simulated_time - last_time; if(delta > 0) { EpoxyTest::add_millis(delta * 1000); + DBUGVAR(millis()); } } last_time = simulated_time; @@ -237,7 +253,11 @@ int main(int argc, char** argv) double smoothed = divert.smoothedAvailableCurrent() * voltage; - std::cout << buffer << "," << solar << "," << grid_ie << "," << ev_pilot << "," << ev_watt << "," << min_ev_watt << "," << state << "," << smoothed << std::endl; + int live_power = shaper.getLivePwr(); + int smoothed_live_pwr = shaper.getLivePwr(); + int shapper_max_power = shaper.getMaxPwr(); + + std::cout << buffer << "," << solar << "," << grid_ie << "," << ev_pilot << "," << ev_watt << "," << min_ev_watt << "," << state << "," << smoothed << "," << live_power << "," << smoothed_live_pwr << "," << shapper_max_power << std::endl; } catch(const std::invalid_argument& e) { diff --git a/divert_sim/interactive.html b/divert_sim/interactive.html index 825bb415e..d064fd5a1 100644 --- a/divert_sim/interactive.html +++ b/divert_sim/interactive.html @@ -18,7 +18,11 @@ "divert_PV_ratio": parseFloat($("#divert_PV_ratio").val()), "divert_attack_smoothing_time": parseInt($("#divert_attack_smoothing_time").val()), "divert_decay_smoothing_time": parseInt($("#divert_decay_smoothing_time").val()), - "divert_min_charge_time": parseInt($("#divert_min_charge_time").val()) + "divert_min_charge_time": parseInt($("#divert_min_charge_time").val()), + "current_shaper_max_pwr": parseInt($("#current_shaper_max_pwr").val()), + "current_shaper_smoothing_time": parseInt($("#current_shaper_smoothing_time").val()), + "current_shaper_min_pause_time": parseInt($("#current_shaper_min_pause_time").val()), + "current_shaper_data_maxinterval": parseInt($("#current_shaper_data_maxinterval").val()) }), () => { init_summary(profiles); loadSummary("output/summary_master.csv", () => { @@ -50,6 +54,14 @@

OpenEVSE Solar Divert Simulations



+ +
+ +
+ +
+ +
diff --git a/divert_sim/run_simulations.py b/divert_sim/run_simulations.py index 897d1a0c0..0d588f682 100644 --- a/divert_sim/run_simulations.py +++ b/divert_sim/run_simulations.py @@ -40,7 +40,8 @@ def run_simulation(dataset: str, output: str, config: bool = False, grid_ie_col: bool = False, solar_col: bool = False, voltage_col: bool = False, - separator: str = ',', is_kw: bool = False) -> None: + separator: str = ',', is_kw: bool = False, + live_power_col: bool = False) -> None: """Run the divert_sim process on the given dataset and return the results""" line_number = 0 @@ -73,6 +74,9 @@ def run_simulation(dataset: str, if solar_col: command.append("-s") command.append(str(solar_col)) + if live_power_col: + command.append("-l") + command.append(str(live_power_col)) if voltage_col: command.append("-v") command.append(str(voltage_col)) @@ -82,6 +86,8 @@ def run_simulation(dataset: str, if is_kw: command.append("--kw") + print(f"cat {input_data.name} | {' '.join(command)}") + divert_process = Popen(command, stdin=input_data, stdout=PIPE, stderr=PIPE, universal_newlines=True) while True: diff --git a/divert_sim/server.py b/divert_sim/server.py index 0629115f3..759771f24 100644 --- a/divert_sim/server.py +++ b/divert_sim/server.py @@ -32,41 +32,43 @@ def do_POST(self): # Run the simulation setup_summary('_interactive') run_simulation('almostperfect', 'almostperfect_interactive', - config=json.dumps(config)) + solar_col=1, config=json.dumps(config)) run_simulation('CloudyMorning', 'CloudyMorning_interactive', - config=json.dumps(config)) + solar_col=1, config=json.dumps(config)) run_simulation('day1', 'day1_interactive', - config=json.dumps(config)) + solar_col=1, config=json.dumps(config)) run_simulation('day2', 'day2_interactive', - config=json.dumps(config)) + solar_col=1, config=json.dumps(config)) run_simulation('day3', 'day3_interactive', - config=json.dumps(config)) + solar_col=1, config=json.dumps(config)) run_simulation('day1_grid_ie', 'day1_grid_ie_interactive', - grid_ie_col=2, config=json.dumps(config)) + solar_col=1, grid_ie_col=2, config=json.dumps(config)) run_simulation('day2_grid_ie', 'day2_grid_ie_interactive', - grid_ie_col=2, config=json.dumps(config)) + solar_col=1, grid_ie_col=2, config=json.dumps(config)) run_simulation('day3_grid_ie', 'day3_grid_ie_interactive', - grid_ie_col=2, config=json.dumps(config)) + solar_col=1, grid_ie_col=2, config=json.dumps(config)) run_simulation('solar-vrms', 'solar-vrms_interactive', - voltage_col=2, config=json.dumps(config)) + solar_col=1, voltage_col=2, config=json.dumps(config)) run_simulation('Energy_and_Power_Day_2020-03-22', 'Energy_and_Power_Day_2020-03-22_interactive', - separator=';', is_kw=True, config=json.dumps(config)) + solar_col=1, separator=';', is_kw=True, config=json.dumps(config)) run_simulation('Energy_and_Power_Day_2020-03-31', 'Energy_and_Power_Day_2020-03-31_interactive', - separator=';', is_kw=True, config=json.dumps(config)) + solar_col=1, separator=';', is_kw=True, config=json.dumps(config)) run_simulation('Energy_and_Power_Day_2020-04-01', 'Energy_and_Power_Day_2020-04-01_interactive', - separator=';', is_kw=True, config=json.dumps(config)) + solar_col=1, separator=';', is_kw=True, config=json.dumps(config)) + run_simulation('data_shaper', 'data_shaper_interactive', + live_power_col=1, separator=';', config=json.dumps(config)) self.wfile.write("OK".encode('utf-8')) diff --git a/divert_sim/simulations.css b/divert_sim/simulations.css index 81aa25823..4c6eed36d 100644 --- a/divert_sim/simulations.css +++ b/divert_sim/simulations.css @@ -13,6 +13,12 @@ width: 100%; } +.shapper +{ + height: 370px; + width: 100%; +} + .gridie { height: 740px; diff --git a/divert_sim/simulations.js b/divert_sim/simulations.js index 1d373bfad..9d46e63be 100644 --- a/divert_sim/simulations.js +++ b/divert_sim/simulations.js @@ -11,6 +11,7 @@ var datasets = [ { id: "Energy_and_Power_Day_2020-03-22", class: "solar", title: "Energy and Power Day 2020-03-22" }, { id: "Energy_and_Power_Day_2020-03-31", class: "solar", title: "Energy and Power Day 2020-03-31" }, { id: "Energy_and_Power_Day_2020-04-01", class: "solar", title: "Energy and Power Day 2020-04-01" }, + { id: "data_shaper", class: "shapper", title: "Shapper example 1" }, ]; var summary = {}; @@ -87,38 +88,72 @@ function loadChart(id, csv, title, type) { showInLegend: true, dataPoints: points[3] }); - opts.data.push({ - name: "Solar", - type: "line", - lineThickness: 1, - showInLegend: true, - dataPoints: points[0] - }); - if("gridie" === type) { + if("gridie" === type || "solar" === type) { opts.data.push({ - name: "Grid IE", + name: "Solar", type: "line", lineThickness: 1, showInLegend: true, - dataPoints: points[1] + dataPoints: points[0] + }); + if("gridie" === type) { + opts.data.push({ + name: "Grid IE", + type: "line", + lineThickness: 1, + showInLegend: true, + dataPoints: points[1] + }); + opts.data.push({ + name: "Smoothed", + type: "line", + lineThickness: 1, + showInLegend: true, + dataPoints: points[6] + }); + opts.data.push({ + name: "Min Charge", + type: "line", + lineThickness: 1, + lineDashType: "shortDash", + lineColor: "#38761d", + showInLegend: true, + dataPoints: points[4] + }); + opts.data.push({ + name: "Min Grid IE", + type: "line", + lineThickness: 1, + lineDashType: "shortDash", + lineColor: "#38761d", + showInLegend: true, + dataPoints: points[2] + }); + } + } + if("shapper" == type) { + opts.data.push({ + name: "Live Power", + type: "line", + lineThickness: 1, + showInLegend: true, + dataPoints: points[7] + }); + opts.data.push({ + name: "Live Power (Smoothed)", + type: "line", + lineThickness: 1, + showInLegend: true, + dataPoints: points[8] + }); + opts.data.push({ + name: "Max Power", + type: "line", + lineThickness: 1, + showInLegend: true, + dataPoints: points[9] }); } - opts.data.push({ - name: "Smoothed", - type: "line", - lineThickness: 1, - showInLegend: true, - dataPoints: points[6] - }); - opts.data.push({ - name: "Min Charge", - type: "line", - lineThickness: 1, - lineDashType: "shortDash", - lineColor: "#38761d", - showInLegend: true, - dataPoints: points[4] - }); var chart = new CanvasJS.Chart(id, opts); diff --git a/divert_sim/test_divert.py b/divert_sim/test_divert.py index 73b1abce8..e3bd57fb9 100644 --- a/divert_sim/test_divert.py +++ b/divert_sim/test_divert.py @@ -25,6 +25,9 @@ def run_test_with_dataset(dataset: str, separator: str = ',', is_kw: bool = False) -> None: """Run the divert_sim process on the given dataset and return the results""" + if False is solar_col: + solar_col = 1 + ( solar_kwh, ev_kwh, kwh_from_solar, diff --git a/src/current_shaper.cpp b/src/current_shaper.cpp index 100359fa1..208aa5bd9 100644 --- a/src/current_shaper.cpp +++ b/src/current_shaper.cpp @@ -196,6 +196,9 @@ int CurrentShaperTask::getMaxPwr() { int CurrentShaperTask::getLivePwr() { return _live_pwr; } +int CurrentShaperTask::getSmoothedLivePwr() { + return _smoothed_live_pwr; +} double CurrentShaperTask::getMaxCur() { return _max_cur; @@ -210,4 +213,4 @@ bool CurrentShaperTask::isActive() { bool CurrentShaperTask::isUpdated() { return _updated; -} \ No newline at end of file +} diff --git a/src/current_shaper.h b/src/current_shaper.h index eee7ab288..d534cefe0 100644 --- a/src/current_shaper.h +++ b/src/current_shaper.h @@ -56,6 +56,7 @@ class CurrentShaperTask: public MicroTasks::Task bool getState(); int getMaxPwr(); int getLivePwr(); + int getSmoothedLivePwr(); double getMaxCur(); bool isActive(); bool isUpdated();