Complex program logging/debug

linnemann

Member
Join Date
Aug 2016
Location
Aars
Posts
30
How do you guys handle complex program logging?

When something in the program changes how to you handle logging this without code repetition all over the place?

Do you use a function block, a global function?
Also what about the distinction between error messages and only warnings or info logging.

I am not asking for code examples, more a description of how you guys approach this issue.
 
Hi.

Nope it is just logging states and changes in the program.

eg. log to a file or an array "Motor 1 started", "Infrared sensor no signal" sort of these things.
 
Most hmi have possibility to check events on tags. Add a long list, create multiple scripts, depending on hmi and get a row for time user tagname new value.

I will soon do a job where the hmi does not have alarm list, so I am forced to do something similar what you ask in plc.
 
We outlined specific parts we wanted to monitor and then protected them by placing them on a panelview screen that you can only access with an IR card reader and a passcode. Each passcode is assigned to a particular employee with elevated rights (team leaders and maintenance people) so when they scan the card and enter their unique password, we log the time and the person on a FIFO to keep track of who accessed the protected screens and when. That way if there is any issue, we can track the person down to get information about what they changed and why.
 
Hi LoganB

I think you misunderstood.

I am not talking about Log-in but about logging.

Like datalog or eventlog.

Other than that I appreciate the solution you presented for security reasons.
 
If you have an HMI use the built in message function then you don't have to worry about things like the log file getting full and things like that. The HMI will take care of that for you. I did an application once where a lot of things needed to be logged. It was a Siemens HMI/PLC so in simply created a new alarm class to separate the events from normal warnings and alarms and then i tied a separate message bit in the PLC to all events i wanted to end up in the log.

If you don't have an HMI you can create a FIFO in the PLC and assign each event to an unique number and push that number along with a timestamp into the fifo upon change of the event you want to monitor.
 
Hi Bratt

I get the HMI part, but I'm avoiding any vendor specifik HMI right now.
I/We are moving towards std. open HTML5 for the HMI.
That way everything is local to the PLC and the HMI is just a glorified touch screen.

See this example.
https://github.com/tomcx/tame4/blob/master/examples/tamex/svg/Beckhoff_Car_SVG.gif


The second thing you mention is what we are doing today.
I have a "Info", "Warning" and "Error" global FB's that captures all the relevant messages and put into a FIFO array that pushes out log files regularly.

Just looking for inspiration on maybe more "clever" ways of doing it.
 
How do you guys handle complex program logging?

Poorly. Save program, view data table, copy/paste to excel, run report. Elapsed time 1 hour.

When something in the program changes how to you handle logging this without code repetition all over the place?

Do you use a function block, a global function?
Also what about the distinction between error messages and only warnings or info logging.

I use a subroutine, storing info to a global array. Originally there was one array for status logging, one for alarm logging. I have added a command log, for HMI commands like start, stop, auto, manual, open, close, etc. And a Second status array for interlock signals, limit switches, high levels, the sort of thing that is not associated with a specific device (that has a motor or a valve number)

Running on 10 ControlLogix (L63 and L73) across 8 buildings

I am not asking for code examples, more a description of how you guys approach this issue.

I have tried stuffing report-by-exception data and timestamps into Emerson DeltaV (does not accept timestamps in milliseconds), PI (requires a tag to store the result for the historian, and the administrator won't give me 30,000 tags), and Ignition (does not us RSLinx for communications and the built-in driver won't talk to a ControlLogix unless it is in the same rack as an ethernet card).

A possible solution, that does something similar but does not use my existing data, is an RSLogix digital alarm block with the resulting data sent to Factorytalk View. I have not invested the time to try it. There is a limit of 2000 alarm blocks per ControlLogix. My largest ControlLogix would require about 5000. I can work around this problem if that is the only problem. We have some old L61 that can be slapped into a rack and used to process alarm blocks ....

There were another couple of suggestions when I posted a couple of years ago. I don't remember why they did not work out.

Looking for any suggestions, besides 'poll really fast'. It's just not workable.
 
Thx for the info.

I ended up writing my own function block that takes an action and stores a string to a global array for memory logging.

Then for more permanent logging in the same FB I have a FIFO buffer that writes to a file on the PLC disk. Using the FIFO buffer makes so I can store very fast to memory and inbetween the PLC writes to the disk.

The functionblock is Global and I have the other FB's always have a pointer to this global logging functionblock.

This way I can have one function block for info, one for errors and one for warnings and even polling data.

I have a function to concat the current date/time with the info I want to log and I can log actions that happens at the same milisecond written correctly to memory and also a file.

If anyone are interested I can post the content of the FBs here, beware it is structured text written in Twincat 3.
 
Great job on getting a solution implemented!

I'd like to see the code, if you can post it or PM it. You can ALWAYS learn from looking at what has worked for others.

I have a FIFO buffer that writes to a file on the PLC disk

That would make things easier! Having disk access directly from a PLC ... something that I have been asking Rockwell for since the PLC 5/250 in 1991.

An update on my not-yet-tried solution for the ALMD in ControlLogix.

L63 has a limit of 2000

L73 has a limit of 3000

The block allows external timestamps to be fed into it, from Sequence of event modules that timestamp their own data.

I may be able to make that work.
 
Hi

well here goes.

---- FB_BASE_LOG ----
Code:
FUNCTION_BLOCK FB_BASE_LOG
VAR_INPUT
	bExecute 			: BOOL;
	bReset 				: BOOL;
	sFilename 			: T_MaxString;
	pString				: POINTER TO T_MaxString;
	pBuf				: POINTER TO ARRAY [0..99] OF T_MaxString;
END_VAR
VAR_OUTPUT
	bBusy				: BOOL;
	bError				: BOOL;			
	iErrId				: UDINT;
	bResetOk			: BOOL;
	sLast				: STRING;
	nCount				: UINT;
END_VAR
VAR
	iCount				: UINT := 0;
	fbOpen 				: FB_FileOpen;
	fbClose 			: FB_FileClose;
	fbPuts				: FB_FilePuts;
	fbDelete			: FB_FileDelete;
	hFile				: UINT;
	fbStringBuf			: FB_StringRingBuffer:=(pBuffer:=ADR(aRingBuf),cbBuffer:=SIZEOF(aRingBuf),bOverwrite:=TRUE);
	aRingBuf			: ARRAY [0..1024] OF STRING;
	sRingValue			: T_MaxString;
	nRingCount			: UDINT;
	iStep				: DWORD;
	stRTRIG				: R_TRIG;			
END_VAR


fbStringBuf(getValue => sRingValue, nCount => nRingCount);

stRTRIG(CLK:=nRingCount > 0);

CASE iStep OF
	0: (*Idle state*)
		IF stRTRIG.Q THEN
			bBusy := TRUE;
			iStep := 1;
			bResetOk := FALSE;
		END_IF;
	1: (* Open source file*)
		fbOpen( bExecute := FALSE);
		fbOpen(sNetId:='',sPathName:=sFilename,nMode:=PATH_GENERIC,nMode:=FOPEN_MODEAPPEND OR FOPEN_MODEPLUS OR FOPEN_MODETEXT,tTimeout:=T#3S,bExecute:=TRUE, hFile => hFile, bError => bError, bBusy => bBusy, nErrId => iErrId);
		iStep := iStep + 1;
		
	2:
		fbOpen( bExecute := FALSE);
		IF NOT(fbOpen.bBusy) THEN
			hFile := fbOpen.hFile;
			iStep := iStep + 1;
		END_IF;
		
	3:  (* Write to source file*)
		fbStringBuf.A_RemoveHead(getValue => sRingValue, nCount => nRingCount);
		fbPuts(bExecute:=FALSE);
		fbPuts(sNetId:='',hFile:=hFile,sLine:=fbStringBuf.getValue,bExecute:=TRUE, tTimeout:=T#5S, bError => bError, bBusy => bBusy, nErrId => iErrId);
		iStep := iStep + 1;	
		
	4:
		fbPuts(bExecute:=FALSE);
		IF NOT(fbPuts.bBusy) THEN
			iStep := iStep + 1;
		END_IF;
	5:
		IF nRingCount > 0 THEN
			iStep := 3;
		ELSE
			iStep := iStep + 1;
		END_IF;
		
	6: (* Close source file *)
		fbClose(bExecute := FALSE);
		fbClose(sNetId:='',hFile:=hFile, bExecute:= TRUE, tTimeout:=T#3S, bError => bError, bBusy => bBusy, nErrId => iErrId);
		iStep := iStep + 1;
	
	7:
		fbClose(bExecute := FALSE);
		IF NOT(fbClose.bBusy) THEN
			iStep := iStep + 1;
			bBusy := FALSE;
		END_IF;
		
	8: (* Delete file *)
		IF bReset THEN
			fbDelete(bExecute:=FALSE);
			fbDelete(sPathName:=sFilename, ePath:=PATH_GENERIC, bExecute:=TRUE);
			iStep := iStep + 1;
		ELSE
			iStep := 0; (* Go to 0 *)
		END_IF;
	
	9:
		fbDelete(bExecute:=FALSE);
		IF NOT(fbDelete.bBusy) THEN
			bResetOk := TRUE;
			iStep := 0; (* Go to 0 *)
		END_IF
END_CASE;

---- FB_BASE_LOG.ADD ----
Code:
//this is the pushing part 
FOR iCount := 99 TO 1 BY -1 DO
  pBuf^[iCount] := pBuf^[iCount - 1] ;
END_FOR ;
//this is where the first array position is filled
pBuf^[0] := pString^;

fbStringBuf.A_AddTail(putValue:=pString^);

---- FB_BASE ----
Code:
FUNCTION_BLOCK FB_BASE
VAR_INPUT
	sFBName				: STRING;
	pInfoLog			: POINTER TO FB_BASE_LOG;
END_VAR
VAR_OUTPUT
	bBusy				: BOOL;
END_VAR
VAR
	bBase				: BOOL;
END_VAR


So whenever I want some info done I can either extend the base function block by doing.

Code:
FUNCTION_BLOCK FB_TIMED_OUTPUT_NO EXTENDS FB_BASE

Or I can use it directly like this.
Code:
IF fbFTRIGAskeMotor.Q THEN
	sLogM := CONCAT(F_STRING_DATE('Ashauger'),' | Motorprotection$R');
	GVL.gWarning := sLogM; 
	GVL.fbWarning.pString := ADR(sLogM);
	GVL.fbWarning.A_ADD();
END_IF;

the F_STRING_DATE just returns the input string with the current date and time in front of it.
 
If anyone are interested I can post the content of the FBs here, beware it is structured text written in Twincat 3.

Thanks for the post. As I said, it is interesting to see successful implementations on different platforms and with different programming styles.

That ST is quite different from any Structured Text that I have seen.

This code looks pretty close to C. I understand why there are pointers, but it makes me nervous messing with them. Does Twincat have bounds checking to prevent functions from accessing memory that is not 'owned' by the function?

I think that I understand the basics - mostly the Add functionality.

Do you run another process at a lower priority to dump the info to disk, so that it does not mess with your scan times?
 
This code looks pretty close to C. I understand why there are pointers, but it makes me nervous messing with them. Does Twincat have bounds checking to prevent functions from accessing memory that is not 'owned' by the function?

There are some checks, but bounds checking you have to do yourself, Twincat pointers.
I generally avoid pointers if I can, but sometimes they can help.
I would not use pointers for system critical things but for logging functionality they work fine.

Do you run another process at a lower priority to dump the info to disk, so that it does not mess with your scan times?

Nope, I just use the FIFO buffer to fill using the .ADD() action, that runs real time. When the buffer is filling up it starts writing to disk independent of the logging.
 
Last edited:

Similar Topics

What application in your experience has demanded the most complex PLC program? I suppose I should clarify what manner "complex" I'm asking about...
Replies
37
Views
12,175
Yes it's very legacy.. but sometimes it's necessary to use old stuff for fun.. and because it's so much cheaper. Crimson 3.0 had the ability to...
Replies
4
Views
1,573
Hello all, I am fairly new to programming HMI's and am working with Crimson 3.1 for the first time. I am trying to recreate an annunciator of...
Replies
12
Views
6,271
I have this complex code tag that returns as a string variable. For some reason it returns the two strings with a ?? between them -- not the...
Replies
2
Views
3,469
I have my L5K file imported into my project, and the tags I need in the "available addresses". Is there a way to access my PLC tags in a complex...
Replies
2
Views
2,910
Back
Top Bottom