Studio 5000 Structured Text Loop

RonB

Member
Join Date
Feb 2012
Location
Hickory
Posts
36
Hey all,

Studio v20.05

Background.
We are piggy backing another system to record attributes for product travelling down the line and several data points recorded. The scale system records weight, along with several other items, then outputs a string that is passed through a socket connection to a 1756-L72.

The string is parsed into most of the time, but occasionally there's a hiccup.
In that string there is a 6 digit sequence and a plant identification number.
I wrote a loop in structured text to read the current sequence, subtract the last recorded sequence, and fill the empty array records with basic data so that it can be picked up by an external application. It worked in emulate, but last night it didn't fix the arrays that were filled with 0 - causing the external application to halt.

If you can offer any insight I would really appreciate it. I'm sure there are things I'm forgetting to mention, so please let me know if you need more details to get a better idea.

Code:
IF Sequence >= 1 Then
	Sequence := Current_Seq - 1; 
	Else Sequence := 1;
End_if;

IF Current_Shift = 1 AND Sequence >= 1  AND (Current_Seq - 1stShift_Last_Seq) >= 1  Then
	While Carcasses_1stShift[Sequence].Plant_ID <> 4010 Do
	1stShift[Sequence].Plant_ID := 4010;
	1stShift[Sequence].Carcass_Count := Sequence;
	1stShift[Sequence].Shift_Number := 1;
	1stShift[Sequence].Species := 1;
	1stShift[Sequence].Year := Carcass_Date_Time.Year;
	1stShift[Sequence].Month := Carcass_Date_Time.Month;
	1stShift[Sequence].Day := Carcass_Date_Time.Day;     
	1stShift[Sequence].Hour := Carcass_Date_Time.Hour; 
	1stShift[Sequence].Minute := Carcass_Date_Time.Minute;
			1stShift[Sequence].Tattoo := 7779;
		IF Sequence = 1 THEN
			EXIT;
		ELSE 
			Sequence := Sequence -1;
		END_IF;
	End_while;
End_if;
 
I haven't looked at it in a lot of detail, but straight off the bat I wouldn't use a While loop without a get out clause in it, preferably a timer less than your watchdog in the processor. Doesn't mean it's the source of your problem, but good practice.

I'm also confused with your treatment of the Sequence variable. Could there be some issue around it or is it used elsewhere which may confuse your logic if it's not used sequentially?
 
cardosocea, I'm sorry for the slow reply. I've been watching my emails for notifications, but the forum is acting quirky on my end. When I try to login it continuously shows my name at the top, but still asks for login credentials. It's like having one foot in and out.
There are 3 steps to worry about in this scenario;

1. Vendor- Supplies a string over a socket connection to PLC. Records are sequentially numbered, unless errors/issues occur.
2. PLC- reads/parses the string into arrays containing the above fields along with many additional fields.
3. NET server runs an application to look at the PLC arrays and collect data for use in SQL. Keeps a list of what records have been collected each cycle and resumes based on the last success. (The fields above are the required fields that must be populated for it to be picked up)

The ST is supposed to watch the incoming sequence in step 2, check the array for a populated PlantID.
IF it isn't populated- then fill out the basic information to allow step 3 to not get stuck.
The "sequence" section was probably confusing since I did a copy / replace and couldn't update the post. Sorry for the confusion. The goal is to use the deincrementer as a placeholder for the current sequence -1, and to fill out all the arrays with basic information until it either finds a populated record or gets down to 1 and escapes.


IF DeIncrementer >= 1 Then
DeIncrementer := CurrentSequence - 1;
Else DeIncrementer := 1;
End_if;

IF CurrentShift = 1 AND DeIncrementer >= 1 AND (CurrentSequence - LastSeq) >= 1 Then
While 1stShift[DeIncrementer].PlantID <> 4010 Do
1stShift[DeIncrementer].PlantID := 4010;
1stShift[DeIncrementer].CarcassCount := DeIncrementer;
1stShift[DeIncrementer].ShiftNumber := 1;
1stShift[DeIncrementer].Species := 1;
1stShift[DeIncrementer].Year := Date_Time.Year;
1stShift[DeIncrementer].Month := Date_Time.Month;
1stShift[DeIncrementer].Day := Date_Time.Day;
1stShift[DeIncrementer].Hour := Date_Time.Hour;
1stShift[DeIncrementer].Minute := Date_Time.Minute;
1stShift[DeIncrementer].Tattoo := 7779;
IF DeIncrementer = 1 THEN
EXIT;
ELSE
DeIncrementer := DeIncrementer -1;
END_IF;
End_while;
End_if;
 
The struck-out lines are redundant if you make the following change:

IF DeIncrementer >= 1 Then
DeIncrementer := CurrentSequence - 1;
Else DeIncrementer := 1;
End_if;

IF CurrentShift = 1 AND DeIncrementer >= 1 AND (CurrentSequence - LastSeq) >= 1 Then
While 1stShift[DeIncrementer].PlantID <> 4010 AND DeIncrementer >= 1 Do
1stShift[DeIncrementer].PlantID := 4010;
1stShift[DeIncrementer].CarcassCount := DeIncrementer;
1stShift[DeIncrementer].ShiftNumber := 1;
1stShift[DeIncrementer].Species := 1;
1stShift[DeIncrementer].Year := Date_Time.Year;
1stShift[DeIncrementer].Month := Date_Time.Month;
1stShift[DeIncrementer].Day := Date_Time.Day;
1stShift[DeIncrementer].Hour := Date_Time.Hour;
1stShift[DeIncrementer].Minute := Date_Time.Minute;
1stShift[DeIncrementer].Tattoo := 7779;

IF DeIncrementer = 1 THEN
EXIT;
ELSE
DeIncrementer := DeIncrementer -1;
END_IF;
End_while;
End_if;

 
Thanks for the cleanup. Any reason that you can see why it wouldn't work?

I tested with emulate and didn't have any trouble, but in a real world scenario it has failed to help on two occasions.
 
Thanks for the cleanup. Any reason that you can see why it wouldn't work?

I tested with emulate and didn't have any trouble, but in a real world scenario it has failed to help on two occasions.


My code is not equivalent; sorry about that. You should go back to what it was, i.e. with the IF-...-THEN-EXIT-ELSE-...

What assigns the values of CurrentSequence, CurrentShift, and LastSeq?

Also, I am pretty sure (though gunshy) that the logical expression (CurrentSequence - LastSeq) >= 1 can be replaced by CurrentSequence > LastSeq, which would be easier to understand/read, if those are integers.
 
Latest

After playing with it some more and running it through the emulator I put it in ChatGpt to see if maybe it could see something.

Here's the response and it appears I had a bit of tunnel vision. I asked that it compare my initial ST with the suggested changes, as well as I gave it an example to run through.

In both cases, the code will attempt to iterate through the array 1stShift from index DeIncrementer (which would be set to Carcass_Seq - 1 initially, i.e., 99 in this case) downwards until it finds an element where Plant_ID is not equal to 4010 or until it reaches DeIncrementer = 1.

Given that the arrays from 1 to 100 are filled with 0s, the loop conditions in both code snippets will encounter the end of the array (DeIncrementer = 1) without finding any element with Plant_ID not equal to 4010. This will result in the loop terminating without making any changes to the array elements.

Both code snippets, in this specific scenario, would iterate through the array from Carcass_Seq - 1 downwards, but since the array elements are all 0s and the condition checks for Plant_ID <> 4010, the loop won't perform any modifications or updates to the array elements.

The proposed solution included checking the plant id explicitly using an "IF".
IF Current_Shift = 1 AND DeIncrementer >= 1 AND (Carcass_Seq - Last_Seq) >= 1 Then
While DeIncrementer >= 1 AND 1stShift[DeIncrementer].Plant_ID <> 4010 Do
IF 1stShift[DeIncrementer].Plant_ID = 0 THEN
// Update elements with a value of 0 to 4010

In emulate it seems to run fine without faulting, so I will probably test in production this evening. Thanks for the assist and if you see anything, please, let me know.
 
... Given that the arrays from 1 to 100 are filled with 0s, the loop conditions in both code snippets will encounter the end of the array (DeIncrementer = 1) without[???] finding any element with Plant_ID not equal to 4010.

... but since the array elements are all 0s and the condition checks for Plant_ID <> 4010, the loop won't[???] perform any modifications or updates to the array elements ...
Whatever "intelligence" wrote this does not understand WHILE-DO-END_WHILE loops and/or logical comparison expressions.

If "array elements" are all 0s, and .Plant_ID is one of those array elements, then I suspect the compiled ST code will interpret the expression ...Plant_ID <> 4010 as TRUE.

Or am I missing something?
 
When something is happening is more important than what is happening (twice in one day; whaddya know?).

When do the values in the PLC array get updated? Does it happen from a network-driven event?

When does the value of CurrentSequence get updated?

When does this filler code run? Does it run immediately after 0s might be written into the array and/or the value of CurrentSequence is updated? Could it run after 0s are written but before the value of CurrentSequence is updated?

When does the .NET server look at the values PLC array? Could it happen after 0s are written and before the filler code runs?

What are all possible sequences for those four events?
 
have you considered making the sequence longer, so you can determine at what point the issue occurs?

without knowing the specifics of how the handling occurs, my instinct tells me you need more checks and balances within the process.

I would typically write individual values into a "holding struct", and upon completion and verification push that struct into the struct array after which completion restarts the process.

Maybe I'm way off base...
 
Trying to answer all at once

Whatever "intelligence" wrote this does not understand WHILE-DO-END_WHILE loops and/or logical comparison expressions.
If "array elements" are all 0s, and .Plant_ID is one of those array elements, then I suspect the compiled ST code will interpret the expression ...Plant_ID <> 4010 as TRUE.
Or am I missing something?

As mentioned previously, in emulate it worked fine, but in production it did not. After shift 1 is over, shift 2 begins, shift 1 is moved to a backup array and then filled with 0's. I can't see that calling it to check explicitly for "0" would hurt as I can't put it further in the ditch.
I agree with you that it should see plant id as not equal to 4010 and set each of those fields, but it didn't. Emulate yes, Production no. Both were populating a sequence and at the end of each sim I would set them to fill all the arrays with 0's when I wanted to restart.

When something is happening is more important than what is happening (twice in one day; whaddya know?).

When do the values in the PLC array get updated? Does it happen from a network-driven event?
Socket connection between vendor machine and PLC - string is sent over and parsed into the arrays
When does the value of CurrentSequence get updated?
Every time the string is parsed the sequence is updated- generally up to 15 times per minute - assuming not on lunch or break
When does this filler code run? Does it run immediately after 0s might be written into the array and/or the value of CurrentSequence is updated? Could it run after 0s are written but before the value of CurrentSequence is updated?
It is active at all times- that's why it checks that the deincrementer at the beginning.
When does the .NET server look at the values PLC array? Could it happen after 0s are written and before the filler code runs?
Yes, the .net server is looking ever 2 minutes. If it doesn't see those basic fields like plant id, shift, time/date it won't recognize the array as holding a valid record and stops.
What are all possible sequences for those four events?

have you considered making the sequence longer, so you can determine at what point the issue occurs?

without knowing the specifics of how the handling occurs, my instinct tells me you need more checks and balances within the process.

I would typically write individual values into a "holding struct", and upon completion and verification push that struct into the struct array after which completion restarts the process.

Maybe I'm way off base...
This was my first run with ST. Being stuck in the middle and at the mercy of processes on both ends- as I have no control of the vendor programming or the application programming on the .net side- I'm trying to catch it as quickly as possible and sort the data. If I had to guess I'm probably going about it in a bass-ackwards format :D
 
Last edited:
I agree with you that it should see plant id as not equal to 4010 and set each of those fields, but it didn't. Emulate yes, Production no.


1. What are the differences between Emulate and Production?
1.1. For example, is the .NET process reading these data running against the Emulate system?

2. How do you know the PLC code did not see the plant id <> 4010 when in Production? Was it
2.1. EITHER because you monitored the records plant id values in the PLC memory array, while online with the PLC, and visually confirmed they had not been converted to 4010 even though the ST program was running,
2.2. OR because the .NET application stopped, perhaps with a warning message that it found an invalid plant id value of 0,

or some other diagnostic?
 
When do the values in the PLC array get updated? Does it happen from a network-driven event?
Socket connection between vendor machine and PLC - string is sent over and parsed into the arrays
Here is how I interpret than answer in blue: when the vendor machine sends a string to the PLC,

  • The arrival of that string on the network immediately interrupts whatever the PLC is doing
  • The interrupt handler in the PLC
    • parses the string and writes new data to the array at index CurrentSequence.
    • increments the CurrentSequence integer
  • The interrupt handler then exits
If that is the case, is it possible for the .NET application to read the newly-written, but invalid array data ***BEFORE*** the ST routine has a chance to clean those data up? That would account for the error you are seeing.

====================================

If that is not the case, go back and look at my original question:

  • "When do the values in the PLC array get updated?"
because you may not be answering the question I asked. Specifically, I understand what triggers the update i.e. the network string arriving. I am asking specifically when the bits in the array are written as a result of that network string being parsed, and I am further asking, in the last question of that list:

  • "What are all possible sequences for those four events"
i.e. the possible order in time in which though four events could occur. That last question is still awaiting an answer.

You have been focusing on what each piece of this system, the vendor network connection, the PLC string-parsing code, the PLC ST code, the .NET application, does when it executes; I believe it will be more productive to focus on when each piece of this system executes.
 

Similar Topics

I have attached a photo, can you add something to be able to collapse x amount of lines? in the picture to add something like I drew on the image...
Replies
3
Views
937
I'm working on learning Structured Text, and I'm stuck on something so simple: I have an If-Then statement using Bool tags, and I'm having a hard...
Replies
18
Views
5,123
Hey, i need help to write a structured text program that set a signal to high 6s and to low 6s. I used TONR to do this but it doesn't work...
Replies
5
Views
4,446
Hello, I setup a Studio 5000 project, with a Guardlogix 1756 controller. I want to program an Add-On instruction in Structured Text, all other...
Replies
9
Views
2,488
Hi Everyone. Not posted on here for a long time, but I am hoping someone can help me. I am doing a differential pressure calculation in a L27ERM...
Replies
15
Views
234
Back
Top Bottom