Skip to content

Dual Output

We can modify the Cylinder component to use two digital outputs for controlling a double-acting pneumatic cylinder. This example will demonstrate how to create a reusable Cylinder component that can be easily integrated into various projects. The cylinder will have separate outputs for extending and retracting.

Step 1: Define the Interface

Create an interface I_Cylinder that defines the methods for the Cylinder component. Note that this interface remains the same as in the single output example.

1
2
3
4
INTERFACE I_Cylinder
    METHOD Extend
    METHOD Retract
END_INTERFACE

Step 2: Create the Cylinder Component

Create a function block CylinderDualOutput that implements the I_Cylinder interface. This component will depend on two Digital Output components through the I_DigitalOutput interface, adhering to the Dependency Inversion Principle (DIP).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
FUNCTION_BLOCK CylinderDualOutput IMPLEMENTS I_Cylinder
VAR
    ExtendOutput  : I_DigitalOutput; // Dependency Injection of Digital Output component for extending
    RetractOutput : I_DigitalOutput; // Dependency Injection of Digital Output component for retracting
END_VAR

METHOD FB_Init : BOOL
    VAR_INPUT
        bInitRetains  : BOOL; // TRUE: the retain variables are initialized (reset warm / reset cold)
        bInCopyCode   : BOOL; // TRUE: the instance will be copied to the copy code afterward (online change)   
        ExtendOutput  : I_DigitalOutput;
        RetractOutput : I_DigitalOutput;
    END_VAR

    THIS^.ExtendOutput  := ExtendOutput;
    THIS^.RetractOutput := RetractOutput;
END_METHOD

METHOD Extend
    ExtendOutput.On  := TRUE;    
    RetractOutput.On := FALSE; // Ensure retract output is off
END_METHOD

METHOD Retract
    RetractOutput.On := TRUE;
    ExtendOutput.On  := FALSE; // Ensure extend output is off
END_METHOD

END_FUNCTION_BLOCK

Step 3 : Using a Null Object for the Digital Output Dependencies

To adhere to the Dependency Inversion Principle (DIP) and avoid null reference issues, we can create Null Object implementations of the I_DigitalOutput interface. These Null Objects will provide default behavior when no actual digital outputs are injected. In the CylinderDualOutput function block declare the Null Object variables for the digital output dependencies:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
FUNCTION_BLOCK CylinderDualOutput IMPLEMENTS I_Cylinder
VAR
    ExtendOutput  : I_DigitalOutput; // Dependency Injection of Digital Output component for extending
    RetractOutput : I_DigitalOutput; // Dependency Injection of Digital Output component for retracting

    // Null Objects for Digital Output dependencies
    NullEO            : BOOL;
    NullExtendOutput  : DigitalOutput(Output := NullEO);
    NullRO            : BOOL;
    NullRetractOutput : DigitalOutput(Output := NullRO);
END_VAR
Then, in the FB_Init method, check if the ExtendOutput and RetractOutput inputs are provided. If not, assign the respective Null Objects to the references.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
METHOD FB_Init : BOOL
    IF ExtendOutput = 0 THEN
        THIS^.ExtendOutput := NullExtendOutput;
    ELSE
        THIS^.ExtendOutput := ExtendOutput;
    END_IF
    IF RetractOutput = 0 THEN
        THIS^.RetractOutput := NullRetractOutput;
    ELSE
        THIS^.RetractOutput := RetractOutput;
    END_IF

END_METHOD

Step 4: Using the CylinderDualOutput Component

To use the CylinderDualOutput component, create instances of it in your main program or another function block. Initialize it with actual digital output variables for extending and retracting the cylinder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PROGRAM MAIN
VAR
    ExtendOutputVar at %Q*  : BOOL; // Must be declared before injected into the component
    RetractOutputVar at %Q* : BOOL; // Must be declared before injected into the component
    ExtendOutput            : DigitalOutput(Output := ExtendOutputVar);
    RetractOutput           : DigitalOutput(Output := RetractOutputVar);
    Cylinder                : CylinderDualOutput(ExtendOutput := ExtendOutput, RetractOutput := RetractOutput);

    ExtendCylinder   : BOOL;
    RetractCylinder  : BOOL;
END_VAR

// Extend the cylinder
IF ExtendCylinder THEN
    ExtendCylinder  := FALSE;
    Cylinder.Extend();
END_IF

// Retract the cylinder
IF RetractCylinder THEN
    RetractCylinder := FALSE;
    Cylinder.Retract();
END_IF

END_PROGRAM

Conclusion

The CylinderDualOutput component provides a reusable and maintainable way to control a double-acting pneumatic cylinder using two digital outputs. By adhering to the Dependency Inversion Principle (DIP) and utilizing Null Object patterns, we ensure that the component is robust and can be easily integrated into various projects without null reference issues.