Connecting 3000 tags using script that makes Ignition gateway restart automatically (ERROR)

For Single Array means DI (double int) data type?

I don't know what you're asking, but yes DI is DInt, 32-bit signed. The driver doesn't support arrays.

1 Like

Okay, I got pulled into an issue yesterday so took me a while, but here is my take on this.

Note I also use @pturmel's Integration Toolkit, so I take advantage of that here to. Meaning I would expect it to update as quickly as you can get the tag to update.

First you'll need to embed a blank SVG to the view. (technically doesn't need to be blank, but that's what I do. You only need the starting structure, and I have found no other way to add it.

First construct a pattern for the background grid, That will look something like this:

Grid SVG JSON
{
  "type": "defs",
  "id": "defs1",
  "elements": [
    {
      "type": "pattern",
      "id": "mainPattern",
      "name": "mainPattern",
      "href": "#Grid",
      "patternTransform": "scale(1)",
      "preserveAspectRatio": "xMidYMid"
    },
    {
      "type": "pattern",
      "fill": {
        "paint": "#000000"
      },
      "id": "Grid",
      "elements": [
        {
          "type": "group",
          "elements": [
            {
              "type": "rect",
              "fill": {
                "paint": "#D5D5D5"
              },
              "stroke": {
                "paint": "#000000",
                "width": "1px"
              },
              "x": 0,
              "width": 25.531915,
              "y": 0,
              "height": 12.5
            },
            {
              "type": "rect",
              "fill": {
                "paint": "#D5D5D5"
              },
              "stroke": {
                "paint": "#000000",
                "width": "1px"
              },
              "x": 25.531915,
              "width": 25.531915,
              "y": 0,
              "height": 12.5
            }
          ]
        },
        {
          "type": "group",
          "elements": [
            {
              "type": "rect",
              "fill": {
                "paint": "#FFFFFF"
              },
              "stroke": {
                "paint": "#000000",
                "width": "1px"
              },
              "x": 0,
              "width": 25.531915,
              "y": 12.5,
              "height": 12.5
            },
            {
              "type": "rect",
              "fill": {
                "paint": "#FFFFFF"
              },
              "stroke": {
                "paint": "#000000",
                "width": "1px"
              },
              "x": 25.531915,
              "width": 25.531915,
              "y": 12.5,
              "height": 12.5
            }
          ]
        }
      ],
      "name": "Grid",
      "height": 25,
      "patternUnits": "userSpaceOnUse",
      "width": 51.06383
    }
  ],
  "name": "defs1"
}

Then you need a rectangle to display the pattern in. That would look something like this:

{
  "type": "group",
  "id": "GridLayer",
  "elements": [
    {
      "type": "rect",
      "fill": {
        "opacity": 1,
        "url": "url(#mainPattern)"
      },
      "stroke": {
        "width": "1px"
      },
      "id": "rect1",
      "name": "rect1",
      "x": 0,
      "y": 0,
      "width": 1200,
      "height": 750
    }
  ]
}

Finally, add a group element for the text. This will have an elements array that holds the individual text elements generated by a binding.

That binding would look something like this:

flatten( // Flatten the lists of lists to just a single list.
	forEach( // 60 rows
		60,
		forEach( // 47 elements per row
			47,
			asMap( // Build the element
				"type", "text",
				"x", (8 + (idx() * {this.props.elements[0].elements[1].width} / 2)),
				"y", (9 + (idx(1) * {this.props.elements[0].elements[1].height} / 2)),
				"text", stringFormat(
						'%02d', 
						{[default]EchelonSinglePage/MatrixMD04}[idx() + 46 * idx(1)]
					)
				)
			)
		)
	)
)

Here is a sample, I added some sliders and modified the expression a bit to suite, but this shows the responsiveness. This was run on my local machine, in the browser, with multiple applications running including AutoCad Electrical, SSMS, a Vision Client.

grid

For completeness sake, here is the

View JSON

{
"custom": {},
"params": {
"tagList": [
"[RedGold_Elwood]Elwood/Line 0/*"
]
},
"propConfig": {
"params.tagList": {
"paramDirection": "input",
"persistent": true
}
},
"props": {
"defaultSize": {
"height": 1080,
"width": 1920
}
},
"root": {
"children": [
{
"meta": {
"name": "Label"
},
"position": {
"height": 32,
"width": 339,
"x": 45,
"y": 278
},
"type": "ia.display.label"
},
{
"meta": {
"name": "pattern"
},
"position": {
"height": 1080,
"width": 1732
},
"propConfig": {
"props.elements[2].elements": {
"binding": {
"config": {
"expression": "flatten(\r\n\tforEach( // 60 rows\r\n\t\t60,\r\n\t\tforEach( // 47 elements per row\r\n\t\t\t47,\r\n\t\t\tasMap( // Build the element\r\n\t\t\t\t"type", "text",\r\n\t\t\t\t"x", (8 + (idx() * {this.props.elements[0].elements[1].width} / 2)),\r\n\t\t\t\t"y", (9 + (idx(1) * {this.props.elements[0].elements[1].height} / 2)),\r\n\t\t\t\t"text", stringFormat(\u0027%02d\u0027, idx()),\r\n\t\t\t\t"stroke", asMap(\r\n\t\t\t\t\t"paint", if((idx() + 46 * idx(1)) \u003d ({../Slider.props.value} + 46 * {../Slider_0.props.value}) , "#FF0000", "#000000")\r\n\t\t\t\t\t)\r\n\t\t\t)\r\n\t\t)\r\n\t)\r\n)"
},
"type": "expr"
}
}
},
"props": {
"elements": [
{
"elements": [
{
"href": "#Grid",
"id": "mainPattern",
"name": "mainPattern",
"patternTransform": "scale(1)",
"preserveAspectRatio": "xMidYMid",
"type": "pattern"
},
{
"elements": [
{
"elements": [
{
"fill": {
"paint": "#D5D5D5"
},
"height": 12.5,
"stroke": {
"paint": "#000000",
"width": "1px"
},
"type": "rect",
"width": 25.531915,
"x": 0,
"y": 0
},
{
"fill": {
"paint": "#D5D5D5"
},
"height": 12.5,
"stroke": {
"paint": "#000000",
"width": "1px"
},
"type": "rect",
"width": 25.531915,
"x": 25.531915,
"y": 0
}
],
"type": "group"
},
{
"elements": [
{
"fill": {
"paint": "#FFFFFF"
},
"height": 12.5,
"stroke": {
"paint": "#000000",
"width": "1px"
},
"type": "rect",
"width": 25.531915,
"x": 0,
"y": 12.5
},
{
"fill": {
"paint": "#FFFFFF"
},
"height": 12.5,
"stroke": {
"paint": "#000000",
"width": "1px"
},
"type": "rect",
"width": 25.531915,
"x": 25.531915,
"y": 12.5
}
],
"type": "group"
}
],
"fill": {
"paint": "#000000"
},
"height": 25,
"id": "Grid",
"name": "Grid",
"patternUnits": "userSpaceOnUse",
"type": "pattern",
"width": 51.06383
}
],
"id": "defs1",
"name": "defs1",
"type": "defs"
},
{
"elements": [
{
"fill": {
"opacity": 1,
"url": "url(#mainPattern)"
},
"height": 750,
"id": "rect1",
"name": "rect1",
"stroke": {
"width": "1px"
},
"type": "rect",
"width": 1200,
"x": 0,
"y": 0
}
],
"id": "GridLayer",
"type": "group"
},
{
"id": "textLayer",
"style": {
"classes": "",
"fontFamily": "Arial",
"fontSize": 10
},
"type": "group"
}
],
"viewBox": "0 0 1200 750"
},
"type": "ia.shapes.svg"
},
{
"meta": {
"name": "Slider"
},
"position": {
"height": 380,
"width": 138,
"x": 1764,
"y": 34
},
"props": {
"labels": {
"interval": 10,
"show": true
},
"max": 46,
"orientation": "vertical"
},
"type": "ia.input.slider"
},
{
"meta": {
"name": "Slider_0"
},
"position": {
"height": 380,
"width": 138,
"x": 1764,
"y": 500
},
"props": {
"labels": {
"interval": 10,
"show": true
},
"max": 59,
"orientation": "vertical"
},
"type": "ia.input.slider"
}
],
"meta": {
"name": "root"
},
"position": {
"x": -231,
"y": -250
},
"type": "ia.container.coord"
}
}

8 Likes

I'd like to add that since we're using data from an array of photocells, essentially what we're trying to do here is generate a 60x47 image where each pixel (cell) needs to display a value. The goal here, I assume, is to give the user a qualitative idea of what the photocells are seeing.

My recommendation is to use color instead of digits for each cell.

Let's assume the value at each cell ranges from 0 to 100. If you can create a simple SVG grid of small squares where each cell is colored so that 0 is white, 50 is gray, 100 is black, and every color in-between uses that gradient, I think you'd get the picture you're looking for.

@lrose I think your example could be easily modified to create this example.

flatten( // Flatten the lists of lists to just a single list.
	forEach( // 60 rows
		60,
		forEach( // 47 elements per row
			47,
			asMap( // Build the element
				"type", "rect", // I'm just guessing here
				"x", (8 + (idx() * {this.props.elements[0].elements[1].width} / 2)),
				"y", (9 + (idx(1) * {this.props.elements[0].elements[1].height} / 2)),
				"width", "5px", // Still just guessing
				"height", "5px",
				"color", color( // Multiply by 2.55 to convert range from [0-100] to [0-255]
					{[default]EchelonSinglePage/MatrixMD04}[idx() + 46 * idx(1)] * 2.55,
					{[default]EchelonSinglePage/MatrixMD04}[idx() + 46 * idx(1)] * 2.55,
					{[default]EchelonSinglePage/MatrixMD04}[idx() + 46 * idx(1)] * 2.55
				)
			)
		)
	)
)

The color() function probably won't work here since it returns a Color object, but it's only meant to serve an illustrative purpose.

2 Likes

So what I have concluded is that the design structure proposed by @lrose and @Brandon_Peterson are the best ways to visualise the matrix on perspective.

And for updating the tags quickly is to use the siemens driver in Ignition rather than using OPC server of the PLC.

But as I have an array of 3060 double integers, I need to do addressing for each double integer 1 by 1 (Eg: [Siemens S71500]DB67,DI0 and so on) as the driver does not have option to address the complete integer array.

If you wanted to change the colors, that could be done fairly easily, though I wouldn't do it the way you are.

  1. Rectangles are positioned by the upper right corner
  2. I try to avoid using anything that resembles a magic number. Instead use a CSS variable

Here is an example of the expression I would use if I were doing this in a production environment. It's not perfect as it doesn't account for wrap, nice thought exercise though.

flatten(
	forEach( // 60 rows
		60,
		forEach( // 47 elements per row
			47,
			asMap( // Build the element
				"type", "rect",
				"x", (1 + (idx() * {this.props.elements[0].elements[1].width} / 2)),
				"y", (1 + (idx(1) * {this.props.elements[0].elements[1].height} / 2)),
				"width", ({this.props.elements[0].elements[1].width} / 2)-2,
				"height", ({this.props.elements[0].elements[1].height} /2-2 ),
				"fill", asMap(
					"paint", case( 
								(idx() + 46 * idx(1)),  // True index of actual element
								// One element to the left of true index
								({../Slider.props.value} + 46 * {../Slider_0.props.value}) - 1, "var(--qual-5)",
								// One element to the left and one row up
								({../Slider.props.value} + 46 * ({../Slider_0.props.value} - 1)) - 1, "var(--qual-5)",
								// One element to the left and one row down
								({../Slider.props.value} + 46 * ({../Slider_0.props.value} + 1)) - 1, "var(--qual-5)",
								// True element
								({../Slider.props.value} + 46 * {../Slider_0.props.value}), "var(--qual-8)",
								// One row up
								({../Slider.props.value} + 46 * ({../Slider_0.props.value} - 1)), "var(--qual-5)",
								// One row down
								({../Slider.props.value} + 46 * ({../Slider_0.props.value} + 1)), "var(--qual-5)",
								// One element to the right
								({../Slider.props.value} + 46 * {../Slider_0.props.value}) + 1, "var(--qual-5)",
								// One element to the right and one row up
								({../Slider.props.value} + 46 * ({../Slider_0.props.value} - 1)) + 1, "var(--qual-5)",
								// One element to the right and one row down
								({../Slider.props.value} + 46 * ({../Slider_0.props.value} + 1)) + 1, "var(--qual-5)",
								// All other elements
								"var(--white)"
							)
					)
			)
		)
	)
)

This would definitely be easier on the eyes, but again I am not completely sure what exactly the OP is attempting to accomplish, so the actual application may very.

Here is that expression in action:

grid

9 Likes

I'm currently running a little over 10,000 tags in Ignition doing something similar, however my implementation is very different. I cant get too deep into the weeds, but a little connecting the dots will get you there. Ignition is not handling heatmap or nor am I using its drivers. Nothing against their drivers, just not trying to bog down the memory usage of Ignition by giving it a lot of extra work.

I have a python program running on the same device as Ignition just running in parallel. I am using snap7 to read the tags asynchronously, and publish them to an internal webpage to the device. The JavaScript handles the interactive heatmap and updating the heatmap at a set interval, and Ignition accesses that heatmap using the Inline Frame component.

Now my data is still getting into the tags in Ignition by other means but it is not directly impacting the heatmap. Also with that said, my heatmap has a different data rate change than the data coming in. My heatmap adjusts every few seconds while my tags are more real-time. There is no lag in data vs the heatmap, its just more of a snapshot every couple of seconds.

Realistically, I had to ask myself, if I am collecting the data at high speeds, am I accomplishing anything by visually seeing the heatmap in high speed vs every couple of seconds. In my business use case, not much was gained which is why this method works well. Your situation might be different. This took some stress off of Ignition while still maintaining data integrity and a live-ish view.

A super computer with 256GB DDR35 RAM (from the future) , 24 CPUs utilising holographic technology, each with 9000k passmarks... :stuck_out_tongue:

Jokes aside, that's really good performance, I'm surprised

1 Like

Hi Irose, I am looking for how you build this svg. can you please assist? I am trying to copy your json and paste on my svg but did not worked.

You do not paste the JSON on the SVG, you paste it on the view. Careful as it will overwrite anything that is already on the view.

If you just want the SVG, you can paste the JSON on a new view and the copy just the SVG.

1 Like