ANY-maze Help > The ANY-maze reference > ANY-maze plug-ins > Writing an Action plug-in

Writing an Action plug-in

Introduction

This topic assumes you have already read the step-by-step instructions for writing an ANY-maze plug-in, so if you've not done so yet, I suggest you do this now.

An Action plug-in is a plug-in which can be invoked by an ANY-maze procedure firing an action (or, if the protocol is using the older 'Events and Actions' system, by an action directly triggered by an event). This makes Action plug-ins very powerful; they effectively get 'called' when something happens during the test, and what they then do can be anything the author wants.

 1.Making a plug-in into an 'Action' plug-in 
 2.Action plug-in settings 
 3.Performing the action 
 4.Example Action plug-in  

Making a plug-in into an 'Action' plug-in

An Action plug-in is like a normal plug-in, so it needs a GUID and should respond to most of the same messages. What makes it into an Action plug-in is its response to the AME_GETRESULTTYPE message - this should be RT_ACTION.

  

  

case AME_GETRESULTTYPE :

  //Return the result type in the unsigned char pointed to by lParam

  *((unsigned char*)lParam) = RT_ACTION;

  return AME_OK;

  

This return value tells ANY-maze that the plug-in is an Action plug-in, and it will then treat the plug-in a little differently, specifically:

 The plug-in will not be listed in the plug-ins list in the protocol. 
 The list of actions available to a procedure will contain actions to trigger each of the Action plug-ins available - these will be listed under the plug-in's name (i.e. whatever the plug-in returns from the AME_GETNAME message). If the protocol is using the older 'Events and Actions' system, then an entry will be added to the available 'effects' of an action. 
 If the user clicks the plug-in's 'effect' in this list, then the plug-in will receive an AME_EDITSETTINGS message, and should display a window where the user can specify any settings for the action. 
 The plug-in will receive AME_TESTSTART, AME_TESTEND and AME_TESTABORT messages, but it won't receive AME_POSN. 
 The plug-in will receive an AME_PERFORMACTION message when the action is triggered.    
Plug-ins are only available in licensed copies of ANY-maze.

Action plug-in settings

As mentioned above, an Action plug-in is listed as a possible effect of an action. What the plug-in does is, of course, defined by the plug-in's author, but in some cases it may be useful to specify some settings for the action. For example, imagine you wrote an Action plug-in which can switch some piece of equipment on or off, and you want the equipment to switch on when the animal enters a zone and off when it exits the zone. In this case, you would include in your procedure an action which would have your plug-in as its effect. But how would the plug-in know whether it should switch the equipment on or off? As this action would be linked to an event that is triggered by the animal entering the zone, we would want the equipment to switch on - but you would need to tell the plug-in that this is what it should do.

This is where an Action plug-in's settings come in - you could include in your plug-in a dialogue box where the user could specify what the plug-in should do, perhaps two radio-buttons: 'Switch On' and 'Switch Off'. Now when the user selects the plug-in as the effect of the action, the dialogue box will open and he will need to specify what the plug-in will do - so he'd choose 'Switch On'.

ANY-maze takes care of the settings for an Action plug-in, so it passes them to the plug-in when the user selects the plug-in as the effect of an action, and it passes them back when the plug-in action is to be performed. Physically, the settings are just a block of memory of 48 bytes. You can use as much or as little of this memory to store settings as you wish. It's a good idea to declare a struct for the settings, and just include a field called something like spare to pad the size to 48 bytes. For example:

  

  

typedef struct {

  bool           SwitchOn;

  unsigned char  spare[47];

}MYACTIONSETTINGS;

  

Note that ANY-maze will zero-initialise the settings buffer. This means that you should either arrange things so that your 'default' values are all zero, or use a flag to determine whether the settings have been initialised. For example, you might declare your settings as:

  

  

typedef struct {

  bool           Initialised;

  bool           SwitchOn;

  unsigned char  spare[46];

}MYACTIONSETTINGS;

  

Now the initialised flag will default to false, so you will be able to tell whether the settings have been initialised or not. When they have not been initialised, you can set them to some default values before using them - for example:

  

if (!Settings->Initialised) {

  Settings->SwitchOn    = true;

  Settings->Initialised = true;

}

  

   

When the user selects an Action plug-in as the effect of an action, the plug-in will receive an AME_EDITSETTINGS message. This is the same message as is used to edit a non-action plug-in's settings, and it works in the same way. If your Action plug-in doesn't have any settings, then you can just return AME_NOTIMPLEMENTED; otherwise you should display a modal dialogue box where the user can edit the settings. Return AME_OK to have the updated settings saved by ANY-maze, or AME_CANCELLED if the user closes the dialogue box using a cancel button.

It's important to understand that ANY-maze will record a different settings record for your plug-in for each ANY-maze action that has the plug-in as an effect. Returning to our earlier example, when the animal enters the zone we want to switch the equipment on, so we will have an action whose effect is to invoke our plug-in and the settings will have the SwitchOn flag set. When the animal exits the zone, we want to switch the equipment off. In this case we will have a different action, and this will also have the effect of invoking our plug-in - just that in this case the plug-in's settings will have the SwitchOn flag set to false.

Processing AME_TESTSTART and AME_TESTEND messages

Like a normal plug-in, an Action plug-in is sent an AME_TESTSTART message when a test begins. This is identical to the message sent to a non-action plug-in, and you should process it in the same way - see Step-by-step instructions for writing an ANY-maze plug-in for full details.

Your plug-in will also receive an AME_TESTEND or AME_TESTABORT message when the test ends; again these are processed in the standard way.

Performing the action

When ANY-maze determines that the action should be performed, it will send your plug-in an AME_PERFORMACTION message. The lParam of this message points to an AME_ACTIONDATA record, which contains information about the action to perform. The wParam contains the size of this record.

The AME_ACTIONDATA record is defined as:

  

  

typedef struct {

  LPVOID   Context;

  DWORD    EventTime;

  char     TriggeringEventName[80];

  LPVOID   Settings;

  DWORD    SettingsSize;

}AME_ACTIONDATA, *LPAME_ACTIONDATA;

  

Full details can be found in the AME_ACTIONDATA topic, but briefly:

 The Context field holds whatever value you returned as your 'context' during AME_TESTSTART processing (typically, an Action plug-in doesn't use this).  
 EventTime is the time the event which triggered this action occurred, and TriggeringEventName is the name of the event. If this plug-in is triggered from a procedure, this will be the name of the procedure. 
 The Settings and SettingsSize are the action's settings, as entered by the user when he set up the action in the protocol or procedure (see Action plug-in settings for more details). 

When it receives this message, your plug-in should perform whatever action it is designed to do - so it might communicate with some other program, or switch some piece of equipment on or off.

Often, you will need to use the settings to decide what it is you need to do - but, as mentioned above, the settings record may be uninitialised, in which case you will need to decide what to do. You could choose not to perform any action; you could set the settings to some default state and then perform the default action; or you could organise things in such a way that an entirely null settings record is your default (in which case, you won't need to worry about whether the settings have been initialised or not). Of course, your plug-in may not have any settings, in which case you won't care about the settings record's state.

In most cases, you probably won't be interested in the name of the event or procedure that caused the plug-in to be invoked, but it can be useful as it will usually tell you what it was that happened in the test - for example, it might be 'Entered open arm'. If your plug-in communicates with another program, perhaps to tell it when specific things occur in the test, then you may wish to pass this information to the other program.

Example Action plug-in

Here's the code for a very minimal Action plug-in. When invoked, this plug-in will make the computer go 'beep'. Note that for clarity, this implementation does not check the GUID passed to messages such as AME_GETNAME, as the DLL only implements one plug-in - this will work correctly, but it is good practice to always check the GUID.

  

  

extern "C"

__declspec(dllexport) int ANYmazeExtension_Main (LPGUID guid,

                                                 DWORD  Mesg,

                                                 WPARAM wParam,

                                                 LPARAM lParam)

{

//Const

  static const GUID GUID_BEEPERPLUGIN = {0x7f32b71d,0x8985,0x4e71,{0xb1,0xcc,0x2c,0x83,0xa,0x43,0xb5,0xb1}};

  

//BEGIN

  //Action depends on the Mesg

  switch (Mesg)

  {

    case AME_INITIALISE :

    case AME_TERMINATE  :

      //We must return AME_OK, even if we do nothing

      return AME_OK;

  

    case AME_ENUMERATE :

      //We only implement a single plug-in

      if (*guid == GUID_NULL)

        *((LPGUID)lParam) = GUID_BEEPERPLUGIN;

      else

        return AME_NOTIMPLEMENTED;

      return AME_OK;

  

    case AME_GETNAME :

      //Set the name

      StringCbCopy ((LPSTR)lParam, (int)wParam, "Make the computer beep");

      return AME_OK;

  

    case AME_GETRESULTTYPE :

      //Return our result type which is RT_ACTION

       *((unsigned char*)lParam) = RT_ACTION;

      return AME_OK;

     

    case AME_TESTSTART :

    case AME_TESTEND   :

      //We must return something otherwise the plug-in will not work. We have

      //nothing to do when a test starts or ends so we just return AME_OK

      return AME_OK;

     

    case AME_TESTPAUSE :

    case AME_TESTABORT :

      //We don't care about test pause or abort, so return AME_OK

      return AME_OK;

     

    case AME_PERFORMACTION :

      //Make the speaker beep

      MessageBeep (0);

      return AME_OK;

  

    default :

      //We don't need to process any other messages

      return AME_NOTIMPLEMENTED;

  }

  

 

© Copyright 2003-2026 Stoelting Co. All rights reserved

ANY-maze help topic T1000