Extending Custom Widgets in Web-Based SCADA Configuration Tool

Demo URL: http://121.40.16.189:12000

1. Adding a New Widget Category to the Sidebar

1.1. Registering the Menu Entry

To add a new category (e.g., "Control Widgets") to the left sidebar, modify Sidebar.js. Locate the Sidebar.prototype.categoryMenus array and insert a new entry:

Sidebar.prototype.categoryMenus = [
  {
    id: 4,
    name: 'Control Widgets',
    defaultSrc: mxUtils.staticImg('/rcscada/menu/ic_menu_control_default.svg'),
    checkedSrc: mxUtils.staticImg('/rcscada/menu/ic_menu_control_check.svg'),
    checked: false,
    funcNames: ['addCustomStateImagePalette'],
  },
];

The funcNames array references methods attached to Sidebar.prototype. To avoid bloating Sidebar.js, these methods can be defined in external files like CustomExpands.js and imported:

import { CustomExpands } from './sidebar/CustomExpands';

1.2. Defining Widget Groups

In CustomExpands.js, implement the referenced method (e.g., addCustomStateImagePalette) to register individual widgets:

Sidebar.prototype.addCustomStateImagePalette = function () {
  const templates = [];

  // Toggle Switch
  const closedIcon = `/rcscada/images/usr/switch/1.svg`;
  const openIcon = `/rcscada/images/usr/switch/2.svg`;
  let style = `shape=mxgraph.rc.mxRc_stateSwitch;readonly=1;rcDprop=openCloseValues;igDprop=commonStrokeColor;openStateImg=${openIcon};closeStateImg=${closedIcon};html=1;shadow=0;dashed=0;strokeWidth=1;stateValue=0;title=Toggle Switch;opacity=100;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 150, 150, '', 'Toggle Switch', true, null, null, mxUtils.staticImg(closedIcon)));

  // State Image
  let imgPath = `/rcscada/images/usr/light/1.png`;
  style = `shape=mxgraph.rc.mxRc_stateImage;readonly=1;igDprop=commonStrokeColor;rcSprop=defaultImg;rcDprop=stateImageValues;defaultImg=${imgPath};stateImage=;html=1;shadow=0;dashed=0;strokeWidth=1;stateValue=0;title=State Image;opacity=100;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 150, 150, '', 'State Image', true, null, null, mxUtils.staticImg(imgPath)));

  // Dynamic Text
  imgPath = `/rcscada/menu/ic_menu_text.svg`;
  style = 'text;rcDprop=stateTextValues;readonly=1;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;';
  templates.push(this.createVertexTemplateEntry(style, 200, 100, 'Text', 'Dynamic Text', true, null, null, mxUtils.staticImg(imgPath)));

  // Flow Indicator (Edge)
  imgPath = `/rcscada/menu/ic_menu_pip_flow.gif`;
  templates.push(this.createEdgeTemplateEntry(
    'endArrow=none;html=1;enableFlow=1;strokeWidth=10;strokeColor=#28E8D8;pipWidth=13;pipDash=10;strokeBgColor=#312727;flowDirection=1;',
    200, 200, '', 'Flow Bar', null, null, null, true, mxUtils.staticImg(imgPath)
  ));

  // Liquid Tank
  imgPath = `/rcscada/menu/ic_menu_trough.svg`;
  style = `shape=mxgraph.rc.mxRc_squareTrough;igDprop=commonStrokeColor;rcDprop=troughLiquidProgressValues;rcSprop=troughMinScale,troughMaxScale,troughBorderWidth,troughBorderRadius,troughBorderColor,troughBackgroundColor,troughLiquidColor;troughMinScale=0;troughMaxScale=100;troughBorderWidth=5;troughBorderRadius=5;troughBorderColor=#000000;troughBackgroundColor=#FFFFFF;troughLiquidColor=#00AAFF;html=1;shadow=0;dashed=0;strokeWidth=1;stateValue=0;title=Liquid Tank;opacity=100;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 200, 200, '', 'Liquid Tank', true, null, null, mxUtils.staticImg(imgPath)));

  // Digital Clock
  imgPath = `/rcscada/menu/ic_menu_shuzi_time.svg`;
  style = `shape=mxgraph.rc.mxRc_shu_zi_time;rcDprop=shuziTimeValues;showFontStyle=1;readonly=1;timeFormat=0;igDprop=commonStrokeColor;rcSprop=timeFormat;html=1;shadow=0;dashed=0;strokeWidth=1;stateValue=0;title=Digital Clock;opacity=100;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=middle;`;
  templates.push(this.createVertexTemplateEntry(style, 180, 180, '', 'Digital Clock', true, null, null, mxUtils.staticImg(imgPath)));

  // Video Player
  imgPath = `/rcscada/menu/ic_menu_video_play.svg`;
  style = `shape=mxgraph.rc.mxRc_video_play;rcDprop=videoPlayerValues;readonly=1;igDprop=commonStrokeColor,commonFontColor;html=1;shadow=0;dashed=0;strokeWidth=0;title=Video Player;opacity=100;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 200, 120, '', 'Video Player', true, null, null, mxUtils.staticImg(imgPath)));

  // Thermometer
  imgPath = `/rcscada/menu/ic_menu_thermometer.png`;
  style = `shape=mxgraph.rc.mxRc_thermometer;readonly=1;rcDprop=scaleValValues;rcSprop=showScale,bgFillColor1,bgFillColor2,minScale,maxScale,smallUnitScale,bigUnitScale,scaleValDuration,scaleTransX,scaleFontSize,scaleColor,scaleFontColor;scaleTransX=0.0;scaleColor=#528CFF;bgFillColor1=#F8D7D1;bgFillColor2=#FF5D3C;igDprop=commonStrokeColor,commonFontColor;html=1;shadow=0;dashed=0;showScale=1;scaleValDuration=1.0;minScale=0;maxScale=100;smallUnitScale=2;bigUnitScale=20;strokeWidth=1;strokeColor=#528CFF;fontColor=#528CFF;fontSize=12;title=Thermometer;opacity=100;fillColor=none;aspect=fixed;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 120, 200, '', 'Thermometer', true, null, null, mxUtils.staticImg(imgPath)));

  // Gauge Meter
  imgPath = `/rcscada/menu/ic_menu_rcgauge1.png`;
  style = `shape=mxgraph.rc.mxRc_gauge1;readonly=1;rcDprop=scaleValValues;rcSprop=showScale,bgFillColor1,bgFillColor2,minScale,maxScale,smallUnitScale,bigUnitScale,scaleValDuration,scaleTransX,scaleFontSize,scaleColor,scaleFontColor;perimeterSpacing=40;scaleTransX=0.0;scaleColor=#528CFF;bgFillColor1=#F8D7D1;bgFillColor2=#FF5D3C;igDprop=commonStrokeColor,commonFontColor;html=1;shadow=0;dashed=0;showScale=1;scaleValDuration=1.0;minScale=0;maxScale=100;smallUnitScale=2;bigUnitScale=20;strokeWidth=1;strokeColor=#528CFF;fontColor=#528CFF;fontSize=12;title=Gauge Meter;opacity=100;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 150, 200, '', 'Gauge Meter', true, null, null, mxUtils.staticImg(imgPath)));

  // Text Input
  imgPath = `/rcscada/menu/ic_menu_htmlInput.png`;
  style = `shape=mxgraph.rc.mxRc_htmlInput;showFontStyle=1;placeholderText=Input here;readonly=1;rcDprop=htmlTextInputDefaultValues;igDprop=commonStrokeColor,commonFontColor;html=1;shadow=0;dashed=0;strokeWidth=1;strokeColor=#528CFF;fontColor=#333;fontSize=12;title=Text Input;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 200, 60, '', 'Text Input', true, null, null, mxUtils.staticImg(imgPath)));

  // Text Area
  imgPath = `/rcscada/menu/ic_menu_htmlTextArea.png`;
  style = `shape=mxgraph.rc.mxRc_htmlTextarea;showFontStyle=1;placeholderText=Input here;textAreaRows=4;readonly=1;rcDprop=htmlTextareaDefaultValues;igDprop=commonStrokeColor,commonFontColor;html=1;shadow=0;dashed=0;strokeWidth=1;strokeColor=#528CFF;fontColor=#333;fontSize=12;title=Text Area;fillColor=none;${mxConstants.STYLE_VERTICAL_LABEL_POSITION}=bottom;${mxConstants.STYLE_VERTICAL_ALIGN}=top;`;
  templates.push(this.createVertexTemplateEntry(style, 300, 120, '', 'Text Area', true, null, null, mxUtils.staticImg(imgPath)));

  this.addPaletteFunctions('mxRc_stateSwitch', 'Variable Controls', true, templates);
};

2. Implementing Custom Widgets

2.1. Widget Registration

Widgets are added via addPaletteFunctions, which internally uses createVertexTemplateEntry or createEdgeTemplateEntry. Each entry defines:

  • Style string with shape identifier and properties
  • Initial dimnesions (width, height)
  • Display name and preview icon

2.2. Shape Rendering Class

Each widget references a shape class (e.g., mxgraph.rc.mxRc_stateSwitch). This name must match the SHAPE_NAME constant in the corresponding renderer class (mxRc_stateSwitch.js):

mxRc_stateSwitch.prototype.cst = {
  SHAPE_NAME: 'mxgraph.rc.mxRc_stateSwitch',
};

All custom shapes are registered in ShapesExpands.js:

mxCellRenderer.registerShape(mxRc_stateSwitch.prototype.cst.SHAPE_NAME, mxRc_stateSwitch);

2.3. Custom Rendering Logic

Rendering logic resides in the paintVertexShape method of the shape class:

mxRc_stateSwitch.prototype.paintVertexShape = function(canvas, x, y, width, height) {
  // Custom drawing implementation
};

Tags: SCADA mxGraph javascript Web-based HMI Custom Widgets

Posted on Thu, 02 Jul 2026 17:29:51 +0000 by ALEXKENT18