Input Belt State Machine Design
We have already defined the sequence diagrams for the Helix Router. This
article will focus on designing the C++ classes to implement the design defined
by the sequence diagrams. We will step through the development process of one of
the most complicated classes in the system, the InputConveyorBelt.
The design process is divided into three distinct steps:
Here is the state machine design derived from the Helix
Router Sequence Diagrams we developed earlier:

Based on the above design we have defined a state hierarchy. The states have
been classified into Inservice and OutOfService base states.

The source code for the Input Conveyor Belt is based almost completely the
sequence diagrams. The Input Conveyor Belt has been modeled as a hierarchical
state machine.
| Input
Conveyor Belt Header File |
class InputConveyorBelt : public ConveyorBelt
{
. . .
MagneticSensor *m_pMagneticSensor;
Package *m_pCurrentPackage;
// State description
class BaseState
{
public:
// Dummy handlers
void HandleSensorInterrupt(InputConveyorBelt& m) {}
void HandleAcknowledgePackage(InputConveyorBelt &m,
const AcknowledgePackageMsg *pMsg) {}
void HandleSensorTimeout(InputConveyorBelt &m) {}
void OperatorInservice(InputConveyorBelt &m) {}
void ConfigureBelt(InputConveyorBelt &m, const ConfigMsg *pMsg) {}
// Handlers implementing functionality
void UnConfigureBelt(InputConveyorBelt &m, const UnConfigMsg *pMsg);
};
class Inservice : public InputBeltBaseState
{
. . .
};
class OutOfService : public InputBeltBaseState
{
. . .
};
class Idle : public Inservice
{
public:
void HandleSensorInterrupt(InputConveyorBelt& m);
};
class AwaitingPackageRouting : public Inservice
{
public:
void HandleAcknowledgePackage(InputConveyorBelt &m,
const AcknowledgePackageMsg *pMsg);
void HandleSensorTimeout(InputConveyorBelt &m);
// Unconfigure is not supported in this state, so add a dummy handler to
// override handling of this event by the base state
void UnConfigureBelt(InputConveyorBelt &m, const UnConfigMsg *pMsg) {}
};
class Jammed : public OutOfService
{
public:
void OperatorInservice(InputConveyorBelt &m);
};
class AwaitingConfiguration : public OutOfService
{
public:
void ConfigureBelt(InputConveyorBelt &m, const ConfigMsg *pMsg);
};
// Friend declarations
friend BaseState;
friend Inservice;
friend OutOfService;
friend Idle;
friend AwaitingPackageRouting;
friend Jammed;
friend AwaitingConfiguration;
// Static declaration for states
static Idle idle;
static AwaitingPackageRouting awaitingPackageRouting;
static Jammed jammed;
static AwaitingConfiguration awaitingConfiguration;
// Current state pointer
BaseState *m_pCurrentState;
// Method to change state
void ChangeState(BaseState &state)
{ m_pCurrentState = &state; }
public:
InputConveyorBelt(int beltId) : ConveyorBelt(beltId)
{
ChangeState(awaitingConfiguration);
}
// Public handlers (implemented as inline functions)
void HandleSensorInterrupt()
{ m_pCurrentState->HandleSensorInterrupt(*this); }
void HandleAcknowledgePackage(const AcknowledgePackageMsg *pMsg)
{ m_pCurrentState->HandleAcknowledgePackage(*this,
const AcknowledgePackageMsg *pMsg); }
void HandleSensorTimeout()
{ m_pCurrentState->HandleSensorTimeout(*this); }
void OperatorInservice()
{ m_pCurrentState->OperatorInservice(*this); }
void ConfigureBelt(const ConfigMsg *pMsg)
{ m_pCurrentState->ConfigureBelt(*this, pMsg); }
void UnConfigureBelt(const UnConfigMsg *pMsg)
{ m_pCurrentState->UnConfigureBelt(*this, const UnConfigMsg *pMsg); }
};
|
| Input
Conveyor Belt Source File |
// Static state variables
InputConveyorBelt::Idle InputConveyorBelt::idle;
InputConveyorBelt::AwaitingPackageRouting InputConveyorBelt::awaitingPackageRouting;
InputConveyorBelt::Jammed InputConveyorBelt::jammed;
InputConveyorBelt::AwaitingConfiguration InputConveyorBelt::awaitingConfiguration;
// Base state implementation for UnConfigure executes in all state unless
// explicitly overriden by a state
void InputConveyorBelt::BaseState::HandleUnConfigure(InputConveyorBelt &m,
UnConfigMsg *pMsg)
{
// Conveyor Belt has been unconfigured, so deactivate the sensor
m_pMagneticSensor->Deactivate();
// Delete the sensor as it is no longer required.
delete m_pMagneticSensor;
// Move to unconfigured state
m.ChangeState(m.awaitingConfiguration);
}
void InputConveyorBelt::Idle::HandleSensorInterrupt(InputCoveyorBelt &m)
{
// Create a package object to keep track of the package
m_pCurrentPackage = new Package(m_beltId);
// Now read the sensor to get information about the sensor
SensorInfo *pSensorInfo = m_pMagneticSensor->ReadSensor();
// Save the product info in the package
m_pCurrentPackage->SetProductInfo(pSensorInfo->productInfo);
// Obtain the destination conveyor belt id from the
// routing table
int destinationBeltId =
RoutingTable::GetInstance()->GetRouting(m_pCurrentPackage);
// Store the desitnation belt id in the package
pPackage->SetDestinationBeltId(destinationBeltId);
// Set the router to pass the package to the destination belt
// Note: This important step was missed during sequence diagram design
Router::GetInstance()->SetRouterTo(destinationBeltId);
// Start a timer to check for timely routing of the package
StartTimer(T_AWAIT_PACKAGE_ROUTING);
// Change state to awaiting package
m.ChangeState(m.awaitingPackageRouting);
}
void InputConveyorBelt::AwaitingPackageRouting::HandleAcknowledgePackage
(InputConveyorBelt &m, const AcknowledgePackageMsg *pMsg)
{
// Package has been acknowledged by the destination conveyor belt
// so stop the timer
StopTimer(T_AWAIT_PACKAGE_ROUTING);
// Delete the package object as routing has been completed
delete m_pCurrentPackage;
// Now move back to idle state
m.ChangeState(m.idle);
}
void InputConveyorBelt::AwaitingPackageRouting::HandleSensorTimeout
(InputConveyorBelt &m)
{
// Package is jammed so we can delete the package
// object keeping track of the package related information
delete m_pCurrentPackage;
// Raise the jammed alarm
Router::GetInstance()->RaiseAlarm(m_beltId, JAMMED_ALARM);
// Now move back to idle state
m.ChangeState(m.jammed);
}
void InputConveyorBelt::Jammed::OperatorInservice(InputConveyorBelt &m)
{
// Clear the jammed alarm
Router::GetInstance()->ClearAlarm(m_beltId, JAMMED_ALARM);
// Operator has fixed the jam condition and is requesting a transition to
// inservice state
m.ChangeState(m.idle);
}
void InputConveyorBelt::AwaitingConfiguration::ConfigureBelt
(InputConveyorBelt &m, const ConfigMsg *pMsg)
{
// Create the magnetic sensor
m_pMagneticSensor = new MagneticSensor;
// Activate the sensor
m_pMagneticSensor->Activate();
// Move to the idle state
m.ChangeState(m.idle);
}
|
|