Neben HTML sind CSS und JavaScript grundlegende Technologien von Webseiten:

  • HTML (HyperText Markup Language) definiert den Inhalt und die Struktur der Seiten.
  • CSS (Cascading Style Sheets) bestimmt davon getrennt das Layout der HTML-Elemente.
  • JavaScript ist hier eine vom Browser ausgeführte (interpretierte) Programmiersprache, die Interaktionen mit der geladenen Webseite ermöglicht. So kann dynamisch auf Benutzeraktionen (wie z.B. Klicks, Eingaben) reagiert werden. Z.B. können Teilinhalte der Seite bearbeitet, neu geladen oder animiert werden, insofern der Server dies auch unterstützt.

Quellcode der Beispiellösung: https://github.com/ateachment/RemoteControlPLC/tree/main/WebPage2  (22.10.2024)

Um die einzelnen Komponenten sauber voneinander zu trennen, wurde folgende Verzeichnisstruktur gewählt:

Webpage2/
index.html
css/
index.css
js/
index.js


index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!-- AWP_In_Variable Name='"Datenbaustein_Motorschaltung".WebStart' -->
<!-- AWP_In_Variable Name='"Datenbaustein_Motorschaltung".WebStop' -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="refresh" content="10">  <!-- page reload every 10 sec -->
    <title>Motorsteuerung v2</title>
    <link rel="stylesheet" href="css/index.css">
    <script type="text/javascript" src="js/index.js">
    </script> 
</head>
<body onload="initLights( :="Motorschütz":, :="Motorschutzschalter":)">  <!-- receive AWP data --> 
	<div id="main">
		<div class="switch">
			<form method="post">
				<input hidden name='"Datenbaustein_Motorschaltung".WebStart' type="text" value="1"/>
				<input class="button start" type="submit" value="I">
			</form>
		</div>
		<div class="switch">
			<form method="post">
				<input hidden name='"Datenbaustein_Motorschaltung".WebStop' type="text" value="0"/>
				<input class="button stop" type="submit" value="0">
			</form>
		</div>
		<div>
			<span class="text">Motor</span>
		</div>
		<div>
			<!-- vector graphic motor -->
			<svg height="100" width="100">         
				<circle id="motor.red" cx="50" cy="50" r="25" stroke="#ff0033" stroke-dasharray="39.27 39.27" stroke-width="20" fill="white"></circle>
				<circle cx="50" cy="50" r="35" stroke="black" stroke-width="2" fill="transparent"></circle>
				<circle cx="50" cy="50" r="15" fill="black" stroke-width="0"></circle>
			</svg>
		</div>
		<div>
			<span class="text">Betrieb</span>
		</div>
		<div>
			<div id="motorschuetz" class="light"></div>
		</div>
		<div>
			<span class="text">Störung</span>
		</div>
		<div>
			<div id="motorschutzschalter" class="light"></div>
		</div> 
	</div>
</body>
</html>


Im folgenden werden nur Änderungen gegenüber der ersten Anwenderwebseite beschrieben:


Seite regelmäßig neu laden

In Zeile 7 wird durch ein Metatag der Browser angewiesen, die Seite alle 10 Sekunden neu zu laden. Nur so kann ohne Benutzereingabe Änderungen des bezüglich des Motorschhützes bzw. des Motorschutzschalters dargestellt werden.

Einbindung von CSS- und JavaScript-Dateien

In den Zeilen 9-10 werden die externen CSS- und JavaScript-Dateien mittels relativer Pfadangaben auf die Unterverzeichnisse eingebunden, die  für Gestaltung und Programmfunktionalität der Seite verantwortlich sind. So werden Struktur, Designbeschreibung und Programmfunktionalität voneinander getrennt. Die Alternative alles direkt in die HTML-Datei zu schreiben, würde diese zu sehr aufblähen.

Zuweisung von AWP-Werten in JavaScript-Variablen

In der Zeile 13 wird die Siemens-spezifische Art und Weise, wie Werte aus der SPS in die vom internen Webserver auszuliefernden Seiten "injiziert" wird, nutzt hier die zwei Werte direkt als Argurmente für die JavaScript-Funktion initLights(). Diese Funktion selbst wird hier nur durch das Kommando onload aufgerufen, wenn index.html geladen wird (also alle 10 Sekunden). Ihre Definition erfolgt in der eingebundenen index.js-Datei.

Formulare sind unverändert

Die in den Zeilen 16-19 und 22-25 sind funktional unverändert. Das bedeutet, dass ein Klick auf das jeweilige Element nach wie vor genügt, um einen POST-Request abzuschicken und damit auch die geamte Seite neu zu laden. Es ist hierfür noch kein JavaScript-Code nötig. Die Verschachtelung in mehrere <div>-Elemente dient lediglich einer einfacheren Zuweiseung der Designvorgaben durch die eingebundene CSS-Datei.

Serielle Vektorgraphik

In den Zeilen 31-36 wird eine skalierbare Vektorgraphik (SVG = Scalable Vector Graphic) für die Darstellung der Motordrehung definiert.

SVG soll die Motordrehung durch Animation darstellen

Sie können ohne Qualitätsverlust in jede Größe skaliert werden. Im Gegensatz zu Rastergrafiken (wie PNG oder JPEG) bleiben SVGs auch bei starker Vergrößerung oder Verkleinerung scharf und klar. Das macht sie ideal für responsive Webdesigns, die auf verschiedenen Bildschirmgrößen funktionieren müssen. Sie werden mittels ihrer mathematischen Definition vom Browser entsprechend gezeichnet. Daher weisen sie auch eine geringe Datenmenge auf, was zu schnellen Ladezeiten führt. In unserem speziellen Fall würde eine Rastergrafik auch zu viel vom kleinen Speicher der SPS in Anspruch nehmen. Sie könnten außerdem interaktiv von JavaScript verändert werden. In dieser Beispiellösung wird die Graphik allerdings nicht geändert, sondern lediglich mittels CSS-Transition und JavaScript gedreht.

Aufbau des SVGs
Aufbau des SVGs  https://support.industry.siemens.com/cs/attachments/68011496/68011496_Examples_for_S7WebServer_DOC_v3_de.pdf  (22.10.2024)

1. Kreis

cx="50" und cy="50": Der Mittelpunkt des Kreises befindet sich bei den Koordinaten (50, 50) relativ zum SVG-Container.
r="25": Der Radius des Kreises beträgt 25 Pixel.
stroke="#ff0033": Die Kreislinie (Umrandung) hat die Farbe Rot (#ff0033).
stroke-dasharray="39.27 39.27": Dies erstellt eine gestrichelte Linie mit abwechselnden Strichen und Lücken, wobei die Länge beider 39,27 beträgt (Umfang des Kreises = 2 * π * 25 ≈ 157,08, ein Viertel davon ergibt die Länge der Striche).
stroke-width="20": Die Dicke der Kreislinie beträgt 20 Pixel.
fill="white": Die Füllfarbe des Kreises ist weiß.
id="motor.red": Dies gibt dem Kreis eine ID, sodass er später durch CSS oder JavaScript referenziert werden kann.

2. Kreis

Der zweite Kreis stellt eine dünne, schwarze Umrandung dar:

r="35": Der Radius beträgt 35 Pixel.
stroke="black": Die Umrandung ist schwarz.
stroke-width="2": Die Dicke der Umrandung beträgt 2 Pixel.
fill="transparent": Der Kreis ist transparent, d.h. er hat keine Füllung.

3. Kreis

Dies ist der kleine, innere Kreis:

r="15": Der Radius beträgt 15 Pixel.
fill="black": Der Kreis ist komplett schwarz gefüllt.
stroke-width="0": Es gibt keine Umrandung.


index.css

Diese Datei wird von index.html eingebunden und gestaltet diese Seite (daher auch die Benennung index.css).

Gestaltung der Seite
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
body {
  background: white;
  margin: 0;
}
#main {
  margin: auto;
  width: 400px;
  display: flex;
  flex-wrap: wrap;
}
#main>div {
  position: relative;
  width: 200px;
  height: 200px;
  text-align: left;
}

input.button, .light{
  height: 120px;
  width: 120px;
  border: 10px solid silver;
  border-radius: 50%;
  color: white;
  text-decoration: none;
  font-size: 36px;
}
.start {
  background-color: darkgreen;
}
.stop {
  background-color: darkred;
}

/* Lights */
.light {
  background: dimgrey;
}
#motorschuetz.on {
    background: lightgreen;
}
#motorschutzschalter.on {
    background: red;
}
.text, svg, .light, form{
    margin: 0;
    position: absolute;
    top: 50%;
    -ms-transform: translateY(-50%);
    transform: translateY(-50%);
}


Flexible Container

Hier wird ein zweispaltiges Design mittels flexibler Container umgesetzt. In Zeile 8 wird festgelegt, dass der div-Container mit der id #main ein Flex-Container ist. Jedes unmittelbar enthaltende Kind-Element ist damit ein sogenanntes Flex-Item. Der Flex-Container hat eine feste Breite von 400 Pixeln und wird mit margin: auto zentriert. In Zeile 9 wird mit flex-wrap: wrap festgelegt, das der Flex-Container mit Flex-Items von links nach rechts fließend aufgefüllt wird. Reicht der Platz nicht mehr aus, erfolgt ein Umbruch.

Die enthaltenden Flex-Items werden in Zeile 11 spezifiziert und haben eine feste Breite von 200 Pixeln. Das bedeutet, dass nach zwei Flex-Items ein Umbruch erfolgt. Hierdurch ergibt sich die zweispaltige Anordnung der Elemente.

Taster und Signallampen

Die Klassen input.button und .light werden in Zeile 18-25 spezifiziert und repräsentieren die beiden Taster und die beiden Signallampen. Dabei rundet in Zeile 22 die Anweisung border-radius: 50% die Ecken maximal so stark ab, dass sich eine runde Form ergibt.

Die Zeilen 27 bis 32 legen jeweils die Farbe des Ein- und Austasters fest.

Farben der Signallampen

In den Zeilen 35-37 wird die Farbe beider Signallampen in ausgeschaltetem Zustand auf eine abgedunkeltes Grau festgelegt.

Die KIasse on der IDs motorschuetz und motorschutzschalter definiert die Farben der Signallampen. Die Klasse on ist in index.html noch nicht eingetragen, sondern wird mit JavaScript nach Bedarf hinzugefügt oder entfernt. Damit wechselt dann die Farbe der jeweiligen Signallampe zwischen Dunkelgrau und Grün bzw. Dunkelgrau und Rot.

Vertikale Ausrichtung

Die Zeilen 44-50 sorgen dafür, dass die jeweiligen Elemente in ihren Flex-Items vertikal ausgerichtet werden.


index.js

Diese Datei wird von index.html eingebunden. Die enthaltenen JavaScript-Funktionen werden vom Browser ausgeführt und animieren die Seite.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var angle = 0;
var rotationInterval;

function initLights(motorschuetz, motorschutzschalter)
{
  if (motorschuetz == 1)
  {
    var element = document.getElementById("motorschuetz");
    element.classList.toggle("on");
    rotationIntervall = setInterval(function () { rotateMotor(20); }, 50);
  }
  if (motorschutzschalter == 0)  //  normally closed
  {
    var element = document.getElementById("motorschutzschalter");
    element.classList.toggle("on");
    clearInterval(rotationInterval);
  }
}
function rotateMotor(newValue)   // rotate motor
{
    angle += newValue;
    if(angle == 200)
        angle == 0;
    document.getElementById("motor.red").setAttribute("transform", "rotate(" + angle + ",50,50)");
}


Signallampen schalten

In den Zeilen 4-18 ist die Funktion initLights() definiert. Sie wird Aufruf der Seite (eigenständiger Refresh alle 10 Sekunden) aufgerufen. Das wurd in index.html durch body onload festgelegt.

Die Argumente der Funktion sind die Aktivität des Motorschützes bzw. des Motorschutzschalter. Der Motorschütz ist normallerweise offen: d.h. 0 = nicht aktiv und 1 = aktiv. Beim Motorschutzschalter verhält es sich umgekehrt, denn er ist normalerweise geschlossen: d.h. 1 = nicht aktiv und 0 = aktiv. Dementsprechend wird die Klasse on zu den jeweiligen Elementen hinzugefügt oder entfernt. (Genauer gesagt erfolgt ein Wechsel von nicht vorhanden in index.html zu vorhanden, bzw. zurück zu nicht vorhanden.) Hierdurch wechselt die Farbe der jeweiligen Signallampe wie in index.css definiert.

Außerdem wird in Zeile 10 bzw. 16 ein Intervall-Timer gestartet bzw. gestoppt, der für die Drehung der skalierbaren Vektorgraphik, die den laufenden Motor symbolisiert, sorgt. Der laufende Intervall-Timer ruft alle 50 Millisekunden die Methode rotateMotor(20) auf. Die Graphik wird so scheinbar flüssig jeweils um 20 Grad weitergedreht.

Motor animieren

In den Zeilen 19-25 wird die Funktion rotateMotor() definiert, die eine Drehung der skalierbaren Vektorgraphik veranlasst. Ein Zähler für den Winkel wird inkrementiert und nach einer halben Drehung zurückgesetzt. Der Drehpunkt wird mit den 2 letzten Argumenten 50, 50 auf den Mittelpunkt des SVG gesetzt, Deren Definition erfolgte in index.html mit ein Größe von 100 x 100 Pixeln.



Last modified: Friday, 31 January 2025, 12:32 PM