Find peak values

I like to find the peak value of each cycle/hill from a tag in historian within a time range in script, only if the peak is above 7000 lbs (ignore the small hills). Is there a faster way than looking at each point in the dataset, such as using a combination of querytaghistory and scripting. Just looking for ideas not entire code. Each cycle/hill is a batch. I need to add all the peak values to come up with the weight of raw materials. Thanks!

You could simplify the problem by creating an expression tag to monitor the weight. An expression like this:

if(
	{[~]Weight} > 7000,
	{[~]Weight},
	0
)

might do the trick. Then log that rather than, or as well as, the actual weight.

You still have some work to do though!

You'll need to not only determine the minimum peak value, but also the maximum "reset" value. That is, once your weight exceeds 7000lb, you capture the next peak, but then how do you determine when that peak has completed and the next one begins? Generally you would say something like "when the weight drops back below x". For the sake of the example, let's say the "reset" value is 250lb.

One approach might be to have an expression tag that references the live weight, with an expression that looks something like

if(myValue = 0 && liveWeight < 7000, 0,
if(liveWeight >= 7000 && liveWeight > myValue, liveWeight,
if(liveWeight < 250, 0,
myValue)))

Line by line, this basically says:

  1. If we're at zero and the live weight is less than 7000lb, remain at zero, otherwise continue
  2. If the actual weight is greater than 7000lb, and greater than the current value of this tag, then update this tag to reflect the higher weight. If not, continue
  3. If the live weight is less than 250lb, set my value back to zero
  4. If none of the above are true, hold current value

So what should happen if I've got that right is that the tag will stay at zero until the weight exceeds 7000lb, then track the weight as high as it goes, then reset back to zero when the weight falls below 250lb

Then, add a change script to that tag. Every time it changes, if it changes from a non-zero value to a zero value, write the previous value of the tag to another memory tag called BatchWeight. So, each time your weight falls below 250lb, and your expression tag resets to zero, it will set BatchWeight to the highest greater-than-7000lb weight previously reached.

Finally, configure tag history on BatchWeight, with it set to log on change only, and just pull the entire tag history from that tag. It'll get you each batch weight and an almost-correct timestamp for that batch (i.e. not the time that the batch was completed, but the time when the batch was removed from the scale).

You'll have to consider what will happen if you get two batches in a row with the exact same weight. Depending on the resolution of your measurement this may or may not be an issue. If it is, you could do something like reset the BatchWeight tag to -1 between each batch, and when you run your tag history query, only retrieve positive values.

Of course I'd much rather do this in the PLC and just push the final batch weights to Ignition, but if you don't have that option something like this should work. Plenty of ways to deconstruct this feline!

Expression tags cannot safely reference their own value. Don't do this.

Thanks for the tip! Maybe you could just put a change script on the live weight tag then, and do effectively the same thing there?

If you really need state like that, I recommend using the state dictionary that my objectScript() function offers.

Thanks! I think I understand your logic and Phil's suggestion and will try them out. We tried that in the PLC but the outcome wasn't consistent, resulting raw materials less than the final product sometimes. I am hoping I don't have to use expression tag but rather use the mixer weight tag that has months of data already. If that's not possible or working, I will resort to expression and memory tags.

Just to clarity, the raw material is dumping into the blender for mixing. When the weight reaches maximum (peak) that's the weight of that batch, when it decreases, that's when the gate valve and pump are running to move the product to filling. The little hills are washing.

split the list up every time you drop below 7k, pick the max from each sublist?

The weight scale fluctuates, could be bouncing around 7k.

Say what?

Then do the same for 6k/5k

What do you mean by "ignore" ?

In the PLC do you have a "batch start" and "batch complete" ? You could track those start and end times and find the max between start and end.

Or use a combination of tags and expression tags on the weight value change script to "detect" batch start/stop in Ignition. Something like Batch_Running, Batch_Start_Min [7000] Batch_Stop_Max [100] Expression tag check the weight.

Something like (pseudocode),

if NOT Batch_Running and ( Batch_Weight > Batch_Start_Min )
    Batch_Running = True

if Batch_Running and ( Batch_Weigh < Batch_Start_Max )
    Batch_Running = False

Thank you! Unfortunately no batch start or end in PLC. Ignoring the small hills because they are washouts not actual raw materials.

If you have access to the PLC program you can do this in Ignition or in the PLC. I would use the same approach either way. Either the PLC or Ignition will writhe to the Batch_Running tag.

Turn history on for the Batch_Running tag. Query it to get the last start and stop times, use those to query again and get the max between start and stop and into a Last_Batch_Weight_Max and also turn history on on that.

You should be able to use aggregation methods to get whatever data you need from there.

1 Like

A simplified flowchart with one pass through the dataset.

Peaks.pdf (14.7 KB)

2 Likes

...if you can't get it to happen accurately in the PLC, you won't be able to make it happen accurately with Ignition. Anything you can do with Ignition is only as good as the data you give it, and if the PLC can't track it in realtime, Ignition has no hope.

It should be as simple as this, really (not sure of your PLC, this is Allen-Bradley, but they're all generic instructions).

Feed the weight reading that you're looking at on your trends into LiveWeight, and put tag history on LastBatchWeight, and you're basically done.

3 Likes

Bonus points if you convert this rung to utilize an output coil, instead of latch/unlatch.

That changes behavior through program mode changes or power blips. Maybe for the better, but not always.

So much needs to be discussed for this to be a consideration for this rung (output state during controller not scanning, power supply feeding PLC, whether or not that programmer who made a program mode change while a system is running should ever be invited back, etc.).
Logic is almost-always more robust - and easier to maintain - when latches are replaced by coils.

Completely agree. But that "almost" stuck in there means unconditional bonus points are not appropriate.

(I've seen enough places with poor utility reliability to not knock defensive programming.)

1 Like