CSV-Daten auswählen, Graph anzeigen

Meine Eltern möchten auch immer wissen, wie das Wetter wird… dann hab ich eine Wetterseite erstellt… und dann war das auf einmal uninteressant. :sweat:


Allgemein: So ein Temperatursensor ist winzig, kostet als Baustein im Vergleich zur Größe aber viel Geld… Ein Beeper für Morsecodes ist da bspw. günstiger.

Der Code, um den Temperatursensor anzusteuern, würd mich interessieren… der ist ja sicherlich nicht in Java geschrieben.

Ich glaube, man schickt bestimmte Startbits (Datenblatt…) hin, und erhält zwei Bytes, die man zusammensetzen und interpretieren muss.

ich habe mir einfach mal angeschaut, wie man contains() anwendet & ich hab nichts davon gesehen, dass man auch eine Methode (in diesem Fall getName()) verwenden darf…
deshalb probiere ich mich ja auch an etwas in meiner freien Zeit, um besser zu werden und ein gewisses Verständnis & Gefühl zu entwickeln

Da hast du Recht, ich habe mich vorher falsch ausgedrückt denke ich. Bei allen csv-Dateien mit einer 2 bekomme ich true zurück, also wie es zu erwarten ist :slight_smile:
Ich würde mich trotzdem freuen, weitermachen zu können & auch von dir auf meine 1000 Fehler aufmerksam gemacht zu werden, nur so lernt man doch was

Haha :joy: naja, meine wollen nur die Temperatur im Gewächshaus wissen, am besten graphisch…
mit code kann ich nicht dienen, wie gesagt, der Sensor misst & macht einmal am Tag einen csv-Export

contains() funktioniert auf einen String bzw. ist eine Methode dieser Klasse,
wo der String zur Untersuchung herkommt, ob vorher in einer lokalen Variable oder just von einer Methode zurückgegeben, das spielt für contains()-Aufruf offensichtlich keine Rolle

wenn es aber übersichlicher ist, spricht auch nichts gegen

String fileName = f.getName();
boolean b = fileName.contains(..);
System.out.println("Prüfung auf fileName "+fileName+", ..");

usw., durchaus eh zu empfehlen

genau wie System.out.println() einen String ausgibt, egal wo der herkommt

über solche Grundlagen nachzudenken bringt hoffentlich irgendwann Fortschritte…


und selbst wenn sich diese technische Frage stellte, hilft es ja nicht, dann irgendeinen klar nicht hilfreichen String wie ‚path‘ zu prüfen, sondern dann muss man halt die Frage ‚wie kann ich den Namen der Datei bekommen und prüfen?‘ exakt formulieren und angehen,

die fachliche Seite, was tatsächlich als Ziel getan werden muss, ist neben technischen Fragen immer unverzichtbar,
wissen was gerade zu tun ist

Jetzt hast du mich heiß gemacht. :smile: Zeig’ noch mal deinen überarbeiteten Code, wenn er fertig ist - und wir können das ein oder andere verändern… also auf fehlerhafte Stellen aufmerksam machen.

okay, das finde ich jetzt intuitiver zu verstehen (wenn sowas von mir kommen darf :slight_smile:)

Dann hätte ich nun eine weitere (wsl technische) Frage:
Ich habe das Programm ja sozusagen (mit direktem Aufruf einer bestimmten csv Datei) schoneinmal geschrieben.
Dabei habe ich mir auch die Werte, die ich aus der csv Datei benötige für meinen Graphen sozusagen „zurechtgesplittet“. Nun würde ich auch gerne meinen Code „anpassen“, bzw ich habe ihn meiner Meinung nach schon angepasst…was sagt ihr?

public class Test4 {
public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
        ApplicationFrame frame = new ApplicationFrame("Temperaturprofil");
        Test4 test = new Test4();
        try {
			frame.add(test.createChart("Temperatur profile"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        frame.pack();
        frame.setLocationRelativeTo(null);;
        frame.setVisible(true);
    });
}
private ChartPanel createChart(String chartTitle) throws IOException {
	//title, x-axis label, y-axis label, data, create legend?, generate tooltips?, generate URLs?
    JFreeChart chart = ChartFactory.createTimeSeriesChart(chartTitle,
        "Time", "Temperature", createDataset(), true, true, false);
    ChartPanel chartPanel = new ChartPanel(chart);
    XYPlot plot = chart.getXYPlot();
    DateAxis axis = (DateAxis) plot.getDomainAxis();
    SimpleDateFormat df = new SimpleDateFormat("HH:mm:ssX");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));
    axis.setDateFormatOverride(df.getTimeInstance());
    plot.setBackgroundPaint(Color.WHITE);
    return chartPanel;
}
public TimeSeriesCollection createDataset() throws IOException {
    TimeSeries series = new TimeSeries("Temperature");
    TimeSeriesCollection dataset = new TimeSeriesCollection(series);
  
    File h = new File("../Einlesen/TestzumPfad.txt");

        // Read from the file
        BufferedReader br = new BufferedReader(new FileReader(h));
        String path = br.readLine();
        String nrString = br.readLine();
     
        File[] fileList = new File (path).listFiles(); 
        // Sort files by name
        Arrays.sort(fileList); 
   
	long mod = 0;
    File found = null;
    System.out.println("nrString:" + nrString);
    for (File f : fileList)
    {
       

       String fileName = f.getName();
       boolean b = fileName.contains(nrString);
       System.out.println("Prüfung auf fileName: "+ fileName + ":" + b);
       
        if (b && f.lastModified() > mod)
        {
            mod = f.lastModified();
            found = f;
        }
         //**hier beginnt der eingefügte Code**
    while ((fileName = br.readLine()) != null) {
        //Trenne den String nach jedem Komma
           String[] split = fileName.split(",");
        //Zeige mir alles zur Überpfrüfung auf der Konsole an
    System.out.println(ZonedDateTime.parse(split[1]).format(DateTimeFormatter.ISO_LOCAL_TIME)  + "," +split[2]);
         ZonedDateTime zdt = ZonedDateTime.parse(split[0]);

         Second second =  new Second(Date.from(zdt.toInstant()));
         series.add(second, Double.valueOf(split[2]));
   }
   }
    
    return (TimeSeriesCollection) dataset;
 }
 }

Ich verwende hier nun bei der while-Schleife den String fileName, da er mir nun alle Dateien aus dem Verzeichnis ausliest & der Inhalt getrennt werden soll…richtig?
Ich splitte also am Komma, in jeweils 3 Teile. Anzeigen lasse ich mir jedoch nur Array 1 (bearbeitet) & Array 2, durch ein Komma getrennt.

Anbei die csv-Dateien um die Splittung zu verdeutlichen
Export.zip (3,8 KB)

die Probleme bleiben haarsträubend

zum einen machst du etwas in der Schleife, für jedes File, ganz egal ob das aktuelle akzeptiert wird oder nicht, found spielt keine Rolle,
besser wäre ja wohl, wenn eine Methode allein das Einlesen der Txt-Datei macht und die gefundene CSV-Datei (found eben!) als Rückgabewert liefert,

weiteres können nächste Methoden machen, die frisch anfangen:

private TimeSeriesCollection createDataset() {
  File csvFile = findCSVFile();
   ...
}

hättest du das Programm früher schon geschrieben, wäre aktuell nur findCSVFile() zu ändern, dass eine andere Datei gefunden wird, und der Rest bleibt gleich,
in der Annahme, dass es immer darum geht, ein konkretes CSV einzulesen und anzuzeigen

es lohnt sich, strukturiert aufzubauen, nicht DataSet-Code irgendwo wild in neue Dateisuche hineinzuschieben


zudem zu diesem Code in der Schleife

br ist der BufferedReader zur Txt-Datei, wo doch nur das Verzeichnis und der nrString drinstehen?
steht da noch mehr? bei all deinen Ideen nicht auszuschließen

wenn hier ein BufferedReader auf die aktuelle Datei in der Schleife gemeint ist, also ‚br‘ neu anzulegen,
dann noch Anmerkung: ok, mit dem Namen der Datei machst du hier wohl gar nichts mehr, trotz ‚fileName‘,
du nutzt hier nur die String-Variable zum Einlesen einer Zeile?

mach sowas nicht…,
was spricht gegen eine neue Variable ‚String line‘?

na, wenn etwas mehr in Methoden aufgetrennt, dann wäre beim Einlesen der CSV die Schleife und die Variable fileName auch gar nicht mehr verfügbar…


also wenn das so weitergeht, werde ich dir kaum bei jedem Einzelschritt alles korrigieren…,
falls CyborgBeta noch als Hilfe bleibt, weiß ich auch nicht ob das zu empfehlen ist :wink:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mycompany.test4;

import java.awt.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.*;
import org.jfree.data.time.*;
import org.jfree.ui.*;

/**
 * @author
 */
public class Test4 {

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            ApplicationFrame frame = new ApplicationFrame("Temperaturprofil");
            Test4 test = new Test4();
            try {
                frame.add(test.createChart("Temperatur profile"));
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

    private ChartPanel createChart(String chartTitle) throws IOException {
        //title, x-axis label, y-axis label, data, create legend?, generate tooltips?, generate URLs?
        JFreeChart chart = ChartFactory.createTimeSeriesChart(chartTitle,
                "Time", "Temperature", createDataset(), true, true, false);
        ChartPanel chartPanel = new ChartPanel(chart);
        XYPlot plot = chart.getXYPlot();
        DateAxis axis = (DateAxis) plot.getDomainAxis();
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ssX");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));
        axis.setDateFormatOverride(SimpleDateFormat.getTimeInstance());
        plot.setBackgroundPaint(Color.WHITE);
        return chartPanel;
    }

    private TimeSeriesCollection createDataset() throws IOException {
        File config = new File("config.txt");
        String path;
        String nrString;
        try ( // Read from the file
                BufferedReader br = new BufferedReader(new FileReader(config))) {
            path = br.readLine();
            nrString = br.readLine();
        }
        File exportFile = getExport(path, nrString);
        System.out.println("exportFile = " + exportFile); // debug output...

        TimeSeries series = new TimeSeries("Temperature");
        TimeSeriesCollection dataset = new TimeSeriesCollection(series);

        try ( // Read from the file
                BufferedReader br = new BufferedReader(new FileReader(exportFile))) {
            br.readLine();
            String line;
            while ((line = br.readLine()) != null) {
                String[] split = line.split(",");

                /* TODO: YOUR KRAMS */

            }
        }

        return (TimeSeriesCollection) dataset;
    }

    private File getExport(String path, String nrString) {
        Pattern p1 = Pattern.compile("_\\d{4}-\\d{2}-\\d{2}");
        Pattern p2 = Pattern.compile("-(\\d+)");
        File exportDir = new File(path);
        if (exportDir.exists() && exportDir.isDirectory()) {
            File[] files = exportDir.listFiles();
            Arrays.sort(files, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    Matcher m1 = p1.matcher(o1.getName());
                    Matcher m2 = p1.matcher(o2.getName());
                    m1.find();
                    m2.find();
                    return m2.group().compareTo(m1.group()); // not smallest first
                }
            });
            Arrays.sort(files, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    Matcher m1 = p2.matcher(o1.getName());
                    Matcher m2 = p2.matcher(o2.getName());
                    m1.find();
                    m2.find();
                    return Integer.valueOf(m1.group(1)).compareTo(Integer.valueOf(m2.group(1)));
                }
            });
            for (File file : files) {
                if (file.getName().startsWith("export_test_practice-" + nrString)) {
                    return file;
                }
            }
        }
        return null;
    }
}

Hab z. B. die Methode getExport hinzugefügt…

Also es gibt folgende Dateien in Export:

export_test_practice-1_2017-06-22_2017-06-22
export_test_practice-1_2017-06-23_2017-06-23
export_test_practice-1_2017-06-24_2017-06-24
export_test_practice-2_2017-06-24_2017-06-24

Deine Ma / Pa / der Kunde möchte jetzt den Datensatz mit der 1 auswählen, dann wählt das Programm export_test_practice-1_2017-06-24_2017-06-24 diesen Datensatz aus, da es der neuste mit der 1 ist.

Den Datensatz hat das Programm jetzt… Bei TODO: YOUR KRAMS müsstest du jetzt weitermachen.

Nebenbei fiel mir auf: main ruft createChart auf, createChart ruft createDataset auf, createDataset ruft getExport auf… Das ist, wie @SlaterB schrieb, nicht ganz so toll.


BTW. Inline-Code, also das in ’ '-Zeichen, ist echt schwer zu lesen / hebt sich nicht genug ab. :confused:

Viel Text und Code, vielleicht etwas wenig Struktur. Ich möchte nochmal betonen, dass es, wie bereits weiter oben erwähnt, WICHTIG ist, solche Tasks in kleine, testbare Einheiten zu zerlegen. Und … sie auch zu testen. Wenn man irgendeine Funktion schreibt wie fileNameMatchesForSomeNumber, dann hilft es nichts, wenn man feststellt, dass ~“das Programm nicht funktioniert” und das vielleicht (oder eben vielleicht auch nicht) an dieser Funktion liegt.

public static void main(String args[]) {
    String fileName = "prakticisezeum-1_JJJJ-MM-DD_JJJJ-MM-DD.csv";
    System.out.println(fileNameMatchesForSomeNumber(fileName, 1));
}
private static boolean fileNameMatchesForSomeNumber(String fileName) { ... }

wäre da schon ein ausreichender Test, wo man notfalls auch mit weiteren System.out.printlns oder eine Debugger nachbohren kann … ggf. um festzustelen, dass "practice" und "practise" unterschiedliche Wörter sind…

Das stimmt wohl… Auch die Tiefe / Breite einer Methode legt nahe, diese weiter zu splitten… Aber das könnte sie als nächster Schritt machen.

Bei Unit Tests waren wir doch noch gar nicht. Erst mal der agile Ansatz, später vielleicht TDD, wenn der TO langweilig ist.

Weiterhin… dass ich im Comparator .matcher() aufrufe, ist etwas unschön… Aber bevor das langsam wird, muss es schon sehr sehr viele Exports geben…

Ist dir denn sonst nix aufgefallen?


Inline-Code hebt sich echt nicht genug ab!

Das bezog sich noch gar nicht auf irgendwas sophisticatetes with JUnit-Test-Suites mit Mockito & Co, sondern ganz schlicht und ergreifend darauf, dass man

  1. Methoden sowieso möglichst “klein” machen sollte (d.h. sie sollten nur eine Aufgabe erfüllen, und die kann man meistens (!) relativ leicht testen: Parameter rein - Ergebnis raus.)
  2. Man schlicht einen Ansatzpunkt braucht, an dem man ausprobieren kann, ob das ganze funktioniert. Die Funktionen, die ich oben angedeutet hatte, sollten in diese Richtung gehen.

Die createDataset-Methode in der zuletzt geposteten Form versucht (!) in diesem Sinne, “zu viel auf einmal” zu machen. (Wenn man etwas sicherer ist, und je nachdem, worum es geht, kann man auch mal “mehr auf einmal” machen, aber gerade, wenn es potentiell noch an Grundlagen hakt, kann es frustrierend sein, wenn man dann 100 Zeilen hat, die man selbst kaum versteht, und bei denen man nur raten kann, wo der Fehler liegen könnte…)

mit dem BufferdReader hast du vollkommen Recht, erscheint jetzt wie du es sagst viel logischer…naja ich habe jetzt eine neue Variable "string line" eingeführt & jetzt schauen wir mal weiter. Werde diese Woche nicht mehr viel machen…der Codeinhalt von CyborgBeta verwirrt mich nur noch mehr.
Hast du vielleicht irgendeine Idee/Seite oder ein Buch (nicht Java ist auch eine Insel), wo ich gut durchstrukturiert nocheinmal das Grundwerkzeug sowie wirklich guten Codeaufbau & Struktur erlernen kann. Mir mangelt es ja an allem scheinbar :frowning:

Danke für deine Mühen, aber deine Methode getExport verwirrt mich nur noch mehr und das ist ja auch nicht sinn&zweck des ganzen. Ich schau mir das ganze mal in der kommenden Woche an, schau wieso dein Code bei mir nicht funktioniert & vielleicht geht mir dann ein Lichtlein auf…:slight_smile:
Hast du noch irgendwelche Tipps, sei es an Büchern/Tutorials/Seiten wo ich nochmal schön Schritt für Schritt alles von Grund auf lernen kann? Mit anständiger Codestruktur & Vorgehensweise?

Verwirrung stiften wollte ich nicht.

Es geht auch ganz ohne .sort(), wenn manuell über die Files gelaufen wird, das Datum geparst wird und der Größe verglichen wird. Wenn es allerdings yyyy-MM-dd ist, können auch einfach Strings verglichen werden.

Ein gutes Buch? Da gibt es mehrere Schinken… Kommt drauf an, wie dolle du dich damit auseinandersetzen möchtest. :slight_smile:

Edit: Das oben heißt Natural sort order - Wikipedia - eben wie im Windows-Explorer (etwas vereinfacht, aber prinzipiell…)

Also es soll von grund auf Java erklären & leicht verständlich sein, sodass auch ich damit zurecht komme. Darf auch gern dick sein, hauptsache es bringt mir was :slight_smile:

Okay, neugierig bin ich ja schon ein klein wenig…was machst du mit den regex genau?
Du suchst dir aus dem Namen der Export csv, bestimmte Teile raus (Unterstrich & Bindestrich) die diese Bedingung erfüllen…hab ich das richtig verstanden?

Also eigentlich hab ich da eine unnötige Idee eingebracht…

Es genügt auch eine simple Schleife, wie du sie zuerst hattest…

Java ist auch eine Insel von Christian Ullenboom (Das umfassende Handbuch) online und auch erwerbbar

Und dann sicherlich noch, was du bereits kennst whrs: String (Java Platform SE 7 )

:

    private File getExportFile(String path, String nrString) {
        File toReturn = null;
        Pattern p1 = Pattern.compile("_\\d{4}-\\d{2}-\\d{2}");
        Pattern p2 = Pattern.compile("-(\\d+)");
        File exportDir = new File(path);
        if (exportDir.exists() && exportDir.isDirectory()) {
            String maxDatum = "";
            File[] files = exportDir.listFiles();
            for (File file : files) {
                String name = file.getName();
                Matcher m1 = p1.matcher(name);
                Matcher m2 = p2.matcher(name);
                if (m1.find() && m2.find()) {
                    String datum = m1.group();
                    String nr = m2.group(1);
                    if (nrString.equals(nr)) {
                        if (maxDatum.compareTo(datum) < 0) {
                            maxDatum = datum;
                            toReturn = file;
                        }
                    }
                }
            }
        }
        return toReturn;
    }

Ich finde, das Buch geht viel zu sehr ins Detail für Anfänger, die erstmal die Grundlagen lernen wollen.
Also eher etwas, wenn man wirklich Durchhaltevermögen hat, als Nachschlagewerk oder wenn man ins Detail gehen möchte.

Meine Emfehlung wäre:
„Sprechen Sie Java?“ Mössenböck (ISBN: 978-3-86490-099-0)
„Programmieren lernen mit Java“ Hans-Peter Habelitz (ISBN: 978-3836241304)
Ansonsten, wer schon beim vielen Lesen, gerne etwas visuelle Eindrücke hat:

  • „Java von Kopf bis Fuß“ Kathy Sierra / Bert Bates (ISBN: 978-3-89721-448-4)

Oder das, wobei selbst mir das etwas zu weit geht:

  • „Schrödinger programmiert Java“ Philip Ackermann (ISBN: 978-3-8362-1740-8)

Ansonsten gäbe es noch haufenweise Internet-Tutorials, wo einige gute dabei sind. Kann jetzt nur keine aus dem Ärmel schütteln. Dann gibt es auch noch Seiten wie SoloLearn oder Udemy, codeacademy. Einfach mal ein bisschen rumgucken, für die Grundlagen ist das meiste sinnvoll.


SO, ick probier’s!

Hier einfach mal mein Versuch, hab versucht es simpel zu halten!? Naja, ist wahrscheinlich auch abseits von perfekt. Mit Streams und so wären das nur ein paar wenige Zeilen. :smiley:

package net.bytewelt;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RecordFileSelector
{
    private final static File infoFile = new File("info.txt");

    private Pattern recordTypePattern = Pattern.compile("(\\b\\d{1})");
    private Pattern datePattern = Pattern.compile("([0-9]{4}-0[1-9]|1[0-2]-0[1-9]|[1-2][0-9]|3[0-1])");
    private Matcher regexMatcher;

    private File recordFile;

    public static void main(String[] args)
    {
        RecordFileSelector recordFileSelector = new RecordFileSelector();
        recordFileSelector.findRecordFile(infoFile);
        recordFileSelector.printRecordFileName();
    }

    private void findRecordFile(File recordInfoFile)
    {
        try (BufferedReader reader = new BufferedReader(new FileReader(recordInfoFile)))
        {
            String recordDirectory = reader.readLine();
            String recordType = reader.readLine();

            selectRecordFile(new File(recordDirectory), recordType);

        } catch (FileNotFoundException e) {
            System.out.println("Couldn't find file with record information: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("Couldn't read file with record information: " + e.getMessage());
        }
    }

    private void selectRecordFile(File recordDirectory, String recordType) throws FileNotFoundException
    {
        if(validRecordInfo(recordDirectory.toString(), recordType))
        {
            List<File> recordFiles = Arrays.asList(recordDirectory.listFiles());

            if(recordFiles.isEmpty())
                throw new FileNotFoundException("Couldn't read files from directory.");

            recordFiles = filterRecordsByType(recordFiles, recordType);

            findLatestRecordFile(recordFiles);
        }
        throw new FileNotFoundException("Couldn't find record file.");
    }

    private boolean validRecordInfo(String recordDirectory, String recordType)
    {
        return !(recordDirectory == null || recordType == null
                || recordDirectory.isEmpty() || recordType.trim().isEmpty());
    }

    private void findLatestRecordFile(List<File> recordFileList)
    {
        if(recordFileList == null || recordFileList.size() == 0) return;

        recordFileList.sort(new Comparator<File>()
        {
            @Override
            public int compare(File record1, File record2)
            {
                return ~getRecordDate(record1).compareTo(getRecordDate(record2));
            }
        });
        recordFile = recordFileList.get(0);
    }

    private String getRecordDate(File recordFile)
    {
        regexMatcher = datePattern.matcher(recordFile.getName());

        if(regexMatcher.find())
            return regexMatcher.group();

        return "";
    }

    private List<File> filterRecordsByType(List<File> recordFileList, String recordType)
    {
        List<File> filteredRecords = new ArrayList<>();

        for(File recordFile : recordFileList)
        {
            if(isRecordType(recordFile, recordType))
            {
                filteredRecords.add(recordFile);
            }
        }
        return filteredRecords;
    }

    private boolean isRecordType(File file, String recordType)
    {
        regexMatcher = recordTypePattern.matcher(file.getName());
        return regexMatcher.find() && regexMatcher.group().equals(recordType);
    }

    private void printRecordFileName()
    {
        if(recordFile != null && recordFile.isFile())
            System.out.println(recordFile.getName());
    }
}

Das kann dann noch umgeschrieben werden (die Datei oder den Dateinamen/-pfad zurückgeben).


Nachtrag

Wobei mir auch nicht ganz klar ist, woran es jetzt eigentlich noch gehangen hat oder noch hängt? Was ist das aktuelle Problem? Das parsen des Dateiinhalts?
Bitte zukünftig das Problem klar schildern:

  • Möglichst genaue Beschreibung, was das Programm/der Code machen soll
  • Möglichst nur den relevanten Code posten, wenn Unsicherheit besteht lieber mehr
  • Möglichst genaue Beschreibung was nicht funktioniert + mögliche Exception anhängen