.

     

MPI Application Template
template.c
 

Watchdog3.c -- Set up a watchdog by using user buffer fields, user limits, and a sequencer

/* Watchdog3.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 a sequencer 

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:

 A sequence that will perform watchdog action (usually abort) on all motions if
  the user buffer abort field is set to a non-zero value 


Please see the following sample applications for more information on the
 techniques used within this sample application:

   User Limits:  usrlim1.c, usrlim4.c
   Sequencer:    seqkill.c



Watchdog3.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)

#define SEQUENCE_NUMBER             (-1) /* next available sequencer */

typedef struct WatchDogConfigData
{
    MPIControl   control;
    MPIMotor*    motor;
    long         motorCount;
    MPIMotion*   motion;
    long         motionCount;
    MPISequence  sequence;

    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,
                 MPIMotion          *motion,
                 long               *numberOfMotions,
                 MPIMotor           *motor,
                 long               *numberOfMotors,
                 MPISequence        *sequence,
                 long                sequenceNumber)
{
    MPIControlConfig controlConfig;
    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]));
    }

    /* Record the number of enabled motions */
    *numberOfMotions = controlConfig.motionCount;

    /* Create motion objects */
    for (index = 0; index < controlConfig.motionCount; index++)
    {
        motion[index] =
            mpiMotionCreate(*control, index, MPIHandleVOID);
        msgCHECK(mpiMotionValidate(motion[index]));
    }

    /* Make sure a sequencer is enabled */
    if (controlConfig.sequenceCount == 0)
    {
        controlConfig.sequenceCount = 1;

        returnValue =
            mpiControlConfigSet(*control, &controlConfig, NULL);
        msgCHECK(returnValue);
    }

    /* Create sequencer object */
    *sequence =
        mpiSequenceCreate(*control, sequenceNumber, 2+(*numberOfMotions));
    msgCHECK(mpiSequenceValidate(*sequence));
}


/* 
   Perform certain cleanup actions and delete MPI objects 

   Deleting the program sequencer will stop the sequence.  Then the sequencer
   will not be able to perform its watchdog duties.  For this reason, we
   will not delete the sequencer.
*/
void programCleanup(MPIControl  *control,
                    MPIMotion   *motion,
                    long         numberOfMotions,
                    MPIMotor    *motor,
                    long         numberOfMotors)
{
    long            index;
    long            returnValue;


    /* Delete motion objects */
    for (index = 0; index < numberOfMotions; index++)
    {
        returnValue =
            mpiMotionDelete(motion[index]);
        msgCHECK(returnValue);
        motion[index] = 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;
}


/*
   sequenceCommandAdd() creates a command (MPICommand object) and appends it to
   sequence's list of commands.  This function does return MPI error codes so
   that sequence setup code may be more easily debugged.

   Warning:  sequenceCommandAdd() does not keep track of created command objects
   so it is important that before a sequence object is deleted, that each
   command object on sequence's list is itself deleted.  Otherwise, there will
   be memory leaks.  One can use sequenceProgramDelete() to accomplish this.
*/
long sequenceCommandAdd(MPISequence          sequence,
                        MPICommandType       commandType,
                        MPICommandParams    *commandParams,
                        const char          *label)
{
    MPICommand  command;
    long        returnValue;


    command =
        mpiCommandCreate(commandType,
                         commandParams,
                         label);
    returnValue =
        mpiCommandValidate(command);

    if (returnValue == MPIMessageOK) {
        returnValue =
            mpiSequenceCommandAppend(sequence,
                                     command);
    }

    return returnValue;
}


/* Setup watchdog on the controller and return watchdog data */
void setupWatchdog(WatchDogConfigData* watchdogConfig,
                   WatchDogData*       watchdogData)
{
    MEIXmpBufferData*   external;
    MEIMotorEventConfig motorEventConfig;
    MPICommandParams    commandParams;
    MPICommandMotion    motionCommand;
    MEIMotorConfig      meiMotorConfig;
    MPIMotion*          motion = watchdogConfig->motion;
    MPIMotor*           motor = watchdogConfig->motor;
    MPISequence         sequence = watchdogConfig->sequence;
    MEIXmpLimit         xmpMonitorLimit;
    double              sampleRate;
    long                waitSamples;
    long*               abortAddr;
    long                motorIndex;
    long                motionIndex;
    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);
    }

    /* Setup sequencer */

    /* Determine correct action for sequencer */
    switch (watchdogConfig->watchdog_action)
    {
        case MPIActionSTOP:
            motionCommand = MPICommandMotionSTOP;
            break;
        case MPIActionE_STOP:
            motionCommand = MPICommandMotionE_STOP;
            break;
        case MPIActionE_STOP_ABORT:
            motionCommand = MPICommandMotionE_STOP_ABORT;
            break;
        case MPIActionABORT:
            motionCommand = MPICommandMotionABORT;
            break;
        default:
            fprintf(stderr, "Unknown action for watchdog failure\n");
            exit(-1);
    }

    /* Wait for the abort field to be set to a non-zero value */
    commandParams.wait.expr.oper       = MPICommandOperatorNOT_EQUAL;
    commandParams.wait.expr.address.l  = abortAddr;
    commandParams.wait.expr.by.value.l = 0;

    returnValue =
        sequenceCommandAdd(sequence,
                           MPICommandTypeWAIT,
                           &commandParams,
                           "Start");
    msgCHECK(returnValue);

    for (motionIndex=0; motionIndex<watchdogConfig->motionCount; ++motionIndex)
    {
        /* command watchdog action */
        commandParams.motion.motionCommand = motionCommand;
        commandParams.motion.motion        = motion[motionIndex];

        returnValue =
            sequenceCommandAdd(sequence,
                               MPICommandTypeMOTION,
                               &commandParams,
                               NULL);
        msgCHECK(returnValue);
    }

    /* Goto the first statement */
    commandParams.branch.label      = "Start";
    commandParams.branch.expr.oper  = MPICommandOperatorALWAYS;

    returnValue =
        sequenceCommandAdd(sequence,
                           MPICommandTypeBRANCH,
                           &commandParams,
                           NULL);
    msgCHECK(returnValue);

    /* Start sequence */
    returnValue =
        mpiSequenceStart(sequence,
                         MPIHandleVOID);
    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;
    MPIMotion           motion[MEIXmpMAX_MSs];
    MPIMotor            motor[MEIXmpMAX_Motors];
    MPISequence         sequence;
    WatchDogConfigData  watchdogConfig;
    WatchDogData        watchdogData;
    long                motionCount;
    long                motorCount;


    /* Perform basic command line parsing. (-control -server -port -trace) */
    basicParsing(argc,
                 argv,
                 &controlType,
                 &controlAddress);

    /* Create and initialize MPI objects */
    programInit(&control,
                controlType,
                &controlAddress,
                motion,
                &motionCount,
                motor,
                &motorCount,
                &sequence,
                SEQUENCE_NUMBER);

    /* Set up watchdog configuration */
    watchdogConfig.control     = control;
    watchdogConfig.motor       = motor;
    watchdogConfig.motorCount  = motorCount;
    watchdogConfig.motion      = motion;
    watchdogConfig.motionCount = motionCount;
    watchdogConfig.sequence    = sequence;

    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,
                   motion,
                   motionCount,
                   motor,
                   motorCount);

    return MPIMessageOK;
}


 
      
       Legal Notice  |  Tech Email  |  Feedback
      
Copyright ©
2001-2009 Motion Engineering