Commands

Executing Commands without a Response

This model applies when a module needs to send a command to another module, and does not need to receive a response or confirmation. It’s a one-way channel. Here we have a motor subsystem that subscribes to a global PowerAllOn command.

Subsystem

The subsystem is responsible for running the command. It subscribes to the command that it implements. The motor.cmd.PowerOn boolean will be set when a command is executed. Response from the subscribing system is not required.


// MotorManager

PROGRAM _INIT

    //Register a subsystems command to a global command
    //  Here: 
    //  - The global command is "PowerAllOn"
    //  - The subsystem is Axis
    //  - The command bit is motor.cmd.powerOn

    //Subscribe a single command to two different global events   
    subscribeCommand( gCommands.PowerAllOn, 'Axis', motor.cmd.powerOn );
    subscribeCommand( gCommands.PowerAxesOn, 'Axis', motor.cmd.powerOn );

    //Subscribe a second command to a different event
    subscribeCommand( gCommands.HomeAllOn, 'Axis', motor.cmd.home );

END_PROGRAM


PROGRAM _CYCLIC
    
    //Implement Commands
    IF motor.cmd.powerOn THEN

        powerOnFub.Enable := TRUE;

    IF motor.cmd.powerOff THEN

        powerOnFub.Enable := FALSE;

    ELSIF motor.cmd.home THEN

        home.execute := TRUE;

    END_IF

    powerOnFub();
    home();

    home.execute := 0;

    //Reset command
    motor.cmd.powerOn := 0;
    motor.cmd.powerOff := 0;

END_PROGRAM

Executer

A process controller can execute a command that many listeners can listen for.


PROGRAM _CYCLIC

    IF powerAll THEN
        
        powerAll := 0;

        executeCommand( gCommands.PowerAllOn );

    END_IF

END_PROGRAM

Executing Commands with a Response

This model is useful when the executer needs a status response for the command that it issues. This type of command/response communication is modeled after the PLCOpen FUBs: the executer sets an execute bit, and the subsystem responds with one of several status bits: busy, done, or error. Here the same motor subsystem is used as above.

Subsystem

The subsystem is responsible for running the command. It subscribes to the command that it implements. The motor.cmd.powerOn boolean will be set when a command is executed. Response from the subscribing system is required.

The motor.PLCOpen parameter is of type AtnPlcOpenStatus, and is used in code to report back on the status of the command.


// MotorManager

PROGRAM _INIT

    //Register a subsystems command to a global command
    //  Here: 
    //  - The global command is "PowerAllOn"
    //  - The subsystem is Axis
    //  - The command bit is gMotor.cmd.powerOn
    //  - The plcopen status is returned using motor.PLCOpen.status

    //Subscibe to an axis Command
    subscribePLCOpen( gCommands.PowerAllOn, 'Axis', motor.cmd.powerOn, motor.PLCOpen );

END_PROGRAM


PROGRAM _CYCLIC
    
    //Implement Command
    IF motor.cmd.powerOn THEN

        state := POWERON;

    END_IF

    CASE state OF

        IDLE:
            //Do nothing

        POWERON:

            powerOnFub.enable := TRUE;

            IF powerOnFub.isPowered THEN

                state := IDLE;
                
                motor.PLCOpen.status := ERR_OK;

            ELSIF powerOnFub.Error THEN

                state := IDLE;

                powerOnFub.enable := FALSE;
                
                motor.PLCOpen.status := MOTOR_ERROR;

            END_IF

    END_CASE

    powerOnFub();

END_PROGRAM

Executer

A process controller can execute a command that many listeners can listen for. With the PLCOpen format, the executer can listen for a response from the subsystem.

The PLCOpenCommand is of type AtnPLCOpen. PLCOpenCommand.command specifies the string that represents the command to execute, and then the FUB can be called cyclically as any other PLCOpen FUB would be called. When its .execute is set, ATN sends the command to the subsystem, and the executer can monitor the status via PLCOpenCommand.busy, PLCOpenCommand.done, and PLCOpenCommand.error.


PROGRAM _CYCLIC

    CASE state OF

        IDLE:
            //Do nothing

        SEQ_POWERALL:


            IF PLCOpenCommand.done THEN

                state := SEQ_NEXT_STEP;

            ELSIF PLCOpenCommand.error OR PLCOpenCommand.aborted THEN

                state := IDLE;

               RaiseError( 'Error Powering on' );

            ELSIF NOT PLCOpenCommand.busy THEN

                PLCOpenCommand.Execute := TRUE;

            END_IF

        SEQ_NEXT_STEP:

            ..

    END_CASE

    PLCOpenCommand.command :=   gCommands.PowerAllOn;
    PLCOpenCommand();
    PLCOpenCommand.Execute :=   FALSE;


END_PROGRAM