Store relevant data of each issued MotoLogix command in a log buffer.
About
MotoLogix log data can be helpful for both troubleshooting and optimizing your PLC code. It gives you a better understanding of:
- In which order commands were sent to the robot controller
- What parameter values were used
- The timing of commands
- The relation between commands and the MotoLogix SystemState
- The fill levels of the MotoLogix command buffer
User data
The log function also supports adding your own variables through the
user data input.
This can be used to analyze the MotoLogix commands in relation to other
program parts.
Mechanism
Log data is stored at the event of sending the command data to the robot controller. It logs every MotoLogix command which interacts with the robot controller – buffered and parallel commands.
The logging is using a ring buffer mechanism. This means:
- Only one entry is being written at a time
- There is no shifting of data
- The
indexvariable points to the most recent log entry - Once it reaches the end of the array, it will start over from the beginning
For certain MotoLogix commands the logging is either disabled or limited. This due to the fact that these functions would quickly “flood” the log, overwriting other (more relevant) log entries.
An example of this is the MLxGetMessageDetail function, which is
typically used in a polling scenario (e.g. issued every n seconds).
Another example is the jog function, which creates a flow of commands as long as a jog button is pressed.
MLxLogging for details on each function.Library components
| Data types | |
|---|---|
MLxLogData | MotoLogix command log data. |
MLxLogEntry | Data for a MotoLogix command log entry. |
| Function blocks | |
|---|---|
MLxLogging | Store MotoLogix command log data. |
Log size
Only a limited set of parameter data is stored per command.
See MLxLogging for details on which data is stored for
each command.
We have set the log buffer size in MLxLogData to 50 entries,
resulting in a total size of approx. 2300 byte.
Depending on the use case and the amount of free memory in the PLC,
you might want to change this size.
MLxLogging.Usage
Create the (global) variables for the log data:
MLxLog : ARRAY [0..0] OF MLxLogData; // MotoLogix log data
Create the instances:
fbLogging : ARRAY[0..0] OF MLxLogging;
Map the optional user data, for example:
fbLogging[0].UserData[0] := PosTable.LoadIndex;
fbLogging[0].UserData[1] := someProgram.state;
Call the instances in a loop:
// function call (place this just before MLxCommunicationWrite)
FOR i := 0 TO 0 DO
fbLogging[i](
TimeNow := now,
LogData := MLxLog[i],
MLX := MLX[i]);
END_FOR;
MLxLogging has to be called just before MLxCommunicationWriteNow verify if the logging works correctly:
Make sure to test exceptional situations such as pendant operation. The last thing you want is that precious log data gets overwritten as it being flooded with irrelevant commands.
PLC system time
The log function needs the system time in milliseconds since midnight. Below some examples for getting this data on the various PLC platforms.
B&R
VAR
fbDTStructureGetTime : DTStructureGetTime;
systemTime : DTStructure;
now : DINT; (*current time [ms since midnight]*)
END_VAR
// get the plc system time
fbDTStructureGetTime(enable := TRUE, pDTStructure := ADR(systemTime));
now := UINT_TO_DINT(systemTime.millisec)
+ (systemTime.second * 1000) +
+ (systemTime.minute * 60 * 1000) +
+ (systemTime.hour * 60 * 60 * 1000);
Beckhoff
VAR
fbLocalSystemTime : FB_LocalSystemTime;
now : DINT; // current time [ms since midnight]
END_VAR
// get the plc system time
fbLocalSystemTime(bEnable := TRUE);
now := WORD_TO_DINT(fbLocalSystemTime.systemTime.wMilliseconds)
+ (fbLocalSystemTime.systemTime.wSecond * 1000)
+ (fbLocalSystemTime.systemTime.wMinute * 60 * 1000)
+ (fbLocalSystemTime.systemTime.wHour * 60 * 60 * 1000);
Codesys
VAR_TEMP
result : ULINT;
systemTimeUtc : ULINT;
systemTimeLocal : ULINT;
std : SysTimeDate;
now : DINT; // current time [ms since midnight]
END_VAR
// get the plc system time
result := SysTimeRtcHighResGet(systemTimeUtc);
result := SysTimeRtcConvertHighResToLocal(systemTimeUtc, std);
result := SysTimeRtcConvertDateToHighRes(std, systemTimeLocal);
now := TO_DINT(systemTimeLocal MOD TO_ULINT(T#1D));
Rockwell
VAR_TEMP
systemTime : ARRAY [0..6] OF DINT;
now : DINT; // current time [ms since midnight]
END_VAR
// get the plc system time
GSV(WallClockTime, ,LocalDateTime, systemTime[0]);
now := (systemTime[3] * 60 * 60 * 1000) // hour
+ (systemTime[4] * 60 * 1000) // minute
+ (systemTime[5] * 1000) // second
+ (systemTime[6] / 1000); // microsecond
S7-1500
VAR_TEMP
result : INT;
systemTime : DTL;
now : DINT; // current time [ms since midnight]
END_VAR
// get the plc system time
result := RD_LOC_T(OUT => systemTime);
now := TOD_TO_DINT(DTL_TO_TOD(IN := systemTime));
