Skip to content

What is the SPT Framework?

The SPT framework utilizes the PackML libraries in order to allow for a defined set of states in which the control code can be organized. A key element of developing machine control is ensuring that the correct commands are given at the proper time and coordinated with the other parts of the machine. It is also important to ensure that commands are not given at an incorrect time.

Libraries

The functionality of PackML state machine requires the Tc3_PackML_V2 library. The SPT Framework is built on top of this library to aid in rapid code development. The 4 SPT libraries are SPT Base Types, SPT Components, SPT Event Logger, and SPT Utilities. The SPT Base Types library contains the core of the SPT Framework, the function blocks contained within will be extended to allow the machine code to interact with the PackML state machine. The SPT Components library holds some prewritten components for various sensors and actuators, including servos. The SPT Utilities library contains small pieces of code that are often repeated in the development of larger projects; for example: Analog Scaling, Data Buffers, and Comparators. Lastly the SPT Event Logger is a wrapper around the Tc3_EventLogger that is tailored to be used within the SPT Framework.

Cyclic Logic vs Single Method Calls

The framework uses Methods to call specific pieces of the code, such M_ExtendCylider or M_ResetAxis. These methods can be called once as needed, or multiple times until the requested command has completed or faulted. Additionally, the framework uses methods to call commands during specific states. For example, any code that needs to be executed while a Module is in the Resetting state can be placed in the Resetting method and it will be called at the proper time. The framework also makes use of Cyclic method calls to update the parts of the code that are responsible for monitoring changes, such as alarms, faults, and state changes made by other methods. These also provide a way to cyclically call and update all of the Modules and Components that have been registered to a higher-level module.

Does a Component need an EM?

So far, the hierarchy has been explained as Machine Module, Equipment Module, and Component. A Machine is made up of one or more pieces of Equipment, and Equipment is made up of Components. But it could be that a Machine itself has a component, such as a stack light, that indicates the overall status of the machine. Therefore, it is important to be aware that an Equipment module is not always required, and a Machine Module can have a direct connection to a Component.

What is a Submodule?

The PackML standard defines only Machine, Equipment, and Component. However there has occasionally been a need or a request to organize machines with multiple levels of Equipment Modules. To accommodate this the SPT Framework uses the term Submodule instead of Equipment Module. This allows for some flexibility in the layout of the architecture so that a Machine can have one or more Submodule (and 0 or more Components), and then a Submodule can also have one or more Submodules and 0 or more Components. In theory there is no end to the number levels that can be obtained with this architecture. However, the use of more than 3 levels (MM, EM, and Component) is highly discouraged and should only be used when having a Submodule of a Submodule truly is the best option.

Inheriting the base classes

When creating a Module or Component they must inherit the functionality of the base classes in the SPT Framework libraries. FB_PackML_BaseModule will be inherited by the Machine Module and all submodules. FB_ComponentBase will be inherited by all components. This will allow all of the modules and components to have everything they need to interact with the PackML state machine and the SPT Framework.

Registering an EM or Component with its higher-level Module

In order for the Cyclic updates to happen properly, a SubModule or Component must be registered with its higher-level Machine Module, or Submodule. The Variable name of each, that is declared must be passed into an array that will initialize it with the module.

Creating the array of interfaces for submodules and components

Registering Modules and Components is done using an array of interfaces. Within each Machine and Submodule there will exist the declaration of the SubModules and Components of that Module. Each of these instance variables will be used as the initial value of an index within the array. For example, this Machine Module contains three Equipment Modules referred to as SubModules in the code.

1
2
3
4
5
6
7
FUNCTION_BLOCK FB_Machine EXTENDS FB_PackML_BaseModule
// Sub Modules
PullWheels : FB_PullWheel;
Sealer : FB_Sealer;
Unwind : FB_Unwind;
//Initial List of SubModules
ipSubModules_Init : ARRAY[1..Parameters_PackML_Base.MAX_NO_OF_SUBMODULES] OF I_PackML_BaseModule := [PullWheels, Sealer, Unwind];

The three modules (PullWheels, Sealer, and Unwind) are all function blocks that extend the FB_PackML_BaseModule which Implements the I_PackML_BaseModule; therefore, they can be passed into the array of I_PackML_BaseModule as initial values. Understanding how this works internally is not important, what is important is remembering to place each instance variable name in the array initialization. When creating components, the same process is used, the only difference is that the components Extend FB_ComponentBase which Implements I_ComponentBase and the array is of type I_ComponentBase.

1
2
3
FUNCTION_BLOCK FB_PullWheel EXTENDS FB_PackML_BaseModule
LeftPullWheel : FB_Component_BasicSlaveAxis := (Name := 'Left');
ipComponents_Init : ARRAY[1..Parameters_PackML_Base.MAX_NO_OF_COMPONENTS] OF I_ComponentBase := [LeftPullWheel];

Overriding methods of the base class

The SPT Framework contains a complete set of function blocks to run the state machine. FB_PackML_BaseModule has a method for each of the PackML states. Each of these methods has the code needed progress the state machine. When adding a new Equipment module that will extend FB_PackML_BaseModule, call the same methods with their unique code and call the method in the base using SUPER^.Method() or SUPER^.Resetting within your Resetting method. The use of SUPER will call the method of the base class and run the code that exists there. The typical implementation would call SUPER at the end of the method in the EM. If a method is not needed by an Equipment Module, then there is no need for the EM to call that method. At the Machine module level unless you need to override the method, DON’T. Just let the CyclicLogic method handle it. Additionally, while it is common to override the CyclicLogic method when inheriting from a library in the base class, it is not required to do so. If you do not override the CyclicLogic method the underlying code for the method will be executed.

When to call StateComplete() vs Super^.Method()

//This section needs work, maybe an example

The Acting states are exited when the StateComplete method is called. This is done in base class for you when you call SUPER^.Method(). It could be that you wish to call the StateComplete method from the EM, and that can also be done. The waiting states are exited when a change state command is given, for example ipModule.ChangeState(Tc3_PackML_V2.E_PMLCommand.ePMLCommand_Stop) or ChangeState(E_PMLCommand.ePMLCommand_Unsuspend)

If you are not sure, don't call StateComplete(). Call Super^.Method() after your code and let the base class handle it.

Initializing

Each Machine and Equipment module has a CyclicLogic method, and that method should call the method of the base class using Super^.CyclicLogic(). An Initialize method is also required. The first lines of code in the CyclicLogic method are to call to the Initialize method if it has not completed.

1
METHOD PUBLIC Cyclic Logic
1
2
3
4
IF NOT _InitComplete THEN
    _InitComplete := Initialize();
    RETURN;
END_IF

The Initialize method is used to register the submodules and components to the internals of the framework. A CASE statement is used to ensure that everything happens in the proper order.

1
METHOD PROTECTED Initialize
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Initialize := FALSE;
CASE DescendantSequenceState OF
    0:
        // Define the interfaces to the different subunits (Must take place before SUPER^.initialize call)
        FOR i := 1 TO SPT_BaseTypes.Parameters_PackML_Base.MAX_NO_OF_SUBMODULES DO
            ipSubModules[i] := ipSubModules_Init[i];
        END_FOR

        FOR i := 1 TO SPT_BaseTypes.Parameters_PackML_Base.MAX_NO_OF_COMPONENTS DO
            ipComponents[i] := ipComponents_Init[i];
        END_FOR

        _LogModeChanges         := FALSE;
        _LogStateChanges        := FALSE;
        DescendantSequenceState := DescendantSequenceState + 10;

The SUPER^.Initialize method must also be called as the last step of the CASE statement.

1
2
3
4
5
6
7
8
    30:
        //Call the Super
        IF SUPER^.Initialize() THEN
            DescendantSequenceState := DescendantSequenceState + 10;
        END_IF
    40:
        Initialize := TRUE;
END_CASE

What is a Control Source

A control source is used to provide a connection from outside of the PLC code to the State Machine. This could be through physical buttons, an HMI, or an OPC connection to another machine. The base library (SPT Base Types) contains an instance of FB_ControlSource. This function block can be overridden as needed. The most important part for implementation is to provide code for the Cyclic Logic method. The Cyclic Logic method contains 3 major parts.

  1. The R_TRIG that captures the incoming command.
  2. The monitoring of the permissives that will allow or prevent a state change based on the current or other machine conditions.
  3. The monitoring of the commands and permissives to call the ChangeState method with the proper state.

Registering the Control Source with the Machine Module

The Initialization method of the Main POU needs to register a control source with the Machine Module. In the below example ControlSource_HMI is the instance name of the control source function block, and VFFSDemo is the instance name of the Machine Module function block.

1
2
    ControlSource_HMI : FB_ControlSource;
    VFFSDemo          : FB_Machine := (Name := 'VFFS Demo');
1
2
3
    IF NOT ControlSource_HMI.Registered THEN
        VFFSDemo.RegisterExternalController(ControlSource_HMI);
    END_IF

Testing the code and the state machine

At this point it should be possible to Activate Configuration, Restart TwinCAT in Run Mode, and test the code for proper functionality.

Once the PLC is running you can login and check a few online values. Within MAIN, expand the declaration of your FB_Machine and check the values of the following variables:

1
2
3
4
5
_CurrentMode        ePMLUnitMode_Production
_CurrentState       ePMLState_Aborted
NumberOfSubModules  //This value should be equal to the number of Submodules declared within your instance of FB_Machine. If not then check the declaration of the ipSubModules_Init ARRAY
NumberOfComponents  //This value should be equal to the number of Components declared within your instance of FB_Machine. If not then check the declaration of the ipComponents_Init ARRAY
InitComplete        TRUE
Once those values have been confirmed you can use your Control Source the test the state machine.

Adding Parameters / Recipe Values

Parameters and Recipe values can be declared and passed to SubModules or Components through the use of Properties. Starting with a local variable of a Component that is read adn written by a Property, the Component can then use the local variable name as if it was the actual value. When a SubModule creates an instance of the Component the SubModule is then responsible for setting the value of the Property in the Component.

Adding more Equipment Modules (SubModules)

1
2
3
4
5
6
7
Create a new function block that Extends FB_PackML_BaseModule
Add the Local variables and Properties needed for parameters
Add the Methods needed for the Acting and Waiting states
Add the Cyclic Logic method if needed
Add the Initialize Method
Within the EM declare any Components and add them to the  ipComponents_Init ARRAY
Within MAIN, declare an instance of the new EM and add it to the ipSubModules_Init ARRAY

Creating Components

1
2
3
4
5
6
Create a new function block that Extends FB_ComponentBase
Add the Local variables and Properties needed for parameters
Add methods as required to perform the function of the component
Add the Cyclic Logic method if needed
Add and events or alarm logic
Add the Component to a SubModule, register it, and implement it within the SubModule, including ant parameters that need to be passed to it.

Additional Control Sources

Coming Soon...