WinCC Unified HMI MTP 1000 Unified Archive Dateien als csv-Datei auf USB

Wie gesagt, ich bin da nicht so der Skript-Experte aber ich denke du kannst nicht einfach den UDT angeben sondern du wirst eine Schleife programmieren müssen die dann jede Variable einzeln in die csv-Datei schreibt.
Wird sicherlich auch davon abhängen, wie du die Datei aufbaust bzw. für welchen Weg du dich entscheidest. Bei den Archiv-Variablen gibst du im Skript ja nur das Archiv an, das in die csv geschrieben werden soll. Wie deine Daten da in der SPS strukturiert sind, spielt dann keine Rolle.
 
Das mit dem Archiv ist mir noch ein bisschen unklar. Ich weiss nicht welchen Vor- bzw Nachteil das ganze hat. Wichtig ist mir nur, dass die Werte nicht verloren gehen, das kann ich auch mit der Eigenschaft remanent erreichen.
 
Grüße,

nachdem ich den Blog eingehend studiert habe, möchte ich eine kurze Lösung präsentieren, wie man mithilfe des CSV-Exports mehrere Spalten erstellen kann. Meiner Erfahrung nach bietet dieser Code bei überschaubaren Datenmengen eine praktikable Lösung. Ich habe ihn bisher mit 40.000 Zeilen getestet – der Abschluss erfolgte in etwa 1–2 Sekunden. Ob er auch bei größeren Datenmengen bzw. Spaltenanzahlen optimal funktioniert, kann ich daher nicht mit Gewissheit sagen. In meinem Beispiel wurden insgesamt 10 Variablen (ca. 400.000 Einträge, bestehend aus Wstring und LReal) verarbeitet. Natürlich ließe sich beim Code noch das ein oder andere optimieren, dass lass ich jedoch euch über ;)

[Verwendete HMI: MTP1500 Unified Comfort PRO]

Code:


Javascript:
export async function Schaltfläche_1_OnDown(item, x, y, modifiers, trigger) {

  // Bestimme den Filepfad. Bei Speicherung auf externen USB checke welcher Port verwendet wird 
let fileName = "/media/simatic/X61/Day01_.csv";

  // Endzeit: aktueller Zeitpunkt
  let end = new Date();
  // Startzeit: 24 Stunden in die Vergangenheit adaptiv
  let start = new Date(end.getTime() - 1000 * 60 * 60 * 24);

  // Trennzeichen für die CSV-Datei
  let delimiter = ";";

  // CSV-Header mit 10 Spalten
  let csvData =
    "SinglePartID" + delimiter +
    "ProductID" + delimiter +
    "ProductType" + delimiter +
    "Result" + delimiter +
    "PathNV" + delimiter +
    "PathAV" + delimiter +
    "ForceNV" + delimiter +
    "ForceAV" + delimiter +
    "Errorcode" + delimiter +
    "Errormessages\n";

  // Tags definieren (alle 10 Tags)
  let tag1  = HMIRuntime.TagLogging.LoggedTags("receiveValue_SinglePartID:SinglePartID");
  let tag2  = HMIRuntime.TagLogging.LoggedTags("receiveValue_ProductID:ProductID");
  let tag3  = HMIRuntime.TagLogging.LoggedTags("receiveValue_ProductType:ProductType");
  let tag4  = HMIRuntime.TagLogging.LoggedTags("receiveValue_Result:Result");
  let tag5  = HMIRuntime.TagLogging.LoggedTags("receiveValue_PathNV:PathNV");
  let tag6  = HMIRuntime.TagLogging.LoggedTags("receiveValue_PathAV:PathAV");
  let tag7  = HMIRuntime.TagLogging.LoggedTags("receiveValue_ForceNV:ForceNV");
  let tag8  = HMIRuntime.TagLogging.LoggedTags("receiveValue_ForceAV:ForceAV");
  let tag9  = HMIRuntime.TagLogging.LoggedTags("receiveValue_ErrorcodesScrap:Errorcode");
  let tag10 = HMIRuntime.TagLogging.LoggedTags("receiveValue_Errormessages:Errormessage");

  try {
    // Alle Tag-Lesevorgänge gleichzeitig starten
    let [
      result1, result2, result3, result4, result5,
      result6, result7, result8, result9, result10
    ] = await Promise.all([
      tag1.Read(start, end, 0),
      tag2.Read(start, end, 0),
      tag3.Read(start, end, 0),
      tag4.Read(start, end, 0),
      tag5.Read(start, end, 0),
      tag6.Read(start, end, 0),
      tag7.Read(start, end, 0),
      tag8.Read(start, end, 0),
      tag9.Read(start, end, 0),
      tag10.Read(start, end, 0)
    ]);

    // Ergebnisse der einzelnen Tag-Lesevorgänge extrahieren
    let tagArray1  = result1.Values;
    let tagArray2  = result2.Values;
    let tagArray3  = result3.Values;
    let tagArray4  = result4.Values;
    let tagArray5  = result5.Values;
    let tagArray6  = result6.Values;
    let tagArray7  = result7.Values;
    let tagArray8  = result8.Values;
    let tagArray9  = result9.Values;
    let tagArray10 = result10.Values;

    // Statusmeldung (Debug) – Alle Tags wurden gelesen
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "Alle Tags gelesen");

    // Bestimme die Länge des kürzesten Arrays, um Indexfehler zu vermeiden
    let loopLength = Math.min(
      tagArray1.length,
      tagArray2.length,
      tagArray3.length,
      tagArray4.length,
      tagArray5.length,
      tagArray6.length,
      tagArray7.length,
      tagArray8.length,
      tagArray9.length,
      tagArray10.length
    );
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "Looplength ok: " + loopLength);

    // Zeilenweise zusammenführen der Tag-Werte in die CSV-Zeichenkette
    for (let i = 0; i < loopLength; i++) {
      let value1  = tagArray1[i].Value;
      let value2  = tagArray2[i].Value;
      let value3  = tagArray3[i].Value;
      let value4  = tagArray4[i].Value;
      let value5  = tagArray5[i].Value;
      let value6  = tagArray6[i].Value;
      let value7  = tagArray7[i].Value;
      let value8  = tagArray8[i].Value;
      let value9  = tagArray9[i].Value;
      let value10 = tagArray10[i].Value;

      csvData += value1 + delimiter +
                 value2 + delimiter +
                 value3 + delimiter +
                 value4 + delimiter +
                 value5 + delimiter +
                 value6 + delimiter +
                 value7 + delimiter +
                 value8 + delimiter +
                 value9 + delimiter +
                 value10 + "\n";
    }
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "For-Schleife durch");

    // CSV-Datei schreiben
    await HMIRuntime.FileSystem.WriteFile(fileName, csvData, "utf8");

    // Erfolgsmeldungen ausgeben
    HMIRuntime.Trace("Write file finished successfully");
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "CSV created");

  } catch (err) {
    // Fehlerbehandlung: Fehler loggen und Status setzen
    HMIRuntime.Trace("Fehler: " + err);
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "CSV not created");
  }
}


Das Ergebnis war eine wunderschöne .csv Datei:
1738770846415.png
 
Hallo foxixof,

Vielen Dank für den nützlichen Beitrag. Ich habe bisher 3 verschiedene Javascript Codes getestet, leider funktionierte keiner davon. Deshalb glaube ich langsam, dass es einen Unterschied zwischen Unified Basic und Unified Comfort gibt. (ich bin mir sicher, dass ich die Codes richtig übernommen habe).

Bei Bsp 1 (auf der Siemens Homepage zu finden) wurde mir bei den ersten Versuchen eine csv Datei erstellt, es kam aber jedesmal beim Einstecken des Usb stick s in den Laptop die Meldung, dass der Stick beschädigt wäre.

Ich verwende keine Archiv Variablen, ich will nur die Werte die in einem DB drin stehen exportieren und wieder importieren.

Die anderen Javascript Codes stammen auch hier aus dem Forum und wurden als gut befunden. Bei mir geht es leider nicht. Es muss daran liegen, dass ich ein Basic Panel habe. Meine Firmware ist 19.00.3 und Tia hat V19 upd 3

Gruß
 
Ich habe mir ein Script gebastelt (mit Der Hecht 4.0), wo ich immer nur eine Archivvariable übertrage. Mit einem Button rufe ich dann alle Scripte auf. Funktioniert auch. Einschließlich Meldearchiv, das sieht etwas anders aus.

Es ist ein Comfort-Panel und das Ziel ist eine SD-Karte. Getestet mit V18 und V20.


Javascript:
export function csvDruckHsp(parameter1, parameter2) {
let timeStamp = new Date().toLocaleDateString().replace(/[/]/g, '_');
// let path = "/net/mount/BoilerUnten" + timeStamp + ".csv" // mit Zeitstempel immer neuer Dateiname
let path = "/media/simatic/data-storage/CSV/DruckHsp" + ".csv" // net/mount = projektierte Verbindung zum Netzlaufwerk (HMI-Netzwerk-Einstellungen)

let delimeter = ';';
let start = new Date();
let end = new Date(start.getTime() - 1000 * 60 *60 *168); // 24 = 24h, 48 = 2 Tage usw.

let tag = HMIRuntime.TagLogging.LoggedTags("dbAnalog_DruckHsp_OutValue:DruckHsp"); // HMI-Variable-Name:Archivvariable-Name
let tagValue = tag.Read(start, end, 0);
let CSVData = "Tag Name" + delimeter + "Logged Time Stamp" + delimeter + "Logged Value" + delimeter + "\n";

tagValue.then((loggedResult) => {
let logArrayTag = loggedResult.Values;

for (let loggedTag of logArrayTag) {
CSVData += tag.Name + delimeter + loggedTag.TimeStamp + delimeter + loggedTag.Value + delimeter + "\n";
}

HMIRuntime.FileSystem.WriteFile(path, CSVData, "utf8").then(
function() {
  HMIRuntime.Trace("Write CSV Druck Hauptspritzpumpe OK");
}).catch(function(errorCode) {
  HMIRuntime.Trace("Write CSV Druck Hauptspritzpumpe errorcode=" + errorCode);
});

}).catch(function(errorCode) {
  HMIRuntime.Trace("Read Druck Hauptspritzpumpe errorCode=" + errorCode);
});
}
 
Nochmals herzliche Grüße,

zunächst möchte ich mich dafür entschuldigen, dass ich einen kleinen Bug übersehen hatte – bei der Ausführung des Codes trat zeitweise das Problem auf, dass die CSV-Datei gar nicht geschrieben wurde. Dieses Problem konnte ich mittlerweile beheben, indem ich die WriteFile-Abfrage korrekt implementiert habe.

Javascript:
HMIRuntime.FileSystem.WriteFile(fileName, csvData, "utf8").then(
function() {

    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "CSV created");

}).catch(function(errorCode) {
  HMIRuntime.Trace("Write failed errorcode=" + errorCode);
});

Außerdem plane ich, mir das direkte Schreiben aus einer Datenbank noch genauer anzusehen. Den Import habe ich zwar noch nicht ausprobiert, aber der Export sollte mit dem folgenden Code wie erwartet funktionieren.
Ich verwende keine Archiv Variablen, ich will nur die Werte die in einem DB drin stehen exportieren und wieder importieren.

Das mit dem Importieren habe ich nicht versucht jedoch das mit dem Exportieren sollte nach diesem Code funktionieren:
Javascript:
export function Schaltfläche_3_OnDown(item, x, y, modifiers, trigger) {
 
  let end = new Date();
  // Dateiname inkl. Zeitstempel, um eindeutige Dateinamen zu erzeugen
  let fileName = "/media/simatic/X61/DBTest_" + end.getTime() + ".csv";
 
  // Trennzeichen für die CSV-Datei
  let delimiter = ";";
 
  // CSV-Header initialisieren
  let csvData = "Arraytest\n";

  try {

    let tag1 = Tags("testarray");

    // Lese das Array über die Read()-Methode
    // Hinweis: Falls Read() asynchron ist, sollte hier 'await' verwendet werden (z. B. in einer async-Funktion)
    // let tagArray = await tag1.Read(...);
    let tagArray =  tag1.Read(); // ggf. Parameter wie (start, end, 0) hinzufügen

    // Debug-Ausgabe: Statusmeldung, dass die Tags gelesen wurden
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "Alle Tags gelesen");

    // Bestimme die Länge des Arrays
    let loopLength = tagArray.length;
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "Looplength: " + loopLength);

    // Iteriere über alle Elemente des Arrays und erstelle Zeilen für die CSV-Datei
    for (let i = 0; i < loopLength; i++) {
      let value = tagArray[i];
      // Füge jeder Zeile den String "Hallo" gefolgt vom Wert hinzu
      csvData += "Hallo" + value + "\n";
      
      // Debug-Ausgabe: Zeige an, welcher Wert gerade verarbeitet wurde
      HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "Schreibe Tags: " + value);
    }

    // Schreibe die CSV-Daten in die Datei (asynchron)
    HMIRuntime.FileSystem.WriteFile(fileName, csvData, "utf8")
      .then(function() {
        // Erfolgsstatus setzen, wenn die Datei erfolgreich geschrieben wurde
        HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "CSV created");
      })
      .catch(function(errorCode) {
        // Fehler beim Schreiben der Datei loggen
        HMIRuntime.Trace("Write failed errorcode=" + errorCode);
      });

  } catch (err) {
    // Fehlerbehandlung: Fehler loggen und entsprechenden Status setzen
    HMIRuntime.Trace("Fehler: " + err);
    HMIRuntime.Tags.SysFct.SetTagValue("handlingerrorstring", "CSV not created");
  }
}

Ich möchte zudem darauf hinweisen, dass dieser Test mit einem Array von 1000 Elementen durchgeführt wurde, was zu längeren Ladezeiten führen kann. Außerdem läuft der gesamte Zyklus nicht asynchron, was – meines Erachtens – Auswirkungen auf die Runtime haben kann. Dies ist besonders relevant in Szenarien, in denen beispielsweise sichergestellt werden muss, dass die Maschine stillsteht, während Daten aus der SPS ausgelesen werden.

Ich hoffe, der Code und meine Hinweise helfen dir weiter! :)
Beste Grüße!
 
Hallo Foxixof, danke für deine Ausführliche Lösung.
Ich benutze Unified MTP 1000 Comfort. An dem Panel habe ich zwei USB-Ansclüsse X61 und X62 . Die sind soweit bekannt.
Hier habe ich meine Archiverung LogingTags soweit erstellt und diese Werte kann ich auf dem Diagramm beobachten.

Unser Kunde möchte zwei Zeiteingabefelder für den Zeitraum haben, wo man die Export der CSV Dateien nur in diesem Zeitraum realisieren soll

Hast du hierzu vielleicht eine Lösung?

Danke und Gruß

erdi
 
Hallo erd,

Prinzipiell dürfte das kein Problem darstellen.

Javascript:
  // Endzeit: aktueller Zeitpunkt
  let end = new Date();
  // Startzeit: 24 Stunden in die Vergangenheit adaptiv
  let start = new Date(end.getTime() - 1000 * 60 * 60 * 24);

Prinzipiell kann man das System so verstehen: "end" definiert den Startzeitpunkt und "start" den Endzeitpunkt. Witzig was? :D
(Kann man auch gerne drehen wenn es für das Verständnis weiterhilft.)

Bei der Read-Funktion wird daher von start - end gelesen, wie im Code unten dargestellt. Das heißt würde man hier vorgegebene Werte einsetzen würde das genau gleich funktionieren. In meinem Fall hatte ich ja einfach die Zeit "end - 24h" gerechnet. Das heißt nichts anderes das er anhand seiner gesetzten Timestamps vom Logging den Zeitpunkt "end" sucht und darauf 24h in die Vergangenheit iteriert.

Javascript:
tag1.Read(start, end, 0),


In deinem Falle würde ich einfach empfehlen 2 Variablen mit dem Datentypen "Date" anzulegen und diese dementsprechend auslesen & verarbeiten. Im optimalen Falle gleich mit einer Abfrage wenn EMPTY -> Fehlermeldung.

Schönen Montag noch!
 
Hallo Foxixof,

das heißt aber auch, wenn ich nur einen Variablen als startzeit (date) als Eingabezeit erstelle
, würde das Programm von end zeit bis zu meiner startzeit speichern.

End zeit wäre heue 17.02.2024 , 16:21

Startzeit (Eingabefeld) 16.02.2024, 00:00 , so kann man auch mit einem Variable (Eingabefeld) auch lösen?
Verstehe ich dich richtig?

Gruß
Erdi


 
End zeit wäre heue 17.02.2024 , 16:21

Startzeit (Eingabefeld) 16.02.2024, 00:00 , so kann man auch mit einem Variable (Eingabefeld) auch lösen?
Verstehe ich dich richtig?
Hallo erd,

ja absolut richtig!
Wichtig ist nur das die beiden Datenformate dieselben sind.
Hier kann ich dir empfehlen einfach 2 Ausgabefenster nebeneinander zu machen oder die Datenformate zu loggen um die Kongruenz zu prüfen.

Beste Grüße
Foxixof
 
Hallo Foxixof,

die Trenddiagramms auf dem HMI machen mich einfach fertig. Auf der Zeit Achse zeigt mir einfach die Uhrzeit als falsche Uhrzeit. Auf dem HMI Runtime habe ich die Uhrzeit richtig eingestellt. Danach habe ich auf dem Mainbild einen Uhrzeitanzeiger mit Skript erstellt und der Skript zeigt mir die richtige Uhrzeit. Leider auf dem Diagramm habe ich immer die falsche Uhrzeit . Siehe die beiden Bilder unten. Ich kriege Krise. Woran kann es liegen?
Gruß

erdi
 

Anhänge

  • IMG_0906.jpg
    IMG_0906.jpg
    50,6 KB · Aufrufe: 13
  • IMG_0907.jpg
    IMG_0907.jpg
    53,4 KB · Aufrufe: 13
Hallo Foxixof,

die Trenddiagramms auf dem HMI machen mich einfach fertig. Auf der Zeit Achse zeigt mir einfach die Uhrzeit als falsche Uhrzeit. Auf dem HMI Runtime habe ich die Uhrzeit richtig eingestellt. Danach habe ich auf dem Mainbild einen Uhrzeitanzeiger mit Skript erstellt und der Skript zeigt mir die richtige Uhrzeit. Leider auf dem Diagramm habe ich immer die falsche Uhrzeit . Siehe die beiden Bilder unten. Ich kriege Krise. Woran kann es liegen?
Gruß

erdi
Hallo erdi,

Guck mal in den Einstellungen des Trends, ob die Zeitzone auf -1 eingestellt ist.
 
Zurück
Oben