Grafik mittels einer JavaScript-Datei in HMI implementieren

brandlpb

Level-1
Beiträge
24
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
TwinCAT 3 HMI - Grafik mittels einer JavaScript-Datei in HMI implementieren

Hallo zusammen,

im Rahmen meiner Bachelorarbeit muss ich nun eine Visualisierung mittels einer JavaScript-Datei in meinem HMI-Projekt darstellen. Hier im Forum wurde eigentlich bereits über das Thema besprochen aber leider kriege ich es nicht hin (falls jemand auch den erwähnten Beitrag sehen möchte: HMI Server HTML5 / JavaScript).

In unserem Projekt besteht der Demonstrator aus mehreren Bestandteilen und zwar wie XTS-Systeme, Delta-Roboter usw. Mein Ziel ist es nun, mehrere CodeBehind-Dateien zur Visualisierung jeder einzelnen Elementen zu erstellen und die Parameter (wie Höhe, Länge, Position) in JavaScript aus dem PLC-Programm abzurufen.

Jedesmal wenn ein Beckhoff-Controller wie Ellipse oder Kreis in den grafischen Editor (Desktop) eingefügt wird, werden ihre Codes im HTML-Editor mit eingetragen. Kann ich die Codes in meiner CodeBehind-Datei verwenden, sodass ich in einem JavaScript mit den Parametern aus dem PLC-Programm einen Kreis erstelle und diesen beispielsweise in einem HTML HOST in Desktop/ Live View aufgezeichnet bekomme? Weiß jemand, ob das möglich ist? Bis jetzt bin ich diesbezüglich nicht fündig geworden.

Ich freue mich auf jede Antwort.

vielen Dank schonmal!

VG
Brand
 
Zuletzt bearbeitet:
Hi, ich hatte dir eigentlich in deinem anderen Beitrag schon einmal ein Beispiel gezeigt, also möglich ist es auf jeden Fall.

Also die Beckhoff Controls kannst du in Javascript über die Id ansprechen...ich versuche solche Sachen eigentlich immer erst in der Console, dort siehst du die ganzen Attribute usw. wobei ne Google Suche meistens schneller ist.

Anhang anzeigen 43288


Ich würde übrigens keine Code Behind Funktion nehmen, sondern eine normale Funktion die beim Desktop onAttached Event aufgerufen wird, dann kannst du dir sicher sein das die Elemente auch gefunden werden.

Wenn du genau beschreibst was du vor hast oder dein HMI Projekt zur Verfügung stellst kann ich dir auch konkreter helfen.

Gruß Mathias
 

Anhänge

  • consoleBeispiel.jpg
    consoleBeispiel.jpg
    58,1 KB · Aufrufe: 66
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,

danke erstmal für die Antwort.

Erstmal zu deinem Beispiel. Das habe ich leider nicht hingekriegt. Die Bilder, wie ich damit umgegangen bin, lade ich hier hoch.

1.jpg2.jpg3.jpg4.jpg

Ich habe einige Überlegungen, woran es liegen könnte.
Style.css habe ich direkt unter Themes->Base ausgewählt. Ist das der richtige Ort?
Bei mir sind die Parameter "d, e, rx, ry" gar nicht definiert. Müsste man die erstmal als interne Symbole deklarieren? Wenn ja, kann ich diese direkt mir den Parametern aus dem PLC-Programm verknüpfen?
In deinem Beispiel hattest du interne Symbole verwendet, ich hingegen Server Symbole, aber das sollte eigentlich zu keinem Problem führen oder?

Ich freue mich, wenn Du sagen könntest, wo das Problem ist.

Ich beschreibe dir jetzt kurz, was ich eigentlich erziele.
Mein Hauptziel ist es, auf die Visualisierung im Anhang zu kommen.
5.jpg
Die dazugehörigen Elemente siehst du unten links in Document Outline. Ich muss aber das ganze über HTML/JavaScript darstellen, damit wir die Parameter (wie die Höhe, Länge oder Position der einzelnen Bestandteile des Demonstrators) erstmal in JavaSkript deklarieren können und diese später zu den Eingangsparameter mehrerer Steuerungen zuordnen können. Wie es gerade ist, kann man das schwer verwirklichen. Deshalb muss ich es irgendwie über HTML/JavaScript gut strukturiert hinbekommen.

Ich bin sehr dankbar für jeden einzelnen Tipp.


Gruß

Brand
 
Hi, also wie immer gibt es mehrere Möglichkeiten und verschiedene Ansätze, in meinem Beispiel hatte ich der Funktion keine Parameter übergeben.


Willst du die Grafiken auf Knopfdruck oder zyklisch bei Änderung einer SPS Variable ändern ?


Wenn du z.B. bei einem Button Klick was machen willst würde ich für diese Aufgabe eine Funktion anlegen der du dann auch Daten per Parameter übergeben kannst.


Aber wenn du zyklisch auf SPS Änderungen reagieren willst würde ich wie in meinem Beispiel eine "Init" Funktion machen die einmal beim Desktop onAttached Event aufgerufen wird.


Darin werden dann diese Events registriert, diese werden beim ersten mal laden der HMI immer ausgeführt (danach bei Änderung), aber Vorsicht wenn du eine Variable falsch schreibst oder du hast keine Verbindung zur SPS wird kein Fehler ausgegeben, der Code in den Events wird einfach nicht ausgeführt.


Hier noch mal ein Beispielt für eine deiner Variablen:


TcHmi.EventProvider.register('%s%PLC1.MAIN.fb_xts1::vardistance%/s%', function (e, d) {
console.log(d);
});


Also console.log() wirst du oft benutzen, gibt dir halt das in Klammer stehende in der Console aus. Wie du die function Parameter (e,d) nennst ist egal...kannst auch (event, data) schreiben.


Ich hatte in meinem Beispiel aber ein HTML-Host Control benutzt und händisch das Rechteck angelegt, so konnte ich direkt über die id die Attribute ansprechen.


Bei den Beckhoff Controls wird noch ne Menge unnützes mit generiert, entscheidend ist was für die Live View erzeugt wird und nicht was du im Visual Studio Editor siehst.


War mein Fehler, über die ID des Controls kommst du nicht direkt an die Elemente, so sieht z.B. eine Ellipse aus...


<div id="Fig_DR_1" data-tchmi-type="tchmi-ellipse" data-tchmi-height="75" data-tchmi-height-unit="px" data-tchmi-left="399" data-tchmi-left-unit="px" data-tchmi-stroke-thickness="1" data-tchmi-top="113" data-tchmi-top-unit="px" data-tchmi-width="150" data-tchmi-width-unit="px" class="tchmi-box tchmi-control tchmi-ellipse" style="left: 399px; top: 113px; width: 150px; height: 75px; z-index: 0; border-width: 0px;"><div class="tchmi-ellipse-template tchmi-box">
<svg class="tchmi-ellipse-template-svg" version="1.1" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<ellipse class="tchmi-ellipse-template-svg-ellipse" cx="75" cy="37.5" rx="74" ry="36.5" style="fill: transparent; stroke: rgb(0, 0, 0); stroke-width: 1px;"></ellipse>
</svg>
</div></div>




...um an das Ellipsen Element zu kommen brauchst du eine Kombination aus CSS Selektoren (einfach mal googlen)...


let ellipse = document.querySelector('#Fig_DR_1 svg > *');


Lesen eines Attributes:


ellipse.getAttributeNS(null, 'rx');


Und Schreiben:


ellipse.setAttributeNS(null, 'rx', '100');




Oder mit dem Event so:


TcHmi.EventProvider.register('%s%PLC1.MAIN.fb_xts1::vardistance%/s%', function (e, data) {
let ellipse = document.querySelector('#Fig_DR_1 svg > *');
ellipse.setAttributeNS(null, 'rx', data);
});




Da du jetzt aber doch alle Elemente mit Javascript manipulieren willst würde ich an deiner Stelle auch ein HTML-Host nehmen, dann brauchst du nur ein großes SVG Element und darin deine ganzen anderen Formen. Dann kannst du auch über CSS Klassen diese relativ einfach stylen und einfacher ansprechen, was dann so aussehen könnte...

<div id="TcHmiHtmlHost" data-tchmi-type="tchmi-html-host" data-tchmi-height="300" data-tchmi-height-unit="px" data-tchmi-left="182" data-tchmi-left-unit="px" data-tchmi-top="406" data-tchmi-top-unit="px" data-tchmi-width="600" data-tchmi-width-unit="px">
<svg width="600" height="300" viewBox="0 0 600 300">
<rect id="rect1" class="rechteck" x="0" y="0" width="0" height="0" rx="0" ry="0" />
<rect id="rect2" class="rechteck" x="0" y="0" width="0" height="0" rx="0" ry="0" />
<ellipse id="ellipse1" class="ellipse" cx="0" cy="0" rx="0" ry="0"></ellipse>
<ellipse id="ellipse2" class="ellipse" cx="0" cy="0" rx="0" ry="0"></ellipse>
</svg>
</div>



CSS:

.rechteck {
fill: none;
stroke: hsla(230, 50%, 40%,1);
stroke-width: 2px;
}

.ellipse {
fill: none;
stroke: hsla(0, 50%, 40%,1);
stroke-width: 2px;
}

Würde auch die vorhandene Style Datei bei den Themes nehmen, kannst aber auch mehrere anlegen und ja interne Symbole funktionieren wie Server Symbole bei den Events ausser das diese anstatt %s% mit %i% geschrieben werden.

So hoffe das sind mal genug Information aber ich helfe gerne weiter...

Gruß Mathias
 
Hi,

vielen herzlichen Dank erstmal für die ausführliche Antwort.

Bei mir sind jedoch noch paar offene Punkte. Hoffe, du kannst mir da weiterhelfen.

Wie gesagt, ich beschäftige mich erst seit 2 Wochen mit JavaScript/HTML/CSS und daher komme ich leicht durcheinander, obwohl ich mir den Beitrag mehmals angeschaut und ausprobiert habe, diese in meinem Projekt anzuwenden.

Erstmal habe ich in Desktop lediglich ein HTML Host und hier gebe ich in den HTML-Editor den folgenden Code ein:

<div id="TcHmiHtmlHost" data-tchmi-type="tchmi-html-host" data-tchmi-height="300" data-tchmi-height-unit="px" data-tchmi-left="182" data-tchmi-left-unit="px" data-tchmi-top="406" data-tchmi-top-unit="px" data-tchmi-width="600" data-tchmi-width-unit="px">
<svg width="600" height="300" viewBox="0 0 600 300">
<rect id="rect1" class="rechteck" x="0" y="0" width="0" height="0" rx="0" ry="0" />
<rect id="rect2" class="rechteck" x="0" y="0" width="0" height="0" rx="0" ry="0" />
<ellipse id="ellipse1" class="ellipse" cx="0" cy="0" rx="0" ry="0"></ellipse>
<ellipse id="ellipse2" class="ellipse" cx="0" cy="0" rx="0" ry="0"></ellipse>
</svg>
</div>

1) Soll ich hier den ID-Namen ändern? (In unserem Fall dann: Fig_DR_1)


Als Nächstes haben wir CSS-Ebene, wo wir die eingegebenen Elemente stylen können. Da kommt der folgende Code rein:

.rechteck {
fill: none;
stroke: hsla(230, 50%, 40%,1);
stroke-width: 2px;
}

.ellipse {
fill: none;
stroke: hsla(0, 50%, 40%,1);
stroke-width: 2px;
}


In JavaScript:

(function (TcHmi) {


TcHmi.EventProvider.register('%s%PLC1.MAIN.fb_xts1::vardistance%/s%', function (e, data) { //um den gewünschten Parameter aus dem PLC-Programm hier zu registrieren
let ellipse = document.querySelector('#Fig_DR_1 svg > *'); //dem Variablen "ellipse" ordnen wir das Element mit dem ID "Fig_DR_1" zu.
ellipse.setAttributeNS(null, 'rx', data); //hiermit schreiben wir ein neues Attribut für rx und ry, damit ich einen Kreis erstellen kann.
ellipse.setAttributeNS(null, 'ry', data);
});


})(TcHmi);


Siehst du, wo der Fehler ist? Ich kann das nämlich nicht nachvollziehen, warum ich hier immer noch keine Visualisierung bekomme.

Besten Gruß

Brand
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Guten Morgen,

mein Fehler, die Beispiele passen nicht zusammen.

das ist die umständliche Art mit Beckhoff Controls:
document.querySelector('#Fig_DR_1 svg > *')

in dem Host Beispiel ist es so zu machen:

let ersteFig = document.querySelector('#rect1');
let zweiteFig = document.querySelector('#rect2');
let dritteFig = document.querySelector('#ellipse1');
let vierteFig = document.querySelector('#ellipse2');

oder auch:
document.getElementById('rect1')

Id´s sollten nur einmalig vorkommen, also nen sie so wie du es bisher auch hast.

Gruß Mathias
 
Hey :)

nochmal ganz kurz: Wie kann man einen Wert aus dem PLC-Programm in JavaScript abrufen?

Ich habe zunächst versucht, den folgenden Code
TcHmi.EventProvider.register('%s%PLC1.MAIN.fb_xts1::vardistance%/s%', function (e, d) {
console.log(d);
});
anzuwenden, leider ohne Erfolg.

In der Console taucht bei mir dann der folgende Fehler auf.

1.jpg

Wüsste jemand woran es liegt oder kennt jemand einen anderen Weg, um die Werte aus dem PLC abzurufen? Außerdem kann ich keine vorprogrammierte Funktion wie getTransform() problemlos in JavaScript durchführen. Das finde ich schon seltsam. 2.jpg3.jpg

Jedenfalls freue ich mich auf eine Antwort. Vielen Dank.



 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi, ohje wieder ein Fehler von mir %s%PLC1.MAIN.fb_xts1::vardistance%/s% sollte wohl %s%PLC1.MAIN.fb_xts1.vardistance%/s% sein, hatte es einfach irgendwo abgeschrieben, am besten kopierst du den richtigen string eines gemappten Symbols aus dem Configuration Fenster.

configWindow.png

Und zu deinem anderen Problem, ich hab es jetzt nicht getestet aber es sieht so aus als ob der Button nicht gefunden wird da 'undefined'.

Du solltest das einfach mal in der Console ausprobieren, wann rufst du die Funktion auf ?

Kann sein das die Controls zu dem Zeitpunkt noch nicht existieren.

Gruß Mathias
 
Hi,

danke erstmal für die schnelle Antwort :)

ne, ich glaube, das war schon richtig so, denn ich bekomme auch die gleiche Schreibweise, wenn ich den String aus dem Configuration Fenster kopiere. Sonst weiß ich aber leider nicht, was da noch falsch sein könnte.

Bei dem anderen mit dem einen Button habe ich nochmal überprüft. Der ID-Name ist schon richtig, und ich führe es erstmal aus, bevor ich in JavaScript was schreibe. Meintest du genau das?

Gruß
Brand
 
Hi, hab die Button Sache grad noch mal probiert und wenn ich die Function beim Desktop onAttached Event ausführe darfst du kein Fehler bekommen.

Du kannst auch mal folgendermaßen versuchen ein Symbol zu lesen...

TcHmi.Symbol.readEx2('%s%PLC1.MAIN.fb_xts1::vardistance%/s%', function (data) {
if (data.error === TcHmi.Errors.NONE) {
console.log(data);
} else {
console.alert("Symbol kann nicht gelesen werden");
}
});
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hey, mir ist noch was eingefallen wegen dem Button Problem, hast du den zufällig in einem anderen Content liegen ?

Falls ja geh mal in die Eigenschaften des Contents und aktiviere "Preload this Partial" sonst wird er auch nicht gefunden.


Gruß Mathias
 
Hi,

Mathias, vielen herzlichen Dank erstmal für deine Antwort und Unterstützung :)

Ne, bei dem neuen Projekt habe ich erstmal keinen Content aber es ist trotzdem gut zu wissen, dass man darauf achten muss.

So jetzt weiß ich, wo der Fehler war. Ich glaube, das Problem lag an .onAttachedEvent.

1. Aber erstmal zum readEx-Befehl. Bei readEx bekomme ich jetzt keinen Fehler mehr aber der Wert wird in der Konsole nicht ausgespuckt. Die Stelle, wo sich console.log befindet, ist glaube ich auch nicht falsch. Sieht jemand da einen Fehler?
JS_4.jpg
Muss da vielleicht irgendwas mit einem Event aus dem Desktop oder vielleicht HTML Host verknüpft werden? Bei mir besteht die Funktion nur aus dem JavaScript und dem PLC-Programm, von wo der Wert gelesen werden soll.

2. Bei get- und getTransform-Befehle habe ich insgesamt drei Seiten:
i. JavaScript

[FONT=&quot] [/FONT][FONT=&quot]var[/FONT][FONT=&quot] myControl = TcHmi.Controls.get([/FONT][FONT=&quot]"TcHmiButton"[/FONT][FONT=&quot]);[/FONT]
[FONT=&quot] [/FONT][FONT=&quot]var[/FONT][FONT=&quot] myTransList = myControl.getTransform();[/FONT]
[FONT=&quot] console.log([/FONT][FONT=&quot]myTransList);[/FONT]

JS_5.jpgJS_6.jpg

Kann es sein, dass die Funktion gar nicht gültig ist, da bei mir im JavaScript "Register Function" (TcHmi.Functions.registerFunction('FunctionJS2', FunctionJS2)) nicht existiert? oder muss man vielleicht erstmal in der JSON-Datei des dazugehörigen Javascriptes über Edit/Define Function Metadata einen Parameter definieren?

ii. Im grafischen Editor (Desktop) habe ich nur einen Control (Rechtangle) mit dem Transform und .onAttached-Event:

JS_7.jpgJS_8.jpg

iii. Als dritte Komponente, um get() und getTransform()-Befehle erfolgreich abzurufen, habe ich den im PLC deklarierten Wert.

Ich freue mich, wenn mir jemand weiterhelfen könnte.

Vielen Dank vorab schonmal.
 
Hi,

also dein erstes Symbol Beispiel hab ich so noch nie verwendet, ist aber glaub ich eher dazu gedacht interne Symbole zu erstellen was man am Schlüsselwort "new" sieht, ich bezweifle das man Server Symbole so erstellen kann...du willst auch eigentlich nur was lesen oder ?

Das Beispiel hier ist aus dem Infosys und sollte funktionieren, ansonsten muss irgendwas anderes faul sein.

TcHmi.Symbol.readEx2('%s%PLC1.MAIN.sTest%/s%', function (data) {
if (data.error === TcHmi.Errors.NONE) {
// Handle result value...
var value = data.value;
console.log(value);
} else {
// Handle error...
}
});
 
Zuviel Werbung?
-> Hier kostenlos registrieren
So nun zur anderen Sache, bei deiner FunctionJS1 muss irgendwas schief gelaufen sein, dieses ...js:formatted hab ich so noch nicht gesehen und du versuchst einen Parameter zu übergeben obwohl dieser in der Funktion nicht mehr definiert ist.

Und diese Zeile solltest du auf jeden Fall beibehalten...
TcHmi.Functions.registerFunction('FunctionJS1', FunctionJS1);

Denn Funktionsaufruf solltest du auch wirklich beim Desktop onAttached Event machen, bei dir ist der wohl auf dem HTML Host, es kommt natürlich drauf an was du mit deinem Java Script machen willst, aber so kannst du dir sicher sein das alle Controls verfügbar sind.
 
Hi,

ich habe jetzt deine Variante probiert, wieder kein Fehler aber auch kein Ergebnis in Console.

JS_10.jpg

Obwohl es mir schon ganz peinlich ist, muss ich nochmal nachfragen, da meine Programmierkenntnisse gerade nicht so toll sind.

Ich glaube, mir fehlt da irgendein Befehl oder eine Zuweisung. Du tippst das nur in JavaScript, sonst machst du auch nichts anderes oder?

Gruß
 

Anhänge

  • JS_9.jpg
    JS_9.jpg
    182 KB · Aufrufe: 12
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

@MathiasV, vielen Dank erstmal für die hilfreichen Antworten.

Deine zweite Nachricht hatte ich nicht gelesen, als ich den letzten Beitrag posten wollte.

Mit dem JavaScript (Function) Item klappt es immer noch nicht, also ich bekomme zwar keinen Fehler aber auch keine Werte unter Console zurück. Ich habe übrigens Verbose Level in der Projekteinstellungen auf 3 gesetzt. Das hat leider auch nichts gebracht.
Am Ende sah das JavaScript so aus:

Anhang anzeigen 43457

Da das jetzt mit JavaScript nicht geklappt hat, habe ich mich entschieden, die Befehle unter einem CodeBehind-Item einzugeben. Mit CodeBedind-Item funktioniert es tatsächlich einwandfrei. Ich kann wirklich nicht nachvollziehen, was bei JavaScript der Fehler war. Sieht jemand im obigen Bild einen Fehler? Vielleicht irgendwas mit "TcHmi.Functions.registerFunction('FunctionJS1', FunctionJS1)"?

Nun kann ich irgendeinen Wert aus dem PLC lesen oder die Werte für Transformationseditor eines Controls in der Console abrufen.
JS_12.jpgJS_13.jpg

Jetzt muss ich die abgelesene Transform-Liste durch eine neue ersetzen und zwar mit den Werten aus dem PLC. Die Schreibweise, was ich für setTransform() eingebe, ist jedoch fehlerhaft.
JS_14.jpgJS_15.jpg


Hätte jemand eine Idee, wie die richtige Schreibweise von setTransform-Befehl aussieht?

Der Link aus der Beckhoff-Seite bzgl. setTransform():
https://infosys.beckhoff.de/english...18014402356199179.html&id=4730662780507833661

Vielen Dank schonmal! ;)
 
Hi,

also erstmal eine CodeBehind Datei ist auch ganz normales JavaScript, aber diese Dateien werden halt irgendwann beim initialisieren der HMI ausgeführt, sprich wenn du darin auf Controls zugreifen willst kann es sein das diese nicht gefunden werden, darum benutze ich diese nicht.

Den Anhang konnte ich nicht öffnen, kannst du das Projekt zur Verfügung stellen ?

Zum Transform Problem, so sieht die richtige Schreibweise aus...

myControl.setTransform([{"transformType": "Translate", "x": "100", "xUnit": "px", "y": "100", "yUnit": "px", "z": "1", "zUnit": "px"}]);

Und du darfst das nicht nochmal einer Variable zuweisen, wobei es eh besser ist "var" durch "let" zu ersetzen, da "var" global ist und so zu unerwünschten Problemen führen kann, aber das nur am Rande, spielt jetzt in dem einfachen Beispiel keine Rolle.

Gruß Mathias
 
Hallo,

@MathiasV, vielen herzlichen Dank erstmal für die Antwort!

zurzeit muss ich mich überwiegend auf den schriftlichen Teil meiner Bachelorarbeit fokussieren, da ich nicht mehr so viel Zeit habe. Daher kommt die Antwort jetzt so spät. :D

Jetzt verstehe ich immer noch nicht, warum ich keinen Rückwert unter der Console bekomme, wenn ich den folgenden Befehl für "Paramaeterabruf aus dem PLC" in einen JavaScript-Item eingebe. Der genau gleiche Befehl funktioniert aber in einem CodeBehind-Item (s.Bild 2). Das verwirrt mit gerade. Sieht jemand einen Fehler beim Platzieren des Befehls im JavaSript oder hat jemand eine Idee, woran das Problem liegen könnte?

JS_16.jpgJS_17.jpg

Vielen Dank vorab schonmal!

Gruß
 
Zuletzt bearbeitet:
Zurück
Oben