This is part of 2.2.0-devlatest release: 2.1.0.
🚧 Development version – not released yet! 🚧

Log function

featured image
Part of the log data displayed in MS Excel
5 minutes  

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 index variable points to the most recent log entry
  • Once it reaches the end of the array, it will start over from the beginning
The log function is processed entirely on PLC side – there is no interaction with the robot controller.

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.

See MLxLogging for details on each function.

Library components

Data types
MLxLogDataMotoLogix command log data.
MLxLogEntryData for a MotoLogix command log entry.
Function blocks
MLxLoggingStore 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.

On some PLC platforms it is not possible to change the log buffer size yourself because it cannot recompile the know-how protected MLxLogging.

Usage

Each MotoLogix system needs its own log instance.
Multiple manipulators in the same MotoLogix system share the same log.

Create the (global) variables for the log data:

MLxLog : ARRAY [0..0] OF MLxLogData; // MotoLogix log data
This variable should be remanent (data not lost after a power off situation).
Adding more log instances is as easy as changing the array sizes.

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 MLxCommunicationWrite

Now verify if the logging works correctly:

MotoLogix commands being logged as expected
User data being logged as expected (If applicable)
No “flooding” of the log

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));

Displaying log data

We are currently investigating options for displaying the log data in a PC environment – more info will follow.

Pages built with Hugo - 17 Dec 2025 17:26 CET