#ifndef __HM_INVERTER_H__ #define __HM_INVERTER_H__ #include "hmDefines.h" /** * For values which are of interest and not transmitted by the inverter can be * calculated automatically. * A list of functions can be linked to the assignment and will be executed * automatically. Their result does not differ from original read values. * The special command 0xff (CMDFF) must be used. */ // forward declaration of class template class Inverter; // prototypes template static T calcYieldTotalCh0(Inverter<> *iv, uint8_t arg0); template static T calcYieldDayCh0(Inverter<> *iv, uint8_t arg0); template static T calcUdcCh(Inverter<> *iv, uint8_t arg0); template using func_t = T (Inverter<> *, uint8_t); template struct calcFunc_t { uint8_t funcId; // unique id func_t* func; // function pointer } ; // list of all available functions, mapped in hmDefines.h template const calcFunc_t calcFunctions[] = { { CALC_YT_CH0, &calcYieldTotalCh0 }, { CALC_YD_CH0, &calcYieldDayCh0 }, { CALC_UDC_CH, &calcUdcCh } }; template 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 serial_u serial; // serial number as on barcode serial_u radioId; // id converted to modbus uint8_t channels; // number of PV channels (1-4) RECORDTYPE *record; // pointer for values Inverter() { } ~Inverter() { // TODO: cleanup } void init(void) { getAssignment(); toRadioId(); record = new RECORDTYPE[listLen]; memset(name, 0, MAX_NAME_LENGTH); memset(record, 0, sizeof(RECORDTYPE) * listLen); } uint8_t getPosByChFld(uint8_t channel, uint8_t fieldId) { uint8_t pos = 0; for(; pos < listLen; pos++) { if((assign[pos].ch == channel) && (assign[pos].fieldId == fieldId)) break; } return (pos >= listLen) ? 0xff : pos; } const char *getFieldName(uint8_t pos) { return fields[assign[pos].fieldId]; } const char *getUnit(uint8_t pos) { return units[assign[pos].unitId]; } uint8_t getChannel(uint8_t pos) { return assign[pos].ch; } uint8_t getCmdId(uint8_t pos) { return assign[pos].cmdId; } void addValue(uint8_t pos, uint8_t buf[]) { uint8_t ptr = assign[pos].start; uint8_t end = ptr + assign[pos].num; uint16_t div = assign[pos].div; uint32_t val = 0; do { val <<= 8; val |= buf[ptr]; } while(++ptr != end); record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div); } RECORDTYPE getValue(uint8_t pos) { return record[pos]; } void doCalculations(void) { for(uint8_t i = 0; i < listLen; i++) { if(CMDFF == assign[i].cmdId) { record[i] = calcFunctions[assign[i].start].func(this, assign[i].num); } } } private: void toRadioId(void) { radioId.u64 = 0ULL; radioId.b[4] = serial.b[0]; radioId.b[3] = serial.b[1]; radioId.b[2] = serial.b[2]; radioId.b[1] = serial.b[3]; radioId.b[0] = 0x01; } void getAssignment(void) { if(INV_TYPE_HM400 == type) { listLen = (uint8_t)(HM400_LIST_LEN); assign = (byteAssign_t*)hm400assignment; channels = 1; } else if(INV_TYPE_HM600 == type) { listLen = (uint8_t)(HM600_LIST_LEN); assign = (byteAssign_t*)hm600assignment; channels = 2; } else if(INV_TYPE_HM800 == type) { listLen = (uint8_t)(HM800_LIST_LEN); assign = (byteAssign_t*)hm800assignment; channels = 2; } else if(INV_TYPE_HM1200 == type) { listLen = (uint8_t)(HM1200_LIST_LEN); assign = (byteAssign_t*)hm1200assignment; channels = 4; } else { listLen = 0; channels = 0; assign = NULL; } } }; /** * To calculate values which are not transmitted by the unit there is a generic * list of functions which can be linked to the assignment. * The special command 0xff (CMDFF) must be used. */ template static T calcYieldTotalCh0(Inverter<> *iv, uint8_t arg0) { if(NULL != iv) { T yield = 0; for(uint8_t i = 1; i <= iv->channels; i++) { uint8_t pos = iv->getPosByChFld(i, FLD_YT); yield += iv->getValue(pos); } return yield; } return 0.0; } template static T calcYieldDayCh0(Inverter<> *iv, uint8_t arg0) { if(NULL != iv) { T yield = 0; for(uint8_t i = 1; i <= iv->channels; i++) { uint8_t pos = iv->getPosByChFld(i, FLD_YD); yield += iv->getValue(pos); } return yield; } return 0.0; } template static T calcUdcCh(Inverter<> *iv, uint8_t arg0) { // 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); } } return 0.0; } #endif /*__HM_INVERTER_H__*/