diff --git a/meters/measurement_enumer.go b/meters/measurement_enumer.go index a2612a1b..7593c512 100644 --- a/meters/measurement_enumer.go +++ b/meters/measurement_enumer.go @@ -7,9 +7,9 @@ import ( "fmt" ) -const _MeasurementName = "FrequencyCurrentCurrentL1CurrentL2CurrentL3VoltageVoltageL1VoltageL2VoltageL3PowerPowerL1PowerL2PowerL3ImportPowerImportPowerL1ImportPowerL2ImportPowerL3ExportPowerExportPowerL1ExportPowerL2ExportPowerL3ReactivePowerReactivePowerL1ReactivePowerL2ReactivePowerL3ApparentPowerApparentPowerL1ApparentPowerL2ApparentPowerL3CosphiCosphiL1CosphiL2CosphiL3THDTHDL1THDL2THDL3SumSumT1SumT2SumL1SumL2SumL3ImportImportT1ImportT2ImportL1ImportL2ImportL3ExportExportT1ExportT2ExportL1ExportL2ExportL3ReactiveSumReactiveSumT1ReactiveSumT2ReactiveSumL1ReactiveSumL2ReactiveSumL3ReactiveImportReactiveImportT1ReactiveImportT2ReactiveImportL1ReactiveImportL2ReactiveImportL3ReactiveExportReactiveExportT1ReactiveExportT2ReactiveExportL1ReactiveExportL2ReactiveExportL3DCCurrentDCVoltageDCPowerHeatSinkTempDCCurrentS1DCVoltageS1DCPowerS1DCEnergyS1DCCurrentS2DCVoltageS2DCPowerS2DCEnergyS2DCCurrentS3DCVoltageS3DCPowerS3DCEnergyS3ChargeStateBatteryVoltagePhaseAngle" +const _MeasurementName = "FrequencyCurrentCurrentL1CurrentL2CurrentL3VoltageVoltageL1VoltageL2VoltageL3PowerPowerL1PowerL2PowerL3ImportPowerImportPowerL1ImportPowerL2ImportPowerL3ExportPowerExportPowerL1ExportPowerL2ExportPowerL3ReactivePowerReactivePowerL1ReactivePowerL2ReactivePowerL3ApparentPowerApparentPowerL1ApparentPowerL2ApparentPowerL3CosphiCosphiL1CosphiL2CosphiL3THDTHDL1THDL2THDL3SumSumT1SumT2SumL1SumL2SumL3ImportImportT1ImportT2ImportL1ImportL2ImportL3ExportExportT1ExportT2ExportL1ExportL2ExportL3ReactiveSumReactiveSumT1ReactiveSumT2ReactiveSumL1ReactiveSumL2ReactiveSumL3ReactiveImportReactiveImportT1ReactiveImportT2ReactiveImportL1ReactiveImportL2ReactiveImportL3ReactiveExportReactiveExportT1ReactiveExportT2ReactiveExportL1ReactiveExportL2ReactiveExportL3DCCurrentDCVoltageDCPowerHeatSinkTempDCCurrentS1DCVoltageS1DCPowerS1DCEnergyS1DCCurrentS2DCVoltageS2DCPowerS2DCEnergyS2DCCurrentS3DCVoltageS3DCPowerS3DCEnergyS3ChargeStateBatteryVoltageBatteryCurrentBatteryPowerPhaseAngleLoadVoltageLoadCurrentLoadPowerBatteryTemperatureInsideTemperatureMaxPVVoltageMinPVVoltageMaxBattVoltageMinBattVoltageConsumedEnergyTodayConsumedEnergyMonthConsumedEnergyYearConsumedEnergyTotalGeneratedEnergyTodayGeneratedEnergyMonthGeneratedEnergyYearGeneratedEnergyTotalEfficiencyPeakPower" -var _MeasurementIndex = [...]uint16{0, 9, 16, 25, 34, 43, 50, 59, 68, 77, 82, 89, 96, 103, 114, 127, 140, 153, 164, 177, 190, 203, 216, 231, 246, 261, 274, 289, 304, 319, 325, 333, 341, 349, 352, 357, 362, 367, 370, 375, 380, 385, 390, 395, 401, 409, 417, 425, 433, 441, 447, 455, 463, 471, 479, 487, 498, 511, 524, 537, 550, 563, 577, 593, 609, 625, 641, 657, 671, 687, 703, 719, 735, 751, 760, 769, 776, 788, 799, 810, 819, 829, 840, 851, 860, 870, 881, 892, 901, 911, 922, 936, 946} +var _MeasurementIndex = [...]uint16{0, 9, 16, 25, 34, 43, 50, 59, 68, 77, 82, 89, 96, 103, 114, 127, 140, 153, 164, 177, 190, 203, 216, 231, 246, 261, 274, 289, 304, 319, 325, 333, 341, 349, 352, 357, 362, 367, 370, 375, 380, 385, 390, 395, 401, 409, 417, 425, 433, 441, 447, 455, 463, 471, 479, 487, 498, 511, 524, 537, 550, 563, 577, 593, 609, 625, 641, 657, 671, 687, 703, 719, 735, 751, 760, 769, 776, 788, 799, 810, 819, 829, 840, 851, 860, 870, 881, 892, 901, 911, 922, 936, 950, 962, 972, 983, 994, 1003, 1021, 1038, 1050, 1062, 1076, 1090, 1109, 1128, 1146, 1165, 1185, 1205, 1224, 1244, 1254, 1263} func (i Measurement) String() string { i -= 1 @@ -19,101 +19,122 @@ func (i Measurement) String() string { return _MeasurementName[_MeasurementIndex[i]:_MeasurementIndex[i+1]] } -var _MeasurementValues = []Measurement{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92} +var _MeasurementValues = []Measurement{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113} var _MeasurementNameToValueMap = map[string]Measurement{ - _MeasurementName[0:9]: 1, - _MeasurementName[9:16]: 2, - _MeasurementName[16:25]: 3, - _MeasurementName[25:34]: 4, - _MeasurementName[34:43]: 5, - _MeasurementName[43:50]: 6, - _MeasurementName[50:59]: 7, - _MeasurementName[59:68]: 8, - _MeasurementName[68:77]: 9, - _MeasurementName[77:82]: 10, - _MeasurementName[82:89]: 11, - _MeasurementName[89:96]: 12, - _MeasurementName[96:103]: 13, - _MeasurementName[103:114]: 14, - _MeasurementName[114:127]: 15, - _MeasurementName[127:140]: 16, - _MeasurementName[140:153]: 17, - _MeasurementName[153:164]: 18, - _MeasurementName[164:177]: 19, - _MeasurementName[177:190]: 20, - _MeasurementName[190:203]: 21, - _MeasurementName[203:216]: 22, - _MeasurementName[216:231]: 23, - _MeasurementName[231:246]: 24, - _MeasurementName[246:261]: 25, - _MeasurementName[261:274]: 26, - _MeasurementName[274:289]: 27, - _MeasurementName[289:304]: 28, - _MeasurementName[304:319]: 29, - _MeasurementName[319:325]: 30, - _MeasurementName[325:333]: 31, - _MeasurementName[333:341]: 32, - _MeasurementName[341:349]: 33, - _MeasurementName[349:352]: 34, - _MeasurementName[352:357]: 35, - _MeasurementName[357:362]: 36, - _MeasurementName[362:367]: 37, - _MeasurementName[367:370]: 38, - _MeasurementName[370:375]: 39, - _MeasurementName[375:380]: 40, - _MeasurementName[380:385]: 41, - _MeasurementName[385:390]: 42, - _MeasurementName[390:395]: 43, - _MeasurementName[395:401]: 44, - _MeasurementName[401:409]: 45, - _MeasurementName[409:417]: 46, - _MeasurementName[417:425]: 47, - _MeasurementName[425:433]: 48, - _MeasurementName[433:441]: 49, - _MeasurementName[441:447]: 50, - _MeasurementName[447:455]: 51, - _MeasurementName[455:463]: 52, - _MeasurementName[463:471]: 53, - _MeasurementName[471:479]: 54, - _MeasurementName[479:487]: 55, - _MeasurementName[487:498]: 56, - _MeasurementName[498:511]: 57, - _MeasurementName[511:524]: 58, - _MeasurementName[524:537]: 59, - _MeasurementName[537:550]: 60, - _MeasurementName[550:563]: 61, - _MeasurementName[563:577]: 62, - _MeasurementName[577:593]: 63, - _MeasurementName[593:609]: 64, - _MeasurementName[609:625]: 65, - _MeasurementName[625:641]: 66, - _MeasurementName[641:657]: 67, - _MeasurementName[657:671]: 68, - _MeasurementName[671:687]: 69, - _MeasurementName[687:703]: 70, - _MeasurementName[703:719]: 71, - _MeasurementName[719:735]: 72, - _MeasurementName[735:751]: 73, - _MeasurementName[751:760]: 74, - _MeasurementName[760:769]: 75, - _MeasurementName[769:776]: 76, - _MeasurementName[776:788]: 77, - _MeasurementName[788:799]: 78, - _MeasurementName[799:810]: 79, - _MeasurementName[810:819]: 80, - _MeasurementName[819:829]: 81, - _MeasurementName[829:840]: 82, - _MeasurementName[840:851]: 83, - _MeasurementName[851:860]: 84, - _MeasurementName[860:870]: 85, - _MeasurementName[870:881]: 86, - _MeasurementName[881:892]: 87, - _MeasurementName[892:901]: 88, - _MeasurementName[901:911]: 89, - _MeasurementName[911:922]: 90, - _MeasurementName[922:936]: 91, - _MeasurementName[936:946]: 92, + _MeasurementName[0:9]: 1, + _MeasurementName[9:16]: 2, + _MeasurementName[16:25]: 3, + _MeasurementName[25:34]: 4, + _MeasurementName[34:43]: 5, + _MeasurementName[43:50]: 6, + _MeasurementName[50:59]: 7, + _MeasurementName[59:68]: 8, + _MeasurementName[68:77]: 9, + _MeasurementName[77:82]: 10, + _MeasurementName[82:89]: 11, + _MeasurementName[89:96]: 12, + _MeasurementName[96:103]: 13, + _MeasurementName[103:114]: 14, + _MeasurementName[114:127]: 15, + _MeasurementName[127:140]: 16, + _MeasurementName[140:153]: 17, + _MeasurementName[153:164]: 18, + _MeasurementName[164:177]: 19, + _MeasurementName[177:190]: 20, + _MeasurementName[190:203]: 21, + _MeasurementName[203:216]: 22, + _MeasurementName[216:231]: 23, + _MeasurementName[231:246]: 24, + _MeasurementName[246:261]: 25, + _MeasurementName[261:274]: 26, + _MeasurementName[274:289]: 27, + _MeasurementName[289:304]: 28, + _MeasurementName[304:319]: 29, + _MeasurementName[319:325]: 30, + _MeasurementName[325:333]: 31, + _MeasurementName[333:341]: 32, + _MeasurementName[341:349]: 33, + _MeasurementName[349:352]: 34, + _MeasurementName[352:357]: 35, + _MeasurementName[357:362]: 36, + _MeasurementName[362:367]: 37, + _MeasurementName[367:370]: 38, + _MeasurementName[370:375]: 39, + _MeasurementName[375:380]: 40, + _MeasurementName[380:385]: 41, + _MeasurementName[385:390]: 42, + _MeasurementName[390:395]: 43, + _MeasurementName[395:401]: 44, + _MeasurementName[401:409]: 45, + _MeasurementName[409:417]: 46, + _MeasurementName[417:425]: 47, + _MeasurementName[425:433]: 48, + _MeasurementName[433:441]: 49, + _MeasurementName[441:447]: 50, + _MeasurementName[447:455]: 51, + _MeasurementName[455:463]: 52, + _MeasurementName[463:471]: 53, + _MeasurementName[471:479]: 54, + _MeasurementName[479:487]: 55, + _MeasurementName[487:498]: 56, + _MeasurementName[498:511]: 57, + _MeasurementName[511:524]: 58, + _MeasurementName[524:537]: 59, + _MeasurementName[537:550]: 60, + _MeasurementName[550:563]: 61, + _MeasurementName[563:577]: 62, + _MeasurementName[577:593]: 63, + _MeasurementName[593:609]: 64, + _MeasurementName[609:625]: 65, + _MeasurementName[625:641]: 66, + _MeasurementName[641:657]: 67, + _MeasurementName[657:671]: 68, + _MeasurementName[671:687]: 69, + _MeasurementName[687:703]: 70, + _MeasurementName[703:719]: 71, + _MeasurementName[719:735]: 72, + _MeasurementName[735:751]: 73, + _MeasurementName[751:760]: 74, + _MeasurementName[760:769]: 75, + _MeasurementName[769:776]: 76, + _MeasurementName[776:788]: 77, + _MeasurementName[788:799]: 78, + _MeasurementName[799:810]: 79, + _MeasurementName[810:819]: 80, + _MeasurementName[819:829]: 81, + _MeasurementName[829:840]: 82, + _MeasurementName[840:851]: 83, + _MeasurementName[851:860]: 84, + _MeasurementName[860:870]: 85, + _MeasurementName[870:881]: 86, + _MeasurementName[881:892]: 87, + _MeasurementName[892:901]: 88, + _MeasurementName[901:911]: 89, + _MeasurementName[911:922]: 90, + _MeasurementName[922:936]: 91, + _MeasurementName[936:950]: 92, + _MeasurementName[950:962]: 93, + _MeasurementName[962:972]: 94, + _MeasurementName[972:983]: 95, + _MeasurementName[983:994]: 96, + _MeasurementName[994:1003]: 97, + _MeasurementName[1003:1021]: 98, + _MeasurementName[1021:1038]: 99, + _MeasurementName[1038:1050]: 100, + _MeasurementName[1050:1062]: 101, + _MeasurementName[1062:1076]: 102, + _MeasurementName[1076:1090]: 103, + _MeasurementName[1090:1109]: 104, + _MeasurementName[1109:1128]: 105, + _MeasurementName[1128:1146]: 106, + _MeasurementName[1146:1165]: 107, + _MeasurementName[1165:1185]: 108, + _MeasurementName[1185:1205]: 109, + _MeasurementName[1205:1224]: 110, + _MeasurementName[1224:1244]: 111, + _MeasurementName[1244:1254]: 112, + _MeasurementName[1254:1263]: 113, } // MeasurementString retrieves an enum value from the enum constants string name. diff --git a/meters/measurements.go b/meters/measurements.go index 9b013772..cf063751 100644 --- a/meters/measurements.go +++ b/meters/measurements.go @@ -1,265 +1,306 @@ -package meters - -import ( - "fmt" - "time" -) - -// MeasurementResult is the result of modbus read operation -type MeasurementResult struct { - Measurement - Value float64 - Timestamp time.Time -} - -func (r MeasurementResult) String() string { - _, unit := r.Measurement.DescriptionAndUnit() - return fmt.Sprintf("%s: %.2f%s", r.Measurement.String(), r.Value, unit) -} - -// Measurement is the type of measurement, i.e. the physical property being measued in common notation -type Measurement int - -//go:generate enumer -type=Measurement -const ( - _ Measurement = iota - - Frequency - - Current - CurrentL1 - CurrentL2 - CurrentL3 - - // phases and sums - Voltage - VoltageL1 - VoltageL2 - VoltageL3 - - Power // synonymous ActivePower - PowerL1 - PowerL2 - PowerL3 - - ImportPower - ImportPowerL1 - ImportPowerL2 - ImportPowerL3 - - ExportPower - ExportPowerL1 - ExportPowerL2 - ExportPowerL3 - - ReactivePower - ReactivePowerL1 - ReactivePowerL2 - ReactivePowerL3 - - ApparentPower - ApparentPowerL1 - ApparentPowerL2 - ApparentPowerL3 - - Cosphi - CosphiL1 - CosphiL2 - CosphiL3 - - THD - THDL1 - THDL2 - THDL3 - - // energy - Sum // synonymous ActiveEnergy - SumT1 - SumT2 - SumL1 - SumL2 - SumL3 - - Import - ImportT1 - ImportT2 - ImportL1 - ImportL2 - ImportL3 - - Export - ExportT1 - ExportT2 - ExportL1 - ExportL2 - ExportL3 - - ReactiveSum - ReactiveSumT1 - ReactiveSumT2 - ReactiveSumL1 - ReactiveSumL2 - ReactiveSumL3 - - ReactiveImport - ReactiveImportT1 - ReactiveImportT2 - ReactiveImportL1 - ReactiveImportL2 - ReactiveImportL3 - - ReactiveExport - ReactiveExportT1 - ReactiveExportT2 - ReactiveExportL1 - ReactiveExportL2 - ReactiveExportL3 - - // DC - DCCurrent - DCVoltage - DCPower - HeatSinkTemp - - // Strings - DCCurrentS1 - DCVoltageS1 - DCPowerS1 - DCEnergyS1 - DCCurrentS2 - DCVoltageS2 - DCPowerS2 - DCEnergyS2 - DCCurrentS3 - DCVoltageS3 - DCPowerS3 - DCEnergyS3 - - // Battery - ChargeState - BatteryVoltage - - PhaseAngle -) - -var iec = map[Measurement][]string{ - Frequency: {"Frequency", "Hz"}, - Current: {"Current", "A"}, - CurrentL1: {"L1 Current", "A"}, - CurrentL2: {"L2 Current", "A"}, - CurrentL3: {"L3 Current", "A"}, - Voltage: {"Voltage", "V"}, - VoltageL1: {"L1 Voltage", "V"}, - VoltageL2: {"L2 Voltage", "V"}, - VoltageL3: {"L3 Voltage", "V"}, - Power: {"Power", "W"}, - PowerL1: {"L1 Power", "W"}, - PowerL2: {"L2 Power", "W"}, - PowerL3: {"L3 Power", "W"}, - ImportPower: {"Import Power", "W"}, - ImportPowerL1: {"L1 Import Power", "W"}, - ImportPowerL2: {"L2 Import Power", "W"}, - ImportPowerL3: {"L3 Import Power", "W"}, - ExportPower: {"Export Power", "W"}, - ExportPowerL1: {"L1 Export Power", "W"}, - ExportPowerL2: {"L2 Export Power", "W"}, - ExportPowerL3: {"L3 Export Power", "W"}, - ReactivePower: {"Reactive Power", "var"}, - ReactivePowerL1: {"L1 Reactive Power", "var"}, - ReactivePowerL2: {"L2 Reactive Power", "var"}, - ReactivePowerL3: {"L3 Reactive Power", "var"}, - ApparentPower: {"Apparent Power", "VA"}, - ApparentPowerL1: {"L1 Apparent Power", "VA"}, - ApparentPowerL2: {"L2 Apparent Power", "VA"}, - ApparentPowerL3: {"L3 Apparent Power", "VA"}, - Cosphi: {"Cosphi"}, - CosphiL1: {"L1 Cosphi"}, - CosphiL2: {"L2 Cosphi"}, - CosphiL3: {"L3 Cosphi"}, - THD: {"Average voltage to neutral THD", "%"}, - THDL1: {"L1 Voltage to neutral THD", "%"}, - THDL2: {"L2 Voltage to neutral THD", "%"}, - THDL3: {"L3 Voltage to neutral THD", "%"}, - Sum: {"Total Sum", "kWh"}, - SumT1: {"Tariff 1 Sum", "kWh"}, - SumT2: {"Tariff 2 Sum", "kWh"}, - SumL1: {"L1 Sum", "kWh"}, - SumL2: {"L2 Sum", "kWh"}, - SumL3: {"L3 Sum", "kWh"}, - Import: {"Total Import", "kWh"}, - ImportT1: {"Tariff 1 Import", "kWh"}, - ImportT2: {"Tariff 2 Import", "kWh"}, - ImportL1: {"L1 Import", "kWh"}, - ImportL2: {"L2 Import", "kWh"}, - ImportL3: {"L3 Import", "kWh"}, - Export: {"Total Export", "kWh"}, - ExportT1: {"Tariff 1 Export", "kWh"}, - ExportT2: {"Tariff 2 Export", "kWh"}, - ExportL1: {"L1 Export", "kWh"}, - ExportL2: {"L2 Export", "kWh"}, - ExportL3: {"L3 Export", "kWh"}, - ReactiveSum: {"Total Reactive", "kvarh"}, - ReactiveSumT1: {"Tariff 1 Reactive", "kvarh"}, - ReactiveSumT2: {"Tariff 2 Reactive", "kvarh"}, - ReactiveSumL1: {"L1 Reactive", "kvarh"}, - ReactiveSumL2: {"L2 Reactive", "kvarh"}, - ReactiveSumL3: {"L3 Reactive", "kvarh"}, - ReactiveImport: {"Reactive Import", "kvarh"}, - ReactiveImportT1: {"Tariff 1 Reactive Import", "kvarh"}, - ReactiveImportT2: {"Tariff 2 Reactive Import", "kvarh"}, - ReactiveImportL1: {"L1 Reactive Import", "kvarh"}, - ReactiveImportL2: {"L2 Reactive Import", "kvarh"}, - ReactiveImportL3: {"L3 Reactive Import", "kvarh"}, - ReactiveExport: {"Reactive Export", "kvarh"}, - ReactiveExportT1: {"Tariff 1 Reactive Export", "kvarh"}, - ReactiveExportT2: {"Tariff 2 Reactive Export", "kvarh"}, - ReactiveExportL1: {"L1 Reactive Export", "kvarh"}, - ReactiveExportL2: {"L2 Reactive Export", "kvarh"}, - ReactiveExportL3: {"L3 Reactive Export", "kvarh"}, - DCCurrent: {"DC Current", "A"}, - DCVoltage: {"DC Voltage", "V"}, - DCPower: {"DC Power", "W"}, - HeatSinkTemp: {"Heat Sink Temperature", "°C"}, - DCCurrentS1: {"String 1 Current", "A"}, - DCVoltageS1: {"String 1 Voltage", "V"}, - DCPowerS1: {"String 1 Power", "W"}, - DCEnergyS1: {"String 1 Generation", "kWh"}, - DCCurrentS2: {"String 2 Current", "A"}, - DCVoltageS2: {"String 2 Voltage", "V"}, - DCPowerS2: {"String 2 Power", "W"}, - DCEnergyS2: {"String 2 Generation", "kWh"}, - DCCurrentS3: {"String 3 Current", "A"}, - DCVoltageS3: {"String 3 Voltage", "V"}, - DCPowerS3: {"String 3 Power", "W"}, - DCEnergyS3: {"String 3 Generation", "kWh"}, - ChargeState: {"Charge State", "%"}, - BatteryVoltage: {"Battery Voltage", "V"}, - PhaseAngle: {"Phase Angle", "°"}, -} - -// MarshalText implements encoding.TextMarshaler -func (m *Measurement) MarshalText() (text []byte, err error) { - return []byte(m.String()), nil -} - -// DescriptionAndUnit returns a measurements human-readable name and its unit -func (m *Measurement) DescriptionAndUnit() (string, string) { - if details, ok := iec[*m]; ok { - unit := "" - description := details[0] - if len(details) > 1 { - unit = details[1] - } - return description, unit - } - return m.String(), "" -} - -// Description returns a measurements human-readable name -func (m *Measurement) Description() string { - description, unit := m.DescriptionAndUnit() - if unit != "" { - description = description + " (" + unit + ")" - } - return description -} +package meters + +import ( + "fmt" + "time" +) + +// MeasurementResult is the result of modbus read operation +type MeasurementResult struct { + Measurement + Value float64 + Timestamp time.Time +} + +func (r MeasurementResult) String() string { + _, unit := r.Measurement.DescriptionAndUnit() + return fmt.Sprintf("%s: %.2f%s", r.Measurement.String(), r.Value, unit) +} + +// Measurement is the type of measurement, i.e. the physical property being measued in common notation +type Measurement int + +//go:generate enumer -type=Measurement +const ( + _ Measurement = iota + + Frequency + + Current + CurrentL1 + CurrentL2 + CurrentL3 + + // phases and sums + Voltage + VoltageL1 + VoltageL2 + VoltageL3 + + Power // synonymous ActivePower + PowerL1 + PowerL2 + PowerL3 + + ImportPower + ImportPowerL1 + ImportPowerL2 + ImportPowerL3 + + ExportPower + ExportPowerL1 + ExportPowerL2 + ExportPowerL3 + + ReactivePower + ReactivePowerL1 + ReactivePowerL2 + ReactivePowerL3 + + ApparentPower + ApparentPowerL1 + ApparentPowerL2 + ApparentPowerL3 + + Cosphi + CosphiL1 + CosphiL2 + CosphiL3 + + THD + THDL1 + THDL2 + THDL3 + + // energy + Sum // synonymous ActiveEnergy + SumT1 + SumT2 + SumL1 + SumL2 + SumL3 + + Import + ImportT1 + ImportT2 + ImportL1 + ImportL2 + ImportL3 + + Export + ExportT1 + ExportT2 + ExportL1 + ExportL2 + ExportL3 + + ReactiveSum + ReactiveSumT1 + ReactiveSumT2 + ReactiveSumL1 + ReactiveSumL2 + ReactiveSumL3 + + ReactiveImport + ReactiveImportT1 + ReactiveImportT2 + ReactiveImportL1 + ReactiveImportL2 + ReactiveImportL3 + + ReactiveExport + ReactiveExportT1 + ReactiveExportT2 + ReactiveExportL1 + ReactiveExportL2 + ReactiveExportL3 + + // DC + DCCurrent + DCVoltage + DCPower + HeatSinkTemp + + // Strings + DCCurrentS1 + DCVoltageS1 + DCPowerS1 + DCEnergyS1 + DCCurrentS2 + DCVoltageS2 + DCPowerS2 + DCEnergyS2 + DCCurrentS3 + DCVoltageS3 + DCPowerS3 + DCEnergyS3 + + // Battery + ChargeState + BatteryVoltage + BatteryCurrent + BatteryPower + PhaseAngle + LoadVoltage + LoadCurrent + LoadPower + BatteryTemperature + InsideTemperature + MaxPVVoltage + MinPVVoltage + MaxBattVoltage + MinBattVoltage + ConsumedEnergyToday + ConsumedEnergyMonth + ConsumedEnergyYear + ConsumedEnergyTotal + GeneratedEnergyToday + GeneratedEnergyMonth + GeneratedEnergyYear + GeneratedEnergyTotal + Efficiency + PeakPower +) + +var iec = map[Measurement][]string{ + Frequency: {"Frequency", "Hz"}, + Current: {"Current", "A"}, + CurrentL1: {"L1 Current", "A"}, + CurrentL2: {"L2 Current", "A"}, + CurrentL3: {"L3 Current", "A"}, + Voltage: {"Voltage", "V"}, + VoltageL1: {"L1 Voltage", "V"}, + VoltageL2: {"L2 Voltage", "V"}, + VoltageL3: {"L3 Voltage", "V"}, + Power: {"Power", "W"}, + PowerL1: {"L1 Power", "W"}, + PowerL2: {"L2 Power", "W"}, + PowerL3: {"L3 Power", "W"}, + ImportPower: {"Import Power", "W"}, + ImportPowerL1: {"L1 Import Power", "W"}, + ImportPowerL2: {"L2 Import Power", "W"}, + ImportPowerL3: {"L3 Import Power", "W"}, + ExportPower: {"Export Power", "W"}, + ExportPowerL1: {"L1 Export Power", "W"}, + ExportPowerL2: {"L2 Export Power", "W"}, + ExportPowerL3: {"L3 Export Power", "W"}, + ReactivePower: {"Reactive Power", "var"}, + ReactivePowerL1: {"L1 Reactive Power", "var"}, + ReactivePowerL2: {"L2 Reactive Power", "var"}, + ReactivePowerL3: {"L3 Reactive Power", "var"}, + ApparentPower: {"Apparent Power", "VA"}, + ApparentPowerL1: {"L1 Apparent Power", "VA"}, + ApparentPowerL2: {"L2 Apparent Power", "VA"}, + ApparentPowerL3: {"L3 Apparent Power", "VA"}, + Cosphi: {"Cosphi"}, + CosphiL1: {"L1 Cosphi"}, + CosphiL2: {"L2 Cosphi"}, + CosphiL3: {"L3 Cosphi"}, + THD: {"Average voltage to neutral THD", "%"}, + THDL1: {"L1 Voltage to neutral THD", "%"}, + THDL2: {"L2 Voltage to neutral THD", "%"}, + THDL3: {"L3 Voltage to neutral THD", "%"}, + Sum: {"Total Sum", "kWh"}, + SumT1: {"Tariff 1 Sum", "kWh"}, + SumT2: {"Tariff 2 Sum", "kWh"}, + SumL1: {"L1 Sum", "kWh"}, + SumL2: {"L2 Sum", "kWh"}, + SumL3: {"L3 Sum", "kWh"}, + Import: {"Total Import", "kWh"}, + ImportT1: {"Tariff 1 Import", "kWh"}, + ImportT2: {"Tariff 2 Import", "kWh"}, + ImportL1: {"L1 Import", "kWh"}, + ImportL2: {"L2 Import", "kWh"}, + ImportL3: {"L3 Import", "kWh"}, + Export: {"Total Export", "kWh"}, + ExportT1: {"Tariff 1 Export", "kWh"}, + ExportT2: {"Tariff 2 Export", "kWh"}, + ExportL1: {"L1 Export", "kWh"}, + ExportL2: {"L2 Export", "kWh"}, + ExportL3: {"L3 Export", "kWh"}, + ReactiveSum: {"Total Reactive", "kvarh"}, + ReactiveSumT1: {"Tariff 1 Reactive", "kvarh"}, + ReactiveSumT2: {"Tariff 2 Reactive", "kvarh"}, + ReactiveSumL1: {"L1 Reactive", "kvarh"}, + ReactiveSumL2: {"L2 Reactive", "kvarh"}, + ReactiveSumL3: {"L3 Reactive", "kvarh"}, + ReactiveImport: {"Reactive Import", "kvarh"}, + ReactiveImportT1: {"Tariff 1 Reactive Import", "kvarh"}, + ReactiveImportT2: {"Tariff 2 Reactive Import", "kvarh"}, + ReactiveImportL1: {"L1 Reactive Import", "kvarh"}, + ReactiveImportL2: {"L2 Reactive Import", "kvarh"}, + ReactiveImportL3: {"L3 Reactive Import", "kvarh"}, + ReactiveExport: {"Reactive Export", "kvarh"}, + ReactiveExportT1: {"Tariff 1 Reactive Export", "kvarh"}, + ReactiveExportT2: {"Tariff 2 Reactive Export", "kvarh"}, + ReactiveExportL1: {"L1 Reactive Export", "kvarh"}, + ReactiveExportL2: {"L2 Reactive Export", "kvarh"}, + ReactiveExportL3: {"L3 Reactive Export", "kvarh"}, + DCCurrent: {"DC Current", "A"}, + DCVoltage: {"DC Voltage", "V"}, + DCPower: {"DC Power", "W"}, + HeatSinkTemp: {"Heat Sink Temperature", "°C"}, + DCCurrentS1: {"String 1 Current", "A"}, + DCVoltageS1: {"String 1 Voltage", "V"}, + DCPowerS1: {"String 1 Power", "W"}, + DCEnergyS1: {"String 1 Generation", "kWh"}, + DCCurrentS2: {"String 2 Current", "A"}, + DCVoltageS2: {"String 2 Voltage", "V"}, + DCPowerS2: {"String 2 Power", "W"}, + DCEnergyS2: {"String 2 Generation", "kWh"}, + DCCurrentS3: {"String 3 Current", "A"}, + DCVoltageS3: {"String 3 Voltage", "V"}, + DCPowerS3: {"String 3 Power", "W"}, + DCEnergyS3: {"String 3 Generation", "kWh"}, + ChargeState: {"Charge State", "%"}, + BatteryVoltage: {"Battery Voltage", "V"}, + BatteryCurrent: {"Battery Current", "A"}, + BatteryPower: {"Battery Power", "W"}, + PhaseAngle: {"Phase Angle", "°"}, + LoadVoltage: {"Load Voltage", "V"}, + LoadCurrent: {"Load Current", "A"}, + LoadPower: {"Load Power", "W"}, + BatteryTemperature: {"Battery Temperature", "°C"}, + InsideTemperature: {"Battery Temperature", "°C"}, + MaxPVVoltage: {"max PV Voltage today", "V"}, + MinPVVoltage: {"min PV Voltage today", "V"}, + MaxBattVoltage: {"max Battery Voltage today", "V"}, + MinBattVoltage: {"min Battery Voltage today", "V"}, + ConsumedEnergyToday: {"Consumed Energy Today", "kWh"}, + ConsumedEnergyMonth: {"Consumed Energy Month", "kWh"}, + ConsumedEnergyYear: {"Consumed Energy Year", "kWh"}, + ConsumedEnergyTotal: {"Consumed Energy Total", "kWh"}, + GeneratedEnergyToday: {"Consumed Energy Today", "kWh"}, + GeneratedEnergyMonth: {"Consumed Energy Month", "kWh"}, + GeneratedEnergyYear: {"Consumed Energy Year", "kWh"}, + GeneratedEnergyTotal: {"Consumed Energy Total", "kWh"}, + Efficiency: {"Efficiancy Inverter", "%"}, + PeakPower: {"Peak active power of current day", "W"}, +} + +// MarshalText implements encoding.TextMarshaler +func (m *Measurement) MarshalText() (text []byte, err error) { + return []byte(m.String()), nil +} + +// DescriptionAndUnit returns a measurements human-readable name and its unit +func (m *Measurement) DescriptionAndUnit() (string, string) { + if details, ok := iec[*m]; ok { + unit := "" + description := details[0] + if len(details) > 1 { + unit = details[1] + } + return description, unit + } + return m.String(), "" +} + +// Description returns a measurements human-readable name +func (m *Measurement) Description() string { + description, unit := m.DescriptionAndUnit() + if unit != "" { + description = description + " (" + unit + ")" + } + return description +} diff --git a/meters/rs485/epevertracer.go b/meters/rs485/epevertracer.go new file mode 100644 index 00000000..5d1b5c44 --- /dev/null +++ b/meters/rs485/epevertracer.go @@ -0,0 +1,115 @@ +package rs485 + +import ( + . "github.com/volkszaehler/mbmd/meters" +) + +func init() { + Register("Epever", NewEpeverProducer) +} + +const ( + METERTYPE_EPEVER = "EPEVER" +) + +type EpeverProducer struct { + Opcodes +} + +func NewEpeverProducer() Producer { + /** + * Opcodes as defined by EPSolar + * from: http://www.solar-elektro.cz/data/dokumenty/1733_modbus_protocol.pdf + */ + ops := Opcodes{ + + DCVoltage: 0x3100, // Solar charge controller--PV array voltage + DCCurrent: 0x3101, // Solar charge controller--PV array current + DCPower: 0x3102, // Solar charge controller--PV array power + BatteryVoltage: 0x3104, // Batterie voltage + BatteryCurrent: 0x3105, // Batterie charging current + BatteryPower: 0x3106, // Batterie charging power + LoadVoltage: 0x310C, // Load voltage + LoadCurrent: 0x310D, // Load current + LoadPower: 0x310E, // Load power + BatteryTemperature: 0x3110, // Battery Temperature + InsideTemperature: 0x3111, // Temperature inside case + MaxPVVoltage: 0x3300, // maximum input voltage (PV) today + MinPVVoltage: 0x3301, // minimum input voltage (PV) today + MaxBattVoltage: 0x3302, // maximum battery voltage (PV) today + MinBattVoltage: 0x3303, // minimum battery voltage (PV) today + ConsumedEnergyToday: 0x3304, // Consumed Energy Today + ConsumedEnergyMonth: 0x3306, // Consumed Energy Month + ConsumedEnergyYear: 0x3308, // Consumed Energy Year + ConsumedEnergyTotal: 0x330A, // Consumed Energy Totaly + GeneratedEnergyToday: 0x330C, // Generated Energy Today + GeneratedEnergyMonth: 0x330E, // Generated Energy Month + GeneratedEnergyYear: 0x3310, // Generated Energy Year + GeneratedEnergyTotal: 0x3312, // Generated Energy Totaly + } + return &EpeverProducer{Opcodes: ops} +} + +func (p *EpeverProducer) Type() string { + return METERTYPE_EPEVER +} + +func (p *EpeverProducer) Description() string { + return "Epever Tracer" +} + +// snip creates modbus operation +func (p *EpeverProducer) snip(iec Measurement, readlen uint16) Operation { + return Operation{ + FuncCode: ReadInputReg, + OpCode: p.Opcode(iec), // adjust according to docs + ReadLen: readlen, + IEC61850: iec, + } +} + +// snip16 creates modbus operation for single register +func (p *EpeverProducer) snip16(iec Measurement, scaler ...float64) Operation { + snip := p.snip(iec, 2) + + snip.Transform = RTUUint16ToFloat64 // default conversion + if len(scaler) > 0 { + snip.Transform = MakeScaledTransform(snip.Transform, scaler[0]) + } + + return snip +} + +// snip32 creates modbus operation for double register +func (p *EpeverProducer) snip32(iec Measurement, scaler ...float64) Operation { + snip := p.snip(iec, 4) + + snip.Transform = RTUInt32ToFloat64Swapped // default conversion + if len(scaler) > 0 { + snip.Transform = MakeScaledTransform(snip.Transform, scaler[0]) + } + + return snip +} + +func (p *EpeverProducer) Probe() Operation { + return p.snip16(BatteryVoltage, 100) +} + +// Produce implements Producer interface +func (p *EpeverProducer) Produce() (res []Operation) { + + for _, op := range []Measurement{ + DCVoltage, DCCurrent, BatteryVoltage, BatteryCurrent, LoadVoltage, LoadCurrent, BatteryTemperature, InsideTemperature, MaxPVVoltage, MinPVVoltage, MaxBattVoltage, MinBattVoltage, + } { + res = append(res, p.snip16(op, 100)) + } + + for _, op := range []Measurement{ + DCPower, BatteryPower, LoadPower, ConsumedEnergyToday, ConsumedEnergyMonth, ConsumedEnergyYear, ConsumedEnergyTotal, GeneratedEnergyToday, GeneratedEnergyMonth, GeneratedEnergyYear, GeneratedEnergyTotal, + } { + res = append(res, p.snip32(op, 100)) + } + + return res +} diff --git a/meters/rs485/sun2000ktl.go b/meters/rs485/sun2000ktl.go new file mode 100644 index 00000000..e6c55da3 --- /dev/null +++ b/meters/rs485/sun2000ktl.go @@ -0,0 +1,138 @@ +package rs485 + +import ( + . "github.com/volkszaehler/mbmd/meters" +) + +func init() { + Register("Sun2000", NewSun2000Producer) +} + +const ( + METERTYPE_SUN2000 = "SUN2000" +) + +type Sun2000Producer struct { + Opcodes +} + +func NewSun2000Producer() Producer { + /** + * Opcodes as defined by Huawei Sun2000 + * from: https://javierin.com/wp-content/uploads/sites/2/2021/09/Solar-Inverter-Modbus-Interface-Definitions.pdf + */ + ops := Opcodes{ + + DCVoltageS1: 32016, // A maximum of 24 PV strings are supported. The number of PV + DCCurrentS1: 32017, // strings read by the host is defined by the Number of PV strings signal. + DCVoltageS2: 32018, // The voltage and current register addresses for each PV string are as follows: + DCCurrentS2: 32019, // PV n voltage: 32014 + 2 n + DCVoltageS3: 32020, // PV n current: 32015 + 2 n + DCCurrentS3: 32021, // n indicates the PV string number, which ranges from 1 to 24. + DCPower: 32064, // InputPower + Power: 32080, // AC Power Output + PeakPower: 32078, // Peak active power of current day + VoltageL1: 32069, // Phase A voltage + VoltageL2: 32070, // Phase B voltage + VoltageL3: 32071, // Phase C voltage + InsideTemperature: 32087, // Inside Temperature + Efficiency: 32086, // Efficiancy Inverter + GeneratedEnergyToday: 32114, // Daily energy yield + GeneratedEnergyTotal: 32106, // Accumulated energy yield + //VoltageL1: 37101, // Phase A voltage Smartmeter DTSU666-H + //VoltageL2: 37103, // Phase B voltage Smartmeter DTSU666-H + //VoltageL3: 37105, // Phase C voltage Smartmeter DTSU666-H + CurrentL1: 37107, // Phase A current Smartmeter DTSU666-H + CurrentL2: 37109, // Phase B current Smartmeter DTSU666-H + CurrentL3: 37111, // Phase C current Smartmeter DTSU666-H + ExportPower: 37113, // Active power Smartmeter DTSU666-H + ExportPowerL1: 37132, // Phase A Active power Smartmeter DTSU666-H + ExportPowerL2: 37134, // Phase B Active power Smartmeter DTSU666-H + ExportPowerL3: 37136, // Phase C Active power Smartmeter DTSU666-H + Frequency: 37118, // Grid frequency Smartmeter DTSU666-H + + } + return &Sun2000Producer{Opcodes: ops} +} + +func (p *Sun2000Producer) Type() string { + return METERTYPE_SUN2000 +} + +func (p *Sun2000Producer) Description() string { + return "Huawei Sun2000 Inverter" +} + +// snip creates modbus operation +func (p *Sun2000Producer) snip(iec Measurement, readlen uint16) Operation { + return Operation{ + FuncCode: ReadHoldingReg, + OpCode: p.Opcode(iec), // adjust according to docs + ReadLen: readlen, + IEC61850: iec, + } +} + +// snip16 creates modbus operation for single register +func (p *Sun2000Producer) snip16(iec Measurement, scaler ...float64) Operation { + snip := p.snip(iec, 2) + + snip.Transform = RTUUint16ToFloat64 // default conversion + if len(scaler) > 0 { + snip.Transform = MakeScaledTransform(snip.Transform, scaler[0]) + } + + return snip +} + +// snip32 creates modbus operation for double register +func (p *Sun2000Producer) snip32(iec Measurement, scaler ...float64) Operation { + snip := p.snip(iec, 4) + + snip.Transform = RTUInt32ToFloat64 // default conversion + if len(scaler) > 0 { + snip.Transform = MakeScaledTransform(snip.Transform, scaler[0]) + } + + return snip +} + +func (p *Sun2000Producer) Probe() Operation { + return p.snip32(VoltageL1, 10) +} + +// Produce implements Producer interface +func (p *Sun2000Producer) Produce() (res []Operation) { + + for _, op := range []Measurement{ + DCCurrentS1, DCCurrentS2, DCCurrentS3, Frequency, Efficiency, + } { + res = append(res, p.snip16(op, 100)) + } + + for _, op := range []Measurement{ + DCVoltageS1, DCVoltageS2, DCVoltageS3, VoltageL1, VoltageL2, VoltageL3, InsideTemperature, + } { + res = append(res, p.snip16(op, 10)) + } + + for _, op := range []Measurement{ + GeneratedEnergyToday, GeneratedEnergyTotal, + } { + res = append(res, p.snip32(op, 100)) + } + + for _, op := range []Measurement{ + CurrentL1, CurrentL2, CurrentL3, + } { + res = append(res, p.snip32(op, 10)) + } + + for _, op := range []Measurement{ + Power, ExportPowerL1, ExportPowerL2, ExportPowerL3, ExportPower, DCPower, PeakPower, + } { + res = append(res, p.snip32(op, 1)) + } + + return res +}