Files
docker-voltronic-homeassistant/sources/voltronic-cli/main.cpp
T
2019-05-28 14:27:57 +10:00

286 lines
9.7 KiB
C++

#include <algorithm>
#include <fstream>
#include <iostream>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <thread>
#include <unistd.h>
#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(&ampfactor, 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;
}