Watchdog2.c -- Set up a watchdog by using user buffer fields, user limits, and user faults
/* Watchdog2.c */
/* Copyright(c) 1991-2006 by Motion Engineering, Inc. All rights reserved.
*
* This software contains proprietary and confidential information of
* Motion Engineering Inc., and its suppliers. Except as may be set forth
* in the license agreement under which this software is supplied, use,
* disclosure, or reproduction is prohibited without the prior express
* written consent of Motion Engineering, Inc.
*/
/*
: Set up a watchdog by using user buffer fields, user limits, and user faults
Sometimes there is a need to stop or abort motion on all axes if an application
dies. The way to set this up is via an application watchdog. This is where
an application sets a value to a specific controller field. The controller
monitors that field and will stop and/or abort all motion if the application
fails to set the value of the watchdog field.
This application sets up a watchdog by using 2 user buffer fields, 2 user limits
for watchdog monitoring, and 1 user limit per motor to abort motion.
User two user buffer fields:
User Buffer Watchdog Field: The application must periodically set this field
to zero.
User Buffer Abort Field: When this field is set to a non-zero value, axes
will abort.
Set up 2 user limits:
Limit A: Monitor user buffer watchdog field. If the watchdog field is
non-zero for more than a certain period of time, set the abort field
to a non-zero value.
Limit B: Must follow Limit A -- prefferably the immediate next user limit.
Always evaluates TRUE, and sets the watchdog field to a non-zero
value.
Shutdown procedure implemented by:
Having all SynqNet Node user fault settings to watch the user buffer abort
field. Then set all motor user fault actions to desired watchdog action
(usually abort).
Please see the following sample applications for more information on the
techniques used within this sample application:
User Limits: usrlim1.c, usrlim4.c
User Faults: SQEStop1.c
Watchdog2.c is one of three sample applications that implement a watchdog. Each
is identical, except for the method of the shutdown procedure. Here is a
comparison of the three sample applications:
Watchdog1.c
Shutdown procedure implemented by one user limit on each motor that will
perform the watchdog action if user buffer field B is set to a non-zero
value.
Advantages: Does not use the SynqNet Node user faults
Evaluated in one background cycle
Disadvantages: Uses one user limit per motor. However the limits are
plentiful -- 16 per motor.
Watchdog2.c
Shutdown procedure implemented by having all SynqNet Node user fault
settings to watch user buffer field B. Then set all motor user fault
actions to desired watchdog action (usually abort).
Advantages: Only uses two user limits total -- no additional user limits
Evaluated in one background cycle
Disadvantages: Does the SynqNet Node user fault for every node. The user
faults cannot be used for any other functionality.
Watchdog3.c
Shutdown procedure implemented by a sequencer that will perform the
watchdog action on all motions if user buffer field B is set to a non-zero
value
Advantages: Does not use the SynqNet Node user faults
Only uses two user limits total -- no additional user limits
Disadvantages: Comparitively slow compared to other watchdog implementation
Evaluated in (number of motion supervisors + 1) background
cycles.
Warning! This is a sample program to assist in the integration of an
MEI motion controller with your application. It may not contain all
of the logic and safety features that your application requires.
The msgCHECK(...) macros used in the following sample code are intended
to convey our strong belief that ALL error return codes should be checked.
Actual application code should use specific error handling techniques (other
than msgCHECKs) best suited to your internal error recovery methods.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "stdmpi.h"
#include "stdmei.h"
#include "apputil.h"
/* Watchdog time in seconds */
#define WATCHDOG_TIME (0.2)
/* User buffer addresses */
#define USER_BUFFER_INDEX_WATCHDOG (0)
#define USER_BUFFER_INDEX_ABORT (1)
/* Action to take place when watchdog fails */
#define WATCHDOG_ACTION MPIActionE_STOP_ABORT
/* User Limits needed to implement watchdog */
/* Used only by helper motor */
#define WATCHDOG_MONITOR_LIMIT MEIEventTypeLIMIT_USER1
/* Used only by helper motor */
#define WATCHDOG_RESET_LIMIT MEIEventTypeLIMIT_USER2
/* Helper motor to setup the two helper user limits */
#define WATCHDOG_HELPER_MOTOR (0)
typedef struct WatchDogConfigData
{
MPIControl control;
MPIMotor* motor;
long motorCount;
MEISqNode* sqNode;
long sqNodeCount;
double watchdog_time;
long user_buffer_index_watchdog;
long user_buffer_index_abort;
MPIAction watchdog_action;
MEIEventType watchdog_monitor_limit;
MEIEventType watchdog_reset_limit;
long helper_motor;
} WatchDogConfigData;
typedef struct WatchDogData
{
MPIControl control;
long* watchDogField;
} WatchDogData;
/* Perform basic command line parsing. (-control -server -port -trace) */
void basicParsing(int argc,
char *argv[],
MPIControlType *controlType,
MPIControlAddress *controlAddress)
{
long argIndex;
/* Parse command line for Control type and address */
argIndex = argControl(argc, argv, controlType, controlAddress);
/* Check for unknown/invalid command line arguments */
if (argIndex < argc) {
fprintf(stderr,"usage: %s %s\n", argv[0], ArgUSAGE);
exit(MPIMessageARG_INVALID);
}
}
/* Create and initialize MPI objects */
void programInit(MPIControl *control,
MPIControlType controlType,
MPIControlAddress *controlAddress,
MPIMotor *motor,
long *numberOfMotors,
MEISynqNet *synqNet,
MEISqNode *sqNode,
long *numberOfSqNodes)
{
MPIControlConfig controlConfig;
MEISynqNetInfo synqNetInfo;
long index;
long returnValue;
/* Create motion controller object */
*control =
mpiControlCreate(controlType, controlAddress);
msgCHECK(mpiControlValidate(*control));
/* Initialize motion controller */
returnValue =
mpiControlInit(*control);
msgCHECK(returnValue);
/* Obtain controller configuration */
returnValue =
mpiControlConfigGet(*control, &controlConfig, NULL);
msgCHECK(returnValue);
/* Record the number of enabled motors */
*numberOfMotors = controlConfig.motorCount;
/* Create motor objects */
for (index = 0; index < controlConfig.motorCount; index++)
{
motor[index] =
mpiMotorCreate(*control, index);
msgCHECK(mpiMotorValidate(motor[index]));
}
/* Create synqNet object */
*synqNet =
meiSynqNetCreate(*control, 0);
msgCHECK(meiSynqNetValidate(*synqNet));
/* Obtain SynqNet information */
returnValue =
meiSynqNetInfo(*synqNet, &synqNetInfo);
msgCHECK(returnValue);
/* Record the number of enabled motors */
*numberOfSqNodes = synqNetInfo.nodeCount;
/* Create sqNode objects */
for (index = 0; index < synqNetInfo.nodeCount; index++)
{
sqNode[index] =
meiSqNodeCreate(*control, index);
msgCHECK(meiSqNodeValidate(sqNode[index]));
}
}
/* Perform certain cleanup actions and delete MPI objects */
void programCleanup(MPIControl *control,
MPIMotor *motor,
long numberOfMotors,
MEISynqNet *synqNet,
MEISqNode *sqNode,
long numberOfSqNodes)
{
long index;
long returnValue;
/* Delete sqNode objects */
for (index = 0; index < numberOfSqNodes; index++)
{
returnValue =
meiSqNodeDelete(sqNode[index]);
msgCHECK(returnValue);
sqNode[index] = MPIHandleVOID;
}
/* Delete synqnet object */
returnValue =
meiSynqNetDelete(*synqNet);
msgCHECK(returnValue);
*synqNet = MPIHandleVOID;
/* Delete motor objects */
for (index = 0; index < numberOfMotors; index++)
{
returnValue =
mpiMotorDelete(motor[index]);
msgCHECK(returnValue);
motor[index] = MPIHandleVOID;
}
/* Delete motion controller object */
returnValue =
mpiControlDelete(*control);
msgCHECK(returnValue);
*control = MPIHandleVOID;
}
/* Setup watchdog on the controller and return watchdog data */
void setupWatchdog(WatchDogConfigData* watchdogConfig,
WatchDogData* watchdogData)
{
MEIXmpBufferData* external;
MEIMotorEventConfig motorEventConfig;
MEIMotorConfig meiMotorConfig;
MEISqNodeConfig sqNodeConfig;
MPIMotor* motor = watchdogConfig->motor;
MEISqNode* sqNode = watchdogConfig->sqNode;
MEIXmpLimit xmpMonitorLimit;
double sampleRate;
long waitSamples;
long* abortAddr;
long motorIndex;
long nodeIndex;
long zero = 0;
long returnValue;
/* Obtain controller memory addresses */
returnValue =
mpiControlMemory(watchdogConfig->control,
NULL,
&external);
msgCHECK(returnValue);
/* Obtain controller sample rate */
returnValue =
meiControlSampleRate(watchdogConfig->control,
&sampleRate);
msgCHECK(returnValue);
waitSamples = (long) ceil(watchdogConfig->watchdog_time * sampleRate);
/* Write the watchdog data */
watchdogData->control = watchdogConfig->control;
watchdogData->watchDogField =
&external->UserBuffer.Data[watchdogConfig->user_buffer_index_watchdog];
/* Calculate abort field address */
abortAddr =
&external->UserBuffer.Data[watchdogConfig->user_buffer_index_abort];
/* Setup watchdog monitor limit */
returnValue =
mpiMotorEventConfigGet(motor[watchdogConfig->helper_motor],
watchdogConfig->watchdog_monitor_limit,
NULL,
&motorEventConfig);
msgCHECK(returnValue);
motorEventConfig.Logic = MEIXmpLogicSINGLE +
(waitSamples << MEIXmpLogicBITS);
motorEventConfig.Status = MEIXmpStatusLIMIT;
motorEventConfig.Condition[0].Type = MEIXmpLimitTypeNE;
motorEventConfig.Condition[0].SourceAddress = watchdogData->watchDogField;
motorEventConfig.Condition[0].Mask = 0xFFFFFFFF;
motorEventConfig.Condition[0].LimitValue.g32.l = 0;
motorEventConfig.Condition[1].Type = MEIXmpLimitTypeFALSE;
motorEventConfig.Condition[1].SourceAddress = NULL;
motorEventConfig.Condition[1].Mask = 0;
motorEventConfig.Condition[1].LimitValue.g32.l = 0;
motorEventConfig.Output.OutputPtr = abortAddr;
motorEventConfig.Output.AndMask = 0;
motorEventConfig.Output.OrMask = 1;
motorEventConfig.Output.Enabled = TRUE;
returnValue =
mpiMotorEventConfigSet(motor[watchdogConfig->helper_motor],
watchdogConfig->watchdog_monitor_limit,
NULL,
&motorEventConfig);
msgCHECK(returnValue);
/* Setup watchdog reset limit */
returnValue =
mpiMotorEventConfigGet(motor[watchdogConfig->helper_motor],
watchdogConfig->watchdog_reset_limit,
NULL,
&motorEventConfig);
msgCHECK(returnValue);
xmpMonitorLimit = meiXmpLimitFromMPIEventType(watchdogConfig->watchdog_monitor_limit) - MEIXmpLimitDEDICATED_LAST;
motorEventConfig.Logic = MEIXmpLogicSINGLE;
motorEventConfig.Status = MEIXmpStatusLIMIT;
motorEventConfig.Condition[0].Type = MEIXmpLimitTypeEQ;
motorEventConfig.Condition[0].SourceAddress = &external->UserLimit[watchdogConfig->helper_motor].Limit[xmpMonitorLimit].Count;
motorEventConfig.Condition[0].Mask = 0xFFFFFFFF;
motorEventConfig.Condition[0].LimitValue.g32.l = 0;
motorEventConfig.Condition[1].Type = MEIXmpLimitTypeFALSE;
motorEventConfig.Condition[1].SourceAddress = NULL;
motorEventConfig.Condition[1].Mask = 0;
motorEventConfig.Condition[1].LimitValue.g32.l = 0;
motorEventConfig.Output.OutputPtr = watchdogData->watchDogField;
motorEventConfig.Output.AndMask = 0;
motorEventConfig.Output.OrMask = 1;
motorEventConfig.Output.Enabled = TRUE;
returnValue =
mpiMotorEventConfigSet(motor[watchdogConfig->helper_motor],
watchdogConfig->watchdog_reset_limit,
NULL,
&motorEventConfig);
msgCHECK(returnValue);
/*
Set the monitor and abort field to zero.
This clears any past watchdog failures.
*/
returnValue =
mpiControlMemorySet(watchdogConfig->control,
watchdogData->watchDogField,
&zero,
sizeof(zero));
msgCHECK(returnValue);
returnValue =
mpiControlMemorySet(watchdogConfig->control,
abortAddr,
&zero,
sizeof(zero));
msgCHECK(returnValue);
/* Set all motor user fault actions */
for (motorIndex=0; motorIndex<watchdogConfig->motorCount; ++motorIndex)
{
returnValue =
mpiMotorConfigGet(motor[motorIndex],
NULL,
&meiMotorConfig);
msgCHECK(returnValue);
meiMotorConfig.userFaultAction = watchdogConfig->watchdog_action;
returnValue =
mpiMotorConfigSet(motor[motorIndex],
NULL,
&meiMotorConfig);
msgCHECK(returnValue);
}
/* Set all the SynqNet node user faults */
for (nodeIndex=0; nodeIndex<watchdogConfig->sqNodeCount; ++nodeIndex)
{
returnValue =
meiSqNodeConfigGet(sqNode[nodeIndex],
&sqNodeConfig);
msgCHECK(returnValue);
sqNodeConfig.userFault.addr = abortAddr;
sqNodeConfig.userFault.mask = 1;
sqNodeConfig.userFault.pattern = 1;
returnValue =
meiSqNodeConfigSet(sqNode[nodeIndex],
&sqNodeConfig);
msgCHECK(returnValue);
/*
Clear node status (past user fault occurances).
This clears any past watchdog failures.
*/
returnValue =
meiSqNodeStatusClear(sqNode[nodeIndex]);
msgCHECK(returnValue);
}
}
/* Set the watchdog field to zero */
void setWatchdogField(WatchDogData* watchdogData)
{
long zero = 0;
long returnValue;
returnValue =
mpiControlMemorySet(watchdogData->control,
watchdogData->watchDogField,
&zero,
sizeof(zero));
msgCHECK(returnValue);
}
/* Set Watchdog Until a Key is Pressed */
void setWatchdogUntilKeypress(WatchDogData* watchdogData,
double delay /* seconds */)
{
long delayMilliseonds = (long) (delay*1000);
do
{
setWatchdogField(watchdogData);
} while(meiPlatformKey(MPIWaitMSEC*delayMilliseonds) < 0);
}
int main(int argc,
char *argv[])
{
MPIControl control;
MPIControlType controlType;
MPIControlAddress controlAddress;
MPIMotor motor[MEIXmpMAX_Motors];
MEISynqNet synqNet;
MEISqNode sqNode[MEIXmpMaxSynqNetBlocks];
WatchDogConfigData watchdogConfig;
WatchDogData watchdogData;
long motorCount;
long sqNodeCount;
/* Perform basic command line parsing. (-control -server -port -trace) */
basicParsing(argc,
argv,
&controlType,
&controlAddress);
/* Create and initialize MPI objects */
programInit(&control,
controlType,
&controlAddress,
motor,
&motorCount,
&synqNet,
sqNode,
&sqNodeCount);
/* Set up watchdog configuration */
watchdogConfig.control = control;
watchdogConfig.motor = motor;
watchdogConfig.motorCount = motorCount;
watchdogConfig.sqNode = sqNode;
watchdogConfig.sqNodeCount = sqNodeCount;
watchdogConfig.watchdog_time = WATCHDOG_TIME;
watchdogConfig.user_buffer_index_watchdog = USER_BUFFER_INDEX_WATCHDOG;
watchdogConfig.user_buffer_index_abort = USER_BUFFER_INDEX_ABORT;
watchdogConfig.watchdog_action = WATCHDOG_ACTION;
watchdogConfig.watchdog_monitor_limit = WATCHDOG_MONITOR_LIMIT;
watchdogConfig.watchdog_reset_limit = WATCHDOG_RESET_LIMIT;
watchdogConfig.helper_motor = WATCHDOG_HELPER_MOTOR;
/* Setup watchdog on the controller and return watchdog data */
setupWatchdog(&watchdogConfig, &watchdogData);
/* Set Watchdog Until a Key is Pressed */
printf("\nWatchdog is setup. Press any key to quit...\n\n");
setWatchdogUntilKeypress(&watchdogData, WATCHDOG_TIME/2);
/* Perform certain cleanup actions and delete MPI objects */
programCleanup(&control,
motor,
motorCount,
&synqNet,
sqNode,
sqNodeCount);
return MPIMessageOK;
}
|