UNPKG

@etm-professional-control/winccoa-mcp-server

Version:

MCP Server for WinCC OA with field-specific configurations

577 lines 22.4 kB
/** * Widget Factory * * Factory class for creating widget instances with proper schema structure. * Uses Factory Pattern to allow registration of new widget types. */ import { createArrayContext, createSimpleGroupContext } from '../../types/dashboards/schema.js'; /** * Widget Factory Class */ export class WidgetFactory { constructor() { this.creators = new Map(); // Register default widget types this.registerWidgetType('gauge', this.createGauge.bind(this)); this.registerWidgetType('label', this.createLabel.bind(this)); this.registerWidgetType('trend', this.createTrend.bind(this)); this.registerWidgetType('pie', this.createPie.bind(this)); this.registerWidgetType('progressbar', this.createProgressBar.bind(this)); this.registerWidgetType('barchart', this.createBarChart.bind(this)); } /** * Register a new widget type * @param type - Widget type identifier * @param creator - Function to create widget instances */ registerWidgetType(type, creator) { this.creators.set(type, creator); } /** * Create a widget instance * @param config - Widget configuration * @param dimensions - Widget dimensions (position and size) * @returns Complete widget instance */ createWidget(config, dimensions) { const creator = this.creators.get(config.type); if (!creator) { throw new Error(`Unknown widget type: ${config.type}. Register it using registerWidgetType().`); } return creator(config, dimensions); } /** * Generate UUID v4 for widget IDs * @returns UUID v4 string */ generateUuidV4() { // UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } /** * Create general settings (appearance) from widget appearance config * @param appearance - Optional appearance configuration * @returns Group context for settings.general */ createGeneralSettings(appearance) { const config = { titleAlignment: appearance?.titleAlignment || 'center', subtitleAlignment: appearance?.subtitleAlignment || 'center' }; // Add optional appearance settings if provided if (appearance) { if (appearance.titleIcon !== undefined) { config.titleIcon = appearance.titleIcon; } if (appearance.title !== undefined) { config.title = { context: 'group', config: { 'en_US.utf8': appearance.title } }; } if (appearance.subtitleIcon !== undefined) { config.subtitleIcon = appearance.subtitleIcon; } if (appearance.subtitle !== undefined) { config.subtitle = { context: 'group', config: { 'en_US.utf8': appearance.subtitle } }; } if (appearance.backgroundColor !== undefined) { config.backgroundColor = appearance.backgroundColor; } if (appearance.borderColor !== undefined) { config.borderColor = appearance.borderColor; } if (appearance.showFullscreenButton !== undefined) { config.showFullscreenButton = appearance.showFullscreenButton; } if (appearance.linkTitle !== undefined) { config.linkTitle = { context: 'group', config: { 'en_US.utf8': appearance.linkTitle } }; } if (appearance.linkOpenInNewTab !== undefined) { config.linkOpenInNewTab = appearance.linkOpenInNewTab; } } return createSimpleGroupContext(config); } /** * Create Gauge widget */ createGauge(config, dimensions) { const { dataPoint, title, chartType = 'classic', showTooltip = true, color, format, unit, name, isRelative = false, // Global appearance settings animation, font, renderer, theme, backgroundColor, // Widget appearance (header, footer, icons, colors) appearance } = config; const { x, y, cols, rows, minCols, minRows } = dimensions; // Build settings with correct structure const settings = { jsonFileName: 'StandardLibrary/Charts/gauge.widget', config: createSimpleGroupContext({ chartType, // Simplified: direct value datapoint: { context: 'data-point', config: { dpName: dataPoint, definedConfigs: [ 'datapoint', 'value', 'name', 'unit', 'format', 'color', 'min', 'max', 'alertRanges', 'alertColor' ], customAlertColor: true, alertColor: '' } }, showTooltip, // Simplified: direct value ...(color && { color }), ...(format && { format }), ...(unit && { unit }), ...(name && { name }), isRelative, // Global appearance settings ...(animation !== undefined && { animation }), ...(font && { font }), ...(renderer && { renderer }), ...(theme && { theme }), ...(backgroundColor && { backgroundColor }) }), general: this.createGeneralSettings(appearance) }; const component = { tagname: 'wui-widget-gauge', scripts: ['gauge'], styles: [], jsonSchema: 'StandardLibrary/Charts/gauge-json-schema', uiSchema: 'StandardLibrary/Charts/gauge-ui-schema' }; return { id: this.generateUuidV4(), version: 2, name: 'WUI_gauge.Widget.gauge.label', x, y, rows, cols, minCols, minRows, settings, component }; } /** * Create Label widget */ createLabel(config, dimensions) { const { dataPoint, title, icon = '', iconPosition = 'left', iconSizeFactor = 'medium', fontSizeFactor = 'medium', // Changed from 'small' to 'medium' for better readability unitFontSizeFactor = 'small', color, format, unit, name, // Widget appearance (header, footer, icons, colors) appearance } = config; const { x, y, cols, rows, minCols, minRows } = dimensions; const settings = { jsonFileName: 'StandardLibrary/Charts/label.widget', config: createSimpleGroupContext({ datapoint: { context: 'data-point', config: { dpName: dataPoint, definedConfigs: [ 'datapoint', 'value', 'name', 'unit', 'format', 'color', 'alertColor' ], customAlertColor: true, alertColor: '' } }, icon, // Simplified: direct values iconPosition, iconSizeFactor, fontSizeFactor, unitFontSizeFactor, ...(color && { color }), ...(format && { format }), ...(unit && { unit }), ...(name && { name }) }), general: this.createGeneralSettings(appearance) }; const component = { tagname: 'wui-widget-label', scripts: ['label'], styles: [], jsonSchema: 'StandardLibrary/Charts/label-json-schema', uiSchema: 'StandardLibrary/Charts/label-ui-schema' }; return { id: this.generateUuidV4(), version: 2, name: 'WUI_label.Widget.label.label', x, y, rows, cols, minCols, minRows, settings, component }; } /** * Create Trend widget * Supports single or multiple datapoints with custom y-axis per series */ createTrend(config, dimensions) { const { dataPoint, dataPoints, title, timeRange = 'now/h', // Default to current hour (from last full hour) - grows with time for live monitoring legendType = 'scroll', legendOrientation = 'horizontal', legendVerticalPosition = 'top', legendHorizontalPosition = 'center', showLegend = true, showXAxisGrid = false, showYAxisGrid = true, showRangePicker = true, showTooltip = true, yAxisColor = '', yAxisName = '', yAxisUnit, range, zoom = 1, // Global appearance settings animation, font, renderer, theme, backgroundColor, // Widget appearance (header, footer, icons, colors) appearance } = config; const { x, y, cols, rows, minCols, minRows } = dimensions; // Build series array from dataPoints (smart union type) or single dataPoint const dpArray = dataPoints && dataPoints.length > 0 ? dataPoints : [dataPoint]; const seriesArray = dpArray.map((dp) => { const seriesConfig = { datapoint: { context: 'data-point', config: { definedConfigs: [ 'datapoint', 'value', 'name', 'unit', 'format', 'color', 'min', 'max', 'alertColor' ], dpName: typeof dp === 'string' ? dp : dp.dataPoint, fetchMethod: 'historic', compress: true, historic: { sTimeRange: '${sTimeRange}' }, customAlertColor: true, alertColor: '' } }, lineStyle: typeof dp === 'string' ? 'solid' : (dp.lineStyle || 'solid') }; // Add new visual and formatting properties (when dp is an object) if (typeof dp !== 'string') { // Visual customization if (dp.showArea !== undefined) seriesConfig.showArea = dp.showArea; if (dp.showConfidenceBand !== undefined) seriesConfig.showConfidenceBand = dp.showConfidenceBand; if (dp.color) seriesConfig.color = dp.color; // Data formatting if (dp.unit) seriesConfig.unit = dp.unit; if (dp.format) seriesConfig.format = dp.format; if (dp.name) seriesConfig.name = dp.name; // Custom Y-axis configuration if (dp.showCustomYAxis) { seriesConfig.showCustomYAxis = true; seriesConfig.yAxisPosition = dp.yAxisPosition || 'right'; if (dp.min !== undefined) seriesConfig.min = dp.min; if (dp.max !== undefined) seriesConfig.max = dp.max; } } return createSimpleGroupContext(seriesConfig); }); const settings = { jsonFileName: 'StandardLibrary/Charts/linechart.widget', config: createSimpleGroupContext({ legendType, // Simplified: direct values series: createArrayContext(seriesArray), showLegend, zoom, legendOrientation, showYAxisGrid, showXAxisGrid, legendVerticalPosition, legendHorizontalPosition, yAxisColor, yAxisName, ...(yAxisUnit && { yAxisUnit }), ...(range && { range }), showRangePicker, showTooltip, // Global appearance settings ...(animation !== undefined && { animation }), ...(font && { font }), ...(renderer && { renderer }), ...(theme && { theme }), ...(backgroundColor && { backgroundColor }) }), general: this.createGeneralSettings(appearance), variables: createSimpleGroupContext({ sTimeRange: timeRange // Simplified: direct value }) }; const component = { tagname: 'wui-widget-trend', scripts: ['trend'], styles: [], jsonSchema: 'StandardLibrary/Charts/linechart-json-schema', uiSchema: 'StandardLibrary/Charts/linechart-ui-schema' }; return { id: this.generateUuidV4(), version: 2, name: 'WUI_trend.Widget.trend.label', x, y, rows, cols, minCols, minRows, settings, component }; } /** * Create Pie widget * ECharts-based widget extending WuiEchartsWithLegend */ createPie(config, dimensions) { const { dataPoints, dataPointsDescriptions, title, chartType = 'pie', labelsShow = false, labelsPosition = 'outside', labelsDetails = 'value', labelLineLength = 10, legendPosition = 'topright', showTooltip = true, colors, darkModeColors, // Widget appearance (header, footer, icons, colors) appearance } = config; const { x, y, cols, rows, minCols, minRows } = dimensions; // Default color arrays for pie slices const defaultColors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272']; const defaultDarkColors = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091']; const sliceColors = colors || defaultColors; const sliceDarkColors = darkModeColors || defaultDarkColors; // Build series array with datapoint-context (ECharts format) const seriesArray = dataPoints.map((dp, index) => createSimpleGroupContext({ datapoint: { context: 'data-point', config: { dpName: dp, definedConfigs: [ 'datapoint', 'value', 'name', 'unit', 'format', 'color', 'alertColor' ], customAlertColor: true, alertColor: '' } }, name: dataPointsDescriptions[index] || `Slice ${index + 1}`, color: sliceColors[index % sliceColors.length], darkModeColor: sliceDarkColors[index % sliceDarkColors.length] })); const settings = { jsonFileName: 'StandardLibrary/Charts/pie.widget', config: createSimpleGroupContext({ chartType, labelsShow, labelsPosition, labelsDetails, labelLineLength, series: createArrayContext(seriesArray), showTooltip }), general: this.createGeneralSettings(appearance) }; const component = { tagname: 'wui-widget-pie', scripts: ['pie'], styles: [], jsonSchema: 'StandardLibrary/Charts/pie-json-schema', uiSchema: 'StandardLibrary/Charts/pie-ui-schema' }; return { id: this.generateUuidV4(), version: 2, name: 'WUI_pie.Widget.pie.label', x, y, rows, cols, minCols, minRows, settings, component }; } /** * Create Progress Bar widget */ createProgressBar(config, dimensions) { const { dataPoint, title, color, size = '2.25em', unit, format, min, max, showRange = true, isAbsolute = false, alertRanges, // Global appearance settings animation, font, renderer, theme, backgroundColor, // Widget appearance (header, footer, icons, colors) appearance } = config; const { x, y, cols, rows, minCols, minRows } = dimensions; const settings = { jsonFileName: 'StandardLibrary/Charts/progressbar.widget', config: createSimpleGroupContext({ datapoint: { context: 'data-point', config: { dpName: dataPoint, definedConfigs: [ 'datapoint', 'value', 'name', 'unit', 'format', 'color', 'min', 'max', 'alertRanges', 'alertColor' ], customAlertColor: true, alertColor: '' } }, size, showRange, isAbsolute, ...(color && { color }), ...(unit && { unit }), ...(format && { format }), ...(min !== undefined && { min }), ...(max !== undefined && { max }), ...(alertRanges && { alertRanges }), // Global appearance settings ...(animation !== undefined && { animation }), ...(font && { font }), ...(renderer && { renderer }), ...(theme && { theme }), ...(backgroundColor && { backgroundColor }) }), general: this.createGeneralSettings(appearance) }; const component = { tagname: 'wui-widget-progress-bar', scripts: ['progress-bar'], styles: [], jsonSchema: 'StandardLibrary/Charts/progressbar-json-schema', uiSchema: 'StandardLibrary/Charts/progressbar-ui-schema' }; return { id: this.generateUuidV4(), version: 2, name: 'WUI_progressbar.Widget.progressbar.label', x, y, rows, cols, minCols, minRows, settings, component }; } /** * Create Bar Chart widget */ createBarChart(config, dimensions) { const { dataPoints, title, yAxisName = '', yAxisUnit, yAxisColor, range, isStacked = false, isHorizontal = false, showTooltip = true, showLegend = true, legendPosition = 'topright', // Global appearance settings animation, font, renderer, theme, backgroundColor, // Widget appearance (header, footer, icons, colors) appearance } = config; const { x, y, cols, rows, minCols, minRows } = dimensions; // Build series array from dataPoints const seriesArray = dataPoints.map((dp) => createSimpleGroupContext({ datapoint: { context: 'data-point', config: { dpName: dp, definedConfigs: [ 'datapoint', 'value', 'name', 'unit', 'format', 'color', 'alertColor' ], customAlertColor: true, alertColor: '' } } })); const settings = { jsonFileName: 'StandardLibrary/Charts/barchart.widget', config: createSimpleGroupContext({ series: createArrayContext(seriesArray), isStacked, isHorizontal, showTooltip, showLegend, legendPosition, yAxisName, ...(yAxisUnit && { yAxisUnit }), ...(yAxisColor && { yAxisColor }), ...(range && { range }), // Global appearance settings ...(animation !== undefined && { animation }), ...(font && { font }), ...(renderer && { renderer }), ...(theme && { theme }), ...(backgroundColor && { backgroundColor }) }), general: this.createGeneralSettings(appearance) }; const component = { tagname: 'wui-widget-barchart', scripts: ['barchart'], styles: [], jsonSchema: 'StandardLibrary/Charts/barchart-json-schema', uiSchema: 'StandardLibrary/Charts/barchart-ui-schema' }; return { id: this.generateUuidV4(), version: 2, name: 'WUI_barchart.Widget.barchart.label', x, y, rows, cols, minCols, minRows, settings, component }; } } //# sourceMappingURL=WidgetFactory.js.map