mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 00:36:25 +00:00
76_SolarForecast: internal code change for data collection
git-svn-id: https://svn.fhem.de/fhem/trunk@29531 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
46e2aaa51e
commit
212641ccbd
@ -1,5 +1,6 @@
|
||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||
# Do not insert empty lines here, update check depends on it
|
||||
- change: 76_SolarForecast: internal code change for data collection
|
||||
- change: 76_SolarForecast: FlowGraphic: SoC as a Cluster value of all Batts
|
||||
- bugfix: 76_SolarForecast: bugfix of version 1.43.3
|
||||
- change: 76_SolarForecast: consumption fc calc switch from average to median
|
||||
|
@ -157,6 +157,7 @@ BEGIN {
|
||||
|
||||
# Versions History intern
|
||||
my %vNotesIntern = (
|
||||
"1.43.6" => "17.01.2025 _calcCaQcomplex: additional write pvrl, pvfc to separate circular hash Array elements, listDataPool: show these Arrays ",
|
||||
"1.43.5" => "15.01.2025 _flowGraphic: calculate the resulting SoC as a cluster of batteries ",
|
||||
"1.43.4" => "14.01.2025 batsocslidereg: calculate the SoC as summary over all capacities in Wh, bugfix https://forum.fhem.de/index.php?msg=1330559 ",
|
||||
"1.43.3" => "13.01.2025 add Wiki icon in graphic header, _calcConsumptionForecast: switch calc from average to median, edit comref ",
|
||||
@ -7093,9 +7094,8 @@ sub writeCacheToFile {
|
||||
return ('', $nr, $na);
|
||||
}
|
||||
|
||||
my @arr;
|
||||
return if(!keys %{$data{$name}{$cachename}});
|
||||
push @arr, encode_json ($data{$name}{$cachename});
|
||||
push my @arr, encode_json ($data{$name}{$cachename});
|
||||
|
||||
$error = FileWrite ($file, @arr);
|
||||
|
||||
@ -8747,7 +8747,7 @@ sub _transferAPIRadiationValues {
|
||||
my $est = __calcPVestimates ($paref);
|
||||
my ($msg, $pvaifc) = aiGetResult ($paref); # KI Entscheidungen abfragen
|
||||
|
||||
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
|
||||
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
|
||||
|
||||
delete $paref->{fd};
|
||||
delete $paref->{fh1};
|
||||
@ -12225,20 +12225,28 @@ sub _calcCaQcomplex {
|
||||
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the complex correction factor");
|
||||
return;
|
||||
}
|
||||
|
||||
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
|
||||
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
|
||||
|
||||
my $hh = sprintf "%02d", $h;
|
||||
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0); # real erzeugte PV Energie am Ende der vorherigen Stunde
|
||||
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0); # vorhergesagte PV Energie am Ende der vorherigen Stunde
|
||||
|
||||
if (!$pvrl || !$pvfc) {
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
|
||||
return;
|
||||
}
|
||||
|
||||
my $chwcc = HistoryVal ($hash, $day, sprintf("%02d",$h), 'wcc', 0); # Wolkenbedeckung Heute & abgefragte Stunde
|
||||
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
|
||||
my $chwcc = HistoryVal ($hash, $day, $hh, 'wcc', 0); # Wolkenbedeckung heute & abgefragte Stunde
|
||||
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
|
||||
my $crang = cloud2bin ($chwcc);
|
||||
my $sabin = sunalt2bin ($sunalt);
|
||||
|
||||
## Speicherarrays schreiben
|
||||
#############################
|
||||
push @{$data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}}, $pvrl;
|
||||
push @{$data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}}, $pvfc;
|
||||
|
||||
## neuen Korrekturfaktor berechnen
|
||||
####################################
|
||||
$paref->{pvrl} = $pvrl;
|
||||
$paref->{pvfc} = $pvfc;
|
||||
$paref->{crang} = $crang;
|
||||
@ -12253,16 +12261,16 @@ sub _calcCaQcomplex {
|
||||
delete $paref->{sabin};
|
||||
delete $paref->{calc};
|
||||
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
|
||||
|
||||
$aihit = $aihit ? ' AI result used,' : '';
|
||||
|
||||
if ($acu =~ /on_complex/xs) {
|
||||
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
|
||||
}
|
||||
else {
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
|
||||
}
|
||||
}
|
||||
|
||||
@ -12284,28 +12292,29 @@ sub _calcCaQsimple {
|
||||
my $aihit = $paref->{aihit};
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', '');
|
||||
my $hh = sprintf "%02d", $h;
|
||||
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.$hh.'_apipercentil', '');
|
||||
|
||||
if($sr eq "done") {
|
||||
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".sprintf("%02d",$h)." already calculated");
|
||||
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".$hh." already calculated");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$aln) {
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
|
||||
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the simple correction factor");
|
||||
return;
|
||||
}
|
||||
|
||||
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
|
||||
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
|
||||
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0);
|
||||
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0);
|
||||
|
||||
if (!$pvrl || !$pvfc) {
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
|
||||
return;
|
||||
}
|
||||
|
||||
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
|
||||
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
|
||||
my $sabin = sunalt2bin ($sunalt);
|
||||
|
||||
$paref->{pvrl} = $pvrl;
|
||||
@ -12322,16 +12331,16 @@ sub _calcCaQsimple {
|
||||
delete $paref->{crang};
|
||||
delete $paref->{calc};
|
||||
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
|
||||
|
||||
$aihit = $aihit ? ' AI result used,' : '';
|
||||
|
||||
if ($acu =~ /on_simple/xs) {
|
||||
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
|
||||
}
|
||||
else {
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
|
||||
}
|
||||
}
|
||||
|
||||
@ -12359,8 +12368,9 @@ sub __calcNewFactor {
|
||||
|
||||
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> Start calculation correction factor for hour: $h");
|
||||
|
||||
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
|
||||
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0);
|
||||
my $hh = sprintf "%02d", $h;
|
||||
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, $hh, $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
|
||||
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, $hh, $sabin, $crang, 0);
|
||||
$oldfac = 1 if(1 * $oldfac == 0);
|
||||
|
||||
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> read historical values: pv real sum: $pvhis, pv forecast sum: $fchis, days sum: $dnum");
|
||||
@ -12403,18 +12413,18 @@ sub __calcNewFactor {
|
||||
|
||||
if ($crang ne 'simple') {
|
||||
my $idx = $sabin.'.'.$crang; # value für pvcorrf Sonne Altitude
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$idx} = $factor;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$idx} = $qual;
|
||||
$data{$name}{circular}{$hh}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
|
||||
$data{$name}{circular}{$hh}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
|
||||
$data{$name}{circular}{$hh}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
|
||||
$data{$name}{circular}{$hh}{pvcorrf}{$idx} = $factor;
|
||||
$data{$name}{circular}{$hh}{quality}{$idx} = $qual;
|
||||
}
|
||||
else {
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$crang} = $pvrlsum;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$crang} = $pvfcsum;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$crang} = $dnum;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$crang} = $factor;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$crang} = $qual;
|
||||
$data{$name}{circular}{$hh}{pvrlsum}{$crang} = $pvrlsum;
|
||||
$data{$name}{circular}{$hh}{pvfcsum}{$crang} = $pvfcsum;
|
||||
$data{$name}{circular}{$hh}{dnumsum}{$crang} = $dnum;
|
||||
$data{$name}{circular}{$hh}{pvcorrf}{$crang} = $factor;
|
||||
$data{$name}{circular}{$hh}{quality}{$crang} = $qual;
|
||||
}
|
||||
|
||||
$oldfac = sprintf "%.2f", $oldfac;
|
||||
@ -12422,6 +12432,27 @@ sub __calcNewFactor {
|
||||
return ($oldfac, $factor, $dnum);
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Qualität der Vorhersage berechnen
|
||||
################################################################
|
||||
sub __calcFcQuality {
|
||||
my $pvfc = shift; # PV Vorhersagewert
|
||||
my $pvrl = shift; # PV reale Erzeugung
|
||||
|
||||
return if(!$pvfc || !$pvrl);
|
||||
|
||||
$pvrl = sprintf "%.0f", $pvrl;
|
||||
$pvfc = sprintf "%.0f", $pvfc;
|
||||
|
||||
my $diff = $pvfc - $pvrl;
|
||||
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
|
||||
|
||||
$hdv = $hdv < 0 ? 0 : $hdv;
|
||||
$hdv = sprintf "%.2f", $hdv;
|
||||
|
||||
return $hdv;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Berechnen Tag / Stunden Verschieber
|
||||
# aus aktueller Stunde + lfd. Nummer
|
||||
@ -16252,27 +16283,6 @@ sub _addHourAiRawdata {
|
||||
return;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Qualität der Vorhersage berechnen
|
||||
################################################################
|
||||
sub __calcFcQuality {
|
||||
my $pvfc = shift; # PV Vorhersagewert
|
||||
my $pvrl = shift; # PV reale Erzeugung
|
||||
|
||||
return if(!$pvfc || !$pvrl);
|
||||
|
||||
$pvrl = sprintf "%.0f", $pvrl;
|
||||
$pvfc = sprintf "%.0f", $pvfc;
|
||||
|
||||
my $diff = $pvfc - $pvrl;
|
||||
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
|
||||
|
||||
$hdv = $hdv < 0 ? 0 : $hdv;
|
||||
$hdv = sprintf "%.2f", $hdv;
|
||||
|
||||
return $hdv;
|
||||
}
|
||||
|
||||
###############################################################
|
||||
# Eintritt in den KI Train Prozess normal/Blocking
|
||||
###############################################################
|
||||
@ -17112,14 +17122,6 @@ sub listDataPool {
|
||||
|
||||
my $sub = sub {
|
||||
my $day = shift;
|
||||
|
||||
#for my $dh (keys %{$h->{$day}}) {
|
||||
# if (!isNumeric ($dh)) {
|
||||
# delete $data{$name}{pvhist}{$day}{$dh};
|
||||
# Log3 ($name, 2, qq{$name - INFO - invalid key "$day -> $dh" was deleted from pvHistory storage});
|
||||
# }
|
||||
#}
|
||||
|
||||
my $ret;
|
||||
|
||||
for my $key (sort {$a<=>$b} keys %{$h->{$day}}) {
|
||||
@ -17404,7 +17406,7 @@ sub listDataPool {
|
||||
|
||||
for my $ckey (sort keys %{$h->{$idx}}) {
|
||||
if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
|
||||
my $aser = join " ",@{$h->{$idx}{$ckey}};
|
||||
my $aser = join " ", @{$h->{$idx}{$ckey}};
|
||||
$cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
|
||||
}
|
||||
|
||||
@ -17431,6 +17433,35 @@ sub listDataPool {
|
||||
if (!keys %{$h}) {
|
||||
return qq{Circular cache is empty.};
|
||||
}
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
##########################################################################################################################
|
||||
delete $data{$name}{circular}{'01'}{pvrl_5};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_10};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_25};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_60};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_65};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_90};
|
||||
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4562;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4563;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4564;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4565;
|
||||
#
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3562;
|
||||
#
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4562;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4563;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4564;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4565;
|
||||
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3562;
|
||||
|
||||
############################################################################################################
|
||||
|
||||
for my $idx (sort keys %{$h}) {
|
||||
my $pvrl = CircularVal ($hash, $idx, 'pvrl', '-');
|
||||
@ -17490,17 +17521,41 @@ sub listDataPool {
|
||||
$bout .= "batout${bn}: $batout";
|
||||
}
|
||||
|
||||
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl\n";
|
||||
$sq .= " $bin\n";
|
||||
$sq .= " $bout\n";
|
||||
$sq .= " confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c\n";
|
||||
$sq .= " temp: $temp, wid: $wid, wtxt: $wtxt\n";
|
||||
$sq .= " $prdl\n";
|
||||
$sq .= " pvcorrf: $pvcf\n";
|
||||
$sq .= " quality: $cfq\n";
|
||||
$sq .= " pvrlsum: $pvrs\n";
|
||||
$sq .= " pvfcsum: $pvfs\n";
|
||||
$sq .= " dnumsum: $dnus";
|
||||
my ($pvrlnew, $pvfcnew);
|
||||
my @pvrlkeys = map { $_ =~ /^pvrl_/xs ? $_ : '' } sort keys %{$h->{$idx}};
|
||||
my @pvfckeys = map { $_ =~ /^pvfc_/xs ? $_ : '' } sort keys %{$h->{$idx}};
|
||||
|
||||
for my $prl (@pvrlkeys) {
|
||||
next if(!$prl);
|
||||
my $lref = CircularVal ($hash, $idx, $prl, '');
|
||||
next if(!$lref);
|
||||
|
||||
$pvrlnew .= "\n " if($pvrlnew);
|
||||
$pvrlnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $prl, cval => $lref } );
|
||||
}
|
||||
|
||||
for my $pfc (@pvfckeys) {
|
||||
next if(!$pfc);
|
||||
my $cref = CircularVal ($hash, $idx, $pfc, '');
|
||||
next if(!$cref);
|
||||
|
||||
$pvfcnew .= "\n " if($pvfcnew);
|
||||
$pvfcnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $pfc, cval => $cref } );
|
||||
}
|
||||
|
||||
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl";
|
||||
$sq .= "\n $bin";
|
||||
$sq .= "\n $bout";
|
||||
$sq .= "\n confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c";
|
||||
$sq .= "\n temp: $temp, wid: $wid, wtxt: $wtxt";
|
||||
$sq .= "\n $prdl";
|
||||
$sq .= "\n pvcorrf: $pvcf";
|
||||
$sq .= "\n quality: $cfq";
|
||||
$sq .= "\n pvrlsum: $pvrs";
|
||||
$sq .= "\n pvfcsum: $pvfs";
|
||||
$sq .= "\n dnumsum: $dnus";
|
||||
$sq .= "\n $pvrlnew" if($pvrlnew);
|
||||
$sq .= "\n $pvfcnew" if($pvfcnew);
|
||||
}
|
||||
else {
|
||||
my ($batvl1, $batvl2, $batvl3, $batvl4, $batvl5, $batvl6, $batvl7);
|
||||
@ -17772,7 +17827,24 @@ sub _ldchash2val {
|
||||
|
||||
for my $f (sort {$a<=>$b} keys %{$pool->{$idx}{$key}}) {
|
||||
next if($f eq 'simple');
|
||||
if ($f !~ /\./xs) {
|
||||
|
||||
if (ref $pool->{$idx}{$key}{$f} eq 'ARRAY') {
|
||||
my @sub_arrays = arraySplitBy (20, @{$pool->{$idx}{$key}{$f}}); # Array in Teil-Arrays zu je 20 Elemente aufteilen
|
||||
|
||||
for my $suaref (@sub_arrays) { # für jedes Teil-Array Join ausführen
|
||||
my $suajoined = join ' ', @{$suaref};
|
||||
|
||||
if (!$ret) {
|
||||
$ret .= $key.' => ';
|
||||
$ret .= $f.' @ '.$suajoined;
|
||||
}
|
||||
else {
|
||||
$ret .= "\n ";
|
||||
$ret .= $f.' @ '.$suajoined;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($f !~ /\./xs) {
|
||||
$ret .= " " if($ret);
|
||||
$ret .= "$f=".$pool->{$idx}{$key}{$f};
|
||||
my $ct = ($ret =~ tr/=// // 0) / 10;
|
||||
@ -20219,6 +20291,48 @@ sub sunalt2bin {
|
||||
return $bin;
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Teilt das Original-Array in Unter-Arrays auf, die den Inhalt des
|
||||
# Originals enthalten. Die Größe jedes Unterarrays ist gleich oder kleiner als
|
||||
# $split_size, wobei das letzte Array in der Regel kleiner ist, wenn nicht
|
||||
# genügend Elemente in @original vorhanden sind.
|
||||
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
|
||||
#
|
||||
# arraySplitBy ($split_size, @original)
|
||||
###############################################################################
|
||||
sub arraySplitBy {
|
||||
my $split_size = shift;
|
||||
|
||||
$split_size = max ($split_size, 1);
|
||||
my @sub_arrays;
|
||||
|
||||
while (@_) {
|
||||
push @sub_arrays, [splice @_, 0, $split_size];
|
||||
}
|
||||
|
||||
return @sub_arrays;
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Teilt das angegebene Array in die Anzahl $count Unterarrays auf.
|
||||
# Es wird versucht, so viele Unter-Arrays zu erstellen, wie $count angibt,
|
||||
# aber es werden weniger zurückgegeben, wenn nicht genügend Elemente in
|
||||
# @original vorhanden sind.
|
||||
#
|
||||
# Gibt eine Liste von Array-Referenzen zurück.
|
||||
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
|
||||
#
|
||||
# arraySplitInto ($count, @original)
|
||||
###############################################################################
|
||||
sub arraySplitInto {
|
||||
my ($count, @original) = @_;
|
||||
|
||||
$count = max( $count, 1 );
|
||||
my $size = ceil @original / $count;
|
||||
|
||||
return arraySplitBy ($size, @original);
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# verscrambelt einen String
|
||||
###############################################################################
|
||||
|
@ -157,6 +157,8 @@ BEGIN {
|
||||
|
||||
# Versions History intern
|
||||
my %vNotesIntern = (
|
||||
"1.43.6" => "17.01.2025 _calcCaQcomplex: additional write pvrl, pvfc to separate circular hash Array elements, listDataPool: show these Arrays ",
|
||||
"1.43.5" => "15.01.2025 _flowGraphic: calculate the resulting SoC as a cluster of batteries ",
|
||||
"1.43.4" => "14.01.2025 batsocslidereg: calculate the SoC as summary over all capacities in Wh, bugfix https://forum.fhem.de/index.php?msg=1330559 ",
|
||||
"1.43.3" => "13.01.2025 add Wiki icon in graphic header, _calcConsumptionForecast: switch calc from average to median, edit comref ",
|
||||
"1.43.2" => "12.01.2025 _batChargeRecmd: bugfix calc socwh, Attr graphicBeam1MaxVal, (experimental) ctrlAreaFactorUsage are obsolete ".
|
||||
@ -7092,9 +7094,8 @@ sub writeCacheToFile {
|
||||
return ('', $nr, $na);
|
||||
}
|
||||
|
||||
my @arr;
|
||||
return if(!keys %{$data{$name}{$cachename}});
|
||||
push @arr, encode_json ($data{$name}{$cachename});
|
||||
push my @arr, encode_json ($data{$name}{$cachename});
|
||||
|
||||
$error = FileWrite ($file, @arr);
|
||||
|
||||
@ -8746,7 +8747,7 @@ sub _transferAPIRadiationValues {
|
||||
my $est = __calcPVestimates ($paref);
|
||||
my ($msg, $pvaifc) = aiGetResult ($paref); # KI Entscheidungen abfragen
|
||||
|
||||
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
|
||||
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
|
||||
|
||||
delete $paref->{fd};
|
||||
delete $paref->{fh1};
|
||||
@ -9571,14 +9572,15 @@ sub _transferBatteryValues {
|
||||
storeReading ('Current_PowerBatOut_'.$bn, $pbo.' W');
|
||||
storeReading ('Current_BatCharge_'. $bn, $soc.' %');
|
||||
|
||||
$data{$name}{batteries}{$bn}{bname} = $badev; # Batterie Devicename
|
||||
$data{$name}{batteries}{$bn}{balias} = AttrVal ($badev, 'alias', $badev); # Alias Batterie Device
|
||||
$data{$name}{batteries}{$bn}{bpowerin} = $pbi; # momentane Batterieladung
|
||||
$data{$name}{batteries}{$bn}{bpowerout} = $pbo; # momentane Batterieentladung
|
||||
$data{$name}{batteries}{$bn}{bcharge} = $soc; # aktuelle Batterieladung
|
||||
$data{$name}{batteries}{$bn}{basynchron} = $h->{asynchron} // 0; # asynchroner Modus = X
|
||||
$data{$name}{batteries}{$bn}{bicon} = $h->{icon} if($h->{icon}); # Batterie Icon
|
||||
$data{$name}{batteries}{$bn}{bshowingraph} = $h->{show} // 0; # Batterie in Balkengrafik anzeigen
|
||||
$data{$name}{batteries}{$bn}{bname} = $badev; # Batterie Devicename
|
||||
$data{$name}{batteries}{$bn}{balias} = AttrVal ($badev, 'alias', $badev); # Alias Batterie Device
|
||||
$data{$name}{batteries}{$bn}{bpowerin} = $pbi; # momentane Batterieladung
|
||||
$data{$name}{batteries}{$bn}{bpowerout} = $pbo; # momentane Batterieentladung
|
||||
$data{$name}{batteries}{$bn}{bcharge} = $soc; # Batterie SoC (%)
|
||||
$data{$name}{batteries}{$bn}{basynchron} = $h->{asynchron} // 0; # asynchroner Modus = X
|
||||
$data{$name}{batteries}{$bn}{bicon} = $h->{icon} if($h->{icon}); # Batterie Icon
|
||||
$data{$name}{batteries}{$bn}{bshowingraph} = $h->{show} // 0; # Batterie in Balkengrafik anzeigen
|
||||
$data{$name}{batteries}{$bn}{bchargewh} = BatteryVal ($name, $bn, 'binstcap', 0) * $soc / 100; # Batterie SoC (Wh)
|
||||
|
||||
writeToHistory ( { paref => $paref, key => 'batsoc'.$bn, val => $soc, hour => $nhour } );
|
||||
|
||||
@ -9591,7 +9593,7 @@ sub _transferBatteryValues {
|
||||
}
|
||||
|
||||
if ($num) {
|
||||
my $soctotal = sprintf "%.0f", ($socwhsum / $bcapsum * 100) if($bcapsum); # resultierender SoC (%) aller Batterien als "eine"
|
||||
my $soctotal = sprintf "%.0f", ($socwhsum / $bcapsum * 100) if($bcapsum); # resultierender SoC (%) aller Batterien als "eine"
|
||||
push @{$data{$name}{current}{batsocslidereg}}, $soctotal; # Schieberegister average SOC aller Batterien
|
||||
limitArray ($data{$name}{current}{batsocslidereg}, $slidenummax);
|
||||
|
||||
@ -12223,20 +12225,28 @@ sub _calcCaQcomplex {
|
||||
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the complex correction factor");
|
||||
return;
|
||||
}
|
||||
|
||||
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
|
||||
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
|
||||
|
||||
my $hh = sprintf "%02d", $h;
|
||||
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0); # real erzeugte PV Energie am Ende der vorherigen Stunde
|
||||
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0); # vorhergesagte PV Energie am Ende der vorherigen Stunde
|
||||
|
||||
if (!$pvrl || !$pvfc) {
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
|
||||
return;
|
||||
}
|
||||
|
||||
my $chwcc = HistoryVal ($hash, $day, sprintf("%02d",$h), 'wcc', 0); # Wolkenbedeckung Heute & abgefragte Stunde
|
||||
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
|
||||
my $chwcc = HistoryVal ($hash, $day, $hh, 'wcc', 0); # Wolkenbedeckung heute & abgefragte Stunde
|
||||
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
|
||||
my $crang = cloud2bin ($chwcc);
|
||||
my $sabin = sunalt2bin ($sunalt);
|
||||
|
||||
## Speicherarrays schreiben
|
||||
#############################
|
||||
push @{$data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}}, $pvrl;
|
||||
push @{$data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}}, $pvfc;
|
||||
|
||||
## neuen Korrekturfaktor berechnen
|
||||
####################################
|
||||
$paref->{pvrl} = $pvrl;
|
||||
$paref->{pvfc} = $pvfc;
|
||||
$paref->{crang} = $crang;
|
||||
@ -12251,16 +12261,16 @@ sub _calcCaQcomplex {
|
||||
delete $paref->{sabin};
|
||||
delete $paref->{calc};
|
||||
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
|
||||
|
||||
$aihit = $aihit ? ' AI result used,' : '';
|
||||
|
||||
if ($acu =~ /on_complex/xs) {
|
||||
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
|
||||
}
|
||||
else {
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
|
||||
}
|
||||
}
|
||||
|
||||
@ -12282,28 +12292,29 @@ sub _calcCaQsimple {
|
||||
my $aihit = $paref->{aihit};
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', '');
|
||||
my $hh = sprintf "%02d", $h;
|
||||
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.$hh.'_apipercentil', '');
|
||||
|
||||
if($sr eq "done") {
|
||||
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".sprintf("%02d",$h)." already calculated");
|
||||
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".$hh." already calculated");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$aln) {
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
|
||||
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the simple correction factor");
|
||||
return;
|
||||
}
|
||||
|
||||
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
|
||||
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
|
||||
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0);
|
||||
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0);
|
||||
|
||||
if (!$pvrl || !$pvfc) {
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
|
||||
return;
|
||||
}
|
||||
|
||||
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
|
||||
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
|
||||
my $sabin = sunalt2bin ($sunalt);
|
||||
|
||||
$paref->{pvrl} = $pvrl;
|
||||
@ -12320,16 +12331,16 @@ sub _calcCaQsimple {
|
||||
delete $paref->{crang};
|
||||
delete $paref->{calc};
|
||||
|
||||
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
|
||||
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
|
||||
|
||||
$aihit = $aihit ? ' AI result used,' : '';
|
||||
|
||||
if ($acu =~ /on_simple/xs) {
|
||||
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
|
||||
}
|
||||
else {
|
||||
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
|
||||
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
|
||||
}
|
||||
}
|
||||
|
||||
@ -12357,8 +12368,9 @@ sub __calcNewFactor {
|
||||
|
||||
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> Start calculation correction factor for hour: $h");
|
||||
|
||||
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
|
||||
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0);
|
||||
my $hh = sprintf "%02d", $h;
|
||||
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, $hh, $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
|
||||
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, $hh, $sabin, $crang, 0);
|
||||
$oldfac = 1 if(1 * $oldfac == 0);
|
||||
|
||||
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> read historical values: pv real sum: $pvhis, pv forecast sum: $fchis, days sum: $dnum");
|
||||
@ -12401,18 +12413,18 @@ sub __calcNewFactor {
|
||||
|
||||
if ($crang ne 'simple') {
|
||||
my $idx = $sabin.'.'.$crang; # value für pvcorrf Sonne Altitude
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$idx} = $factor;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$idx} = $qual;
|
||||
$data{$name}{circular}{$hh}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
|
||||
$data{$name}{circular}{$hh}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
|
||||
$data{$name}{circular}{$hh}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
|
||||
$data{$name}{circular}{$hh}{pvcorrf}{$idx} = $factor;
|
||||
$data{$name}{circular}{$hh}{quality}{$idx} = $qual;
|
||||
}
|
||||
else {
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$crang} = $pvrlsum;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$crang} = $pvfcsum;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$crang} = $dnum;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$crang} = $factor;
|
||||
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$crang} = $qual;
|
||||
$data{$name}{circular}{$hh}{pvrlsum}{$crang} = $pvrlsum;
|
||||
$data{$name}{circular}{$hh}{pvfcsum}{$crang} = $pvfcsum;
|
||||
$data{$name}{circular}{$hh}{dnumsum}{$crang} = $dnum;
|
||||
$data{$name}{circular}{$hh}{pvcorrf}{$crang} = $factor;
|
||||
$data{$name}{circular}{$hh}{quality}{$crang} = $qual;
|
||||
}
|
||||
|
||||
$oldfac = sprintf "%.2f", $oldfac;
|
||||
@ -12420,6 +12432,27 @@ sub __calcNewFactor {
|
||||
return ($oldfac, $factor, $dnum);
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Qualität der Vorhersage berechnen
|
||||
################################################################
|
||||
sub __calcFcQuality {
|
||||
my $pvfc = shift; # PV Vorhersagewert
|
||||
my $pvrl = shift; # PV reale Erzeugung
|
||||
|
||||
return if(!$pvfc || !$pvrl);
|
||||
|
||||
$pvrl = sprintf "%.0f", $pvrl;
|
||||
$pvfc = sprintf "%.0f", $pvfc;
|
||||
|
||||
my $diff = $pvfc - $pvrl;
|
||||
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
|
||||
|
||||
$hdv = $hdv < 0 ? 0 : $hdv;
|
||||
$hdv = sprintf "%.2f", $hdv;
|
||||
|
||||
return $hdv;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Berechnen Tag / Stunden Verschieber
|
||||
# aus aktueller Stunde + lfd. Nummer
|
||||
@ -15195,9 +15228,12 @@ sub _flowGraphic {
|
||||
|
||||
## definierte Batterien ermitteln und zusammenfassen
|
||||
######################################################
|
||||
my ($batin, $bat2home, $soc, @batsoc);
|
||||
for my $bn (1..$maxbatteries) { # für jede definierte Batterie
|
||||
$bn = sprintf "%02d", $bn;
|
||||
my ($batin, $bat2home);
|
||||
my $socwhsum = 0;
|
||||
my $soc = 0;
|
||||
|
||||
for my $bn (1..$maxbatteries) { # für jede definierte Batterie
|
||||
$bn = sprintf "%02d", $bn;
|
||||
my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev'.$bn, method => 'attr' } );
|
||||
next if($err);
|
||||
|
||||
@ -15206,10 +15242,11 @@ sub _flowGraphic {
|
||||
$batin += $batinpow if(defined $batinpow);
|
||||
$bat2home += $bat2homepow if(defined $bat2homepow);
|
||||
|
||||
push @batsoc, ReadingsNum ($name, 'Current_BatCharge_'.$bn, 0);
|
||||
$socwhsum += BatteryVal ($name, $bn, 'bchargewh', 0); # Batterie SoC in Wh
|
||||
}
|
||||
|
||||
$soc = avgArray (\@batsoc, scalar @batsoc) if(@batsoc);
|
||||
my $batcapsum = CurrentVal ($hash, 'batcapsum', 0); # Summe installierte Batterie Kapazität
|
||||
$soc = sprintf "%.0f", ($socwhsum / $batcapsum * 100) if($batcapsum); # resultierender SoC (%) aller Batterien als Cluster
|
||||
|
||||
if (!defined $batin && !defined $bat2home) {
|
||||
$hasbat = 0;
|
||||
@ -16246,27 +16283,6 @@ sub _addHourAiRawdata {
|
||||
return;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Qualität der Vorhersage berechnen
|
||||
################################################################
|
||||
sub __calcFcQuality {
|
||||
my $pvfc = shift; # PV Vorhersagewert
|
||||
my $pvrl = shift; # PV reale Erzeugung
|
||||
|
||||
return if(!$pvfc || !$pvrl);
|
||||
|
||||
$pvrl = sprintf "%.0f", $pvrl;
|
||||
$pvfc = sprintf "%.0f", $pvfc;
|
||||
|
||||
my $diff = $pvfc - $pvrl;
|
||||
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
|
||||
|
||||
$hdv = $hdv < 0 ? 0 : $hdv;
|
||||
$hdv = sprintf "%.2f", $hdv;
|
||||
|
||||
return $hdv;
|
||||
}
|
||||
|
||||
###############################################################
|
||||
# Eintritt in den KI Train Prozess normal/Blocking
|
||||
###############################################################
|
||||
@ -17106,14 +17122,6 @@ sub listDataPool {
|
||||
|
||||
my $sub = sub {
|
||||
my $day = shift;
|
||||
|
||||
#for my $dh (keys %{$h->{$day}}) {
|
||||
# if (!isNumeric ($dh)) {
|
||||
# delete $data{$name}{pvhist}{$day}{$dh};
|
||||
# Log3 ($name, 2, qq{$name - INFO - invalid key "$day -> $dh" was deleted from pvHistory storage});
|
||||
# }
|
||||
#}
|
||||
|
||||
my $ret;
|
||||
|
||||
for my $key (sort {$a<=>$b} keys %{$h->{$day}}) {
|
||||
@ -17398,7 +17406,7 @@ sub listDataPool {
|
||||
|
||||
for my $ckey (sort keys %{$h->{$idx}}) {
|
||||
if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
|
||||
my $aser = join " ",@{$h->{$idx}{$ckey}};
|
||||
my $aser = join " ", @{$h->{$idx}{$ckey}};
|
||||
$cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
|
||||
}
|
||||
|
||||
@ -17425,6 +17433,35 @@ sub listDataPool {
|
||||
if (!keys %{$h}) {
|
||||
return qq{Circular cache is empty.};
|
||||
}
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
##########################################################################################################################
|
||||
delete $data{$name}{circular}{'01'}{pvrl_5};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_10};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_25};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_60};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_65};
|
||||
delete $data{$name}{circular}{'01'}{pvrl_90};
|
||||
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4562;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4563;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4564;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4565;
|
||||
#
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3562;
|
||||
#
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4562;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4563;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4564;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4565;
|
||||
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3561;
|
||||
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3562;
|
||||
|
||||
############################################################################################################
|
||||
|
||||
for my $idx (sort keys %{$h}) {
|
||||
my $pvrl = CircularVal ($hash, $idx, 'pvrl', '-');
|
||||
@ -17484,17 +17521,41 @@ sub listDataPool {
|
||||
$bout .= "batout${bn}: $batout";
|
||||
}
|
||||
|
||||
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl\n";
|
||||
$sq .= " $bin\n";
|
||||
$sq .= " $bout\n";
|
||||
$sq .= " confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c\n";
|
||||
$sq .= " temp: $temp, wid: $wid, wtxt: $wtxt\n";
|
||||
$sq .= " $prdl\n";
|
||||
$sq .= " pvcorrf: $pvcf\n";
|
||||
$sq .= " quality: $cfq\n";
|
||||
$sq .= " pvrlsum: $pvrs\n";
|
||||
$sq .= " pvfcsum: $pvfs\n";
|
||||
$sq .= " dnumsum: $dnus";
|
||||
my ($pvrlnew, $pvfcnew);
|
||||
my @pvrlkeys = map { $_ =~ /^pvrl_/xs ? $_ : '' } sort keys %{$h->{$idx}};
|
||||
my @pvfckeys = map { $_ =~ /^pvfc_/xs ? $_ : '' } sort keys %{$h->{$idx}};
|
||||
|
||||
for my $prl (@pvrlkeys) {
|
||||
next if(!$prl);
|
||||
my $lref = CircularVal ($hash, $idx, $prl, '');
|
||||
next if(!$lref);
|
||||
|
||||
$pvrlnew .= "\n " if($pvrlnew);
|
||||
$pvrlnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $prl, cval => $lref } );
|
||||
}
|
||||
|
||||
for my $pfc (@pvfckeys) {
|
||||
next if(!$pfc);
|
||||
my $cref = CircularVal ($hash, $idx, $pfc, '');
|
||||
next if(!$cref);
|
||||
|
||||
$pvfcnew .= "\n " if($pvfcnew);
|
||||
$pvfcnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $pfc, cval => $cref } );
|
||||
}
|
||||
|
||||
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl";
|
||||
$sq .= "\n $bin";
|
||||
$sq .= "\n $bout";
|
||||
$sq .= "\n confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c";
|
||||
$sq .= "\n temp: $temp, wid: $wid, wtxt: $wtxt";
|
||||
$sq .= "\n $prdl";
|
||||
$sq .= "\n pvcorrf: $pvcf";
|
||||
$sq .= "\n quality: $cfq";
|
||||
$sq .= "\n pvrlsum: $pvrs";
|
||||
$sq .= "\n pvfcsum: $pvfs";
|
||||
$sq .= "\n dnumsum: $dnus";
|
||||
$sq .= "\n $pvrlnew" if($pvrlnew);
|
||||
$sq .= "\n $pvfcnew" if($pvfcnew);
|
||||
}
|
||||
else {
|
||||
my ($batvl1, $batvl2, $batvl3, $batvl4, $batvl5, $batvl6, $batvl7);
|
||||
@ -17766,7 +17827,24 @@ sub _ldchash2val {
|
||||
|
||||
for my $f (sort {$a<=>$b} keys %{$pool->{$idx}{$key}}) {
|
||||
next if($f eq 'simple');
|
||||
if ($f !~ /\./xs) {
|
||||
|
||||
if (ref $pool->{$idx}{$key}{$f} eq 'ARRAY') {
|
||||
my @sub_arrays = arraySplitBy (20, @{$pool->{$idx}{$key}{$f}}); # Array in Teil-Arrays zu je 20 Elemente aufteilen
|
||||
|
||||
for my $suaref (@sub_arrays) { # für jedes Teil-Array Join ausführen
|
||||
my $suajoined = join ' ', @{$suaref};
|
||||
|
||||
if (!$ret) {
|
||||
$ret .= $key.' => ';
|
||||
$ret .= $f.' @ '.$suajoined;
|
||||
}
|
||||
else {
|
||||
$ret .= "\n ";
|
||||
$ret .= $f.' @ '.$suajoined;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($f !~ /\./xs) {
|
||||
$ret .= " " if($ret);
|
||||
$ret .= "$f=".$pool->{$idx}{$key}{$f};
|
||||
my $ct = ($ret =~ tr/=// // 0) / 10;
|
||||
@ -20213,6 +20291,48 @@ sub sunalt2bin {
|
||||
return $bin;
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Teilt das Original-Array in Unter-Arrays auf, die den Inhalt des
|
||||
# Originals enthalten. Die Größe jedes Unterarrays ist gleich oder kleiner als
|
||||
# $split_size, wobei das letzte Array in der Regel kleiner ist, wenn nicht
|
||||
# genügend Elemente in @original vorhanden sind.
|
||||
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
|
||||
#
|
||||
# arraySplitBy ($split_size, @original)
|
||||
###############################################################################
|
||||
sub arraySplitBy {
|
||||
my $split_size = shift;
|
||||
|
||||
$split_size = max ($split_size, 1);
|
||||
my @sub_arrays;
|
||||
|
||||
while (@_) {
|
||||
push @sub_arrays, [splice @_, 0, $split_size];
|
||||
}
|
||||
|
||||
return @sub_arrays;
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Teilt das angegebene Array in die Anzahl $count Unterarrays auf.
|
||||
# Es wird versucht, so viele Unter-Arrays zu erstellen, wie $count angibt,
|
||||
# aber es werden weniger zurückgegeben, wenn nicht genügend Elemente in
|
||||
# @original vorhanden sind.
|
||||
#
|
||||
# Gibt eine Liste von Array-Referenzen zurück.
|
||||
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
|
||||
#
|
||||
# arraySplitInto ($count, @original)
|
||||
###############################################################################
|
||||
sub arraySplitInto {
|
||||
my ($count, @original) = @_;
|
||||
|
||||
$count = max( $count, 1 );
|
||||
my $size = ceil @original / $count;
|
||||
|
||||
return arraySplitBy ($size, @original);
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# verscrambelt einen String
|
||||
###############################################################################
|
||||
@ -21972,13 +22092,14 @@ to ensure that the system configuration is correct.
|
||||
<ul>
|
||||
<table>
|
||||
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
||||
<tr><td> <b>bname </b> </td><td>Name of the device </td></tr>
|
||||
<tr><td> <b>balias </b> </td><td>Alias of the device </td></tr>
|
||||
<tr><td> <b>basynchron </b> </td><td>Mode of processing received battery events </td></tr>
|
||||
<tr><td> <b>bcharge </b> </td><td>SOC (State of Charge) of the battery (%) </td></tr>
|
||||
<tr><td> <b>binstcap </b> </td><td>installed battery capacity (Wh) </td></tr>
|
||||
<tr><td> <b>bpowerin </b> </td><td>current charging power (W) </td></tr>
|
||||
<tr><td> <b>bpowerout </b> </td><td>current discharge power (W) </td></tr>
|
||||
<tr><td> <b>bname </b> </td><td>Name of the device </td></tr>
|
||||
<tr><td> <b>balias </b> </td><td>Alias of the device </td></tr>
|
||||
<tr><td> <b>basynchron </b> </td><td>Mode of processing received battery events </td></tr>
|
||||
<tr><td> <b>bcharge </b> </td><td>current SoC (State of Charge) of the battery (%) </td></tr>
|
||||
<tr><td> <b>bchargewh </b> </td><td>current SoC (State of Charge) of the battery (Wh) </td></tr>
|
||||
<tr><td> <b>binstcap </b> </td><td>installed battery capacity (Wh) </td></tr>
|
||||
<tr><td> <b>bpowerin </b> </td><td>current charging power (W) </td></tr>
|
||||
<tr><td> <b>bpowerout </b> </td><td>current discharge power (W) </td></tr>
|
||||
</table>
|
||||
</ul>
|
||||
|
||||
@ -23687,7 +23808,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<li><b>batteryTrigger <1on>=<Wert> <1off>=<Wert> [<2on>=<Wert> <2off>=<Wert> ...] </b> <br><br>
|
||||
|
||||
Generiert Trigger bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SoC in %). <br>
|
||||
Der verwendete SoC wird als resultierender SoC (Summe aktuelle Ladung aller Batterie Geräte zur im Verhältnis zur installierten
|
||||
Der verwendete SoC wird als resultierender SoC (Summe aktuelle Ladung aller Batterie Geräte im Verhältnis zur installierten
|
||||
Gesamtkapazität) gebildet, d.h. alle Batterien werden als ein Cluster betrachtet. <br>
|
||||
Überschreiten die letzten drei SoC-Messungen eine definierte <b>Xon-Bedingung</b>, wird das Reading
|
||||
<b>batteryTrigger_X = on</b> erstellt/gesetzt. <br>
|
||||
@ -24444,13 +24565,14 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<ul>
|
||||
<table>
|
||||
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
||||
<tr><td> <b>bname </b> </td><td>Name des Gerätes </td></tr>
|
||||
<tr><td> <b>balias </b> </td><td>Alias des Gerätes </td></tr>
|
||||
<tr><td> <b>basynchron </b> </td><td>Modus der Verarbeitung empfangener Batterie-Events </td></tr>
|
||||
<tr><td> <b>bcharge </b> </td><td>SOC (State of Charge) der Batterie (%) </td></tr>
|
||||
<tr><td> <b>binstcap </b> </td><td>installierte Batteriekapazität (Wh) </td></tr>
|
||||
<tr><td> <b>bpowerin </b> </td><td>momentane Ladeleistung (W) </td></tr>
|
||||
<tr><td> <b>bpowerout </b> </td><td>momentane Entladeleistung (W) </td></tr>
|
||||
<tr><td> <b>bname </b> </td><td>Name des Gerätes </td></tr>
|
||||
<tr><td> <b>balias </b> </td><td>Alias des Gerätes </td></tr>
|
||||
<tr><td> <b>basynchron </b> </td><td>Modus der Verarbeitung empfangener Batterie-Events </td></tr>
|
||||
<tr><td> <b>bcharge </b> </td><td>aktueller SoC (State of Charge) der Batterie (%) </td></tr>
|
||||
<tr><td> <b>bchargewh </b> </td><td>aktueller SoC (State of Charge) der Batterie (Wh) </td></tr>
|
||||
<tr><td> <b>binstcap </b> </td><td>installierte Batteriekapazität (Wh) </td></tr>
|
||||
<tr><td> <b>bpowerin </b> </td><td>momentane Ladeleistung (W) </td></tr>
|
||||
<tr><td> <b>bpowerout </b> </td><td>momentane Entladeleistung (W) </td></tr>
|
||||
</table>
|
||||
</ul>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user