opcua-service/main/java/de/opcua/app/rest/HelpPageHandler.java
2026-05-11 19:40:18 +02:00

538 lines
19 KiB
Java

package de.opcua.app.rest;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import java.io.IOException;
public class HelpPageHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
String query = exchange.getRequestURI().getQuery();
// Determine language from query parameter or Accept-Language header
String lang = "en"; // default
if (query != null && query.contains("lang=de")) {
lang = "de";
} else if (query != null && query.contains("lang=en")) {
lang = "en";
} else {
String acceptLang = exchange.getRequestHeaders().getFirst("Accept-Language");
if (acceptLang != null && acceptLang.toLowerCase().startsWith("de")) {
lang = "de";
}
}
String html = buildHelpPage(lang);
byte[] bytes = html.getBytes("UTF-8");
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8");
exchange.sendResponseHeaders(200, bytes.length);
exchange.getResponseBody().write(bytes);
exchange.getResponseBody().close();
}
private String buildHelpPage(String lang) {
if ("de".equals(lang)) {
return buildGermanHelp();
} else {
return buildEnglishHelp();
}
}
private String buildEnglishHelp() {
return "<!DOCTYPE html>" +
"<html lang=\"en\">" +
"<head>" +
"<meta charset=\"UTF-8\">" +
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" +
"<title>OPC UA API Documentation</title>" +
"<style>" +
":root{--bg:#fff;--text:#1c1c1e;--border:#e5e5ea;--code-bg:#f5f5f7;--blue:#007aff;--green:#34c759;--orange:#ff9500}" +
"@media(prefers-color-scheme:dark){:root{--bg:#000;--text:#fff;--border:#38383a;--code-bg:#1c1c1e}}" +
"*{margin:0;padding:0;box-sizing:border-box}" +
"body{font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text',Arial,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;padding:20px}" +
".container{max-width:1200px;margin:0 auto}" +
"header{border-bottom:2px solid var(--blue);padding-bottom:20px;margin-bottom:40px}" +
"h1{font-size:32px;font-weight:700;margin-bottom:10px}" +
".lang-switch{float:right}" +
".lang-switch a{color:var(--blue);text-decoration:none;margin:0 5px}" +
"h2{font-size:24px;font-weight:600;margin:30px 0 15px;color:var(--blue)}" +
"h3{font-size:18px;font-weight:600;margin:20px 0 10px}" +
".endpoint{background:var(--code-bg);padding:15px;border-radius:8px;margin:15px 0;border-left:4px solid var(--blue)}" +
".method{display:inline-block;padding:4px 8px;border-radius:4px;font-weight:600;font-size:12px;margin-right:10px}" +
".method.get{background:var(--green);color:#fff}" +
".method.post{background:var(--orange);color:#fff}" +
"pre{background:var(--code-bg);padding:15px;border-radius:8px;overflow-x:auto;margin:10px 0}" +
"code{font-family:'SF Mono',Monaco,'Courier New',monospace;font-size:14px}" +
".note{background:#fff3cd;border-left:4px solid var(--orange);padding:15px;margin:15px 0;border-radius:4px}" +
"table{width:100%;border-collapse:collapse;margin:15px 0}" +
"th,td{padding:12px;text-align:left;border-bottom:1px solid var(--border)}" +
"th{font-weight:600;background:var(--code-bg)}" +
".toc{background:var(--code-bg);padding:20px;border-radius:8px;margin:20px 0}" +
".toc ul{list-style:none;padding-left:0}" +
".toc li{margin:8px 0}" +
".toc a{color:var(--blue);text-decoration:none}" +
"</style>" +
"</head>" +
"<body>" +
"<div class=\"container\">" +
"<header>" +
"<div class=\"lang-switch\">" +
"<a href=\"?lang=en\">English</a> | <a href=\"?lang=de\">Deutsch</a>" +
"</div>" +
"<h1>🔧 OPC UA REST API Documentation</h1>" +
"<p>Complete reference for integrating with the OPC UA HTTP API</p>" +
"</header>" +
"<div class=\"toc\">" +
"<h2>Table of Contents</h2>" +
"<ul>" +
"<li><a href=\"#overview\">Overview</a></li>" +
"<li><a href=\"#authentication\">Authentication</a></li>" +
"<li><a href=\"#endpoints\">API Endpoints</a></li>" +
"<li><a href=\"#integration\">3rd Party Integration</a></li>" +
"<li><a href=\"#examples\">Complete Examples</a></li>" +
"<li><a href=\"#errors\">Error Handling</a></li>" +
"</ul>" +
"</div>" +
"<h2 id=\"overview\">Overview</h2>" +
"<p>The OPC UA HTTP API provides RESTful access to OPC UA server data. All endpoints accept and return JSON.</p>" +
"<p><strong>Base URL:</strong> <code>http://localhost:8081</code></p>" +
"<p><strong>Content-Type:</strong> <code>application/json</code></p>" +
"<h2 id=\"authentication\">Authentication</h2>" +
"<p>Currently, no authentication is required for local access. For production deployment, use HTTPS with client certificates.</p>" +
"<h2 id=\"endpoints\">API Endpoints</h2>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method get\">GET</span> /api/status</h3>" +
"<p>Get OPC UA connection status</p>" +
"<h4>Response:</h4>" +
"<pre><code>{" +
" \"connected\": true," +
" \"timestamp\": 1707989123456" +
"}</code></pre>" +
"<h4>Example:</h4>" +
"<pre><code>const response = await fetch('/api/status');" +
"const data = await response.json();" +
"console.log('Connected:', data.connected);</code></pre>" +
"</div>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method post\">POST</span> /api/read</h3>" +
"<p>Read a value from an OPC UA node</p>" +
"<h4>Request Body:</h4>" +
"<pre><code>{" +
" \"nodeId\": \"ns=2;s=Temperature\"" +
"}</code></pre>" +
"<h4>Response:</h4>" +
"<pre><code>{" +
" \"success\": true," +
" \"value\": \"25.5\"," +
" \"timestamp\": 1707989123456" +
"}</code></pre>" +
"<h4>Example:</h4>" +
"<pre><code>const response = await fetch('/api/read', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({nodeId: 'ns=2;s=Temperature'})" +
"});" +
"const data = await response.json();" +
"console.log('Temperature:', data.value);</code></pre>" +
"</div>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method post\">POST</span> /api/write</h3>" +
"<p>Write a value to an OPC UA node</p>" +
"<h4>Request Body:</h4>" +
"<pre><code>{" +
" \"nodeId\": \"ns=2;s=SetPoint\"," +
" \"value\": \"30.0\"" +
"}</code></pre>" +
"<h4>Response:</h4>" +
"<pre><code>{" +
" \"success\": true," +
" \"written\": true" +
"}</code></pre>" +
"<h4>Example:</h4>" +
"<pre><code>const response = await fetch('/api/write', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({" +
" nodeId: 'ns=2;s=SetPoint'," +
" value: '30.0'" +
" })" +
"});" +
"const data = await response.json();" +
"console.log('Write successful:', data.written);</code></pre>" +
"</div>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method post\">POST</span> /api/browse</h3>" +
"<p>Browse OPC UA node hierarchy</p>" +
"<h4>Request Body:</h4>" +
"<pre><code>{" +
" \"nodeId\": \"ns=2;s=Folder\" // Optional, omit for root" +
"}</code></pre>" +
"<h4>Response:</h4>" +
"<pre><code>{" +
" \"success\": true," +
" \"children\": [" +
" {" +
" \"displayName\": \"Temperature\"," +
" \"nodeId\": \"ns=2;s=Temperature\"" +
" }" +
" ]" +
"}</code></pre>" +
"</div>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method post\">POST</span> /api/browse-full</h3>" +
"<p>Browse complete OPC UA tree (server-side recursive)</p>" +
"<h4>Request Body:</h4>" +
"<pre><code>{" +
" \"maxDepth\": 5" +
"}</code></pre>" +
"<h4>Response:</h4>" +
"<pre><code>{" +
" \"success\": true," +
" \"tree\": [...nested nodes...]," +
" \"duration\": 1234," +
" \"maxDepth\": 5" +
"}</code></pre>" +
"</div>" +
"<h2 id=\"integration\">3rd Party System Integration</h2>" +
"<p>Use the API to integrate OPC UA data with external systems.</p>" +
"<h3>Example: Send data to external REST API</h3>" +
"<pre><code>// Read from OPC UA" +
"const opcResponse = await fetch('/api/read', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({nodeId: 'ns=2;s=Temperature'})" +
"});" +
"const opcData = await opcResponse.json();" +
"\n" +
"// Send to external system" +
"await fetch('https://api.example.com/data', {" +
" method: 'POST'," +
" headers: {" +
" 'Content-Type': 'application/json'," +
" 'Authorization': 'Bearer YOUR_TOKEN'" +
" }," +
" body: JSON.stringify({" +
" sensor: 'temperature'," +
" value: opcData.value," +
" timestamp: new Date().toISOString()" +
" })" +
"});</code></pre>" +
"<h3>Example: Login Script with Session Storage</h3>" +
"<pre><code>// Login to external system" +
"const loginResponse = await fetch('https://api.example.com/auth/login', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({" +
" username: 'user'," +
" password: 'secret'" +
" })" +
"});" +
"const session = await loginResponse.json();" +
"\n" +
"// Store session for later use" +
"store.set('externalSession', session.token);" +
"store.set('externalExpiry', session.expiresAt);" +
"\n" +
"// Use in subsequent requests" +
"const token = store.get('externalSession');" +
"await fetch('https://api.example.com/data', {" +
" headers: {'Authorization': 'Bearer ' + token}" +
"});</code></pre>" +
"<h2 id=\"examples\">Complete Examples</h2>" +
"<h3>Polling Loop</h3>" +
"<pre><code>async function pollTemperature() {" +
" const response = await fetch('/api/read', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({nodeId: 'ns=2;s=Temperature'})" +
" });" +
" const data = await response.json();" +
" " +
" if (data.success) {" +
" console.log('Temperature:', data.value);" +
" " +
" // Send to external system" +
" await fetch('https://api.example.com/metrics', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({" +
" metric: 'temperature'," +
" value: parseFloat(data.value)" +
" })" +
" });" +
" }" +
"}" +
"\n" +
"// Poll every 5 seconds" +
"setInterval(pollTemperature, 5000);</code></pre>" +
"<h3>Write with Validation</h3>" +
"<pre><code>async function setSetpoint(value) {" +
" // Validate" +
" if (value < 0 || value > 100) {" +
" console.error('Value out of range');" +
" return;" +
" }" +
" " +
" // Write to OPC UA" +
" const response = await fetch('/api/write', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({" +
" nodeId: 'ns=2;s=SetPoint'," +
" value: value.toString()" +
" })" +
" });" +
" " +
" const result = await response.json();" +
" if (result.success && result.written) {" +
" console.log('Setpoint updated to', value);" +
" } else {" +
" console.error('Write failed');" +
" }" +
"}</code></pre>" +
"<h2 id=\"errors\">Error Handling</h2>" +
"<h3>Error Response Format:</h3>" +
"<pre><code>{" +
" \"success\": false," +
" \"error\": \"Error message\"" +
"}</code></pre>" +
"<h3>Common Errors:</h3>" +
"<table>" +
"<tr><th>Status</th><th>Error</th><th>Solution</th></tr>" +
"<tr><td>503</td><td>OPC UA not connected</td><td>Check OPC UA connection</td></tr>" +
"<tr><td>400</td><td>Invalid nodeId</td><td>Verify node ID format</td></tr>" +
"<tr><td>500</td><td>Read/Write failed</td><td>Check node permissions</td></tr>" +
"</table>" +
"<div class=\"note\">" +
"<strong>💡 Tip:</strong> Use the browser's DevTools Network tab to debug API calls and inspect responses." +
"</div>" +
"</div>" +
"</body>" +
"</html>";
}
private String buildGermanHelp() {
return "<!DOCTYPE html>" +
"<html lang=\"de\">" +
"<head>" +
"<meta charset=\"UTF-8\">" +
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" +
"<title>OPC UA API Dokumentation</title>" +
"<style>" +
":root{--bg:#fff;--text:#1c1c1e;--border:#e5e5ea;--code-bg:#f5f5f7;--blue:#007aff;--green:#34c759;--orange:#ff9500}" +
"@media(prefers-color-scheme:dark){:root{--bg:#000;--text:#fff;--border:#38383a;--code-bg:#1c1c1e}}" +
"*{margin:0;padding:0;box-sizing:border-box}" +
"body{font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text',Arial,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;padding:20px}" +
".container{max-width:1200px;margin:0 auto}" +
"header{border-bottom:2px solid var(--blue);padding-bottom:20px;margin-bottom:40px}" +
"h1{font-size:32px;font-weight:700;margin-bottom:10px}" +
".lang-switch{float:right}" +
".lang-switch a{color:var(--blue);text-decoration:none;margin:0 5px}" +
"h2{font-size:24px;font-weight:600;margin:30px 0 15px;color:var(--blue)}" +
"h3{font-size:18px;font-weight:600;margin:20px 0 10px}" +
".endpoint{background:var(--code-bg);padding:15px;border-radius:8px;margin:15px 0;border-left:4px solid var(--blue)}" +
".method{display:inline-block;padding:4px 8px;border-radius:4px;font-weight:600;font-size:12px;margin-right:10px}" +
".method.get{background:var(--green);color:#fff}" +
".method.post{background:var(--orange);color:#fff}" +
"pre{background:var(--code-bg);padding:15px;border-radius:8px;overflow-x:auto;margin:10px 0}" +
"code{font-family:'SF Mono',Monaco,'Courier New',monospace;font-size:14px}" +
".note{background:#fff3cd;border-left:4px solid var(--orange);padding:15px;margin:15px 0;border-radius:4px}" +
"table{width:100%;border-collapse:collapse;margin:15px 0}" +
"th,td{padding:12px;text-align:left;border-bottom:1px solid var(--border)}" +
"th{font-weight:600;background:var(--code-bg)}" +
".toc{background:var(--code-bg);padding:20px;border-radius:8px;margin:20px 0}" +
".toc ul{list-style:none;padding-left:0}" +
".toc li{margin:8px 0}" +
".toc a{color:var(--blue);text-decoration:none}" +
"</style>" +
"</head>" +
"<body>" +
"<div class=\"container\">" +
"<header>" +
"<div class=\"lang-switch\">" +
"<a href=\"?lang=en\">English</a> | <a href=\"?lang=de\">Deutsch</a>" +
"</div>" +
"<h1>🔧 OPC UA REST API Dokumentation</h1>" +
"<p>Vollständige Referenz zur Integration mit der OPC UA HTTP API</p>" +
"</header>" +
"<div class=\"toc\">" +
"<h2>Inhaltsverzeichnis</h2>" +
"<ul>" +
"<li><a href=\"#overview\">Übersicht</a></li>" +
"<li><a href=\"#authentication\">Authentifizierung</a></li>" +
"<li><a href=\"#endpoints\">API Endpunkte</a></li>" +
"<li><a href=\"#integration\">Drittsystem Integration</a></li>" +
"<li><a href=\"#examples\">Vollständige Beispiele</a></li>" +
"<li><a href=\"#errors\">Fehlerbehandlung</a></li>" +
"</ul>" +
"</div>" +
"<h2 id=\"overview\">Übersicht</h2>" +
"<p>Die OPC UA HTTP API bietet RESTful Zugriff auf OPC UA Server Daten. Alle Endpunkte akzeptieren und liefern JSON.</p>" +
"<p><strong>Basis-URL:</strong> <code>http://localhost:8081</code></p>" +
"<p><strong>Content-Type:</strong> <code>application/json</code></p>" +
"<h2 id=\"authentication\">Authentifizierung</h2>" +
"<p>Aktuell ist keine Authentifizierung für lokalen Zugriff erforderlich. Für Produktionsumgebungen verwenden Sie HTTPS mit Client-Zertifikaten.</p>" +
"<h2 id=\"endpoints\">API Endpunkte</h2>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method get\">GET</span> /api/status</h3>" +
"<p>OPC UA Verbindungsstatus abfragen</p>" +
"<h4>Antwort:</h4>" +
"<pre><code>{" +
" \"connected\": true," +
" \"timestamp\": 1707989123456" +
"}</code></pre>" +
"<h4>Beispiel:</h4>" +
"<pre><code>const response = await fetch('/api/status');" +
"const data = await response.json();" +
"console.log('Verbunden:', data.connected);</code></pre>" +
"</div>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method post\">POST</span> /api/read</h3>" +
"<p>Wert von einem OPC UA Knoten lesen</p>" +
"<h4>Request Body:</h4>" +
"<pre><code>{" +
" \"nodeId\": \"ns=2;s=Temperature\"" +
"}</code></pre>" +
"<h4>Antwort:</h4>" +
"<pre><code>{" +
" \"success\": true," +
" \"value\": \"25.5\"," +
" \"timestamp\": 1707989123456" +
"}</code></pre>" +
"<h4>Beispiel:</h4>" +
"<pre><code>const response = await fetch('/api/read', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({nodeId: 'ns=2;s=Temperature'})" +
"});" +
"const data = await response.json();" +
"console.log('Temperatur:', data.value);</code></pre>" +
"</div>" +
"<div class=\"endpoint\">" +
"<h3><span class=\"method post\">POST</span> /api/write</h3>" +
"<p>Wert zu einem OPC UA Knoten schreiben</p>" +
"<h4>Request Body:</h4>" +
"<pre><code>{" +
" \"nodeId\": \"ns=2;s=SetPoint\"," +
" \"value\": \"30.0\"" +
"}</code></pre>" +
"<h4>Antwort:</h4>" +
"<pre><code>{" +
" \"success\": true," +
" \"written\": true" +
"}</code></pre>" +
"<h4>Beispiel:</h4>" +
"<pre><code>const response = await fetch('/api/write', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({" +
" nodeId: 'ns=2;s=SetPoint'," +
" value: '30.0'" +
" })" +
"});" +
"const data = await response.json();" +
"console.log('Schreiben erfolgreich:', data.written);</code></pre>" +
"</div>" +
"<h2 id=\"integration\">Drittsystem Integration</h2>" +
"<p>Verwenden Sie die API um OPC UA Daten mit externen Systemen zu integrieren.</p>" +
"<h3>Beispiel: Daten an externe REST API senden</h3>" +
"<pre><code>// Von OPC UA lesen" +
"const opcResponse = await fetch('/api/read', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({nodeId: 'ns=2;s=Temperature'})" +
"});" +
"const opcData = await opcResponse.json();" +
"\n" +
"// An externes System senden" +
"await fetch('https://api.example.com/data', {" +
" method: 'POST'," +
" headers: {" +
" 'Content-Type': 'application/json'," +
" 'Authorization': 'Bearer IHR_TOKEN'" +
" }," +
" body: JSON.stringify({" +
" sensor: 'temperatur'," +
" value: opcData.value," +
" timestamp: new Date().toISOString()" +
" })" +
"});</code></pre>" +
"<h3>Beispiel: Login Script mit Session Speicherung</h3>" +
"<pre><code>// Bei externem System anmelden" +
"const loginResponse = await fetch('https://api.example.com/auth/login', {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: JSON.stringify({" +
" username: 'benutzer'," +
" password: 'geheim'" +
" })" +
"});" +
"const session = await loginResponse.json();" +
"\n" +
"// Session für spätere Verwendung speichern" +
"store.set('externeSession', session.token);" +
"store.set('externeAblauf', session.expiresAt);" +
"\n" +
"// In nachfolgenden Requests verwenden" +
"const token = store.get('externeSession');" +
"await fetch('https://api.example.com/data', {" +
" headers: {'Authorization': 'Bearer ' + token}" +
"});</code></pre>" +
"<h2 id=\"errors\">Fehlerbehandlung</h2>" +
"<h3>Fehler-Antwort Format:</h3>" +
"<pre><code>{" +
" \"success\": false," +
" \"error\": \"Fehlermeldung\"" +
"}</code></pre>" +
"<h3>Häufige Fehler:</h3>" +
"<table>" +
"<tr><th>Status</th><th>Fehler</th><th>Lösung</th></tr>" +
"<tr><td>503</td><td>OPC UA nicht verbunden</td><td>OPC UA Verbindung prüfen</td></tr>" +
"<tr><td>400</td><td>Ungültige nodeId</td><td>Node ID Format überprüfen</td></tr>" +
"<tr><td>500</td><td>Lesen/Schreiben fehlgeschlagen</td><td>Node Berechtigungen prüfen</td></tr>" +
"</table>" +
"<div class=\"note\">" +
"<strong>💡 Tipp:</strong> Verwenden Sie die Browser DevTools (Network Tab) um API Aufrufe zu debuggen und Antworten zu inspizieren." +
"</div>" +
"</div>" +
"</body>" +
"</html>";
}
}