@etm-professional-control/winccoa-mcp-server
Version:
MCP Server for WinCC OA with field-specific configurations
153 lines • 6.34 kB
JavaScript
/**
* Icon Generator
*
* Utility class for generating custom SVG icons for WinCC OA dashboards.
* Icons are saved to /data/WebUI/icons/ and can be referenced in widget headers/footers.
*
* IMPORTANT: Icons must be small (24x24 pixels by default) to match Siemens IX icon size.
* Header/footer icons cannot be full-width banners - use small icons only.
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
// ES module equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* SVG Icon Generator Class
*/
export class IconGenerator {
constructor(projectPath) {
// Default to standard WinCC OA project structure
// __dirname points to build/helpers/icons/, go up 6 levels to reach project root
this.iconsPath = projectPath
? path.join(projectPath, 'data', 'WebUI', 'icons')
: path.join(__dirname, '..', '..', '..', '..', '..', '..', 'data', 'WebUI', 'icons');
// Ensure icons directory exists
if (!fs.existsSync(this.iconsPath)) {
fs.mkdirSync(this.iconsPath, { recursive: true });
}
}
/**
* Generate a simple icon SVG
* @param config - Icon configuration
* @returns Path to generated SVG file
*/
generateIcon(config) {
const size = config.size || 24;
const color = config.color || 'currentColor';
let svgContent;
switch (config.type) {
case 'trend':
svgContent = this.createTrendIcon(size, color);
break;
case 'gauge':
svgContent = this.createGaugeIcon(size, color);
break;
case 'alert':
svgContent = this.createAlertIcon(size, color);
break;
case 'custom':
if (!config.customSvg) {
throw new Error('Custom SVG content is required for custom icon type');
}
svgContent = this.createCustomIcon(size, color, config.customSvg);
break;
case 'simple':
default:
svgContent = this.createSimpleIcon(size, color);
break;
}
const filename = `${config.name}.svg`;
const filepath = path.join(this.iconsPath, filename);
fs.writeFileSync(filepath, svgContent, 'utf8');
return `/data/WebUI/icons/${filename}`;
}
/**
* Create a simple geometric icon (circle/square)
*/
createSimpleIcon(size, color) {
const centerX = size / 2;
const centerY = size / 2;
const radius = size / 2 - 3;
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${color}" stroke-width="2" fill="none"/>
</svg>`;
}
/**
* Create a trend icon (line chart with upward trend)
*/
createTrendIcon(size, color) {
const path = `M2 ${size - 4} L${size / 4} ${size / 2} L${size / 2} ${size / 3} L${3 * size / 4} ${size / 4} L${size - 2} 2`;
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
<path d="${path}" stroke="${color}" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="${size / 4}" cy="${size / 2}" r="2" fill="${color}"/>
<circle cx="${size / 2}" cy="${size / 3}" r="2" fill="${color}"/>
<circle cx="${3 * size / 4}" cy="${size / 4}" r="2" fill="${color}"/>
</svg>`;
}
/**
* Create a gauge icon (semicircular meter)
*/
createGaugeIcon(size, color) {
const centerX = size / 2;
const centerY = size - 3;
const radius = size / 2 - 3;
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
<path d="M3 ${centerY} A${radius} ${radius} 0 0 1 ${size - 3} ${centerY}" stroke="${color}" stroke-width="2" fill="none" stroke-linecap="round"/>
<line x1="${centerX}" y1="${centerY}" x2="${centerX + radius * 0.6}" y2="${centerY - radius * 0.6}" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
</svg>`;
}
/**
* Create an alert icon (warning triangle)
*/
createAlertIcon(size, color) {
const path = `M${size / 2} 3 L${size - 3} ${size - 3} L3 ${size - 3} Z`;
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
<path d="${path}" stroke="${color}" stroke-width="2" fill="none" stroke-linejoin="round"/>
<line x1="${size / 2}" y1="${size / 2 - 2}" x2="${size / 2}" y2="${size / 2 + 2}" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
<circle cx="${size / 2}" cy="${size - 8}" r="1.5" fill="${color}"/>
</svg>`;
}
/**
* Create a custom icon from SVG path data
*/
createCustomIcon(size, color, svgPath) {
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="${size}" height="${size}">
<path d="${svgPath}" stroke="${color}" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`;
}
/**
* List all available custom icons
* @returns Array of icon paths
*/
listCustomIcons() {
if (!fs.existsSync(this.iconsPath)) {
return [];
}
const files = fs.readdirSync(this.iconsPath);
return files
.filter(file => file.endsWith('.svg'))
.map(file => `/data/WebUI/icons/${file}`);
}
/**
* Delete a custom icon
* @param iconName - Icon filename (with or without .svg extension)
*/
deleteIcon(iconName) {
const filename = iconName.endsWith('.svg') ? iconName : `${iconName}.svg`;
const filepath = path.join(this.iconsPath, filename);
if (fs.existsSync(filepath)) {
fs.unlinkSync(filepath);
return true;
}
return false;
}
}
//# sourceMappingURL=IconGenerator.js.map