States

Service Provider Model

Service Provider

A service provider is a system that has a function that other systems can request to be active.

In this example a cooling system can be turned on by any client requesting cooling. A service provider can cyclically check if anything is requesting to turn on cooling.

//Implement Cooling system (service provider)

PROGRAM _CYCLIC

	coolingRequired := stateAnyTrue( gStates.coolingRequired, FALSE );

    IF coolingRequired THEN
        
        //Turn on cooling

    ELSE

        //Turn off cooling

    END_IF

END_PROGRAM

The function call stateAnyTrue checks if any module has published a value of TRUE to the gStates.coolingRequired state. It essentially acts as an ‘OR’ across all modules publishing to that state. There are three other variants of this function that work the same way, but accomplish slightly different purposes:

  • stateAnyFalse: returns TRUE if any module is publishing a FALSE value to the state.
  • stateAllTrue: returns TRUE if all modules registered to this state are publishing a TRUE value.
  • stateAllFalse: returns TRUE if all modules registered to this state are publishing a FALSE value.

Client

Here the motor system is acting as a client to the cooling system.

Any number of clients can register a status for the service provider to check.

//Implement Cooling requests

PROGRAM _INIT

    //Register a subsystems status bits to a service provider.
    //  Here: 
    //  - the service is "coolingRequired"
    //  - the client is Motor System
    //  - the status bits are motorSystem.powerOn and motorSystem.tooHot

    //Directly use a state    
	registerStateBool( gStates.coolingRequired, 'Motor System', motorSystem.status.powerOn);

    //Calculated state
	registerStateBool( gStates.coolingRequired, 'Motor System', motorSystem.status.tooHot);
	
END_PROGRAM

PROGRAM _CYCLIC

    //Calculate a State
	motorSystem.status.tooHot := motorSystem.temperature > Configuration.MaxTemp;

END_PROGRAM

Subsystem Hierarchy Model

The following is an example of a subsystem A task that might have dependencies on other subsystem tasks (subsystems B and C).

The primary task can check if all the dependencies are ready, and get a status from them if not.

Subsystem A

//Subsystem A
//Implement "ThisProcess" Axis Process control
PROGRAM _CYCLIC

    //Check if all the subsystems are ready for the process
	localInterface.internal.ready = stateAllTrue( gThisProcess.status.subSystemsReady, TRUE );

    //Get a report string from all the subsystems that are not ready
    IF ( localInterface.internal.ready ) THEN

        stateStatus( gThisProcess.status.subSystemsReady, 
            ADR(localinterface.internal.subSystemStatus), 
            SIZEOF(localinterface.internal.subSystemStatus) );	

    END_IF

END_PROGRAM

localinterface.internal.subSystemStatus is an array of STRINGs, and will be populated with the status of each subsystem that has registered with this state when the stateStatus function is called. In this case, subsystem B is registering as Axis 1 Home and Axis 1 Power with this state, while subsystem C is registering as Pneumatic enabled and Pneumatic exists with this state. This will lead to the following values for the array when viewed from a Watch window:

  • subSystemStatus[0]: “inactive - Axis 1 Home”
  • subSystemStatus[1]: “inactive - Axis 1 Power”
  • subSystemStatus[2]: “inactive - Pneumatic enabled”
  • subSystemStatus[3]: “inactive - Pneumatic exists”

The inactive STRING changes to active when the corresponding subsystem sets it state bit to TRUE. Alternately, you can call the stateTrueStatus and stateFalseStatus functions, and the array of STRINGs will simply show the registered modules that are currently in that state (either TRUE or FALSE).

Subsystems B & C

Here are two examples of tasks that the main process depends on. They register to the state that needs to include them and update their own status normally.

//Subsystem B
//Implement Axis Basic axis that "gThisProcess" depends on

PROGRAM _INIT

    //Register some states from this axis to the ready of the controller component
	registerStateBool( gThisProcess.status.subSystemsReady, 
        'Axis 1 Home', 
        localinterface.internal.isHomed );
	registerStateBool( gThisProcess.status.subSystemsReady, 
        'Axis 1 Power', 
        localinterface.internal.isPowered );

END_INIT
 
PROGRAM _CYCLIC

    //Populate the status normally
    localinterface.internal.isHomed := axisBasicFub.isHome;
    localinterface.internal.isPowered := axisBasicFub.isPowered;

END_PROGRAM
//Subsystem C
//Implement a second subsystem that gThisProcess relies on
PROGRAM _INIT

    //Register some states from this axis to the ready of the controller component
	registerStateBool( gThisProcess.status.subSystemsReady, 
        'Pneumatic enabled', 
        localinterface.internal.hasAir );

	registerStateBool( gThisProcess.status.subSystemsReady, 
        'Pneumatic exists', 
        IO.ModuleOK );

END_INIT

PROGRAM _CYCLIC

    localinterface.internal.hasAir := (IO.Pressure > MIN_AIR_PRESSURE);

END_PROGRAM

Inhibits Model

The following is an example of an Inhibit Model. Here a subsystem has some commands that can be locked out by other systems. It doesn’t need to know what is configured or why it is inhibited. It checks if any of the registered modules have an active value for its inhibit. If it is inhibited, it can easily tell the user what inhibited it by getting a stateTrueStatus message and sending it to the user.

The isInhibited function simply acts as a light wrapper around the stateAnyTrue function, and returns TRUE if any module has published a value of TRUE to the inhibit state.


//Implement "ThisProcess" Process control that can be inhibited by other systems
PROGRAM _CYCLIC

        //Check if there is a command
		IF localinterface.command.executeMotionCommand THEN

            //Check if this command is inhibited            
			IF isInhibited( gThisProcess.inhibit.allMotion) THEN

                //If there are inhibits, tell other systems by setting an error status
				localinterface.internal.PLCOpen.status := ERR_INHIBITIED;
				
                //Get a message to post to the user
				stateTrueStatus(  gThisProcess.inhibit.allMotion, 
                    ADR(localinterface.internal.inhibitMessage), 
                    SIZEOF(localinterface.internal.inhibitMessage));

                //Post the message
                AlarmSet( gThisProcess.alarms.motionInhibit, 
                    localinterface.internal.inhibitMessage )

            ELSE

                //If there are no inhibits, GREAT! do it
				localinterface.internal.state := STATE_EXECUTE_MOTION_COMMAND;

			END_IF
    
        END_IF

        ...

END_PROGRAM       
PROGRAM _INIT

    //Register the pusher out status to inhibit motion on "ThisProcess"
    //  While it is not "In"
    registerStateBool( gThisProcess.alarms.motionInhibit, 
        'Pusher', 
        localInterface.status.actuatorOut );

END_PROGRAM

//Implement "Pusher" Process control that needs "gThisProcess" to not move while the actuator is out
PROGRAM _CYCLIC


        localInterface.status.actuatorOut := NOT IO.diActuatorIn;

        ...

END_PROGRAM