mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-18 03:16:12 +02:00
improved records in hmInverter.h
This commit is contained in:
parent
6bd7e01f1a
commit
56b747709a
9 changed files with 316 additions and 290 deletions
|
@ -192,12 +192,13 @@ void app::loop(void) {
|
||||||
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
if(iv->isAvailable(mTimestamp)) {
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
|
if(iv->isAvailable(mTimestamp, rec)) {
|
||||||
DPRINTLN(DBG_INFO, "Inverter: " + String(id));
|
DPRINTLN(DBG_INFO, "Inverter: " + String(id));
|
||||||
for(uint8_t i = 0; i < iv->listLen; i++) {
|
for(uint8_t i = 0; i < rec->length; i++) {
|
||||||
if(0.0f != iv->getValue(i)) {
|
if(0.0f != iv->getValue(i, rec)) {
|
||||||
snprintf(topic, 30, "%s/ch%d/%s", iv->name, iv->assign[i].ch, iv->getFieldName(i));
|
snprintf(topic, 30, "%s/ch%d/%s", iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
|
||||||
snprintf(val, 10, "%.3f %s", iv->getValue(i), iv->getUnit(i));
|
snprintf(val, 10, "%.3f %s", iv->getValue(i, rec), iv->getUnit(i, rec));
|
||||||
DPRINTLN(DBG_INFO, String(topic) + ": " + String(val));
|
DPRINTLN(DBG_INFO, String(topic) + ": " + String(val));
|
||||||
}
|
}
|
||||||
yield();
|
yield();
|
||||||
|
@ -249,17 +250,21 @@ void app::loop(void) {
|
||||||
resetPayload(iv);
|
resetPayload(iv);
|
||||||
|
|
||||||
yield();
|
yield();
|
||||||
if(mConfig.serialDebug)
|
if(mConfig.serialDebug) {
|
||||||
DPRINTLN(DBG_DEBUG, F("app:loop WiFi WiFi.status ") + String(WiFi.status()));
|
DPRINTLN(DBG_DEBUG, F("app:loop WiFi WiFi.status ") + String(WiFi.status()));
|
||||||
DPRINTLN(DBG_INFO, F("Requesting Inverter SN ") + String(iv->serial.u64, HEX));
|
DPRINTLN(DBG_INFO, F("Requesting Inverter SN ") + String(iv->serial.u64, HEX));
|
||||||
|
}
|
||||||
if(iv->devControlRequest && (iv->powerLimit[0] > 0) && (NoPowerLimit != iv->powerLimit[1])) { // prevent to "switch off"
|
if(iv->devControlRequest && (iv->powerLimit[0] > 0) && (NoPowerLimit != iv->powerLimit[1])) { // prevent to "switch off"
|
||||||
if(mConfig.serialDebug)
|
if(mConfig.serialDebug)
|
||||||
DPRINTLN(DBG_INFO, F("Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0]));
|
DPRINTLN(DBG_INFO, F("Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0]));
|
||||||
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd ,iv->powerLimit);
|
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
|
||||||
|
mPayload[iv->id].txCmd = iv->devControlCmd;
|
||||||
iv->clearCmdQueue();
|
iv->clearCmdQueue();
|
||||||
iv->enqueCommand<InfoCommand>(SystemConfigPara);
|
iv->enqueCommand<InfoCommand>(SystemConfigPara);
|
||||||
} else {
|
} else {
|
||||||
mSys->Radio.sendTimePacket(iv->radioId.u64,iv->getQueuedCmd(), mPayload[iv->id].ts,iv->alarmMesIndex);
|
uint8_t cmd = iv->getQueuedCmd();
|
||||||
|
mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex);
|
||||||
|
mPayload[iv->id].txCmd = cmd;
|
||||||
mRxTicker = 0;
|
mRxTicker = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,7 +325,7 @@ void app::processPayload(bool retransmit) {
|
||||||
mPayload[iv->id].complete = true;
|
mPayload[iv->id].complete = true;
|
||||||
}
|
}
|
||||||
if(!mPayload[iv->id].complete ) {
|
if(!mPayload[iv->id].complete ) {
|
||||||
if(!buildPayload(iv->id)) {
|
if(!buildPayload(iv->id)) { // payload not complete
|
||||||
if(mPayload[iv->id].requested) {
|
if(mPayload[iv->id].requested) {
|
||||||
if(retransmit) {
|
if(retransmit) {
|
||||||
if(mPayload[iv->id].retransmits < mConfig.maxRetransPerPyld) {
|
if(mPayload[iv->id].retransmits < mConfig.maxRetransPerPyld) {
|
||||||
|
@ -339,19 +344,26 @@ void app::processPayload(bool retransmit) {
|
||||||
else {
|
else {
|
||||||
if(mConfig.serialDebug)
|
if(mConfig.serialDebug)
|
||||||
DPRINTLN(DBG_ERROR, F("while retrieving data: last frame missing: Request Retransmit"));
|
DPRINTLN(DBG_ERROR, F("while retrieving data: last frame missing: Request Retransmit"));
|
||||||
if(0x00 != mLastPacketId)
|
//if(0x00 != mLastPacketId)
|
||||||
mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, mLastPacketId, true);
|
// mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, mLastPacketId, true);
|
||||||
else
|
//else
|
||||||
mSys->Radio.sendTimePacket(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts,iv->alarmMesIndex);
|
mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex);
|
||||||
}
|
}
|
||||||
mSys->Radio.switchRxCh(100);
|
mSys->Radio.switchRxCh(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else { // payload complete
|
||||||
|
DPRINTLN(DBG_INFO, F("procPyld: cmd: ") + String(mPayload[iv->id].txCmd));
|
||||||
|
DPRINTLN(DBG_INFO, F("procPyld: txid: ") + String(mPayload[iv->id].txId));
|
||||||
|
DPRINTLN(DBG_INFO, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId));
|
||||||
|
record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser
|
||||||
|
if(NULL == rec)
|
||||||
|
DPRINTLN(DBG_ERROR, F("record is NULL!"));
|
||||||
mPayload[iv->id].complete = true;
|
mPayload[iv->id].complete = true;
|
||||||
iv->ts = mPayload[iv->id].ts;
|
rec->ts = mPayload[iv->id].ts;
|
||||||
|
mStat.rxSuccess++;
|
||||||
uint8_t payload[128];
|
uint8_t payload[128];
|
||||||
uint8_t offs = 0;
|
uint8_t offs = 0;
|
||||||
|
|
||||||
|
@ -367,14 +379,12 @@ void app::processPayload(bool retransmit) {
|
||||||
DPRINT(DBG_INFO, F("Payload (") + String(offs) + "): ");
|
DPRINT(DBG_INFO, F("Payload (") + String(offs) + "): ");
|
||||||
mSys->Radio.dumpBuf(NULL, payload, offs);
|
mSys->Radio.dumpBuf(NULL, payload, offs);
|
||||||
}
|
}
|
||||||
mStat.rxSuccess++;
|
|
||||||
|
for(uint8_t i = 0; i < rec->length; i++) {
|
||||||
iv->getAssignment(); // choose the parser
|
iv->addValue(i, payload, rec);
|
||||||
for(uint8_t i = 0; i < iv->listLen; i++) {
|
|
||||||
iv->addValue(i, payload); // cmd value decides which parser is used to decode payload
|
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
iv->doCalculations(); // cmd value decides which parser is used to decode payload
|
iv->doCalculations();
|
||||||
|
|
||||||
iv->setQueuedCmdFinished();
|
iv->setQueuedCmdFinished();
|
||||||
|
|
||||||
|
@ -386,22 +396,22 @@ void app::processPayload(bool retransmit) {
|
||||||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if (NULL != iv) {
|
if (NULL != iv) {
|
||||||
if (iv->isAvailable(mTimestamp)) {
|
if(iv->getRecordStruct(RealTimeRunData_Debug) == rec) {
|
||||||
for (uint8_t i = 0; i < iv->listLen; i++) {
|
if (iv->isAvailable(mTimestamp, rec)) {
|
||||||
snprintf(topic, 30, "%s/ch%d/%s", iv->name, iv->assign[i].ch, fields[iv->assign[i].fieldId]);
|
for (uint8_t i = 0; i < rec->length; i++) {
|
||||||
snprintf(val, 10, "%.3f", iv->getValue(i));
|
snprintf(topic, 30, "%s/ch%d/%s", iv->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]);
|
||||||
mMqtt.sendMsg(topic, val);
|
snprintf(val, 10, "%.3f", iv->getValue(i, rec));
|
||||||
if(iv->isLiveDataAssignment()) {
|
mMqtt.sendMsg(topic, val);
|
||||||
if(CH0 == iv->assign[i].ch) {
|
if(CH0 == rec->assign[i].ch) {
|
||||||
switch(iv->assign[i].fieldId) {
|
switch(rec->assign[i].fieldId) {
|
||||||
case FLD_PAC: total[0] += iv->getValue(i); break;
|
case FLD_PAC: total[0] += iv->getValue(i, rec); break;
|
||||||
case FLD_YT: total[1] += iv->getValue(i); break;
|
case FLD_YT: total[1] += iv->getValue(i, rec); break;
|
||||||
case FLD_YD: total[2] += iv->getValue(i); break;
|
case FLD_YD: total[2] += iv->getValue(i, rec); break;
|
||||||
case FLD_PDC: total[3] += iv->getValue(i); break;
|
case FLD_PDC: total[3] += iv->getValue(i, rec); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,6 +434,8 @@ void app::processPayload(bool retransmit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetPayload(iv);
|
||||||
|
|
||||||
#ifdef __MQTT_AFTER_RX__
|
#ifdef __MQTT_AFTER_RX__
|
||||||
doMQTT = true;
|
doMQTT = true;
|
||||||
#endif
|
#endif
|
||||||
|
@ -543,7 +555,8 @@ void app::sendMqttDiscoveryConfig(void) {
|
||||||
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
if(iv->isAvailable(mTimestamp) && mMqttConfigSendState[id] != true) {
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
|
if(iv->isAvailable(mTimestamp, rec) && mMqttConfigSendState[id] != true) {
|
||||||
DynamicJsonDocument deviceDoc(128);
|
DynamicJsonDocument deviceDoc(128);
|
||||||
deviceDoc["name"] = iv->name;
|
deviceDoc["name"] = iv->name;
|
||||||
deviceDoc["ids"] = String(iv->serial.u64, HEX);
|
deviceDoc["ids"] = String(iv->serial.u64, HEX);
|
||||||
|
@ -553,21 +566,21 @@ void app::sendMqttDiscoveryConfig(void) {
|
||||||
JsonObject deviceObj = deviceDoc.as<JsonObject>();
|
JsonObject deviceObj = deviceDoc.as<JsonObject>();
|
||||||
DynamicJsonDocument doc(384);
|
DynamicJsonDocument doc(384);
|
||||||
|
|
||||||
for(uint8_t i = 0; i < iv->listLen; i++) {
|
for(uint8_t i = 0; i < rec->length; i++) {
|
||||||
if (iv->assign[i].ch == CH0) {
|
if (rec->assign[i].ch == CH0) {
|
||||||
snprintf(name, 32, "%s %s", iv->name, iv->getFieldName(i));
|
snprintf(name, 32, "%s %s", iv->name, iv->getFieldName(i, rec));
|
||||||
} else {
|
} else {
|
||||||
snprintf(name, 32, "%s CH%d %s", iv->name, iv->assign[i].ch, iv->getFieldName(i));
|
snprintf(name, 32, "%s CH%d %s", iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
|
||||||
}
|
}
|
||||||
snprintf(stateTopic, 64, "%s/%s/ch%d/%s", mConfig.mqtt.topic, iv->name, iv->assign[i].ch, iv->getFieldName(i));
|
snprintf(stateTopic, 64, "%s/%s/ch%d/%s", mConfig.mqtt.topic, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
|
||||||
snprintf(discoveryTopic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->name, iv->assign[i].ch, iv->getFieldName(i));
|
snprintf(discoveryTopic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
|
||||||
snprintf(uniq_id, 32, "ch%d_%s", iv->assign[i].ch, iv->getFieldName(i));
|
snprintf(uniq_id, 32, "ch%d_%s", rec->assign[i].ch, iv->getFieldName(i, rec));
|
||||||
const char* devCls = getFieldDeviceClass(iv->assign[i].fieldId);
|
const char* devCls = getFieldDeviceClass(rec->assign[i].fieldId);
|
||||||
const char* stateCls = getFieldStateClass(iv->assign[i].fieldId);
|
const char* stateCls = getFieldStateClass(rec->assign[i].fieldId);
|
||||||
|
|
||||||
doc["name"] = name;
|
doc["name"] = name;
|
||||||
doc["stat_t"] = stateTopic;
|
doc["stat_t"] = stateTopic;
|
||||||
doc["unit_of_meas"] = iv->getUnit(i);
|
doc["unit_of_meas"] = iv->getUnit(i, rec);
|
||||||
doc["uniq_id"] = String(iv->serial.u64, HEX) + "_" + uniq_id;
|
doc["uniq_id"] = String(iv->serial.u64, HEX) + "_" + uniq_id;
|
||||||
doc["dev"] = deviceObj;
|
doc["dev"] = deviceObj;
|
||||||
doc["exp_aft"] = mMqttInterval + 5; // add 5 sec if connection is bad or ESP too slow
|
doc["exp_aft"] = mMqttInterval + 5; // add 5 sec if connection is bad or ESP too slow
|
||||||
|
@ -644,7 +657,12 @@ void app::resetSystem(void) {
|
||||||
mShowRebootRequest = false;
|
mShowRebootRequest = false;
|
||||||
|
|
||||||
|
|
||||||
memset(mPayload, 0, (MAX_NUM_INVERTERS * sizeof(invPayload_t)));
|
Inverter<> *iv;
|
||||||
|
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
||||||
|
iv = mSys->getInverterByPos(i, false);
|
||||||
|
if(NULL != iv)
|
||||||
|
resetPayload(iv);
|
||||||
|
}
|
||||||
memset(&mStat, 0, sizeof(statistics_t));
|
memset(&mStat, 0, sizeof(statistics_t));
|
||||||
mLastPacketId = 0x00;
|
mLastPacketId = 0x00;
|
||||||
}
|
}
|
||||||
|
@ -806,13 +824,12 @@ void app::setupMqtt(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void app::resetPayload(Inverter<>* iv)
|
void app::resetPayload(Inverter<>* iv) {
|
||||||
{
|
|
||||||
// reset payload data
|
|
||||||
memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES);
|
memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES);
|
||||||
|
mPayload[iv->id].txCmd = 0;
|
||||||
mPayload[iv->id].retransmits = 0;
|
mPayload[iv->id].retransmits = 0;
|
||||||
mPayload[iv->id].maxPackId = 0;
|
mPayload[iv->id].maxPackId = 0;
|
||||||
mPayload[iv->id].complete = false;
|
mPayload[iv->id].complete = false;
|
||||||
mPayload[iv->id].requested = true;
|
mPayload[iv->id].requested = true;
|
||||||
mPayload[iv->id].ts = mTimestamp;
|
mPayload[iv->id].ts = mTimestamp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ typedef HmSystem<RadioType, BufferType, MAX_NUM_INVERTERS, InverterType> HmSyste
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
uint8_t txCmd;
|
||||||
uint8_t txId;
|
uint8_t txId;
|
||||||
uint8_t invId;
|
uint8_t invId;
|
||||||
uint32_t ts;
|
uint32_t ts;
|
||||||
|
|
|
@ -30,6 +30,7 @@ enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT,
|
||||||
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
||||||
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr",
|
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr",
|
||||||
"ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","HWPartId","PowerLimit","LastAlarmCode"};
|
"ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","HWPartId","PowerLimit","LastAlarmCode"};
|
||||||
|
const char* const notAvail = "n/a";
|
||||||
|
|
||||||
// mqtt discovery device classes
|
// mqtt discovery device classes
|
||||||
enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP};
|
enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP};
|
||||||
|
@ -207,7 +208,7 @@ const byteAssign_t hm4chAssignment[] = {
|
||||||
{ FLD_F, UNIT_HZ, CH0, 48, 2, 100 },
|
{ FLD_F, UNIT_HZ, CH0, 48, 2, 100 },
|
||||||
{ FLD_PCT, UNIT_PCT, CH0, 56, 2, 10 },
|
{ FLD_PCT, UNIT_PCT, CH0, 56, 2, 10 },
|
||||||
{ FLD_T, UNIT_C, CH0, 58, 2, 10 },
|
{ FLD_T, UNIT_C, CH0, 58, 2, 10 },
|
||||||
{ FLD_ALARM_MES_ID, UNIT_NONE, CH0, 60, 2, 1 },
|
{ FLD_ALARM_MES_ID, UNIT_NONE, CH0, 60, 2, 1 },
|
||||||
{ FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC },
|
{ FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC },
|
||||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC },
|
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC },
|
||||||
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC },
|
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC },
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// forward declaration of class
|
// forward declaration of class
|
||||||
template <class RECORDTYPE=float>
|
template <class REC_TYP=float>
|
||||||
class Inverter;
|
class Inverter;
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,17 +55,23 @@ struct calcFunc_t {
|
||||||
func_t<T>* func; // function pointer
|
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 {
|
class CommandAbstract {
|
||||||
public:
|
public:
|
||||||
CommandAbstract(uint8_t txType = 0, uint8_t cmd = 0){
|
CommandAbstract(uint8_t txType = 0, uint8_t cmd = 0) {
|
||||||
_TxType = txType;
|
_TxType = txType;
|
||||||
_Cmd = cmd;
|
_Cmd = cmd;
|
||||||
};
|
};
|
||||||
virtual ~CommandAbstract() {};
|
virtual ~CommandAbstract() {};
|
||||||
|
|
||||||
const uint8_t getCmd()
|
const uint8_t getCmd() {
|
||||||
{
|
|
||||||
return _Cmd;
|
return _Cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +81,11 @@ class CommandAbstract {
|
||||||
};
|
};
|
||||||
|
|
||||||
class InfoCommand : public CommandAbstract {
|
class InfoCommand : public CommandAbstract {
|
||||||
public:
|
public:
|
||||||
InfoCommand(uint8_t cmd){
|
InfoCommand(uint8_t cmd){
|
||||||
_TxType = 0x15;
|
_TxType = 0x15;
|
||||||
_Cmd = cmd;
|
_Cmd = cmd;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// list of all available functions, mapped in hmDefines.h
|
// list of all available functions, mapped in hmDefines.h
|
||||||
|
@ -94,35 +100,34 @@ const calcFunc_t<T> calcFunctions[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <class RECORDTYPE>
|
template <class REC_TYP>
|
||||||
class Inverter {
|
class Inverter {
|
||||||
public:
|
public:
|
||||||
uint8_t id; // unique id
|
uint8_t id; // unique id
|
||||||
char name[MAX_NAME_LENGTH]; // human readable name, eg. "HM-600.1"
|
char name[MAX_NAME_LENGTH]; // human readable name, eg. "HM-600.1"
|
||||||
uint8_t type; // integer which refers to inverter type
|
uint8_t type; // integer which refers to inverter type
|
||||||
byteAssign_t* assign; // type of inverter
|
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
|
||||||
uint8_t listLen; // length of assignments
|
uint16_t fwVersion; // Firmware Version from Info Command Request
|
||||||
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
|
uint16_t powerLimit[2]; // limit power output
|
||||||
uint16_t fwVersion; // Firmware Version from Info Command Request
|
uint16_t actPowerLimit; //
|
||||||
uint16_t powerLimit[2]; // limit power output
|
uint8_t devControlCmd; // carries the requested cmd
|
||||||
uint16_t actPowerLimit; //
|
bool devControlRequest; // true if change needed
|
||||||
uint8_t devControlCmd; // carries the requested cmd
|
serial_u serial; // serial number as on barcode
|
||||||
bool devControlRequest; // true if change needed
|
serial_u radioId; // id converted to modbus
|
||||||
serial_u serial; // serial number as on barcode
|
uint8_t channels; // number of PV channels (1-4)
|
||||||
serial_u radioId; // id converted to modbus
|
record_t<REC_TYP> recordMeas; // structure for measured values
|
||||||
uint8_t channels; // number of PV channels (1-4)
|
record_t<REC_TYP> recordInfo; // structure for info values
|
||||||
uint32_t ts; // timestamp of last received payload
|
record_t<REC_TYP> recordConfig; // structure for system config values
|
||||||
RECORDTYPE *record; // pointer for values
|
record_t<REC_TYP> recordAlarm; // structure for alarm values
|
||||||
uint16_t chMaxPwr[4]; // maximum power of the modules (Wp)
|
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;
|
String lastAlarmMsg;
|
||||||
bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null)
|
bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null)
|
||||||
|
|
||||||
Inverter() {
|
Inverter() {
|
||||||
ts = 0;
|
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
|
||||||
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
|
powerLimit[1] = NoPowerLimit; // default power limit setting
|
||||||
powerLimit[1] = NoPowerLimit; //
|
actPowerLimit = 0xffff; // init feedback from inverter to -1
|
||||||
actPowerLimit = 0xffff; // init feedback from inverter to -1
|
|
||||||
devControlRequest = false;
|
devControlRequest = false;
|
||||||
devControlCmd = InitDataState;
|
devControlCmd = InitDataState;
|
||||||
initialized = false;
|
initialized = false;
|
||||||
|
@ -174,187 +179,201 @@ class Inverter {
|
||||||
|
|
||||||
void init(void) {
|
void init(void) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:init"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:init"));
|
||||||
getAssignment();
|
initAssignment(&recordMeas, RealTimeRunData_Debug);
|
||||||
|
initAssignment(&recordInfo, InverterDevInform_All);
|
||||||
|
initAssignment(&recordConfig, SystemConfigPara);
|
||||||
|
initAssignment(&recordAlarm, AlarmData);
|
||||||
toRadioId();
|
toRadioId();
|
||||||
record = new RECORDTYPE[listLen];
|
|
||||||
memset(name, 0, MAX_NAME_LENGTH);
|
memset(name, 0, MAX_NAME_LENGTH);
|
||||||
memset(chName, 0, MAX_NAME_LENGTH * 4);
|
memset(chName, 0, MAX_NAME_LENGTH * 4);
|
||||||
memset(record, 0, sizeof(RECORDTYPE) * listLen);
|
|
||||||
initialized = true;
|
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"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getPosByChFld"));
|
||||||
uint8_t pos = 0;
|
uint8_t pos = 0;
|
||||||
for(; pos < listLen; pos++) {
|
if(NULL != rec) {
|
||||||
if((assign[pos].ch == channel) && (assign[pos].fieldId == fieldId))
|
for(; pos < rec->length; pos++) {
|
||||||
break;
|
if((rec->assign[pos].ch == channel) && (rec->assign[pos].fieldId == fieldId))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (pos >= rec->length) ? 0xff : pos;
|
||||||
}
|
}
|
||||||
return (pos >= listLen) ? 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"));
|
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"));
|
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"));
|
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"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue"));
|
||||||
uint8_t cmd = getQueuedCmd();
|
if(NULL != rec) {
|
||||||
uint8_t ptr = assign[pos].start;
|
uint8_t ptr = rec->assign[pos].start;
|
||||||
uint8_t end = ptr + assign[pos].num;
|
uint8_t end = ptr + rec->assign[pos].num;
|
||||||
uint16_t div = assign[pos].div;
|
uint16_t div = rec->assign[pos].div;
|
||||||
if(CMD_CALC != div) {
|
if(rec == &recordMeas) {
|
||||||
uint32_t val = 0;
|
if(CMD_CALC != div) {
|
||||||
do {
|
uint32_t val = 0;
|
||||||
val <<= 8;
|
do {
|
||||||
val |= buf[ptr];
|
val <<= 8;
|
||||||
} while(++ptr != end);
|
val |= buf[ptr];
|
||||||
if ((RECORDTYPE)(div) > 1){
|
} while(++ptr != end);
|
||||||
record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div);
|
if ((REC_TYP)(div) > 1)
|
||||||
}
|
rec->record[pos] = (REC_TYP)(val) / (REC_TYP)(div);
|
||||||
else {
|
else
|
||||||
record[pos] = (RECORDTYPE)(val);
|
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];
|
|
||||||
//enqueCommand<InfoCommand>(AlarmUpdate); // What is the function of AlarmUpdate?
|
|
||||||
enqueCommand<InfoCommand>(AlarmData);
|
|
||||||
}
|
}
|
||||||
else {
|
// get last alarm message index and save it in the inverter object
|
||||||
alarmMesIndex = record[pos]; // no change
|
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 = rec->record[pos]; // no change
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rec == &recordInfo) {
|
||||||
|
// get at least the firmware version and save it to the inverter object
|
||||||
|
if (getPosByChFld(0, FLD_FW_VERSION, rec) == pos){
|
||||||
|
fwVersion = rec->record[pos];
|
||||||
|
DPRINT(DBG_DEBUG, F("Inverter FW-Version: ") + String(fwVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rec == &recordConfig) {
|
||||||
|
// get at least the firmware version and save it to the inverter object
|
||||||
|
if (getPosByChFld(0, FLD_ACT_PWR_LIMIT, rec) == pos){
|
||||||
|
actPowerLimit = rec->record[pos];
|
||||||
|
DPRINT(DBG_DEBUG, F("Inverter actual power limit: ") + String(actPowerLimit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rec == &recordAlarm){
|
||||||
|
if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){
|
||||||
|
lastAlarmMsg = getAlarmStr(rec->record[pos]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cmd == InverterDevInform_All) {
|
else
|
||||||
// get at least the firmware version and save it to the inverter object
|
DPRINTLN(DBG_ERROR, F("addValue: assignment not found with cmd 0x"));
|
||||||
if (getPosByChFld(0, FLD_FW_VERSION) == pos){
|
|
||||||
fwVersion = record[pos];
|
|
||||||
DPRINT(DBG_DEBUG, F("Inverter FW-Version: ") + String(fwVersion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cmd == SystemConfigPara) {
|
|
||||||
// get at least the firmware version and save it to the inverter object
|
|
||||||
if (getPosByChFld(0, FLD_ACT_PWR_LIMIT) == pos){
|
|
||||||
actPowerLimit = 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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RECORDTYPE getValue(uint8_t pos) {
|
REC_TYP getValue(uint8_t pos, record_t<> *rec) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getValue"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getValue"));
|
||||||
return record[pos];
|
if(NULL == rec)
|
||||||
|
return 0;
|
||||||
|
return rec->record[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
void doCalculations() {
|
void doCalculations() {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:doCalculations"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:doCalculations"));
|
||||||
uint8_t cmd = getQueuedCmd();
|
record_t<> *rec = getRecordStruct(RealTimeRunData_Debug);
|
||||||
getAssignment();
|
for(uint8_t i = 0; i < rec->length; i++) {
|
||||||
if (cmd == RealTimeRunData_Debug){
|
if(CMD_CALC == rec->assign[i].div) {
|
||||||
for(uint8_t i = 0; i < listLen; i++) {
|
rec->record[i] = calcFunctions<REC_TYP>[rec->assign[i].start].func(this, rec->assign[i].num);
|
||||||
if(CMD_CALC == assign[i].div) {
|
|
||||||
record[i] = calcFunctions<RECORDTYPE>[assign[i].start].func(this, assign[i].num);
|
|
||||||
}
|
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAvailable(uint32_t timestamp) {
|
bool isAvailable(uint32_t timestamp, record_t<> *rec) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:isAvailable"));
|
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"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:isProducing"));
|
||||||
if(isAvailable(timestamp)) {
|
if(isAvailable(timestamp, rec)) {
|
||||||
uint8_t pos = getPosByChFld(CH0, FLD_PAC);
|
uint8_t pos = getPosByChFld(CH0, FLD_PAC, rec);
|
||||||
return (getValue(pos) > INACT_PWR_THRESH);
|
return (getValue(pos, rec) > INACT_PWR_THRESH);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getLastTs(void) {
|
uint32_t getLastTs(record_t<> *rec) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getLastTs"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getLastTs"));
|
||||||
return ts;
|
return rec->ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getAssignment() {
|
record_t<> *getRecordStruct(uint8_t cmd) {
|
||||||
DPRINTLN(DBG_DEBUG, F("hmInverter.h:getAssignment"));
|
switch (cmd) {
|
||||||
// Default assignment;
|
case RealTimeRunData_Debug: return &recordMeas;
|
||||||
if (INV_TYPE_1CH == type) {
|
case InverterDevInform_All: return &recordInfo;
|
||||||
listLen = (uint8_t)(HM1CH_LIST_LEN);
|
case SystemConfigPara: return &recordConfig;
|
||||||
assign = (byteAssign_t *)hm1chAssignment;
|
case AlarmData: return &recordAlarm;
|
||||||
channels = 1;
|
default: break;
|
||||||
}
|
|
||||||
else if (INV_TYPE_2CH == type) {
|
|
||||||
listLen = (uint8_t)(HM2CH_LIST_LEN);
|
|
||||||
assign = (byteAssign_t *)hm2chAssignment;
|
|
||||||
channels = 2;
|
|
||||||
}
|
|
||||||
else if (INV_TYPE_4CH == type) {
|
|
||||||
listLen = (uint8_t)(HM4CH_LIST_LEN);
|
|
||||||
assign = (byteAssign_t *)hm4chAssignment;
|
|
||||||
channels = 4;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listLen = 0;
|
|
||||||
channels = 0;
|
|
||||||
assign = NULL;
|
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
switch (getQueuedCmd()) {
|
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:
|
case RealTimeRunData_Debug:
|
||||||
// Do nothing will use default
|
if (INV_TYPE_1CH == type) {
|
||||||
|
rec->length = (uint8_t)(HM1CH_LIST_LEN);
|
||||||
|
rec->assign = (byteAssign_t *)hm1chAssignment;
|
||||||
|
channels = 1;
|
||||||
|
}
|
||||||
|
else if (INV_TYPE_2CH == type) {
|
||||||
|
rec->length = (uint8_t)(HM2CH_LIST_LEN);
|
||||||
|
rec->assign = (byteAssign_t *)hm2chAssignment;
|
||||||
|
channels = 2;
|
||||||
|
}
|
||||||
|
else if (INV_TYPE_4CH == type) {
|
||||||
|
rec->length = (uint8_t)(HM4CH_LIST_LEN);
|
||||||
|
rec->assign = (byteAssign_t *)hm4chAssignment;
|
||||||
|
channels = 4;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rec->length = 0;
|
||||||
|
rec->assign = NULL;
|
||||||
|
channels = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case InverterDevInform_All:
|
case InverterDevInform_All:
|
||||||
listLen = (uint8_t)(HMINFO_LIST_LEN);
|
rec->length = (uint8_t)(HMINFO_LIST_LEN);
|
||||||
assign = (byteAssign_t *)InfoAssignment;
|
rec->assign = (byteAssign_t *)InfoAssignment;
|
||||||
break;
|
break;
|
||||||
case SystemConfigPara:
|
case SystemConfigPara:
|
||||||
listLen = (uint8_t)(HMSYSTEM_LIST_LEN);
|
rec->length = (uint8_t)(HMSYSTEM_LIST_LEN);
|
||||||
assign = (byteAssign_t *)SystemConfigParaAssignment;
|
rec->assign = (byteAssign_t *)SystemConfigParaAssignment;
|
||||||
break;
|
break;
|
||||||
case AlarmData:
|
case AlarmData:
|
||||||
listLen = (uint8_t)(HMALARMDATA_LIST_LEN);
|
rec->length = (uint8_t)(HMALARMDATA_LIST_LEN);
|
||||||
assign = (byteAssign_t *)AlarmDataAssignment;
|
rec->assign = (byteAssign_t *)AlarmDataAssignment;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DPRINTLN(DBG_INFO, "Parser not implemented");
|
DPRINTLN(DBG_INFO, F("initAssignment: Parser not implemented"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool isLiveDataAssignment(void) {
|
if(0 != rec->length) {
|
||||||
if(assign == (byteAssign_t *)hm1chAssignment)
|
rec->record = new REC_TYP[rec->length];
|
||||||
return true;
|
memset(rec->record, 0, sizeof(REC_TYP) * rec->length);
|
||||||
else if(assign == (byteAssign_t *)hm2chAssignment)
|
}
|
||||||
return true;
|
|
||||||
else if(assign == (byteAssign_t *)hm4chAssignment)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getAlarmStr(u_int16_t alarmCode) {
|
String getAlarmStr(u_int16_t alarmCode) {
|
||||||
|
@ -455,10 +474,11 @@ template<class T=float>
|
||||||
static T calcYieldTotalCh0(Inverter<> *iv, uint8_t arg0) {
|
static T calcYieldTotalCh0(Inverter<> *iv, uint8_t arg0) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcYieldTotalCh0"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcYieldTotalCh0"));
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
T yield = 0;
|
T yield = 0;
|
||||||
for(uint8_t i = 1; i <= iv->channels; i++) {
|
for(uint8_t i = 1; i <= iv->channels; i++) {
|
||||||
uint8_t pos = iv->getPosByChFld(i, FLD_YT);
|
uint8_t pos = iv->getPosByChFld(i, FLD_YT, rec);
|
||||||
yield += iv->getValue(pos);
|
yield += iv->getValue(pos, rec);
|
||||||
}
|
}
|
||||||
return yield;
|
return yield;
|
||||||
}
|
}
|
||||||
|
@ -469,10 +489,11 @@ template<class T=float>
|
||||||
static T calcYieldDayCh0(Inverter<> *iv, uint8_t arg0) {
|
static T calcYieldDayCh0(Inverter<> *iv, uint8_t arg0) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcYieldDayCh0"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcYieldDayCh0"));
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
T yield = 0;
|
T yield = 0;
|
||||||
for(uint8_t i = 1; i <= iv->channels; i++) {
|
for(uint8_t i = 1; i <= iv->channels; i++) {
|
||||||
uint8_t pos = iv->getPosByChFld(i, FLD_YD);
|
uint8_t pos = iv->getPosByChFld(i, FLD_YD, rec);
|
||||||
yield += iv->getValue(pos);
|
yield += iv->getValue(pos, rec);
|
||||||
}
|
}
|
||||||
return yield;
|
return yield;
|
||||||
}
|
}
|
||||||
|
@ -483,9 +504,10 @@ template<class T=float>
|
||||||
static T calcUdcCh(Inverter<> *iv, uint8_t arg0) {
|
static T calcUdcCh(Inverter<> *iv, uint8_t arg0) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcUdcCh"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcUdcCh"));
|
||||||
// arg0 = channel of source
|
// arg0 = channel of source
|
||||||
for(uint8_t i = 0; i < iv->listLen; i++) {
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
if((FLD_UDC == iv->assign[i].fieldId) && (arg0 == iv->assign[i].ch)) {
|
for(uint8_t i = 0; i < rec->length; i++) {
|
||||||
return iv->getValue(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) {
|
static T calcPowerDcCh0(Inverter<> *iv, uint8_t arg0) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcPowerDcCh0"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcPowerDcCh0"));
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
T dcPower = 0;
|
T dcPower = 0;
|
||||||
for(uint8_t i = 1; i <= iv->channels; i++) {
|
for(uint8_t i = 1; i <= iv->channels; i++) {
|
||||||
uint8_t pos = iv->getPosByChFld(i, FLD_PDC);
|
uint8_t pos = iv->getPosByChFld(i, FLD_PDC, rec);
|
||||||
dcPower += iv->getValue(pos);
|
dcPower += iv->getValue(pos, rec);
|
||||||
}
|
}
|
||||||
return dcPower;
|
return dcPower;
|
||||||
}
|
}
|
||||||
|
@ -510,12 +533,13 @@ template<class T=float>
|
||||||
static T calcEffiencyCh0(Inverter<> *iv, uint8_t arg0) {
|
static T calcEffiencyCh0(Inverter<> *iv, uint8_t arg0) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcEfficiencyCh0"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcEfficiencyCh0"));
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
uint8_t pos = iv->getPosByChFld(CH0, FLD_PAC);
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
T acPower = iv->getValue(pos);
|
uint8_t pos = iv->getPosByChFld(CH0, FLD_PAC, rec);
|
||||||
|
T acPower = iv->getValue(pos, rec);
|
||||||
T dcPower = 0;
|
T dcPower = 0;
|
||||||
for(uint8_t i = 1; i <= iv->channels; i++) {
|
for(uint8_t i = 1; i <= iv->channels; i++) {
|
||||||
pos = iv->getPosByChFld(i, FLD_PDC);
|
pos = iv->getPosByChFld(i, FLD_PDC, rec);
|
||||||
dcPower += iv->getValue(pos);
|
dcPower += iv->getValue(pos, rec);
|
||||||
}
|
}
|
||||||
if(dcPower > 0)
|
if(dcPower > 0)
|
||||||
return acPower / dcPower * 100.0f;
|
return acPower / dcPower * 100.0f;
|
||||||
|
@ -528,9 +552,10 @@ static T calcIrradiation(Inverter<> *iv, uint8_t arg0) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcIrradiation"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:calcIrradiation"));
|
||||||
// arg0 = channel
|
// arg0 = channel
|
||||||
if(NULL != iv) {
|
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)
|
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;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ class HmRadio {
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data) {
|
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendControlPacket"));
|
DPRINTLN(DBG_INFO, F("sendControlPacket"));
|
||||||
sendCmdPacket(invId, TX_REQ_DEVCONTROL, ALL_FRAMES, false); // 0x80 implementation as original DTU code
|
sendCmdPacket(invId, TX_REQ_DEVCONTROL, ALL_FRAMES, false); // 0x80 implementation as original DTU code
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
mTxBuf[10] = cmd; // cmd --> 0x0b => Type_ActivePowerContr, 0 on, 1 off, 2 restart, 12 reactive power, 13 power factor
|
mTxBuf[10] = cmd; // cmd --> 0x0b => Type_ActivePowerContr, 0 on, 1 off, 2 restart, 12 reactive power, 13 power factor
|
||||||
|
@ -187,17 +187,14 @@ class HmRadio {
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendTimePacket(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId) {
|
void sendTimePacket(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId) {
|
||||||
//DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendTimePacket"));
|
DPRINTLN(DBG_INFO, F("sendTimePacket"));
|
||||||
sendCmdPacket(invId, TX_REQ_INFO, ALL_FRAMES, false);
|
sendCmdPacket(invId, TX_REQ_INFO, ALL_FRAMES, false);
|
||||||
mTxBuf[10] = cmd; // cid
|
mTxBuf[10] = cmd; // cid
|
||||||
mTxBuf[11] = 0x00;
|
mTxBuf[11] = 0x00;
|
||||||
CP_U32_LittleEndian(&mTxBuf[12], ts);
|
CP_U32_LittleEndian(&mTxBuf[12], ts);
|
||||||
if (cmd == RealTimeRunData_Debug || cmd == AlarmData ){
|
if (cmd == RealTimeRunData_Debug || cmd == AlarmData ) {
|
||||||
mTxBuf[18] = (alarmMesId >> 8) & 0xff;
|
mTxBuf[18] = (alarmMesId >> 8) & 0xff;
|
||||||
mTxBuf[19] = (alarmMesId ) & 0xff;
|
mTxBuf[19] = (alarmMesId ) & 0xff;
|
||||||
} else {
|
|
||||||
mTxBuf[18] = 0x00;
|
|
||||||
mTxBuf[19] = 0x00;
|
|
||||||
}
|
}
|
||||||
uint16_t crc = Ahoy::crc16(&mTxBuf[10], 14);
|
uint16_t crc = Ahoy::crc16(&mTxBuf[10], 14);
|
||||||
mTxBuf[24] = (crc >> 8) & 0xff;
|
mTxBuf[24] = (crc >> 8) & 0xff;
|
||||||
|
@ -208,7 +205,7 @@ class HmRadio {
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendCmdPacket(uint64_t invId, uint8_t mid, uint8_t pid, bool calcCrc = true) {
|
void sendCmdPacket(uint64_t invId, uint8_t mid, uint8_t pid, bool calcCrc = true) {
|
||||||
//DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendCmdPacket"));
|
DPRINTLN(DBG_INFO, F("sendCmdPacket, mid: ") + String(mid, HEX) + F(" pid: ") + String(pid, HEX));
|
||||||
memset(mTxBuf, 0, MAX_RF_PAYLOAD_SIZE);
|
memset(mTxBuf, 0, MAX_RF_PAYLOAD_SIZE);
|
||||||
mTxBuf[0] = mid; // message id
|
mTxBuf[0] = mid; // message id
|
||||||
CP_U32_BigEndian(&mTxBuf[1], (invId >> 8));
|
CP_U32_BigEndian(&mTxBuf[1], (invId >> 8));
|
||||||
|
@ -221,7 +218,7 @@ class HmRadio {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkPaketCrc(uint8_t buf[], uint8_t *len, uint8_t rxCh) {
|
bool checkPaketCrc(uint8_t buf[], uint8_t *len, uint8_t rxCh) {
|
||||||
//DPRINTLN(DBG_VERBOSE, F("hmRadio.h:checkPaketCrc"));
|
//DPRINTLN(DBG_INFO, F("hmRadio.h:checkPaketCrc"));
|
||||||
*len = (buf[0] >> 2);
|
*len = (buf[0] >> 2);
|
||||||
if(*len > (MAX_RF_PAYLOAD_SIZE - 2))
|
if(*len > (MAX_RF_PAYLOAD_SIZE - 2))
|
||||||
*len = MAX_RF_PAYLOAD_SIZE - 2;
|
*len = MAX_RF_PAYLOAD_SIZE - 2;
|
||||||
|
@ -237,8 +234,6 @@ class HmRadio {
|
||||||
|
|
||||||
bool switchRxCh(uint16_t addLoop = 0) {
|
bool switchRxCh(uint16_t addLoop = 0) {
|
||||||
//DPRINTLN(DBG_VERBOSE, F("hmRadio.h:switchRxCh"));
|
//DPRINTLN(DBG_VERBOSE, F("hmRadio.h:switchRxCh"));
|
||||||
//DPRINTLN(DBG_VERBOSE, F("R"));
|
|
||||||
|
|
||||||
mRxLoopCnt += addLoop;
|
mRxLoopCnt += addLoop;
|
||||||
if(mRxLoopCnt != 0) {
|
if(mRxLoopCnt != 0) {
|
||||||
mRxLoopCnt--;
|
mRxLoopCnt--;
|
||||||
|
|
|
@ -63,14 +63,8 @@ class HmSystem {
|
||||||
uint8_t len = (uint8_t)strlen(name);
|
uint8_t len = (uint8_t)strlen(name);
|
||||||
strncpy(p->name, name, (len > MAX_NAME_LENGTH) ? MAX_NAME_LENGTH : len);
|
strncpy(p->name, name, (len > MAX_NAME_LENGTH) ? MAX_NAME_LENGTH : len);
|
||||||
|
|
||||||
if(NULL == p->assign) {
|
mNumInv ++;
|
||||||
DPRINT(DBG_ERROR, F("no assignment for type found!"));
|
return p;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mNumInv ++;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INVERTERTYPE *findInverter(uint8_t buf[]) {
|
INVERTERTYPE *findInverter(uint8_t buf[]) {
|
||||||
|
|
|
@ -27,14 +27,14 @@
|
||||||
|
|
||||||
<div id="note">
|
<div id="note">
|
||||||
This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a><br/>
|
This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a><br/>
|
||||||
New updates can be found on Github: <a href="https://github.com/grindylow/ahoy" target="_blank">https://github.com/grindylow/ahoy</a><br/>
|
New updates can be found on Github: <a href="https://github.com/lumapu/ahoy" target="_blank">https://github.com/lumapu/ahoy</a><br/>
|
||||||
<br/>
|
<br/>
|
||||||
Please report issues using the feature provided by <a href="https://github.com/grindylow/ahoy/issues">Github</a><br/>
|
Please report issues using the feature provided by <a href="https://github.com/lumapu/ahoy/issues">Github</a><br/>
|
||||||
<br/>
|
<br/>
|
||||||
Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a>
|
Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a>
|
||||||
<br/>
|
<br/>
|
||||||
<p class="lic"><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/>
|
<p class="lic"><a href="https://creativecommons.org/licenses/by-nc-sa/3.0/de">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/>
|
||||||
Check the licenses which are published on <a href="https://github.com/grindylow/ahoy">https://github.com/grindylow/ahoy</a> as well</p>
|
Check the licenses which are published on <a href="https://github.com/lumapu/ahoy">https://github.com/lumapu/ahoy</a> as well</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
|
|
|
@ -36,8 +36,11 @@
|
||||||
for(var iv of obj) {
|
for(var iv of obj) {
|
||||||
main = div(["iv"]);
|
main = div(["iv"]);
|
||||||
var ch0 = div(["ch-iv"]);
|
var ch0 = div(["ch-iv"]);
|
||||||
|
var limit = iv["power_limit_read"] + "%";
|
||||||
|
if(limit == "65535%")
|
||||||
|
limit = "n/a";
|
||||||
var ctrl = (iv["power_limit_active"]) ? "" : " (not controlled)";
|
var ctrl = (iv["power_limit_active"]) ? "" : " (not controlled)";
|
||||||
ch0.appendChild(span(iv["name"] + " Limit " + iv["power_limit_read"] + "%" + ctrl + " | last Alarm: " + iv["last_alarm"], ["head"]));
|
ch0.appendChild(span(iv["name"] + " Limit " + limit + ctrl + " | last Alarm: " + iv["last_alarm"], ["head"]));
|
||||||
|
|
||||||
for(var j = 0; j < root.ch0_fld_names.length; j++) {
|
for(var j = 0; j < root.ch0_fld_names.length; j++) {
|
||||||
var val = Math.round(iv["ch"][0][j] * 100) / 100;
|
var val = Math.round(iv["ch"][0][j] * 100) / 100;
|
||||||
|
|
|
@ -43,11 +43,6 @@ void webApi::onApi(AsyncWebServerRequest *request) {
|
||||||
if(path == "system") getSystem(root);
|
if(path == "system") getSystem(root);
|
||||||
else if(path == "statistics") getStatistics(root);
|
else if(path == "statistics") getStatistics(root);
|
||||||
else if(path == "inverter/list") getInverterList(root);
|
else if(path == "inverter/list") getInverterList(root);
|
||||||
else if(path == "mqtt") getMqtt(root);
|
|
||||||
else if(path == "ntp") getNtp(root);
|
|
||||||
else if(path == "pinout") getPinout(root);
|
|
||||||
else if(path == "radio") getRadio(root);
|
|
||||||
else if(path == "serial") getSerial(root);
|
|
||||||
else if(path == "index") getIndex(root);
|
else if(path == "index") getIndex(root);
|
||||||
else if(path == "setup") getSetup(root);
|
else if(path == "setup") getSetup(root);
|
||||||
else if(path == "live") getLive(root);
|
else if(path == "live") getLive(root);
|
||||||
|
@ -105,11 +100,6 @@ void webApi::getNotFound(JsonObject obj, String url) {
|
||||||
ep[F("system")] = url + F("system");
|
ep[F("system")] = url + F("system");
|
||||||
ep[F("statistics")] = url + F("statistics");
|
ep[F("statistics")] = url + F("statistics");
|
||||||
ep[F("inverter/list")] = url + F("inverter/list");
|
ep[F("inverter/list")] = url + F("inverter/list");
|
||||||
ep[F("mqtt")] = url + F("mqtt");
|
|
||||||
ep[F("ntp")] = url + F("ntp");
|
|
||||||
ep[F("pinout")] = url + F("pinout");
|
|
||||||
ep[F("radio")] = url + F("radio");
|
|
||||||
ep[F("serial")] = url + F("serial");
|
|
||||||
ep[F("index")] = url + F("index");
|
ep[F("index")] = url + F("index");
|
||||||
ep[F("setup")] = url + F("setup");
|
ep[F("setup")] = url + F("setup");
|
||||||
ep[F("live")] = url + F("live");
|
ep[F("live")] = url + F("live");
|
||||||
|
@ -216,13 +206,14 @@ void webApi::getIndex(JsonObject obj) {
|
||||||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
|
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
|
||||||
iv = mApp->mSys->getInverterByPos(i);
|
iv = mApp->mSys->getInverterByPos(i);
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
JsonObject invObj = inv.createNestedObject();
|
JsonObject invObj = inv.createNestedObject();
|
||||||
invObj[F("id")] = i;
|
invObj[F("id")] = i;
|
||||||
invObj[F("name")] = String(iv->name);
|
invObj[F("name")] = String(iv->name);
|
||||||
invObj[F("version")] = String(iv->fwVersion);
|
invObj[F("version")] = String(iv->fwVersion);
|
||||||
invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp());
|
invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp(), rec);
|
||||||
invObj[F("is_producing")] = iv->isProducing(mApp->getTimestamp());
|
invObj[F("is_producing")] = iv->isProducing(mApp->getTimestamp(), rec);
|
||||||
invObj[F("ts_last_success")] = iv->getLastTs();
|
invObj[F("ts_last_success")] = iv->getLastTs(rec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,42 +258,41 @@ void webApi::getLive(JsonObject obj) {
|
||||||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
|
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
|
||||||
iv = mApp->mSys->getInverterByPos(i);
|
iv = mApp->mSys->getInverterByPos(i);
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
if(iv->isLiveDataAssignment()) {
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
JsonObject obj2 = invArr.createNestedObject();
|
JsonObject obj2 = invArr.createNestedObject();
|
||||||
obj2[F("name")] = String(iv->name);
|
obj2[F("name")] = String(iv->name);
|
||||||
obj2[F("channels")] = iv->channels;
|
obj2[F("channels")] = iv->channels;
|
||||||
obj2[F("power_limit_read")] = iv->actPowerLimit;
|
obj2[F("power_limit_read")] = iv->actPowerLimit;
|
||||||
obj2[F("power_limit_active")] = NoPowerLimit != iv->powerLimit[1];
|
obj2[F("power_limit_active")] = NoPowerLimit != iv->powerLimit[1];
|
||||||
obj2[F("last_alarm")] = String(iv->lastAlarmMsg);
|
obj2[F("last_alarm")] = String(iv->lastAlarmMsg);
|
||||||
obj2[F("ts_last_success")] = iv->ts;
|
obj2[F("ts_last_success")] = rec->ts;
|
||||||
|
|
||||||
JsonArray ch = obj2.createNestedArray("ch");
|
JsonArray ch = obj2.createNestedArray("ch");
|
||||||
JsonArray ch0 = ch.createNestedArray();
|
JsonArray ch0 = ch.createNestedArray();
|
||||||
obj2[F("ch_names")][0] = "AC";
|
obj2[F("ch_names")][0] = "AC";
|
||||||
for (uint8_t fld = 0; fld < 11; fld++) {
|
for (uint8_t fld = 0; fld < 11; fld++) {
|
||||||
pos = (iv->getPosByChFld(CH0, list[fld]));
|
pos = (iv->getPosByChFld(CH0, list[fld], rec));
|
||||||
ch0[fld] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
ch0[fld] = (0xff != pos) ? iv->getValue(pos, rec) : 0.0;
|
||||||
obj[F("ch0_fld_units")][fld] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
obj[F("ch0_fld_units")][fld] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : F("n/a");
|
||||||
obj[F("ch0_fld_names")][fld] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
obj[F("ch0_fld_names")][fld] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : F("n/a");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(uint8_t j = 1; j <= iv->channels; j ++) {
|
for(uint8_t j = 1; j <= iv->channels; j ++) {
|
||||||
obj2[F("ch_names")][j] = String(iv->chName[j-1]);
|
obj2[F("ch_names")][j] = String(iv->chName[j-1]);
|
||||||
JsonArray cur = ch.createNestedArray();
|
JsonArray cur = ch.createNestedArray();
|
||||||
for (uint8_t k = 0; k < 6; k++) {
|
for (uint8_t k = 0; k < 6; k++) {
|
||||||
switch(k) {
|
switch(k) {
|
||||||
default: pos = (iv->getPosByChFld(j, FLD_UDC)); break;
|
default: pos = (iv->getPosByChFld(j, FLD_UDC, rec)); break;
|
||||||
case 1: pos = (iv->getPosByChFld(j, FLD_IDC)); break;
|
case 1: pos = (iv->getPosByChFld(j, FLD_IDC, rec)); break;
|
||||||
case 2: pos = (iv->getPosByChFld(j, FLD_PDC)); break;
|
case 2: pos = (iv->getPosByChFld(j, FLD_PDC, rec)); break;
|
||||||
case 3: pos = (iv->getPosByChFld(j, FLD_YD)); break;
|
case 3: pos = (iv->getPosByChFld(j, FLD_YD, rec)); break;
|
||||||
case 4: pos = (iv->getPosByChFld(j, FLD_YT)); break;
|
case 4: pos = (iv->getPosByChFld(j, FLD_YT, rec)); break;
|
||||||
case 5: pos = (iv->getPosByChFld(j, FLD_IRR)); break;
|
case 5: pos = (iv->getPosByChFld(j, FLD_IRR, rec)); break;
|
||||||
}
|
}
|
||||||
cur[k] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
cur[k] = (0xff != pos) ? iv->getValue(pos, rec) : 0.0;
|
||||||
if(1 == j) {
|
if(1 == j) {
|
||||||
obj[F("fld_units")][k] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
obj[F("fld_units")][k] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : F("n/a");
|
||||||
obj[F("fld_names")][k] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
obj[F("fld_names")][k] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : F("n/a");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue