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