.

     

MPI Application Template
template.c
 

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;
}


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