LLExec Dev

From Axel Public Wiki
Jump to navigation Jump to search

Summary

The purpose of this page is to allow expert developers to extend the PLC runtime.

Prerequisites

To follow this guide you need:

  • A Windows PC with a running and licensed PLC runtime (LLExec >= 2.6.0 is suggested). You can download and install the runtime from site axelsw.it under the section Downloads, or directly from here
  • PluginHeaders SDK that you can download from here. IMPORTANT: always make sure that the version of LLExec matches the version of PluginHeaders!
  • Microsoft Visual Studio 2017

Setup your Windows machine

Create a directory and uncompress the PluginHeaders in it. Plugin headers expose the LLExec API to interface with the runtime.
Please note that every release of LLExec has its own plugin headers, so be sure that the release number of the headers matches with the release of LLExec installed. No backward compatibility is guaranted: compile your plugin for a specific release of LLExec.

Compile the plugin

Please open with Visual Studio 2017 the project

C:\plugin_headers_root\LLXPlugin_EXAMPLE\LLXPlugin_EXAMPLE.vcxproj

Please compile it. If you have done correctly all the previous steps, this will generate the file LLXPlugin_EXAMPLE.dll. Copy this file on the your Windows machine in the same directory where the runtime is installed, for example

C:\Program Files (x86)\Axel PC Tools\LLExec

Plugin concepts

LLExec is a modular runtime designed to run on top of (realtime) operating systems; we also have another type of runtime that can be embedded in the firmware of the board, but this is not the case.
Everything in the runtime is a plugin, the runtime itself is a plugin, while the core module (LLExec) is only responsible for load and link the modules together, implements communication with LogicLab and schedules all the tasks involved.
There are many plugins implementing various features like fieldbusses, local I/O and so on.
If you want to extend the runtime to manage your I/O, you have to implement and link your plugin.
The simplest way a plugin can expose its I/Os to the PLC is by means of data blocks, that are regions of memory, with a logical address, shared between the plugin and the PLC code.
You can also define functions that are invoked syncronously with the PLC tasks, and functions that will be exposed to the PLC and called from the PLC code, like standard IEC 61131 functions.
Once you have written your plugin, you can write a library, which can be included in your PLC program, to access the entities you have published in your plugin.
In the next chapters you will see the detail of the plugin EXAMPLE, and the details of the pll library that exposes the plugin features.
At last you have to modify LLExec configuration file LLExec.conf in order to run your plugin.

Plugin details

The plugin LLXPlugin_EXAMPLE is an example of the common features that can be implemented in a plugin.
The plugin is composed by several files:

  • Project files for many platforms.
  • LLXPlugin_EXAMPLE.cpp: this file contains all the interfaces with LLExec core, in here are defined data blocks and functions to be called.

The fastest way to implement a plugin is to copy and refactor one plugin already written, like this one.
Let's do a closer look to LLXPlugin_EXAMPLE.cpp:
the first section we want to look at is the following

static LLEXEC_DBDEF m_DataBlocks[] =
{
  // example datablock: it will be %MD5.0, 100 elements of 32bit each, read/write, no procImg, allocated by LLExec  
  { FALSE, DBTY_MEMO, 5, 0, 100, sizeof(int32_t),DBRW_RW, },
  // example datablock: c variable made visible to PLC, 10 elements of float, read-only
  { FALSE, DBTY_INP, 6, (uint32_t)&m_internalVar, 10, sizeof(float32_t), DBRW_R, },
};

This defines the two datablocks used to expose input and output to PLC.
the first field says that both data blocks don't have an automatic process image
the second field is the type of data block, that can be input, output or memory (DBTY_MEMO), this type is a part of the logical address
the third field is the index of the data block, which in turn is part of the logical address
the fourth field is the physical address of the data block, where zero means that the LLExec core must allocate automatically the memory at any address
the last fields, self explanatory, are the size in elements, the size of elements, and the read-write access.

Once we have defined the memory regions that we must read and write to perform I/O, we must define a synchronized function wich can do the data exchange between data blocks and actual hardware:

static void syncIO()
{
  // do data exchange here
}


This function have to be called every I/O cycle, before PLC execution. This is defined in the next section

static LLEXEC_FUNDEF m_Functions[] =
{
 // io_function: is called before every PLC cycle
 { LLFUNCT_IO,	(void *)syncIO,		"PLCsyncIO", },
 // example system Init function
 { LLFUNCT_INIT_L0,	(void *)initFunction,			"initFunction", },
 // example PLC function
 { LLFUNCT_PLC,		(void *)exampleFunction,		"exampleFunction", },
 // example function to be called every background PLC run
 { LLFUNCT_BACKGND,	(void *)backgroundFunction,		"PLCBackgroundFunction", },
};

This section defines the function that the plugin exposes to the runtime, and the way the runtime have to handle them. The behaviour is defined in the first field:

  • LLFUNCT_IO: is an I/O function, called every PLC cycle, just before PLC execution.
  • LLFUNCT_INIT_L0: is an initialization function, indeed the function initFunction asks the runtime the actual physical address of data blocks and initializes Spi communication.
  • LLFUNCT_PLC: defines a function that can be called from inside the PLC code
  • LLFUNCT_BACKGND: defines a function to be called every background PLC run

Configure runtime: LLExec.conf

Once you have build the plugin, copy it in the directoy C:\Program Files (x86)\Axel PC Tools\LLExec or where you have installed the runtime.
Now you have to edit the file LLExec.conf

<?xml version="1.0"?>
<llexecconfig overtime_check="0" defaultRetainMemoryService="MemoryBackUpFile">
 <runtimes>
  <runtime filename="LLXRt_x86_PLC.dll" area="0" targetId="LLExec_x86_2p0" targetComm="LLEXECPLC">
   <memory>
    
    <databit size="0x0"/>
    <dataret size="0x1000"/>
    
   </memory>
   <tasks>
    <task name="Fast" id="0" period="10000" type="io" programmableTaskTime="1"/>
    <task name="Slow" id="1" period="50000" type="cyclic" programmableTaskTime="1"/>
    <task name="Background" id="2" period="100000" type="background"/>
    <task name="Boot" id="3" period="1000000" type="boot"/>
    <task name="Init" id="4" period="1000000" type="init"/>
   </tasks>
  </runtime>
 </runtimes>
 <datablocks>
  <db img="no" type="input" id="0" elems="100" datasize="1" access="rw"/>
  <db img="no" type="output" id="0" elems="100" datasize="1" access="rw"/>
  <db img="no" type="memo"  id="1" elems="10000" datasize="1" access="rw" resetOnRestart="yes"/>
  <db img="no" type="memo"  id="2" elems="1000" datasize="1" access="rw" retain="yes" resetOnRestart="yes"/>
  <db img="no" type="memo"  id="60400" elems="1000" datasize="1" access="rw" />
  <db img="no" type="memo"  id="60401" elems="1000" datasize="1" access="rw" retain="yes"/>
  <db img="no" type="memo" id="100" elems="20000" datasize="1" access="r"/>  
  <db img="no" type="memo" id="101" elems="20000" datasize="1" access="rw"/> 
  <db img="no" type="memo" id="200" elems="65536" datasize="1" access="rw" resetOnRestart="yes"/>
  <db img="no" type="memo" id="201" elems="65536" datasize="1" access="rw" resetOnRestart="yes" retain="yes"/>
 </datablocks>
 <plugins>
  <plugin filename="LLXPlugin_MemoryBackUpFile.dll"/>
  <plugin filename="LLModbusTCP.dll"/>
  <plugin filename="LLModbusRTU.dll"/>
  <plugin filename="LLCANOpen.dll"/> 
  <plugin filename="LLXPlugin_Database.dll"/>
  <plugin filename="LLModbusSlave.dll"/>
  <plugin filename="LLXPlugin_FileSystem.dll"/>
  <plugin filename="LLXPluginSoftScope.dll"/>
  <plugin filename="LLXPlugin_Serial.dll"/>
  <plugin filename="LLXPlugin_Alarms.dll"/>
  <plugin filename="LLXPlugin_Recipes.dll"/>
  <plugin filename="LLXPlugin_LLSymbols.dll"/>
  <plugin filename="LLXPlugin_EXAMPLE.dll"/>
 </plugins>
</llexecconfig>


This is the configuration file of LLExec, you can see our modification in the section plugins:

  • we have inserted the plugin LLXPlugin_EXAMPLE.dll in order to load it at startup.

Now you can run the LLExec process.

Edit pll library

Here (EXAMPLE.pll) you can find the file named EXAMPLE.pll which was generated from LogicLab, but is a simple text file, and is a type of library that can be linked in a LogicLab project.
A better way to generate a library is to use the automatic library generation in LogicLab, please consult the chapter "CUSTOM WORKSPACE OPERATIONS" in LogicLab manual.
The library has two sections

VAR_GLOBAL
 {G:"Ungrouped_vars"}
 EXAMPLE_ARRAY1 AT %MD5.0 : ARRAY[ 0..99 ] OF DINT;
 EXAMPLE_ARRAY2 AT %ID6.0 : ARRAY[ 0..9 ] OF REAL;
END_VAR

in this first section the two data blocks that we have exposed in the plugin are defined.
After AT we have the IEC 61131 syntax of logical memory addresses, for example %MD5.0 means:

  • M => Memory (type of data block), we also have I for input and Q for outputs
  • D => Doubleword (size of data block element)
  • 5. => The previously defined data block index
  • 0 => The offset of the first element of array



The second section shows how we can define embedded function that can be called from inside the PLC code:

FUNCTION exampleFunction : DINT
 VAR_INPUT
  par : DINT;
 END_VAR
 {CODE:EMBEDDED}
END_FUNCTION

Note that in IEC 61131 standard every function must have one or more input parameters, and a return value.

Access from LogicLab

Now that we have a PLC runtime with the EXAMPLE plugin running, we can test it with LogicLab. Download the last Automation Suite from the Axel website, download section and install it on your PC.
Open LogicLab, choose "New project", give it a name, and select target LLExec PLC runtime (x86) 2.0.

Open the dialog from the menu On-line==>Set up communication...
Select GDB protocol and press the Properties button
In the new window insert the IP address of the PC running LLExec (ie 127.0.0.1) and make shure the port is set to 5000.

Now you have to connect to your LLExec before any other operation: this because LogicLab must update his knowledge about the target, that is changed because of the new plugin. LogicLab will store the new configuration in the project file.
To connect select from the menu On-line==>Connect.

Select from menu Project==>Library manager
Press button Add then select the EXAMPLE.pll library from the filesystem.

Now, from the project, you can write data and read data from the two arrays EXAMPLE_ARRAY1 and EXAMPLE_ARRAY2.
You can also call the function exampleFunction from a PLC Program, ie in ST language.

Further readings

For more information about the use of LogicLab you can refer to the User manual, you can read it from the Help menu.