Dynamic Range in einem Plot

Begonnen von flurin, 23 Juli 2014, 20:01:29

Vorheriges Thema - Nächstes Thema

holzwurm83

Zitatund schaue im Logfile (fhem-2015-02.log) nach; nach dem Aufruf deines Plots müsste ein Eintrag vorhanden sein, etwa:

Code: [Auswählen]
2015.02.03 06:15:47 3: label = [11:21.0]
Das past soweit. Eintrag wird nach jedem Aufruf erzeugt.

ZitatKönntest du die letzen Einträge deiner FileLog Datei (AZ_RT_links_Clima-2015.log) posten.
2015-02-03_21:00:08 AZ_RT_links_Clima T: 19.7 desired: 16.0 valve: 0
2015-02-03_21:00:08 AZ_RT_links_Clima partyStart: -
2015-02-03_21:00:08 AZ_RT_links_Clima partyEnd: -
2015-02-03_21:00:08 AZ_RT_links_Clima partyTemp: -
2015-02-03_21:03:09 AZ_RT_links_Clima measured-temp: 19.7
2015-02-03_21:03:09 AZ_RT_links_Clima ValvePosition: 0
2015-02-03_21:03:09 AZ_RT_links_Clima motorErr: ok
2015-02-03_21:03:09 AZ_RT_links_Clima desired-temp: 16.0
2015-02-03_21:03:09 AZ_RT_links_Clima controlMode: auto
2015-02-03_21:03:09 AZ_RT_links_Clima boostTime: -
2015-02-03_21:03:09 AZ_RT_links_Clima T: 19.7 desired: 16.0 valve: 0
2015-02-03_21:03:09 AZ_RT_links_Clima partyStart: -
2015-02-03_21:03:09 AZ_RT_links_Clima partyEnd: -
2015-02-03_21:03:09 AZ_RT_links_Clima partyTemp: -
2015-02-03_21:05:55 AZ_RT_links_Clima measured-temp: 19.7
2015-02-03_21:05:55 AZ_RT_links_Clima ValvePosition: 0
2015-02-03_21:05:55 AZ_RT_links_Clima motorErr: ok
2015-02-03_21:05:55 AZ_RT_links_Clima desired-temp: 16.0
2015-02-03_21:05:55 AZ_RT_links_Clima controlMode: auto
2015-02-03_21:05:55 AZ_RT_links_Clima boostTime: -
2015-02-03_21:05:55 AZ_RT_links_Clima T: 19.7 desired: 16.0 valve: 0
2015-02-03_21:05:55 AZ_RT_links_Clima partyStart: -
2015-02-03_21:05:55 AZ_RT_links_Clima partyEnd: -
2015-02-03_21:05:55 AZ_RT_links_Clima partyTemp: -
2015-02-03_21:08:27 AZ_RT_links_Clima measured-temp: 19.7
2015-02-03_21:08:27 AZ_RT_links_Clima ValvePosition: 0
2015-02-03_21:08:27 AZ_RT_links_Clima motorErr: ok
2015-02-03_21:08:27 AZ_RT_links_Clima desired-temp: 16.0
2015-02-03_21:08:27 AZ_RT_links_Clima controlMode: auto
2015-02-03_21:08:27 AZ_RT_links_Clima boostTime: -
2015-02-03_21:08:27 AZ_RT_links_Clima T: 19.7 desired: 16.0 valve: 0
2015-02-03_21:08:27 AZ_RT_links_Clima partyStart: -
2015-02-03_21:08:27 AZ_RT_links_Clima partyEnd: -
2015-02-03_21:08:27 AZ_RT_links_Clima partyTemp: -
2015-02-03_21:10:59 AZ_RT_links_Clima measured-temp: 19.7
2015-02-03_21:10:59 AZ_RT_links_Clima ValvePosition: 0
2015-02-03_21:10:59 AZ_RT_links_Clima motorErr: ok
2015-02-03_21:10:59 AZ_RT_links_Clima desired-temp: 16.0
2015-02-03_21:10:59 AZ_RT_links_Clima controlMode: auto
2015-02-03_21:10:59 AZ_RT_links_Clima boostTime: -
2015-02-03_21:10:59 AZ_RT_links_Clima T: 19.7 desired: 16.0 valve: 0
2015-02-03_21:10:59 AZ_RT_links_Clima partyStart: -
2015-02-03_21:10:59 AZ_RT_links_Clima partyEnd: -
2015-02-03_21:10:59 AZ_RT_links_Clima partyTemp: -



ZitatIst deine Plotdatei oben noch aktuell?
Hier zur Sicherheit noch mal ein aktueller Auszug.
# Created by FHEM/98_SVG.pm, 2014-10-07 23:05:00
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<TL>'
set ytics
set y2tics
set grid
set ylabel "Eingestellt"
set y2label "Gemessen"

#FileLog 4:AZ_RT_links_Clima.measured-temp\x3a::
#FileLog 4:AZ_RT_links_Clima.desired-temp\x3a::

plot "<IN>" using 1:2 axes x1y2 title 'gemessen' ls l2 lw 1 with lines,\
     "<IN>" using 1:2 axes x1y2 title 'eingestellt' ls l1fill lw 1 with lines


Es hat sich an dem Plot selbst noch nicht geändert.
- Fhem auf einem MacMini Server
- CUL; HMLAN; CUNO2 für FS20; HM-Wired RS485 LAN Gateway
- HMW_Sen_SC_12_FM; HMW_LC_Sw2_DR; HMW_LC_Bl1_DR; HMW_IO_12_Sw7; HMW_IO_12_Sw14_DR; HMW_IO_12_FM; HBW_1W_T10
- HM-TC-IT-WM-W-EU; HM-CC-RT-DN

flurin

#31
Ich teste es mal bei mir mit deinen Daten aber was mir sofort auffällt:

in der Plotdatei fehlt:

set yrange <L1>
set y2range <L1>


<L1> kannst du auch mit dem Ploteditor im Feld Range as [min:max] eintragen.

Edit:

Bei mir funktioniert es mit dieser Plotdatei:

# Created by FHEM/98_SVG.pm, 2015-02-03 22:19:29
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<TL>'
set ytics
set y2tics
set grid
set ylabel "Eingestellt"
set y2label "Gemessen"
set yrange <L1>
set y2range <L1>

#FileLog_AZ_RT_links_Clima 4:AZ_RT_links_Clima.measured-temp\x3a::
#FileLog_AZ_RT_links_Clima 4:AZ_RT_links_Clima.desired-temp\x3a::

plot "<IN>" using 1:2 axes x1y2 title 'gemessen' ls l2 lw 1 with lines,\
     "<IN>" using 1:2 axes x1y1 title 'eingestellt' ls l1fill lw 1 with lines

holzwurm83

Hallo flurin,

vielen Dank für deine Hilfe! Funktioniert jetzt super!
- Fhem auf einem MacMini Server
- CUL; HMLAN; CUNO2 für FS20; HM-Wired RS485 LAN Gateway
- HMW_Sen_SC_12_FM; HMW_LC_Sw2_DR; HMW_LC_Bl1_DR; HMW_IO_12_Sw7; HMW_IO_12_Sw14_DR; HMW_IO_12_FM; HBW_1W_T10
- HM-TC-IT-WM-W-EU; HM-CC-RT-DN

flurin

#33
@holzwurm83
OK

In der Zwischenzeit habe ich eine neue Sub [ Ersatz für getLabel() ] geschrieben:

getRange(padding_bottom, padding_top)

getRange ist universell für eine beliebige Anzahl Kurven und zudem können die "Abstände" (padding_bottom und padding_top) als Parameter frei definiert werden. Z.B:

getRange(1,2)

# arguments: padding_bottom, padding_top
# return value: Range as [min:max]
sub
getRange($$)
{
  my ($padding_bottom, $padding_top) = @_;
  my $range = "";
 
  if (exists($data{currval1}) && ($data{min1} ne "undef") && ($data{max1} ne "undef")) {
    my $min = $data{min1};
    my $max = $data{max1};
    my $index = 2;

    while (exists($data{"currval".$index})) {
      my $min_i = $data{"min".$index};
      my $max_i = $data{"max".$index};
      $min = $min_i if (($min_i ne "undef") && ($min_i < $min));
      $max = $max_i if (($max_i ne "undef") && ($max_i > $max));
      $index++;
    }
    $range = sprintf("[%d:%.1f]",$min - $padding_bottom,$max + $padding_top);
  }
  #Log(3, "range = $range");
  return $range;
}


Gruss
flurin

flurin

#34
Nach dem Update von 98_SVG.pm (7926 2015-02-09 16:52:31) lässt sich getRange vereinfachen:


# arguments: padding_bottom, padding_top
# return value: Range as [min:max]
sub
getRange($$)
{
  my ($padding_bottom, $padding_top) = @_;
  my $range = "";
 
  if ($data{minAll} < 999999 && $data{maxAll} > -999999) {
    $range = sprintf("[%d:%.1f]",$data{minAll}-$padding_bottom,$data{maxAll}+$padding_top);
  } else {
    Log(3, "Invalid Range");
  }
  #Log(3, "range = $range");
  return $range;
}

bernhard23

#35
Ich hatte folgendes Problem: Ich wollte mehrere dynamische Ranges für mehrere Kurven in einem Plot. Wenn aber die Temperatur 0°C ist und die Luftfeuchte 99%, ist die Range für sowohl die Temperatur- als auch die Feuchtigkeitsskala [0:99]. Das macht aber wenig Sinn, weil die Temperatur ja nie 99°C oder die Feuchtigkeit nie 0% beträgt. Das verzerrt die Darstellung der Skala im Plot. Ich möchte also für jeden Wert eine eigene Range haben, für Temperatur z.b. [0:20], für Luftfeuchte [50:90], und das jeweils noch mit einem Mindestabstand nach oben und unten, damit der Plot schöner aussieht. So habe ich es gelöst:

Edit: Die folgende Methode ist veraltet und unnötig kompliziert, siehe meinen neuen Post weiter unten auf dieser Seite, wo ich eine noch bessere Lösung gepostet habe.

Zunächst habe ich auf der Eigenschaftsseite des betreffenden SVG-Plots folgendes Attribut hinzugefügt:

attr SVG_FileLog_Balkon label getRange(1)::getRange(2)::getRange(3)

So erzeuge ich drei Label, eins für jedes der gemessenen Readings. 1 ist Temperatur, 2 ist Luftfeuchte, 3 ist absolute Luftfeuchte.

In meiner Plotdatei balkon.gplot steht dann:

set ylabel "Temperature"
set y2label "Humidity"
set y3label "AbsFeuchte"
set yrange <L1>
set y2range <L2>
set y3range <L3>


Die dazugehörige Funktion in der myUtils.pm:


sub
getRange
{
  my $curIndex = shift;
  my ($padding_bottom, $padding_top) = (1,1);
  my $range = "";

  if (exists($data{"currval".$curIndex}) && ($data{"min".$curIndex} ne "undef") && ($data{"max".$curIndex} ne "undef")) {
    my $min = $data{"min".$curIndex};
    my $max = $data{"max".$curIndex};
    $range = sprintf("[%d:%.1f]",$min - $padding_bottom,$max + $padding_top);
  }
# Log(3, "range = $range");
  return $range;
}



Die Funktion getRange() wird dreimal aufgerufen mit den Werten 1, 2 und 3 als Argument, die jeweils für Temperatur, Luftfeuchte und absolute Feuchte stehen. Wer andere Werte oder eine andere Reihenfolge bei den Readings hat, muss das natürlich in der Label-Definition (ganz oben) anpassen. Das Padding habe ich in der Funktion fest eingestellt, da ich dort immer denselben Wert haben will. Schnell und dreckig, ich weiß, aber funktioniert erstmal, und vielleicht hilft es ja jemandem. Im Anhang seht ihr einen Screenshot, wie das ganze in Aktion aussieht. Jeder der drei Werte hat eine eigene Range, und durch das Padding sieht es schöner aus, weil die Werte nicht immer oben und unten an den Plot stoßen.

Vorher (ohne Padding):
(https://abload.de/thumb/balkon1llx62.png)

Nachher (mit Padding):
(https://abload.de/thumb/balkon3m2ass.png)

Ein weiteres Problem war, dass bei Werten mit geringer Schwankung und grober Abstufung riesige Ausschläge in der Plotkurve entstehen. Wenn etwa die Luftfeuchte immer zwischen 60% und 62% schwankt, habe ich eine dicke gezackte Linie, die abgehackt von ganz unten bis nach ganz oben im Plot springt. Gelöst habe ich das, indem ich für solche Werte eine Art "Minimal-Range" nach oben und unten drauf addiere, also für die Luftfeuchte dann von 55-67% anstatt 60-62%. Die Funktion sieht dann so aus:


sub
getRange
{
  my $curIndex = shift;
  my ($padding_bottom, $padding_top) = (1,1);
  my $range = "";

  if (exists($data{"currval".$curIndex}) && ($data{"min".$curIndex} ne "undef") && ($data{"max".$curIndex} ne "undef")) {
    my $min = $data{"min".$curIndex};
    my $max = $data{"max".$curIndex};

        #für die feuchtigkeit höheres padding, weil die werte sehr grob sind
        #und dann die linienschwankungen zu abrupt werden
    if($curIndex==2) {
        if($max - $min <= 5) {
                $padding_bottom+=4;
                $padding_top+=4;
        }
    }
    $range = sprintf("[%d:%.1f]",$min - $padding_bottom,$max + $padding_top);
  }
# Log(3, "range = $range");
  return $range;
}

Sehr hässlicher Code, ich weiss, aber ich bin da pragmatisch und wollte es einfach schnell gelöst haben.

justme1968

der umweg über das label attribut ist seit einiger zeit nicht mehr nötig. man kann direkt für dir yrange attribute perl code als wert angeben. entweder direkt: set y2range {"[".($data{min2}-.1).":".($data{max2}+.1)."]"} oder du rufst dann dort deine routine auf: set y2range {getRange(2)}

statt dem index könnte man auch min und max direkt übergeben und die routine generisch für alle plots verwenden.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

bernhard23

Das ist ja der Hammer. Funktioniert einwandfrei, danke. Das ist natürlich die eindeutig elegantere Lösung gegenüber dem zweckentfremdeten Label.

bernhard23

#38
Hier nochmal eine neue Version mit der Variante ohne Label. Ich habe es jetzt so gemacht, dass der Subroutine ein optionales Argument mitgegeben wird (isIntegerValue), das aussagt, ob ein Reading ganzzahlig ist oder nicht. Wenn ja, wird das Padding zusätzlich etwas erhöht, um die groben Sprünge im Plot abzumildern.

myUtils.pm:


sub getRange {
        my $min = shift;
        my $max = shift;
        my $isIntegerValue = shift;

        my $padding = 1;

        if($isIntegerValue) {
                $padding += 3;
        }

        $min -= $padding;
        $max += $padding;

        my $range = sprintf("[%d:%d]", $min, $max);

        return $range;
}


Plotfile:

set yrange {getRange($data{min1},$data{max1})}
set y2range {getRange($data{min2},$data{max2},1)}
set y3range {getRange($data{min3},$data{max3})}


y2range ist die Feuchtigkeit, also ein ganzzahliger Wert, deshalb wird als Argument am Ende die "1" mitgegeben, um das Padding zu erhöhen und die Kurve zu glätten.

Und so sieht's dann aus. Hier kann man sehr gut sehen, dass jedes Reading seine individuelle Skala hat, dadurch verlaufen die Kurven alle schön gleichmäßig, und dass durch das Padding oben und unten immer ein Abstand eingehalten wird, die Kurven stoßen also nicht mehr an den Rand, was optisch schöner ist.

(https://abload.de/thumb/balkon4xsjg4.png)

Igge

#39
Achtung, alter Thread!

Danke für das Script, ich habe das noch etwas erweitert, falls man 2 Kurven das Minimum und Maximum berechnen will. Ich habe zum Beispiel Soll- und Isttemperatur auf einer Achse, hier muss man prüfen, welche kleiner bzw. größer ist.

sub getRange {
  my $min1 = shift;
  my $min2 = shift;
  my $max1 = shift;
  my $max2 = shift;
  my $padding = shift;

  my $min = $min1 < $min2 ? $min1 : $min2;
  my $max = $max1 > $max2 ? $max1 : $max2;

  $min -= $padding;
  $max += $padding;

  my $range = sprintf("[%d:%d]", $min, ceil($max));

  return $range;
}


set yrange {getRange($data{min1}, $data{min2}, $data{max1}, $data{max2}, 0.5)}

Das Padding kann man direkt als Wert übergeben, ohne das ein True/False-If notwendig ist.

Igge

TheTrumpeter

Ich habe auch versucht die Achsenbeschriftung dynamisch mittels der Labels einzustellen...

Leider klappt das bei einem der Plots nicht. Ich habe
attr SVG_FileLog_LBE_3 label int(min($data{min1},$data{min2},$data{min3},$data{min4},$data{min5},0))-5-(int(min($data{min1},$data{min2},$data{min3},$data{min4},$data{min5},0))%5)::int(max($data{max1},$data{max2},$data{max3},$data{max4},$data{max5},0))+5-(int(max($data{max1},$data{max2},$data{max3},$data{max4},$data{max5},0))%5)
und
set yrange [<L1>:<L2>]
set y2range [<L1>:<L2>]


Für die untere Grenze klappt es, für die obere Grenze nicht.
Hab es nun schon mehrfach durchgeschaut, ich finde den Fehler einfach nicht.

Dargestellt werden übrigens Temperaturen. Die Achsen sollen in ganzen 5er-Schritten beschriftet werden und mindestens von -5 bis 25 reichen, daher die 0 in der Min- und 20 in der Max-Bildung.
FHEM auf RPi3, THZ (LWZ404SOL), RPII2C & I2C_MCP342x (ADCPiZero), PowerMap, CustomReadings, RPI_GPIO, Twilight, nanoCUL (WMBus für Diehl Wasserzähler & Regenerationszähler für BWT AqaSmart), ESPEasy, TPLinkHS110

justme1968

der ganze umweg ist seit langem nicht mehr nötig. du kannst direkt bei den beiden range angaben perl code haben das einen passenden string zurück liefert 

set yrange [{...}]

es gibt einen zweiten thread in dem das beschrieben ist.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

TheTrumpeter

Danke.
Ich finde es mit den Labeln eigentlich übersichtlicher.

Bin nun aber auch selbst draufgekommen... ich habe min/max statt minNum/maxNum verwendet.
War wohl Zufall, dass es in den anderen Plots (bisher) richtig war.
FHEM auf RPi3, THZ (LWZ404SOL), RPII2C & I2C_MCP342x (ADCPiZero), PowerMap, CustomReadings, RPI_GPIO, Twilight, nanoCUL (WMBus für Diehl Wasserzähler & Regenerationszähler für BWT AqaSmart), ESPEasy, TPLinkHS110

jostmario

#43
Hallo,

ist zwar schon älter klink mich aber trotzdem mal hier ein.
wie kann ich es hinbekommen das die Achse Dynamisch bleibt aber auf ca 2000 obergrenze gedeckelt bleiben.
Diagramm Zeigt den Stromverbrauch möchte es Detailierter im unteren Bereich haben.
Die kurzen Peaks interessieren mich nicht möchte es aber nich generell Fix haben.

quasi wenn $data{max1} > 2000 dann die Max YRange = 2000


Gruß Josty
Raspberry Pi  ---  HM-LAN ---  8X HM_HM_CC_RT_DN --- OWL+USB Strommesser    UVR1611