TIA OPC-UA eigene Website

lxo

Level-1
Beiträge
4
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Heyy

Meine Idee war es, eine eigene Website zu machen, welche dann die Werte einiger Variablen anzeigt und im besten falle live aktualisiert.
Dafür benutze ich HTML, JavaScript und Node.js, wobei Node.js neu für mich ist
Mit zwei buttons wollte ich die Verbindung zu OPC-UA aufbauen und auch wieder beenden.
Soweit so gut, die Werte werden auf meiner Website angezeigt, nur nicht live und die Buttons funktionieren nicht.
kann mir jemand helfen? Ich sitze hier schon Tage dran und komme nicht weiter, da ich mit Node.js nicht auf DOM zugrreifen kann..
Javascript:
const { OPCUAClient } = require("node-opcua");

const opcua = require("node-opcua");

let realValue;
let arrayValue;
let intValue;
let boolValue;
const cheerio = require('cheerio');
const fs = require('fs');


/*--Connect--Main--*/
async function connectToOPCUAServer() {
  try {
    const endpointUrl = "opc.tcp://172.20.3.211:4840"; // Serveradresse
    const client = OPCUAClient.create({
      end_point_must_exist: false,
  });
    await client.connect(endpointUrl);
    console.log("Verbindung zum OPC UA Server hergestellt");

    // Anmelden
    const session = await client.createSession();
    console.log("Sitzung erstellt");
    await readData(session);
    //await writeData(session);
    await storeData();

    // Abmelden und Verbindung trennen
    await session.close();
    console.log("Sitzung geschlossen");

    await client.disconnect();
    console.log("Verbindung zum OPC UA Server getrennt");
    console.log("Vor dem Aufruf von updateHTML()");
    await updateHTML();
    console.log("Nach dem Aufruf von updateHTML()");

  }
  catch (err) {
    console.error("Fehler beim Zugriff auf den OPC UA Server:", err);
  }
}



/*--Werte lesen--*/

async function readData(session) {

  const nodesToRead = [
    {
      nodeId: 'ns=3;s="DB_OPC".Real1',
      attributeId: opcua.AttributeIds.Value,
    },
    {
      nodeId: 'ns=3;s="DB_OPC".Array1',
      attributeId: opcua.AttributeIds.Value,
    },
    {
      nodeId: 'ns=3;s="DB_OPC".Int1',
      attributeId: opcua.AttributeIds.Value,
    };
    {
      nodeId: 'ns=3;s="DB_OPC".Bool1',
      attributeId: opcua.AttributeIds.Value,
    },
  ];

  const readResult = await session.read(nodesToRead);
  readResult.forEach((result, index) => {
    const { value } = result;
    console.log(
      `Gelesener Wert für Node ${nodesToRead[index].nodeId}:`,
      value.value
    );
  });

  realValue = readResult[0].value.value;
  arrayValue = readResult[1].value.value;
  intValue = readResult[2].value.value;
  boolValue = readResult[3].value.value;
  console.log("Werte gespeichert");
}

/*--HTML--*/
async function updateHTML() {
  // HTML-Datei laden
  const htmlContent = fs.readFileSync("werte.html", "utf8").toString();
  // Cheerio-Objekt erstellen
  const $ = cheerio.load(htmlContent);

  // Wert für Array-Wert aktualisieren
  const arrayValueElement = $('#arrayValue');
  const allNullValues = arrayValue.every((value) => value === 0);
  if (arrayValue !== undefined) {
    const arrayButton = $('#btnarray');
    arrayValueElement.text(arrayValue.toString());


    if (allNullValues) {
      // Setze den Button rot, wenn alle Werte null sind
      arrayButton.attr('style', 'background-color: red; color: white;');
    } else {
      // Setze den Button grün, wenn mindestens ein Wert im Array nicht null ist
      arrayButton.attr('style', 'background-color: green; color: white;');
    }
  } else {
    arrayValueElement.text('');
    console.log("array not defined");


    // Stil des Array-Buttons zurücksetzen, wenn arrayValue nicht definiert ist

    const arrayButton = $('#btnarray');

    arrayButton.attr('style', 'background-color: gray; color: white;');

  }



  const realValueElement = $('#realValue');
  if (realValue !== undefined) {
    const realButton = $('#btnreal');
    realValueElement.text(realValue.toString());
    if (realValue <= 100) {
      realButton.attr('style', 'background-color: red; color: white;');
    } else {
     realButton.attr('style', 'background-color: green; color: white;');
    }
  } else {
    boolValueElement.text('');
    realButton.attr('style', 'background-color: gray; color: white;')
  }

  // Wert für Integer-Wert aktualisieren

  const intValueElement = $('#intValue');
  if (intValue !== undefined) {
    console.log("int if");
    const intButton = $('#btnint');
    intValueElement.text(intValue.toString());

    if (intValue >= 100) {
      intButton.attr('style', 'background-color: red; color: white;');
    } else {
      intButton.attr('style', 'background-color: green; color: white;');
    }
  } else {
    intValueElement.text('');
    console.log("int not defined");
    const intButton = $('#btnint');
    intButton.attr('style', 'background-color: gray; color: white;');
  }

  const boolValueElement = $('#boolValue');
  if (boolValue !== undefined) {
    console.log("bool if");

    // Stil des Buttons basierend auf boolValue festlegen
    const boolButton = $('#btnbool');
    boolValueElement.text(boolValue.toString())

    if (boolValue) {
      boolButton.attr('style', 'background-color: red; color: white;');
    } else {
      boolButton.attr('style', 'background-color: green; color: white;');
    }

  } else {
    boolValueElement.text('');
    console.log("else");

    // Stil des Buttons zurücksetzen, wenn boolValue nicht definiert ist
   const boolButton = $('#btnbool');
    boolButton.attr('style', 'background-color: gray; color: white;');
  }

  const updatedHtml = $.html();

  // Speichern der aktualisierten HTML-Datei
  fs.writeFileSync("werte.html", updatedHtml, "utf8");
  console.log("HTML-Datei erfolgreich aktualisiert");

}

  connectToOPCUAServer();

Vielen dank schonmal im Voraus!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Das stimmt.
Mein problem ist noch, dass diese Website für den kunden ist und diese können nicht mit der befehlszeile umgehen... also muss der Button funktionieren😐
Aber wenn du die Lösung hast wäre ich sehr dankbar, wenn du diese Teilst :)
 
Leider hab ich die Lösung nicht, da ich selbst noch nicht so weit bin.
Quick and dirty wäre es vermutlich, die alle x Sekunden neu zu laden....
 
wie 'live' muss das denn sein?
mit javascript kenne ich mich nicht so gut aus.
aber vlt wäre ein ansatz die seite automatisch nach x sekunden neu zu laden

einfach im <head> folgende zeile
<meta http-equiv="refresh" Content="5; URL=NameDieserDatei.htm">

evtl noch das zusätzlich
<meta http-equiv=“cache-control“ content=“no-cache“>
<meta http-equiv=“pragma“ content=“no-cache“>
<meta http-equiv=“expires“ content=“0“>
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Node.js läuft auf dem Server / ist der Webserver, der kann natürlich nicht auf dem Client die Webseite manipulieren.

Ich würde das so machen, dass node.js die html Seite als statischen Content ausliefert, und über Javascript in der html Seite wird eine Anfrage an den node.js Server gestellt, bestimmte Variablenwerte zu liefern, die dann in der einfachsten Version als Json zurückkommen.

Ich habe das einmal in Kombination mit nodeS7 zum Variablenzugriff auf die S7 so gemacht:

In der robot.html ist eine Funtion getJsonData() welche die Anfrage an den Server stellt.
 
Ich glaube du solltest uns mal deinen allgemeinen Plan erklären.
- Was/Wer führt deine Nodejs App aus? PM2, Docker, einfach als Prozess gestartet?
- Welchen Web Server willst du nutzen?

Da ich nicht genau verstehe was du da vor hast, kann ich dir einfach mal beschreiben wie ich das angehen würde:

Um dynamische Daten auf einer Website darzustellen, werden diese Daten meitens über HTTP-Request von irgendwo geholt.
Wie oft und wann du dann diesen HTTP-Request machst ist dir überlassen. Alle x Sekunden mit setIntervall(), oder beim Betätigen eines Buttons usw. Das schließt natürlich ein, dass du ein Backend hast, dass die HTTP-Requests entgegen nimmt und die die Daten liefert, die du benötigst.

Da kannst du beispielsweise Expressjs nutzen:
Develop a backend server for your application using express.

Hier ein Beisipel für ein Backend (Nicht getestet).
Mit dem HTTP-GET-Request auf z.B.: http://localhost:3000/opcua-data bekommst du die Werte als JSON.

Javascript:
const express = require("express");
const cors = require("cors");
const app = express();

app.use(express.json());
app.use(
  cors({
    origin: "*",
  })
);

const nodesToRead = [
  {
    nodeId: 'ns=3;s="DB_OPC".Real1',
    attributeId: opcua.AttributeIds.Value,
  },
  {
    nodeId: 'ns=3;s="DB_OPC".Array1',
    attributeId: opcua.AttributeIds.Value,
  },
  {
    nodeId: 'ns=3;s="DB_OPC".Int1',
    attributeId: opcua.AttributeIds.Value,
  },
  {
    nodeId: 'ns=3;s="DB_OPC".Bool1',
    attributeId: opcua.AttributeIds.Value,
  },
];

app.get("/opcua-data", async (req, res) => {
  try {
    const endpointUrl = "opc.tcp://172.20.3.211:4840"; // Serveradresse
    const client = OPCUAClient.create({
      end_point_must_exist: false,
    });
    await client.connect(endpointUrl);
    console.log("Verbindung zum OPC UA Server hergestellt");

    // Anmelden
    const session = await client.createSession();
    console.log("Sitzung erstellt");

    const readResult = await session.read(nodesToRead);
    readResult.forEach((result, index) => {
      const { value } = result;
      console.log(
        `Gelesener Wert für Node ${nodesToRead[index].nodeId}:`,
        value.value
      );
    });

    res.status(200).json({
      realValue: readResult[0].value.value,
      arrayValue: readResult[1].value.value,
      intValue: readResult[2].value.value,
      boolValue: readResult[3].value.value,
    });

    // Abmelden und Verbindung trennen
    await session.close();
    console.log("Sitzung geschlossen");

    await client.disconnect();
    console.log("Verbindung zum OPC UA Server getrennt");
    console.log("Vor dem Aufruf von updateHTML()");
    await updateHTML();
    console.log("Nach dem Aufruf von updateHTML()");
  } catch (err) {
    console.error("Fehler beim Zugriff auf den OPC UA Server:", err);
  }
});

app.listen(3000, () => console.log("Server listening at port 3000"));

Im Endeffekt hast du dann 2 Nodejs Apps, einmal das Backend das die OPCUA Daten bereitstellt,
zum anderen dein Frontend (die Website) die die Daten darstellt.

Im Frontend kannst du die Daten mit fetch() holen:
Using the Fetch API

Javascript:
async function getOPCUAData() {
  const response = await fetch("http://localhost:3000/opcua-data");
  const data = await response.json();
}
 
Ich glaube du solltest uns mal deinen allgemeinen Plan erklären.
- Was/Wer führt deine Nodejs App aus? PM2, Docker, einfach als Prozess gestartet?
- Welchen Web Server willst du nutzen?

Da ich nicht genau verstehe was du da vor hast, kann ich dir einfach mal beschreiben wie ich das angehen würde:

Um dynamische Daten auf einer Website darzustellen, werden diese Daten meitens über HTTP-Request von irgendwo geholt.
Wie oft und wann du dann diesen HTTP-Request machst ist dir überlassen. Alle x Sekunden mit setIntervall(), oder beim Betätigen eines Buttons usw. Das schließt natürlich ein, dass du ein Backend hast, dass die HTTP-Requests entgegen nimmt und die die Daten liefert, die du benötigst.

Da kannst du beispielsweise Expressjs nutzen:
Develop a backend server for your application using express.

Hier ein Beisipel für ein Backend (Nicht getestet).
Mit dem HTTP-GET-Request auf z.B.: http://localhost:3000/opcua-data bekommst du die Werte als JSON.

Javascript:
const express = require("express");
const cors = require("cors");
const app = express();

app.use(express.json());
app.use(
  cors({
    origin: "*",
  })
);

const nodesToRead = [
  {
    nodeId: 'ns=3;s="DB_OPC".Real1',
    attributeId: opcua.AttributeIds.Value,
  },
  {
    nodeId: 'ns=3;s="DB_OPC".Array1',
    attributeId: opcua.AttributeIds.Value,
  },
  {
    nodeId: 'ns=3;s="DB_OPC".Int1',
    attributeId: opcua.AttributeIds.Value,
  },
  {
    nodeId: 'ns=3;s="DB_OPC".Bool1',
    attributeId: opcua.AttributeIds.Value,
  },
];

app.get("/opcua-data", async (req, res) => {
  try {
    const endpointUrl = "opc.tcp://172.20.3.211:4840"; // Serveradresse
    const client = OPCUAClient.create({
      end_point_must_exist: false,
    });
    await client.connect(endpointUrl);
    console.log("Verbindung zum OPC UA Server hergestellt");

    // Anmelden
    const session = await client.createSession();
    console.log("Sitzung erstellt");

    const readResult = await session.read(nodesToRead);
    readResult.forEach((result, index) => {
      const { value } = result;
      console.log(
        `Gelesener Wert für Node ${nodesToRead[index].nodeId}:`,
        value.value
      );
    });

    res.status(200).json({
      realValue: readResult[0].value.value,
      arrayValue: readResult[1].value.value,
      intValue: readResult[2].value.value,
      boolValue: readResult[3].value.value,
    });

    // Abmelden und Verbindung trennen
    await session.close();
    console.log("Sitzung geschlossen");

    await client.disconnect();
    console.log("Verbindung zum OPC UA Server getrennt");
    console.log("Vor dem Aufruf von updateHTML()");
    await updateHTML();
    console.log("Nach dem Aufruf von updateHTML()");
  } catch (err) {
    console.error("Fehler beim Zugriff auf den OPC UA Server:", err);
  }
});

app.listen(3000, () => console.log("Server listening at port 3000"));

Im Endeffekt hast du dann 2 Nodejs Apps, einmal das Backend das die OPCUA Daten bereitstellt,
zum anderen dein Frontend (die Website) die die Daten darstellt.

Im Frontend kannst du die Daten mit fetch() holen:
Using the Fetch API

Javascript:
async function getOPCUAData() {
  const response = await fetch("http://localhost:3000/opcua-data");
  const data = await response.json();
}
Normalerweise führe ich die ja Datei mithilfe von dem Terminal aus, also „nmp Index.js“. Ich habe jetzt aber gesagt bekommen das das nicht möglich ist, und ich in der Website Buttons benutzen soll, welche dann der Kunde ganz einfach drücken muss, sodass sich die Verbindung aufbaut und er keinen Befehl mehr ins Terminal eingeben muss.
Wenn das nicht möglich ist, dann brauche ich das garnicht weiter versuchen und mir die Augen wund zu lesen 😅. Aber danke, das werde ich in zwei Wochen mal ausprobieren, bin ab heute in den Ferien :)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hey, ich weiss alles selber machen ist toll. Aber hast du schon mal Node-Red in Betracht gezogen?
Da gibt es so ziemlich genau das was du machst quasi fertig und noch mehr.
 
Hey, ich weiss alles selber machen ist toll. Aber hast du schon mal Node-Red in Betracht gezogen?
Da gibt es so ziemlich genau das was du machst quasi fertig und noch mehr.
Ich bin im Praktikum und das ist ein Projekt was die mir gegeben haben. Deswegen wollte ich es schon gerne selber machen. Aber ich schau es mir mal an, danke :)
 
Ich bin im Praktikum und das ist ein Projekt was die mir gegeben haben. Deswegen wollte ich es schon gerne selber machen. Aber ich schau es mir mal an, danke :)
Wie deine Auftraggeber staunen werden, wenn du als Praktikant eine Lösung präsentierst, die um Klassen einfacher ist... :)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wie deine Auftraggeber staunen werden, wenn du als Praktikant eine Lösung präsentierst, die um Klassen einfacher ist... :)
Aber auch nur für die allereinfachsten Dinge. Wenn du mit Javascript in den Nodes anfangen musst, dann wird es richtig schlimm. Bei manchen Dingen ist man schneller wenn man alles von Anfang an selber macht, als sich mit halbgaren Lösungen von anderen rumärgern zu müssen.
 
Aber auch nur für die allereinfachsten Dinge.
Ist Node-RED so schlecht? Ich dachte dass es speziell für solch einfache Aufgaben entwickelt wurde. Selbst habe ich es auch noch nicht ausprobiert, aber es steht bereits auf der Agenda.

Wie muss ich dein Kommentar verstehen?
Ist das so ähnlich wie wenn man sich gut mit TIA und S1500 auskennt und dann eine LOGO! Programmieren muss? :unsure:
 
Es ist nicht schlecht, aber ich finde es schnell sehr unübersichtlich, da es nur in den einfachsten Fällen ausreicht nur die Nodes zu verbinden ohne eigenen Code zu schreiben. Im Grunde bin ich auch ein Befürworter von CFC, aber da kann mich mir ja recht einfach für sich wiederholende Funktionen eigene "Nodes" schreiben.
 
Zurück
Oben