#include #include #include #include #include #include #include #include #include #include #include "main.h" #include "inputparser.h" #include "tools.h" bool debugFlag = false; cSkymax *ups = NULL; atomic_bool ups_status_changed(false); atomic_bool ups_qmod_changed(false); atomic_bool ups_qpiri_changed(false); atomic_bool ups_qpigs_changed(false); atomic_bool ups_cmd_executed(false); // --------------------------------------- // Global configs read from 'skymax.conf' string devicename; int runinterval; float ampfactor; float wattfactor; // --------------------------------------- void attemptAddSetting(int *addTo, string addFrom) { try { *addTo = stof(addFrom); } catch (exception e) { cout << e.what() << '\n'; cout << "There's probably a string in the settings file where an int should be.\n"; } } void attemptAddSetting(float *addTo, string addFrom) { try { *addTo = stof(addFrom); } catch (exception e) { cout << e.what() << '\n'; cout << "There's probably a string in the settings file where a floating point should be.\n"; } } void getSettingsFile(string filename) { try { string fileline, linepart1, linepart2; ifstream infile; infile.open(filename); while(!infile.eof()) { getline(infile, fileline); size_t firstpos = fileline.find("#"); if(firstpos != 0 && fileline.length() != 0) // Ignore lines starting with # (comment lines) { size_t delimiter = fileline.find("="); linepart1 = fileline.substr(0, delimiter); linepart2 = fileline.substr(delimiter+1, string::npos - delimiter); if(linepart1 == "device") devicename = linepart2; else if(linepart1 == "run_interval") attemptAddSetting(&runinterval, linepart2); else if(linepart1 == "amperage_factor") attemptAddSetting(&factor, linepart2); else if(linepart1 == "watt_factor") attemptAddSetting(&wattfactor, linepart2); else if(linepart1 == "watt_factor") attemptAddSetting(&wattfactor, linepart2); else continue; } } infile.close(); } catch (...) { cout << "Settings could not be read properly...\n"; } } int main(int argc, char **argv) { // Reply1 float voltage_grid; float freq_grid; float voltage_out; float freq_out; int load_va; int load_watt; int load_percent; int voltage_bus; float voltage_batt; int batt_charge_current; int batt_capacity; int temp_heatsink; float pv_input_current; float pv_input_voltage; float pv_input_watts; float pv_input_watthour; float load_watthour = 0; float scc_voltage; int batt_discharge_current; char device_status[9]; // Reply2 float grid_voltage_rating; float grid_current_rating; float out_voltage_rating; float out_freq_rating; float out_current_rating; int out_va_rating; int out_watt_rating; float batt_rating; float batt_recharge_voltage; float batt_under_voltage; float batt_bulk_voltage; float batt_float_voltage; int batt_type; int max_grid_charge_current; int max_charge_current; int in_voltage_range; int out_source_priority; int charger_source_priority; int machine_type; int topology; int out_mode; float batt_redischarge_voltage; // Get command flag settings from the arguments (if any) InputParser cmdArgs(argc, argv); const string &rawcmd = cmdArgs.getCmdOption("-r"); if(cmdArgs.cmdOptionExists("-h") || cmdArgs.cmdOptionExists("--help")) { return print_help(); } if(cmdArgs.cmdOptionExists("-d")) { debugFlag = true; } lprintf("SKYMAX: Debug set"); // Get the rest of the settings from the conf file if( access( "./skymax.conf", F_OK ) != -1 ) { // file exists getSettingsFile("./skymax.conf"); } else { // file doesn't exist getSettingsFile("/etc/skymax/skymax.conf"); } bool ups_status_changed(false); ups = new cSkymax(devicename); if (!rawcmd.empty()) { ups->ExecuteCmd(rawcmd); // We can piggyback on either GetStatus() function to return our result, it doesn't matter which printf("Reply: %s\n", ups->GetQpiriStatus()->c_str()); } else // No command being sent so just run normally { ups->runMultiThread(); while (true) { lprintf("SKYMAX: Start loop"); // If inverter mode changes print it to screen if (ups_status_changed) { int mode = ups->GetMode(); if (mode) lprintf("SKYMAX: %d", mode); ups_status_changed = false; } // Once we receive all queries print it to screen if (ups_qmod_changed && ups_qpiri_changed && ups_qpigs_changed) { ups_qmod_changed = false; ups_qpiri_changed = false; ups_qpigs_changed = false; int mode = ups->GetMode(); string *reply1 = ups->GetQpigsStatus(); string *reply2 = ups->GetQpiriStatus(); if (reply1 && reply2) { // Parse and display values sscanf(reply1->c_str(), "%f %f %f %f %d %d %d %d %f %d %d %d %f %f %f %d %s", &voltage_grid, &freq_grid, &voltage_out, &freq_out, &load_va, &load_watt, &load_percent, &voltage_bus, &voltage_batt, &batt_charge_current, &batt_capacity, &temp_heatsink, &pv_input_current, &pv_input_voltage, &scc_voltage, &batt_discharge_current, &device_status); sscanf(reply2->c_str(), "%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d - %d %d %d %f", &grid_voltage_rating, &grid_current_rating, &out_voltage_rating, &out_freq_rating, &out_current_rating, &out_va_rating, &out_watt_rating, &batt_rating, &batt_recharge_voltage, &batt_under_voltage, &batt_bulk_voltage, &batt_float_voltage, &batt_type, &max_grid_charge_current, &max_charge_current, &in_voltage_range, &out_source_priority, &charger_source_priority, &machine_type, &topology, &out_mode, &batt_redischarge_voltage); // There appears to be a discrepancy in actual DMM measured current vs what the meter is // telling me it's getting, so lets add a variable we can multiply/divide by to adjust if // needed. This should be set in the config so it can be changed without program recompile. if (debugFlag) { printf("SKYMAX: ampfactor from config is %.2f\n", ampfactor); printf("SKYMAX: wattfactor from config is %.2f\n", wattfactor); } pv_input_current = pv_input_current * ampfactor; // It appears on further inspection of the documentation, that the input current is actually // current that is going out to the battery at battery voltage (NOT at PV voltage). This // would explain the larger discrepancy we saw before. pv_input_watts = (scc_voltage * pv_input_current) * wattfactor; // Calculate watt-hours generated per run interval period (given as program argument) pv_input_watthour = pv_input_watts / (3600 / runinterval); // Only calculate load watt-hours if we are in battery mode (line mode doesn't count towards money savings) if (mode == 4) load_watthour = (float)load_watt / (3600 / runinterval); // Print as JSON (output is expected to be use by telegraf to send to influxdb) printf("{\n"); printf("\"Inverter_mode\":%d,\n", mode); printf("\"AC_grid_voltage\":%.1f,\n", voltage_grid); printf("\"AC_grid_frequency\":%.1f,\n", freq_grid); printf("\"AC_out_voltage\":%.1f,\n", voltage_out); printf("\"AC_out_frequency\":%.1f,\n", freq_out); printf("\"PV_in_voltage\":%.1f,\n", pv_input_voltage); printf("\"PV_in_current\":%.1f,\n", pv_input_current); printf("\"PV_in_watts\":%.1f,\n", pv_input_watts); printf("\"PV_in_watthour\":%.4f,\n", pv_input_watthour); printf("\"SCC_voltage\":%.4f,\n", scc_voltage); printf("\"Load_pct\":%d,\n", load_percent); printf("\"Load_watt\":%d,\n", load_watt); printf("\"Load_watthour\":%.4f,\n", load_watthour); printf("\"Load_va\":%d,\n", load_va); printf("\"Bus_voltage\":%d,\n", voltage_bus); printf("\"Heatsink_temperature\":%d,\n", temp_heatsink); printf("\"Battery_capacity\":%d,\n", batt_capacity); printf("\"Battery_voltage\":%.2f,\n", voltage_batt); printf("\"Battery_charge_current\":%d,\n", batt_charge_current); printf("\"Battery_discharge_current\":%d,\n", batt_discharge_current); printf("\"Load_status_on\":%c,\n", device_status[3]); printf("\"SCC_charge_on\":%c,\n", device_status[6]); printf("\"AC_charge_on\":%c,\n", device_status[7]); printf("\"Battery_recharge_voltage\":%.1f,\n", batt_recharge_voltage); printf("\"Battery_under_voltage\":%.1f,\n", batt_under_voltage); printf("\"Battery_bulk_voltage\":%.1f,\n", batt_bulk_voltage); printf("\"Battery_float_voltage\":%.1f,\n", batt_float_voltage); printf("\"Max_grid_charge_current\":%d,\n", max_grid_charge_current); printf("\"Max_charge_current\":%d,\n", max_charge_current); printf("\"Out_source_priority\":%d,\n", out_source_priority); printf("\"Charger_source_priority\":%d,\n", charger_source_priority); printf("\"Battery_redischarge_voltage\":%.1f\n", batt_redischarge_voltage); printf("}\n"); delete reply1; delete reply2; // Do once and exit instead of loop endlessly lprintf("SKYMAX: All queries complete, exiting using goto"); break; } } sleep(1); } } // Cleanup if (ups) delete ups; return 0; }