|
|
@ -23,7 +23,7 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
// forward declaration of class
|
|
|
|
template <class RECORDTYPE=float> |
|
|
|
template <class REC_TYP=float> |
|
|
|
class Inverter; |
|
|
|
|
|
|
|
|
|
|
@ -55,17 +55,23 @@ struct calcFunc_t { |
|
|
|
func_t<T>* func; // function pointer
|
|
|
|
}; |
|
|
|
|
|
|
|
template<class T=float> |
|
|
|
struct record_t { |
|
|
|
byteAssign_t* assign; // assigment of bytes in payload
|
|
|
|
uint8_t length; // length of the assignment list
|
|
|
|
T *record; // data pointer
|
|
|
|
uint32_t ts; // timestamp of last received payload
|
|
|
|
}; |
|
|
|
|
|
|
|
class CommandAbstract { |
|
|
|
public: |
|
|
|
CommandAbstract(uint8_t txType = 0, uint8_t cmd = 0){ |
|
|
|
CommandAbstract(uint8_t txType = 0, uint8_t cmd = 0) { |
|
|
|
_TxType = txType; |
|
|
|
_Cmd = cmd; |
|
|
|
}; |
|
|
|
virtual ~CommandAbstract() {}; |
|
|
|
|
|
|
|
const uint8_t getCmd() |
|
|
|
{ |
|
|
|
const uint8_t getCmd() { |
|
|
|
return _Cmd; |
|
|
|
} |
|
|
|
|
|
|
@ -75,7 +81,7 @@ class CommandAbstract { |
|
|
|
}; |
|
|
|
|
|
|
|
class InfoCommand : public CommandAbstract { |
|
|
|
public: |
|
|
|
public: |
|
|
|
InfoCommand(uint8_t cmd){ |
|
|
|
_TxType = 0x15; |
|
|
|
_Cmd = cmd; |
|
|
@ -94,14 +100,12 @@ const calcFunc_t<T> calcFunctions[] = { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
template <class RECORDTYPE> |
|
|
|
template <class REC_TYP> |
|
|
|
class Inverter { |
|
|
|
public: |
|
|
|
uint8_t id; // unique id
|
|
|
|
char name[MAX_NAME_LENGTH]; // human readable name, eg. "HM-600.1"
|
|
|
|
uint8_t type; // integer which refers to inverter type
|
|
|
|
byteAssign_t* assign; // type of inverter
|
|
|
|
uint8_t listLen; // length of assignments
|
|
|
|
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
|
|
|
|
uint16_t fwVersion; // Firmware Version from Info Command Request
|
|
|
|
uint16_t powerLimit[2]; // limit power output
|
|
|
@ -111,17 +115,18 @@ class Inverter { |
|
|
|
serial_u serial; // serial number as on barcode
|
|
|
|
serial_u radioId; // id converted to modbus
|
|
|
|
uint8_t channels; // number of PV channels (1-4)
|
|
|
|
uint32_t ts; // timestamp of last received payload
|
|
|
|
RECORDTYPE *record; // pointer for values
|
|
|
|
record_t<REC_TYP> recordMeas; // structure for measured values
|
|
|
|
record_t<REC_TYP> recordInfo; // structure for info values
|
|
|
|
record_t<REC_TYP> recordConfig; // structure for system config values
|
|
|
|
record_t<REC_TYP> recordAlarm; // structure for alarm values
|
|
|
|
uint16_t chMaxPwr[4]; // maximum power of the modules (Wp)
|
|
|
|
char chName[4][MAX_NAME_LENGTH]; // human readable name for channel
|
|
|
|
char chName[4][MAX_NAME_LENGTH]; // human readable name for channels
|
|
|
|
String lastAlarmMsg; |
|
|
|
bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null)
|
|
|
|
|
|
|
|
Inverter() { |
|
|
|
ts = 0; |
|
|
|
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
|
|
|
|
powerLimit[1] = NoPowerLimit; //
|
|
|
|
powerLimit[1] = NoPowerLimit; // default power limit setting
|
|
|
|
actPowerLimit = 0xffff; // init feedback from inverter to -1
|
|
|
|
devControlRequest = false; |
|
|
|
devControlCmd = InitDataState; |
|
|
@ -174,187 +179,201 @@ class Inverter { |
|
|
|
|
|
|
|
void init(void) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:init")); |
|
|
|
getAssignment(); |
|
|
|
initAssignment(&recordMeas, RealTimeRunData_Debug); |
|
|
|
initAssignment(&recordInfo, InverterDevInform_All); |
|
|
|
initAssignment(&recordConfig, SystemConfigPara); |
|
|
|
initAssignment(&recordAlarm, AlarmData); |
|
|
|
toRadioId(); |
|
|
|
record = new RECORDTYPE[listLen]; |
|
|
|
memset(name, 0, MAX_NAME_LENGTH); |
|
|
|
memset(chName, 0, MAX_NAME_LENGTH * 4); |
|
|
|
memset(record, 0, sizeof(RECORDTYPE) * listLen); |
|
|
|
initialized = true; |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t getPosByChFld(uint8_t channel, uint8_t fieldId) { |
|
|
|
uint8_t getPosByChFld(uint8_t channel, uint8_t fieldId, record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getPosByChFld")); |
|
|
|
uint8_t pos = 0; |
|
|
|
for(; pos < listLen; pos++) { |
|
|
|
if((assign[pos].ch == channel) && (assign[pos].fieldId == fieldId)) |
|
|
|
if(NULL != rec) { |
|
|
|
for(; pos < rec->length; pos++) { |
|
|
|
if((rec->assign[pos].ch == channel) && (rec->assign[pos].fieldId == fieldId)) |
|
|
|
break; |
|
|
|
} |
|
|
|
return (pos >= listLen) ? 0xff : pos; |
|
|
|
return (pos >= rec->length) ? 0xff : pos; |
|
|
|
} |
|
|
|
else |
|
|
|
return 0xff; |
|
|
|
} |
|
|
|
|
|
|
|
const char *getFieldName(uint8_t pos) { |
|
|
|
const char *getFieldName(uint8_t pos, record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getFieldName")); |
|
|
|
return fields[assign[pos].fieldId]; |
|
|
|
if(NULL != rec) |
|
|
|
return fields[rec->assign[pos].fieldId]; |
|
|
|
return notAvail; |
|
|
|
} |
|
|
|
|
|
|
|
const char *getUnit(uint8_t pos) { |
|
|
|
const char *getUnit(uint8_t pos, record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getUnit")); |
|
|
|
return units[assign[pos].unitId]; |
|
|
|
if(NULL != rec) |
|
|
|
return units[rec->assign[pos].unitId]; |
|
|
|
return notAvail; |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t getChannel(uint8_t pos) { |
|
|
|
uint8_t getChannel(uint8_t pos, record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getChannel")); |
|
|
|
return assign[pos].ch; |
|
|
|
if(NULL != rec) |
|
|
|
return rec->assign[pos].ch; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void addValue(uint8_t pos, uint8_t buf[]) { |
|
|
|
void addValue(uint8_t pos, uint8_t buf[], record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue")); |
|
|
|
uint8_t cmd = getQueuedCmd(); |
|
|
|
uint8_t ptr = assign[pos].start; |
|
|
|
uint8_t end = ptr + assign[pos].num; |
|
|
|
uint16_t div = assign[pos].div; |
|
|
|
if(NULL != rec) { |
|
|
|
uint8_t ptr = rec->assign[pos].start; |
|
|
|
uint8_t end = ptr + rec->assign[pos].num; |
|
|
|
uint16_t div = rec->assign[pos].div; |
|
|
|
if(rec == &recordMeas) { |
|
|
|
if(CMD_CALC != div) { |
|
|
|
uint32_t val = 0; |
|
|
|
do { |
|
|
|
val <<= 8; |
|
|
|
val |= buf[ptr]; |
|
|
|
} while(++ptr != end); |
|
|
|
if ((RECORDTYPE)(div) > 1){ |
|
|
|
record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div); |
|
|
|
} |
|
|
|
else { |
|
|
|
record[pos] = (RECORDTYPE)(val); |
|
|
|
} |
|
|
|
|
|
|
|
if ((REC_TYP)(div) > 1) |
|
|
|
rec->record[pos] = (REC_TYP)(val) / (REC_TYP)(div); |
|
|
|
else |
|
|
|
rec->record[pos] = (REC_TYP)(val); |
|
|
|
} |
|
|
|
if (cmd == RealTimeRunData_Debug) { |
|
|
|
// get last alarm message index and save it in the inverter object
|
|
|
|
if (getPosByChFld(0, FLD_ALARM_MES_ID) == pos){ |
|
|
|
if (alarmMesIndex < record[pos]){ |
|
|
|
alarmMesIndex = record[pos]; |
|
|
|
if (getPosByChFld(0, FLD_ALARM_MES_ID, rec) == pos){ |
|
|
|
if (alarmMesIndex < rec->record[pos]){ |
|
|
|
alarmMesIndex = rec->record[pos]; |
|
|
|
//enqueCommand<InfoCommand>(AlarmUpdate); // What is the function of AlarmUpdate?
|
|
|
|
enqueCommand<InfoCommand>(AlarmData); |
|
|
|
} |
|
|
|
else { |
|
|
|
alarmMesIndex = record[pos]; // no change
|
|
|
|
alarmMesIndex = rec->record[pos]; // no change
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (cmd == InverterDevInform_All) { |
|
|
|
if (rec == &recordInfo) { |
|
|
|
// get at least the firmware version and save it to the inverter object
|
|
|
|
if (getPosByChFld(0, FLD_FW_VERSION) == pos){ |
|
|
|
fwVersion = record[pos]; |
|
|
|
if (getPosByChFld(0, FLD_FW_VERSION, rec) == pos){ |
|
|
|
fwVersion = rec->record[pos]; |
|
|
|
DPRINT(DBG_DEBUG, F("Inverter FW-Version: ") + String(fwVersion)); |
|
|
|
} |
|
|
|
} |
|
|
|
if (cmd == SystemConfigPara) { |
|
|
|
if (rec == &recordConfig) { |
|
|
|
// get at least the firmware version and save it to the inverter object
|
|
|
|
if (getPosByChFld(0, FLD_ACT_PWR_LIMIT) == pos){ |
|
|
|
actPowerLimit = record[pos]; |
|
|
|
if (getPosByChFld(0, FLD_ACT_PWR_LIMIT, rec) == pos){ |
|
|
|
actPowerLimit = rec->record[pos]; |
|
|
|
DPRINT(DBG_DEBUG, F("Inverter actual power limit: ") + String(actPowerLimit)); |
|
|
|
} |
|
|
|
} |
|
|
|
if (cmd == AlarmData){ |
|
|
|
if (getPosByChFld(0, FLD_LAST_ALARM_CODE) == pos){ |
|
|
|
lastAlarmMsg = getAlarmStr(record[pos]); |
|
|
|
if (rec == &recordAlarm){ |
|
|
|
if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){ |
|
|
|
lastAlarmMsg = getAlarmStr(rec->record[pos]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
DPRINTLN(DBG_ERROR, F("addValue: assignment not found with cmd 0x")); |
|
|
|
} |
|
|
|
|
|
|
|
RECORDTYPE getValue(uint8_t pos) { |
|
|
|
REC_TYP getValue(uint8_t pos, record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getValue")); |
|
|
|
return record[pos]; |
|
|
|
if(NULL == rec) |
|
|
|
return 0; |
|
|
|
return rec->record[pos]; |
|
|
|
} |
|
|
|
|
|
|
|
void doCalculations() { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:doCalculations")); |
|
|
|
uint8_t cmd = getQueuedCmd(); |
|
|
|
getAssignment(); |
|
|
|
if (cmd == RealTimeRunData_Debug){ |
|
|
|
for(uint8_t i = 0; i < listLen; i++) { |
|
|
|
if(CMD_CALC == assign[i].div) { |
|
|
|
record[i] = calcFunctions<RECORDTYPE>[assign[i].start].func(this, assign[i].num); |
|
|
|
record_t<> *rec = getRecordStruct(RealTimeRunData_Debug); |
|
|
|
for(uint8_t i = 0; i < rec->length; i++) { |
|
|
|
if(CMD_CALC == rec->assign[i].div) { |
|
|
|
rec->record[i] = calcFunctions<REC_TYP>[rec->assign[i].start].func(this, rec->assign[i].num); |
|
|
|
} |
|
|
|
yield(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool isAvailable(uint32_t timestamp) { |
|
|
|
bool isAvailable(uint32_t timestamp, record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:isAvailable")); |
|
|
|
return ((timestamp - ts) < INACT_THRES_SEC); |
|
|
|
return ((timestamp - rec->ts) < INACT_THRES_SEC); |
|
|
|
} |
|
|
|
|
|
|
|
bool isProducing(uint32_t timestamp) { |
|
|
|
bool isProducing(uint32_t timestamp, record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:isProducing")); |
|
|
|
if(isAvailable(timestamp)) { |
|
|
|
uint8_t pos = getPosByChFld(CH0, FLD_PAC); |
|
|
|
return (getValue(pos) > INACT_PWR_THRESH); |
|
|
|
if(isAvailable(timestamp, rec)) { |
|
|
|
uint8_t pos = getPosByChFld(CH0, FLD_PAC, rec); |
|
|
|
return (getValue(pos, rec) > INACT_PWR_THRESH); |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
uint32_t getLastTs(void) { |
|
|
|
uint32_t getLastTs(record_t<> *rec) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getLastTs")); |
|
|
|
return ts; |
|
|
|
return rec->ts; |
|
|
|
} |
|
|
|
|
|
|
|
record_t<> *getRecordStruct(uint8_t cmd) { |
|
|
|
switch (cmd) { |
|
|
|
case RealTimeRunData_Debug: return &recordMeas; |
|
|
|
case InverterDevInform_All: return &recordInfo; |
|
|
|
case SystemConfigPara: return &recordConfig; |
|
|
|
case AlarmData: return &recordAlarm; |
|
|
|
default: break; |
|
|
|
} |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
void getAssignment() { |
|
|
|
DPRINTLN(DBG_DEBUG, F("hmInverter.h:getAssignment")); |
|
|
|
// Default assignment;
|
|
|
|
void initAssignment(record_t<> *rec, uint8_t cmd) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:initAssignment")); |
|
|
|
rec->ts = 0; |
|
|
|
rec->length = 0; |
|
|
|
switch (cmd) { |
|
|
|
case RealTimeRunData_Debug: |
|
|
|
if (INV_TYPE_1CH == type) { |
|
|
|
listLen = (uint8_t)(HM1CH_LIST_LEN); |
|
|
|
assign = (byteAssign_t *)hm1chAssignment; |
|
|
|
rec->length = (uint8_t)(HM1CH_LIST_LEN); |
|
|
|
rec->assign = (byteAssign_t *)hm1chAssignment; |
|
|
|
channels = 1; |
|
|
|
} |
|
|
|
else if (INV_TYPE_2CH == type) { |
|
|
|
listLen = (uint8_t)(HM2CH_LIST_LEN); |
|
|
|
assign = (byteAssign_t *)hm2chAssignment; |
|
|
|
rec->length = (uint8_t)(HM2CH_LIST_LEN); |
|
|
|
rec->assign = (byteAssign_t *)hm2chAssignment; |
|
|
|
channels = 2; |
|
|
|
} |
|
|
|
else if (INV_TYPE_4CH == type) { |
|
|
|
listLen = (uint8_t)(HM4CH_LIST_LEN); |
|
|
|
assign = (byteAssign_t *)hm4chAssignment; |
|
|
|
rec->length = (uint8_t)(HM4CH_LIST_LEN); |
|
|
|
rec->assign = (byteAssign_t *)hm4chAssignment; |
|
|
|
channels = 4; |
|
|
|
} |
|
|
|
else { |
|
|
|
listLen = 0; |
|
|
|
rec->length = 0; |
|
|
|
rec->assign = NULL; |
|
|
|
channels = 0; |
|
|
|
assign = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
switch (getQueuedCmd()) { |
|
|
|
case RealTimeRunData_Debug: |
|
|
|
// Do nothing will use default
|
|
|
|
break; |
|
|
|
case InverterDevInform_All: |
|
|
|
listLen = (uint8_t)(HMINFO_LIST_LEN); |
|
|
|
assign = (byteAssign_t *)InfoAssignment; |
|
|
|
rec->length = (uint8_t)(HMINFO_LIST_LEN); |
|
|
|
rec->assign = (byteAssign_t *)InfoAssignment; |
|
|
|
break; |
|
|
|
case SystemConfigPara: |
|
|
|
listLen = (uint8_t)(HMSYSTEM_LIST_LEN); |
|
|
|
assign = (byteAssign_t *)SystemConfigParaAssignment; |
|
|
|
rec->length = (uint8_t)(HMSYSTEM_LIST_LEN); |
|
|
|
rec->assign = (byteAssign_t *)SystemConfigParaAssignment; |
|
|
|
break; |
|
|
|
case AlarmData: |
|
|
|
listLen = (uint8_t)(HMALARMDATA_LIST_LEN); |
|
|
|
assign = (byteAssign_t *)AlarmDataAssignment; |
|
|
|
rec->length = (uint8_t)(HMALARMDATA_LIST_LEN); |
|
|
|
rec->assign = (byteAssign_t *)AlarmDataAssignment; |
|
|
|
break; |
|
|
|
default: |
|
|
|
DPRINTLN(DBG_INFO, "Parser not implemented"); |
|
|
|
DPRINTLN(DBG_INFO, F("initAssignment: Parser not implemented")); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool isLiveDataAssignment(void) { |
|
|
|
if(assign == (byteAssign_t *)hm1chAssignment) |
|
|
|
return true; |
|
|
|
else if(assign == (byteAssign_t *)hm2chAssignment) |
|
|
|
return true; |
|
|
|
else if(assign == (byteAssign_t *)hm4chAssignment) |
|
|
|
return true; |
|
|
|
else |
|
|
|
return false; |
|
|
|
if(0 != rec->length) { |
|
|
|
rec->record = new REC_TYP[rec->length]; |
|
|
|
memset(rec->record, 0, sizeof(REC_TYP) * rec->length); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
String getAlarmStr(u_int16_t alarmCode) { |
|
|
@ -455,10 +474,11 @@ template<class T=float> |
|
|
|
static T calcYieldTotalCh0(Inverter<> *iv, uint8_t arg0) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcYieldTotalCh0")); |
|
|
|
if(NULL != iv) { |
|
|
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
|
T yield = 0; |
|
|
|
for(uint8_t i = 1; i <= iv->channels; i++) { |
|
|
|
uint8_t pos = iv->getPosByChFld(i, FLD_YT); |
|
|
|
yield += iv->getValue(pos); |
|
|
|
uint8_t pos = iv->getPosByChFld(i, FLD_YT, rec); |
|
|
|
yield += iv->getValue(pos, rec); |
|
|
|
} |
|
|
|
return yield; |
|
|
|
} |
|
|
@ -469,10 +489,11 @@ template<class T=float> |
|
|
|
static T calcYieldDayCh0(Inverter<> *iv, uint8_t arg0) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcYieldDayCh0")); |
|
|
|
if(NULL != iv) { |
|
|
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
|
T yield = 0; |
|
|
|
for(uint8_t i = 1; i <= iv->channels; i++) { |
|
|
|
uint8_t pos = iv->getPosByChFld(i, FLD_YD); |
|
|
|
yield += iv->getValue(pos); |
|
|
|
uint8_t pos = iv->getPosByChFld(i, FLD_YD, rec); |
|
|
|
yield += iv->getValue(pos, rec); |
|
|
|
} |
|
|
|
return yield; |
|
|
|
} |
|
|
@ -483,9 +504,10 @@ template<class T=float> |
|
|
|
static T calcUdcCh(Inverter<> *iv, uint8_t arg0) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcUdcCh")); |
|
|
|
// arg0 = channel of source
|
|
|
|
for(uint8_t i = 0; i < iv->listLen; i++) { |
|
|
|
if((FLD_UDC == iv->assign[i].fieldId) && (arg0 == iv->assign[i].ch)) { |
|
|
|
return iv->getValue(i); |
|
|
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
|
for(uint8_t i = 0; i < rec->length; i++) { |
|
|
|
if((FLD_UDC == rec->assign[i].fieldId) && (arg0 == rec->assign[i].ch)) { |
|
|
|
return iv->getValue(i, rec); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -496,10 +518,11 @@ template<class T=float> |
|
|
|
static T calcPowerDcCh0(Inverter<> *iv, uint8_t arg0) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcPowerDcCh0")); |
|
|
|
if(NULL != iv) { |
|
|
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
|
T dcPower = 0; |
|
|
|
for(uint8_t i = 1; i <= iv->channels; i++) { |
|
|
|
uint8_t pos = iv->getPosByChFld(i, FLD_PDC); |
|
|
|
dcPower += iv->getValue(pos); |
|
|
|
uint8_t pos = iv->getPosByChFld(i, FLD_PDC, rec); |
|
|
|
dcPower += iv->getValue(pos, rec); |
|
|
|
} |
|
|
|
return dcPower; |
|
|
|
} |
|
|
@ -510,12 +533,13 @@ template<class T=float> |
|
|
|
static T calcEffiencyCh0(Inverter<> *iv, uint8_t arg0) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcEfficiencyCh0")); |
|
|
|
if(NULL != iv) { |
|
|
|
uint8_t pos = iv->getPosByChFld(CH0, FLD_PAC); |
|
|
|
T acPower = iv->getValue(pos); |
|
|
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
|
uint8_t pos = iv->getPosByChFld(CH0, FLD_PAC, rec); |
|
|
|
T acPower = iv->getValue(pos, rec); |
|
|
|
T dcPower = 0; |
|
|
|
for(uint8_t i = 1; i <= iv->channels; i++) { |
|
|
|
pos = iv->getPosByChFld(i, FLD_PDC); |
|
|
|
dcPower += iv->getValue(pos); |
|
|
|
pos = iv->getPosByChFld(i, FLD_PDC, rec); |
|
|
|
dcPower += iv->getValue(pos, rec); |
|
|
|
} |
|
|
|
if(dcPower > 0) |
|
|
|
return acPower / dcPower * 100.0f; |
|
|
@ -528,9 +552,10 @@ static T calcIrradiation(Inverter<> *iv, uint8_t arg0) { |
|
|
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcIrradiation")); |
|
|
|
// arg0 = channel
|
|
|
|
if(NULL != iv) { |
|
|
|
uint8_t pos = iv->getPosByChFld(arg0, FLD_PDC); |
|
|
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
|
uint8_t pos = iv->getPosByChFld(arg0, FLD_PDC, rec); |
|
|
|
if(iv->chMaxPwr[arg0-1] > 0) |
|
|
|
return iv->getValue(pos) / iv->chMaxPwr[arg0-1] * 100.0f; |
|
|
|
return iv->getValue(pos, rec) / iv->chMaxPwr[arg0-1] * 100.0f; |
|
|
|
} |
|
|
|
return 0.0; |
|
|
|
} |
|
|
|