Difference between revisions of "AnyWave:WriteMatlabScripted"

From WikiMEG
Jump to: navigation, search
(Requirements)
(main.m)
 
(28 intermediate revisions by one user not shown)
Line 3: Line 3:
 
The purpose is to explain how to write a MATLAB function that will be the heart of a plug-in executed in MATLAB by AnyWave.<br/>
 
The purpose is to explain how to write a MATLAB function that will be the heart of a plug-in executed in MATLAB by AnyWave.<br/>
 
We will also explain how to create a text file to describe our plug-in to AnyWave<br/>
 
We will also explain how to create a text file to describe our plug-in to AnyWave<br/>
 +
The AnyWave-MATLAB API consists in a set of MATLAB functions. They will be described in details in a dedicated section.<br/>
  
The AnyWave-MATLAB API (Application Programming Interface) which is a set of MATLAB functions, will be described in details, with examples to illustrate their use.
+
=Setup the plugin folder=
 
+
=Requirements=
+
MATLAB software must be installed on the computer.
+
AnyWave should be able to detect the installation by itself.
+
However, in some special cases, MATLAB could be installed on a custom path AnyWave may not check.
+
In that case, the user must specify the path to MATLAB in AnyWave by changing options in the Preferences settings.
+
 
+
==Linux==
+
On Linux, csh must be installed at /bin/csh, or the MATLAB Engine API used by AnyWave will not function, and AnyWave will report "Failed to connect to MATLAB!" in the corresponding Process log.
+
<syntaxhighlight lang="bash">
+
sudo apt-get install tsch csh
+
</syntaxhighlight>
+
 
+
=Where to start?=
+
 
The first thing to do is to create the basic structure for a plug-in.<br/>
 
The first thing to do is to create the basic structure for a plug-in.<br/>
A MATLAB Scripted plug-in is very simple: it is a folder containing at least two files.
+
A MATLAB Scripted plug-in is very simple: it is a folder containing at least two files.<br/>
  
 
Let's begin by creating a folder (called MyPlugin) somewhere on the computer.<br/>
 
Let's begin by creating a folder (called MyPlugin) somewhere on the computer.<br/>
Line 47: Line 34:
 
Here we have three keywords (name, description, category).<br />
 
Here we have three keywords (name, description, category).<br />
 
Two keywords are mandatory : name and description. Other keywords are optional.<br />
 
Two keywords are mandatory : name and description. Other keywords are optional.<br />
==keywords==
+
==desc.txt keywords==
'''name:''' The plugin name used by Anywave (here PyExample Plugin).<br />
+
Some keywords may have several values. Separate the values by the colon character ''':''' <br/>
'''description:''' a brief description of what the plugin does.<br />
+
The table below shows all the keywords handled by AnyWave and their functions.<br/>
'''category (optional):''' It tells AnyWave where the plug-in will appear in the menus. Here, we decided to make it appear under the Python sub-menu in the Processes main menu.<br />
+
{| class="wikitable"
 
+
|-
The category feature is usefull to separate plug-ins that won't really do some calculation but convert data to another format or launch external tools.
+
! keyword !! description !! type
It could also be useful to classify signal processing algorithms.
+
|-
 +
| name || the name of your plugin. Must be unique.|| MANDATORY
 +
|-
 +
| description || short description || MANDATORY
 +
|-
 +
| category || Where to link the plugin in AnyWave menus || OPTIONAL
 +
|-
 +
| input_flags || what is required for input || OPTIONAL
 +
|-
 +
| flags || The plugin special flags || OPTIONAL
 +
|}
  
Three category keywords are recognized:
+
===category===
 +
'''category:''' It tells AnyWave where the plug-in will appear in the menus. Here, we decided to make it appear under the Python sub-menu in the Processes main menu.<br />
 +
The category feature is usefull to separate plug-ins by theme.<br/>
 +
Three category keywords are recognized:<br/>
 
* Process :  The plug-in will be set in the Processes menu with a subcategory and a name, for example 'Process:Correlation:Compute correlation'
 
* Process :  The plug-in will be set in the Processes menu with a subcategory and a name, for example 'Process:Correlation:Compute correlation'
 
* File: The plug-in will be set in the File Menu under the Export sub-menu. Example : 'File:Export to file.'
 
* File: The plug-in will be set in the File Menu under the Export sub-menu. Example : 'File:Export to file.'
 
* View: The plug-in will be set in the View Menu. Example : 'View:Launch 3D viewer'
 
* View: The plug-in will be set in the View Menu. Example : 'View:Launch 3D viewer'
 +
If no category is specified, AnyWave will set the plug-in in the Processes menu using the name defined in the file.<br/>
 +
<br/>
  
If no category is specified, AnyWave will set the plug-in in the Processes menu using the name defined in the file.
+
===input_flags===
 
+
'''input_flags:''' a list a ":" separated strings that set the input flags for the plugin.<br/>
=Copy the plug-in to the right location=
+
We are ready to add the plug-in to AnyWave.
+
 
+
Copy the folder MyPlugin to your user's AnyWave plugins directory. Remember to place it in the Matlab subfolder.
+
For example on Windows:
+
[[File:Matlab3.png|center]]
+
 
+
As you can see MyPlugin is located in the user's AnyWave path for Matlab Scripted plug-ins.
+
 
+
=Use the plug-in in AnyWave=
+
Launch AnyWave: the plug-in should be shown as available.
+
[[File:Matlab4.png|center]]
+
 
+
=AnyWave-MATLAB functions=
+
The table below shows a summary of all MATLAB functions available when writing a MATLAB Scripted plug-in:<br/>
+
 
+
'''Note:''' All AnyWave MATLAB functions start with 'aw_' to avoid confusions with other MATLAB functions.
+
  
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! Function !! Short description
+
! input_flag !! description  
 
|-
 
|-
| '''[[#aw_getplugininfo()|aw_getplugininfo]]''' || Returns useful information about the plugin.
+
| GetAllMarkers || AnyWave will copy all the markers and set them as input for the plugin. This will impact aw_getmarkers() function.
 
|-
 
|-
| '''[[#aw_getdata()|aw_getdata]]''' || Returns data from AnyWave.
+
| GetDurationMarkers || AnyWave will copy markers with a duration and set them as input. This will impact aw_getmarkers() function
 
|-
 
|-
| '''[[#aw_getmarkers()|aw_getmarkers]]''' || Returns markers from AnyWave.
+
| ProcessIgnoresChannelSelection || The plugin will be run without using the selected channels as input by default.
 
|-
 
|-
| '''[[#aw_addmarkers()|aw_addmarkers]]''' || Sends markers to AnyWave.
+
| ProcessRequiresChannelSelection || The plugin will only run if the user has selected channels.
 +
|-
 +
| GetAsRecordedChannels || AnyWave will copy all the as recorded channels found in the current file as input for the plugin. This will impact aw_getdata() function.
 +
|-
 +
| GetCurrentMontage || AnyWave will copy the current montaged channels and set them as input for the plugin. This will impact aw_getdata() function.
 
|}
 
|}
 
+
Example of desct.txt with input_flags:<br/>
==aw_getplugininfo==
+
<syntaxhighlight lang="text">
<syntaxhighlight lang="matlab">
+
name = MyPlugin
function [ infos ] = aw_getplugininfo()
+
description = do something in MATLAB
%aw_getplugininfo returns information about the input set for the plugin.
+
category = Process:Test:MyPlugin
%  infos = aw_getplugininfo();
+
input_flags = ProcessIgnoresChannelSelection:GetAllMarkers
%
+
%  returns a structure with the following fields:
+
%  .file
+
%      Name of the file open in AnyWave.
+
%  .labels
+
%      labels of channels set as input for the plugin.
+
%  .refs
+
%      references of channels: Empty strings for monopolar channels.
+
%  .max_sr
+
%      maximum sampling rate in Hz.
+
%  .total_duration
+
%      the total duration in seconds of the data.
+
%  .temp_dir
+
%      the path to a temporary directory created by AnyWave for the
+
%      plugin.
+
%
+
end
+
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
===flags===
 +
flags are used to configure the plugin capabilities.<br/>
 +
{| class="wikitable"
 +
|-
 +
! flag !! description
 +
|-
 +
| NoDataRequired or ProcessDoesntRequireData || indicates the plugin doesn't need a file to be open in AnyWave to run.
 +
|-
 +
| CanRunFromCommandLine || indicates that the plugin can also run in batch mode and using the command line.
 +
|}
  
==aw_getdata==
+
=main.m=
 +
This is the entry point function called by AnyWave.<br/>
 +
Depending on the type of plugin you are writing, you may have to setup some variables in this file as an initialisation:<br>
 +
Indeed, you may decide to compile your plugin to distribute it as a standalone software. In this case, use the following code in your main.m:<br/>
 
<syntaxhighlight lang="matlab">
 
<syntaxhighlight lang="matlab">
function [ channels ] = aw_getdata(cfg )
+
function main(varargin)
%aw_getdata request data from AnyWave
+
global args;
%  channels = aw_getdata(cfg)
+
if isdeployed
%  returns channels' data according to the settings defined in cfg structure.
+
% STANDALONE AnyWave Plugin code
%
+
  global host;
%  cfg may contain the following fields:
+
  global port;
+
  global pid;
%  cfg.file = 'file.eeg';
+
%      Specify the data file to use. (optional)
+
%  cfg.start = 10.;
+
%      starting position in seconds of requested data.
+
%      if this field is not specified, AnyWave will return data starting
+
%       at position 0s.
+
%  cfg.duration = 20.;
+
%      duration of requested data in seconds.
+
%      if this field is not specified, AnyWave will return ALL the
+
%      available data.
+
%  cfg.labels = {'A1', 'A2'};
+
%      cell array of strings to identify the required channel labels.
+
%      if no labels are specified, AnyWave will return the current selected
+
%      channels set as input for the plugin.
+
%      Optional field.
+
%  cfg.types = {'EEG'};
+
%      cell array of strings to specify the types of channels we want.
+
%      Optional field.*
+
%
+
%  NOTE: if labels and types are used, then the channel selection will be made with respect with these two constraints.
+
%  For example, if the labels cell array contains the A1 label, and the types cell array contains the EEG type, then
+
%  AnyWave will return the A1 EEG channel if it exists or nothing. If A1 is MEG or SEEG then nothing will be returned.
+
%
+
%  cfg.filtering = 'no';
+
%      specifies that we don't want AnyWave to filter the data.
+
%  cfg.filtering = 'yes';
+
%      specifies that we want data to be filtered.
+
%  Note:
+
%      if the filtering field is not specified, the data will be filtered by
+
%      AnyWave using the current filtering options.
+
%
+
%  cfg.eeg_lp = 10.;  requires filtering = 'yes'.
+
%      specifies that we want EEG data with low pass filter of 10Hz.
+
%  cfg.eeg_hp = 1.;  requires filtering = 'yes'.
+
%      specifies that we want EEG data with high pass filter of 1Hz.
+
%  cfg.meg_lp = 10.;  requires filtering = 'yes'.
+
%      specifies that we want MEG data with low pass filter of 10Hz.
+
%  cfg.meg_hp = 1.;  requires filtering = 'yes'.
+
%      specifies that we want MEG data with high pass filter of 1Hz.
+
%
+
%  cfg.decimate = 8;
+
%      specifies that we only take 1 sample of data every 8 samples.
+
%
+
%Output:
+
%  channels is an array of strucrures with the following fields:
+
%  .label;
+
%      a string representing the channel's label.
+
%  .ref;
+
%      a string representing the reference channel. Can be empty.
+
%  .data;
+
%      a data vector containing the samples.
+
%  .sr;
+
%      the sampling rate of data.
+
%  .hpf; 
+
%      the High Pass filter set.
+
%  .lpf;
+
%      the Low Pass filter set.
+
end
+
</syntaxhighlight>
+
 
+
'''Example:'''<br />
+
<syntaxhighlight lang="matlab">
+
function main
+
 
+
% request 10 seconds of data starting at 2.5s
+
cfg = [];
+
cfg.start = 2.5;
+
cfg.duration = 10;
+
channels = aw_getdata(cfg);
+
 
+
% request raw data (with no filtering)
+
cfg = [];
+
cfg.start = 2.5;
+
cfg.duration = 10;
+
cfg.filtering = 'no';
+
channels = aw_getdata(cfg);
+
 
+
% request data with low pass filter of 25Hz on EEG channels
+
cfg = [];
+
cfg.start = 2.5;
+
cfg.duration = 10;
+
cfg.filtering = 'yes';
+
cfg.eeg_lp = 25;
+
channels = aw_getdata(cfg);
+
 
+
%  request data for all EEG channels, no filtering specified
+
cfg = [];
+
cfg.start = 0;
+
cfg.duration = 10; % 10 seconds of data
+
cfg.types = { 'EEG' };
+
channels = aw_getdata(cfg);
+
  
 +
  if (nargin < 3)
 +
      error('missing arguments.');
 +
  end
 +
  host = varargin{1};
 +
  port = str2num(varargin{2});
 +
  pid = str2num(varargin{3});
 +
  if (nargin > 3)
 +
    args = varargin{4};
 +
  end
 +
 
 +
  assignin('base', 'host',  host);
 +
  assignin('base', 'port', port);
 +
  assignin('base', 'pid', pid);
 +
  assignin('base', 'args', args);
 +
% end of STANDALONE AnyWave Plugin code
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==aw_addmarkers==
+
=Copy the plug-in to the right location=
<syntaxhighlight lang="matlab">
+
We are ready to add the plug-in to AnyWave.
function aw_addmarkers(markers)
+
%aw_addmarkers add new markers to AnyWave
+
%  aw_addmarkers(markers)
+
%
+
%  markers is an array of structs. Each element is a marker to be added.
+
%
+
% See also AwMarker
+
+
%
+
end
+
  
function [ marker ] = AwMarker( label, position, duration, value, targets)
+
Copy the folder MyPlugin to your user's AnyWave plugins directory. Remember to place it in the Matlab subfolder.
% defines the structure for a marker
+
For example on Windows:
%
+
[[File:Matlab3.png|center]]
%  marker.label
+
%      the label for the marker.
+
%  marker.position
+
%      the position in seconds from the beginning of data.
+
%  marker.duration
+
%      the duration in seconds. Can be 0 if the marker is just a position
+
%      in time.
+
%  marker.value
+
%      the value associated to the marker. -1 indicates that no value is
+
%      set.
+
%  marker.targets
+
%      a cell array containing the targeted channels. Empty if the marker
+
%      is global.
+
marker.label = label;               
+
marker.position = position;
+
marker.duration = duration;         
+
marker.value = value;
+
marker.targets = targets;           
+
  
end
+
As you can see MyPlugin is located in the user's AnyWave path for Matlab Scripted plug-ins.
</syntaxhighlight>
+
  
'''Example:'''<br />
+
=Use the plug-in in AnyWave=
<syntaxhighlight lang="matlab">
+
Launch AnyWave: the plug-in should be shown as available.
function main
+
[[File:Matlab4.png|center]]
%
+
% add one marker labeled spike at position 3s.
+
%
+
marker.label = 'spike';
+
marker.position = 3;
+
 
+
 
+
% send the marker to AnyWave
+
aw_addmarkers(marker);
+
 
+
% add two markers with value and targeted channels
+
markers(1).label = 'spike';
+
markers(1).position = 3;
+
markers(1).value = 2;
+
markers(1).targets = {'A2'};  % this marker is targeting the channel named A2
+
 
+
markers(2).label = 'selection';
+
markers(2).position = 15;
+
markers(2).duration = 1.5; % this marker is a selection of 1.5s starting at position 15s.
+
% no targets are specified, the marker is GLOBAL
+
 
+
% send the markers to AnyWave
+
aw_addmarkers(markers);
+
 
+
end
+
</syntaxhighlight>
+
 
+
==aw_getmarkers==
+
<syntaxhighlight lang="matlab">
+
function [ markers ] = aw_getmarkers( cfg )
+
%aw_getmarkers retrieves markers from AnyWave
+
%  markers = aw_getmarkers(cfg)
+
%  markers = aw_getmarkers()
+
%  returns markers which match the settings defined in cfg structure or
+
%  all available markers if no cfg structure is passed as paramater.
+
%
+
%  cfg may contain the following fields:
+
+
%  cfg.values = [1 10 12];
+
%      get markers depending on their value. Several values can be
+
%      specified.
+
%  cfg.labels = {'Spike1', 'Spike2'};
+
%      get markers depending on their labels.
+
%  cfg.channels = {'A1', 'A2'};
+
%      get markers which target specific channels.
+
%
+
% Note: if a marker matches several conditions (values, labels or channels) only one
+
% instance of the marker is returned.
+
%
+
%Output:
+
%  markers is an array of strucrures with the following fields:
+
%  .label
+
%      a string representing the marker's label.
+
%  .position
+
%      the position in seconds from beginning of data.
+
%  .duration
+
%      duration in seconds. Can be zero if the marker is just a time position.
+
%  .value
+
%      a numerical value associated with the marker. -1 indicates that no value is set.
+
%  .channels 
+
%      a cell array of channel labels. Can be empty if the marker is
+
%      global to all channels.
+
%
+
end
+
</syntaxhighlight>
+
 
+
'''Example:'''<br />
+
<syntaxhighlight lang="matlab">
+
function main()
+
% get all the markers currently available in AnyWave
+
markers = aw_getmarkers();
+
 
+
% get markers labeled spike
+
cfg=[];
+
cfg.labels = {'spike'};
+
 
+
spikes = aw_getmarkers(cfg);
+
 
+
% get markers with specified values
+
cfg=[];
+
cfg.values = [2 3 4];
+
 
+
markers = aw_getmarkers(cfg);
+
 
+
% mixing values and labels conditions
+
cfg=[];
+
cfg.labels = {'spike', 'selection'};
+
cfg.values = [2 10];
+
 
+
% Note that the returned markers are markers which satisfied one of the conditions (it's a logical OR test).
+
% That means you can get a marker labeled spike but with a value that is not 2 or 10;
+
% It's up to you to parse the array of structures afterward.
+
markers = aw_getmarkers(cfg);
+
 
+
% CAUTION
+
markers can be an empty matrix if no markers are returned by AnyWave
+
 
+
end
+
</syntaxhighlight>
+
 
+
==AwSendMessage(message)==
+
This function sends a text message to AnyWave. The message will appear in the Processes console in AnyWave and also in the plugin's log.
+
 
+
Example:
+
<syntaxhighlight lang="matlab">
+
 
+
cfg = [];
+
cfg.start = 0;
+
cfg.duration = -1;
+
channels = aw_getdata(cfg); % get all data
+
 
+
for (i=1:numel(channels)
+
  AwSendMessage(sprintf('Processing channel %d', i));
+
  % do something
+
end
+
</syntaxhighlight>
+
 
+
==AwIsProcessTerminated==
+
This function will return true or false depending on the action of the user about the currently running plug-in.
+
 
+
This function is usefull when processing heavy and long calculations. If the user want to cancel the current processing, it may be suitable to cancel the current calculation running in MATLAB as well.
+
 
+
Example:
+
<syntaxhighlight lang="matlab">
+
 
+
cfg = [];
+
cfg.start = 0;
+
cfg.duration = -1;
+
channels = aw_getdata(cfg); % get all data
+
 
+
%
+
for (i=1:numel(channels)
+
    if (~AwProcessIsTerminated())
+
        % do some heavy calculation on a data contained in channel
+
  end
+
end
+
 
+
</syntaxhighlight>
+

Latest revision as of 14:17, 30 March 2020

Introduction

This section targets people who have a good knowledge and practice of the MATLAB programming language.
The purpose is to explain how to write a MATLAB function that will be the heart of a plug-in executed in MATLAB by AnyWave.
We will also explain how to create a text file to describe our plug-in to AnyWave
The AnyWave-MATLAB API consists in a set of MATLAB functions. They will be described in details in a dedicated section.

Setup the plugin folder

The first thing to do is to create the basic structure for a plug-in.
A MATLAB Scripted plug-in is very simple: it is a folder containing at least two files.

Let's begin by creating a folder (called MyPlugin) somewhere on the computer.
This can be done in MATLAB: create a folder and create a new function called main in that folder.
The main function is MANDATORY. It will be the main function AnyWave will call to execute our plugin.

Matlab1.png

As shown in the image above, a MyPlugin folder has been created and a main function was added.

We are now ready to write our first Matlab plug-in!!

Refer to the AnyWave-MATLAB functions section to learn how to program a plug-in that will communicate with AnyWave.

Writing the desc.txt file

We have written the MATLAB code and now all we have to do is to create a descriptive text file to inform AnyWave about our plug-in.
Let's dot it in Maltab:

Matlab2.png

The file must be named desc.txt and may looks like:

name = My Matlab Plugin
description = I am a plug-in written in Matlab
category = Process:MATLAB:My MATLAB Plugin

The syntax is to set keywords and values.

Here we have three keywords (name, description, category).
Two keywords are mandatory : name and description. Other keywords are optional.

desc.txt keywords

Some keywords may have several values. Separate the values by the colon character :
The table below shows all the keywords handled by AnyWave and their functions.

keyword description type
name the name of your plugin. Must be unique. MANDATORY
description short description MANDATORY
category Where to link the plugin in AnyWave menus OPTIONAL
input_flags what is required for input OPTIONAL
flags The plugin special flags OPTIONAL

category

category: It tells AnyWave where the plug-in will appear in the menus. Here, we decided to make it appear under the Python sub-menu in the Processes main menu.
The category feature is usefull to separate plug-ins by theme.
Three category keywords are recognized:

  • Process : The plug-in will be set in the Processes menu with a subcategory and a name, for example 'Process:Correlation:Compute correlation'
  • File: The plug-in will be set in the File Menu under the Export sub-menu. Example : 'File:Export to file.'
  • View: The plug-in will be set in the View Menu. Example : 'View:Launch 3D viewer'

If no category is specified, AnyWave will set the plug-in in the Processes menu using the name defined in the file.

input_flags

input_flags: a list a ":" separated strings that set the input flags for the plugin.

input_flag description
GetAllMarkers AnyWave will copy all the markers and set them as input for the plugin. This will impact aw_getmarkers() function.
GetDurationMarkers AnyWave will copy markers with a duration and set them as input. This will impact aw_getmarkers() function
ProcessIgnoresChannelSelection The plugin will be run without using the selected channels as input by default.
ProcessRequiresChannelSelection The plugin will only run if the user has selected channels.
GetAsRecordedChannels AnyWave will copy all the as recorded channels found in the current file as input for the plugin. This will impact aw_getdata() function.
GetCurrentMontage AnyWave will copy the current montaged channels and set them as input for the plugin. This will impact aw_getdata() function.

Example of desct.txt with input_flags:

name = MyPlugin
description = do something in MATLAB
category = Process:Test:MyPlugin
input_flags = ProcessIgnoresChannelSelection:GetAllMarkers

flags

flags are used to configure the plugin capabilities.

flag description
NoDataRequired or ProcessDoesntRequireData indicates the plugin doesn't need a file to be open in AnyWave to run.
CanRunFromCommandLine indicates that the plugin can also run in batch mode and using the command line.

main.m

This is the entry point function called by AnyWave.
Depending on the type of plugin you are writing, you may have to setup some variables in this file as an initialisation:
Indeed, you may decide to compile your plugin to distribute it as a standalone software. In this case, use the following code in your main.m:

function main(varargin)
global args;
if isdeployed
% STANDALONE AnyWave Plugin code
   global host;
   global port;
   global pid;
 
   if (nargin < 3)
       error('missing arguments.');
  end
  host = varargin{1};
  port = str2num(varargin{2});
  pid = str2num(varargin{3});
  if (nargin > 3)
    args = varargin{4};
  end
 
  assignin('base', 'host',  host);
  assignin('base', 'port', port);
  assignin('base', 'pid', pid);
  assignin('base', 'args', args);
% end of STANDALONE AnyWave Plugin code
end

Copy the plug-in to the right location

We are ready to add the plug-in to AnyWave.

Copy the folder MyPlugin to your user's AnyWave plugins directory. Remember to place it in the Matlab subfolder. For example on Windows:

Matlab3.png

As you can see MyPlugin is located in the user's AnyWave path for Matlab Scripted plug-ins.

Use the plug-in in AnyWave

Launch AnyWave: the plug-in should be shown as available.

Matlab4.png