Custom Web Controls (CWC) von Grund auf: Moderne Web-Entwicklung ohne TIA-Portal-Zwang
Wie Sie Siemens WinCC Unified Custom Web Controls mit Vite, React und TypeScript auf macOS oder Linux entwickeln, ohne jedes Mal ein 5 GB TIA-Portal-Projekt neu zu laden.
Siemens SIMATIC WinCC Unified bietet mit Custom Web Controls (CWC) eine der mächtigsten Erweiterungsschnittstellen in der industriellen Automatisierung: Die Möglichkeit, beliebige HTML5/CSS/JavaScript-Komponenten direkt in das HMI-Bild einzubetten.
In der Praxis erleben Web-Entwickler und SPS-Programmierer jedoch oft Frustration:
- Jeder Testdurchlauf erfordert das Packen eines ZIP-Archivs,
- das Importieren in die TIA-Portal-Bibliothek,
- das Neukompilieren des Unified Panels,
- und den Download auf das Testgerät.
Ein Zyklus dauert nicht selten 3 bis 7 Minuten – für das Ändern einer simplen CSS-Farbe!
In diesem Guide zeigen wir Ihnen, wie die interne WebCC-Architektur funktioniert und wie Sie einen lokalen Entwicklungs-Workflow mit Vite Hot Module Replacement (HMR) etablieren, der in unter 50 Millisekunden aktualisiert.
1. Die Anatomie eines Custom Web Controls
Ein Custom Web Control ist im Kern eine isolierte Webanwendung, die innerhalb eines sandboxed <iframe> in der WinCC Unified Runtime ausgeführt wird.
Die Struktur eines minimalen CWC-Pakets:
my-cwc-widget.zip
├── manifest.json // Metadaten, Properties, Events
├── index.html // Einstiegspunkt für den Iframe
├── assets/
│ ├── index.js // Gebündelter Code
│ └── index.css // Styling
Die Schlüsseldatei: manifest.json
Das Manifest definiert den Vertrag zwischen TIA Portal und Ihrem Web-Code:
{
"manifestVersion": "2.0.0",
"name": "LiveMotorGauge",
"version": "1.2.0",
"type": "control",
"entry": "index.html",
"properties": [
{
"name": "CurrentAmps",
"type": "Double",
"defaultValue": 0.0,
"description": "Aktuelle Motorstromaufnahme in Ampere"
},
{
"name": "MaxLimitAmps",
"type": "Double",
"defaultValue": 45.0,
"description": "Maximaler Grenzwert für Warnanzeige"
}
],
"events": [
{
"name": "OnLimitExceeded",
"parameters": [
{ "name": "CurrentValue", "type": "Double" },
{ "name": "Timestamp", "type": "DateTime" }
]
}
]
}
2. Der Siemens WebCC Lifecycle
Wenn WinCC Unified das Iframe lädt, injiziert die Runtime ein globales Objekt WebCC:
// Typdefinition für die Siemens WebCC API
interface WebCCEventObject {
name: string;
parameters: Record<string, unknown>;
}
interface SiemensWebCC {
start(
onSuccess: (result: boolean) => void,
onError: (error: unknown) => void,
subscriptions: string[],
customTimeout?: number
): void;
on(event: string, callback: (data: unknown) => void): void;
fire(event: string, data: Record<string, unknown>): void;
isPropertyConnected(propertyName: string): boolean;
}
declare const WebCC: SiemensWebCC;
Initialisierung & Event-Registrierung
import React, { useEffect, useState } from 'react';
export const MotorGaugeWidget: React.FC = () => {
const [currentAmps, setCurrentAmps] = useState<number>(0);
const [maxLimit, setMaxLimit] = useState<number>(45);
useEffect(() => {
// 1. Initialisierung mit Siemens Runtime
WebCC.start(
(result) => {
console.log('WebCC erfolgreich verbunden:', result);
},
(error) => {
console.error('WebCC Verbindungsfehler:', error);
},
['CurrentAmps', 'MaxLimitAmps'] // Properties abonnieren
);
// 2. Listener für Property-Änderungen von der SPS
WebCC.on('propertyChange', (prop: { name: string; value: unknown }) => {
if (prop.name === 'CurrentAmps') {
const val = prop.value as number;
setCurrentAmps(val);
if (val > maxLimit) {
// Event zurück an TIA Portal melden
WebCC.fire('OnLimitExceeded', {
CurrentValue: val,
Timestamp: new Date().toISOString()
});
}
} else if (prop.name === 'MaxLimitAmps') {
setMaxLimit(prop.value as number);
}
});
}, [maxLimit]);
return (
<div className="gauge-container">
<div className="readout">{currentAmps.toFixed(1)} A</div>
<div className="limit-bar" style={{ width: `${(currentAmps / maxLimit) * 100}%` }} />
</div>
);
};
3. Die Lösung für schnelles Entwickeln: Lokale WebCC Mock Engine
Statt für jedes Debugging auf das echte TIA Portal zu warten, erstellen wir einen transparenten Browser-Mock:
// mockWebCC.ts
export function setupLocalWebCCMock() {
if (typeof window === 'undefined' || (window as any).WebCC) return;
const listeners: Record<string, Function[]> = {};
(window as any).WebCC = {
start: (onSuccess: Function) => {
console.info('[Mock WebCC] Initialisiert im Standalone-Modus');
setTimeout(() => onSuccess(true), 50);
},
on: (eventName: string, callback: Function) => {
if (!listeners[eventName]) listeners[eventName] = [];
listeners[eventName].push(callback);
},
fire: (eventName: string, payload: any) => {
console.log(`[Mock WebCC Event -> TIA] ${eventName}:`, payload);
},
// Hilfsmethode für das Mocking UI (z.B. Sliders)
__simulatePropertyChange: (name: string, value: any) => {
if (listeners['propertyChange']) {
listeners['propertyChange'].forEach(cb => cb({ name, value }));
}
}
};
}
Durch das Einbinden dieses Mocks in der Entwicklungs-Umgebung (z.B. bedingt über import.meta.env.DEV) können Sie:
- Komponenten auf macOS oder Linux in Sekunden entwickeln,
- State-Änderungen über HTML-Schieberegler simulieren,
- Vite Hot Module Replacement (HMR) voll ausschöpfen.
4. Automatisierter CWC-Export per Build-Skript
Nach erfolgreicher Entwicklung packt ein simples Node.js-Skript die gebauten statischen Dateien und das Manifest in ein normkonformes ZIP-Archiv:
# Baut das CWC Paket und generiert das importfertige ZIP
npm run build && node ./scripts/pack-cwc.mjs
Mit diesem Workflow sinkt die Iterationszeit von 5 Minuten auf unter 2 Sekunden.
Möchten Sie CWC-Entwicklung in Ihrem Team etablieren? Erfahren Sie mehr über [CWC Studio](/ #features).
Planen Sie ein WinCC Unified Projekt oder eine HMI-Modernisierung?
Ob Migration bestehender VBScripts, Entwicklung individueller Custom Web Controls oder Performance-Optimierung: Sprechen Sie direkt mit Boris Kotov über Ihr Vorhaben.