%mocha
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Jan Chrin, Paul Scherrer Institut
% March 2017
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  (1) Establishing connections to EPICS Process Variables (PVs) 
%  (2) Simple single channel operations (and the PVData Object)
%  (3) Understanding the Status Error Code / MATLAB Exceptions
%  (4) Waveforms and Arrays
%  (5) Handling Enumerated Types
%  (6) Multiple scalar operations, i.e., operations on several PVs with scalar values 
%  (7) Multiple cell array ('compound') operations, i.e., on several PVS with scalar/vector values
%  (8) Multiple Struct operations on several PVS returning PVData Objects plus Multiple Sets for Scalars/Vectors
%  (9) Asynchronous interactions and retrieving data from Cache
% (10) Synchronous group operations, also with externally defined groups 
% (11) Monitors, either with or without user supplied callbacks (in essence, matlab scripts)
% (12) Control system parameters, i.e., operating limits, engineerimg units
% (13) Retrieve channel medadata, e.g., data type, native no. elements, connection state, read/write access, etc.
% (14) Setting timeouts for set/get operations
% (15) Setting synchronous group timeouts for set/get operations
% (16) Special Methods, e.g., setAndMatch
% (17:END) Gracefully terminate CAFE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Test channels
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% To run this test script, first create 5 soft channels of the given class
% MATLAB's Run and Advance utility will run each of these 17 sections
% at the user's prompt. 

% pv(1:2) are numeric, pv(3) is a wf, pv(4) is a mbbo record (enum),
% pv(5) is a dbr_string_t, pv(6) does not exist
pv={'ARIDI-BPM-01LE:X-AVG','ARIDI-BPM-01LE:Y-AVG','ARIDI-BPM-01LE:WF-INT-2',...
    'ARIDI-BPM-01LE:SET-ENABLE','ARIDI-BPM-01LE:SET-ENABLE.DESC','PV-JOKE'};


% Create the following monActionLocal.m in your local directory from where matlab 
% will be started. This file can optionally be used as a callback function for monitors.

%monActionLocal.m
%{
function varargout = monActionLocal(varargin)
disp('monActionLocal called for:');
pvName=mocha('getPVFromHandle', varargin{1});
disp(pvName);
X=['Handle =', num2str(varargin{1})];
disp(X);

%[varargout{1} varargout{2}] = mocha ('getCache', varargin{1});
[var1,var2]=mocha ('getCache', varargin{1});
[s,t]=mocha('getStatus',  varargin{1});
%X=['Value=', num2str(varargout{1}), ' Status=', num2str(s), ' [',t(1), ' ', t(2),']'];
X=['Value =', num2str(var1)];
Y=['Status=', num2str(s)];
disp(X)
disp(Y)   
disp([t(1),t(2)])
%}



%%%%
mocha('test'); %test if mocha MEX-file is accessible
mocha('version'); %lists mocha/cafe version
%mocha('show'); %lists all available mocha methods
%%%%




%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% (1) Establishing connections to EPICS Process Variables (PVs) 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Explicitly opening channels is good practice even if only optional
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (1) Establishing connections to EPICS Process Variables (PVs) ');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');

%returns an array of handles (object references)
hpv=mocha('open',pv);

%When executing the 'open' message as above, the default behaviour
%is for each 'open' to wait for a fixed amount of time, given by:
openTime = mocha ('getOpenWaitTime'); %currently 0.4s
%The 'open' wait time can, however, be configured by the user as follows:
mocha ('setOpenWaitTime', 0.2); %set to 0.2s
mocha ('setOpenWaitTime'); %reset to default (0.4s)
%The above shoud be set before the mocha 'open' method is executed


%If opening numerous channels, however, better to do this with a single message.
%This is done as shown here:
mocha('openPrepare');
hpv=mocha('open',pv); 
% and more mocha 'open' messages would follow here...
% Finally send the messages and wait for 0.2s for connections to complete
mocha('openNowAndWait', 0.2)

%There are a number of ways to extract information on the created handles
%print all handles whether connected or not 
mocha('printHandles');

%Array of handles and matching PVnames
[handles, pvs] = mocha ('getHandles');
[handles, pvs] = mocha ('getConnectedHandles');
[handles, pvs] = mocha ('getDisconnectedHandles');
%Array of handles, matching PVnames and connection states
[handles, pvs, states] = mocha ('getHandleStates');

%print information on disconnected channels
if (~mocha('allConnected')) 
	mocha('printDisconnectedHandles');
	%or
	[handles, pvs] = mocha ('getDisconnectedHandles');
	for n =1 : length(handles)
	  X=['Handle=', num2str(handles(n)), 'PV=',pvs(n)];
	  disp(X);
	end
end	

%To check on the connection state of a single channel
logicValue=mocha('isConnected', pv(1)); %by pvName
logicValue=mocha('isConnected', hpv(1)); %byHandle


%Handle and PV Mapping
%Retrieve handle for a given PV
handle = mocha ('getHandleFromPV', pv(1));
pvName = mocha ('getPVFromHandle', hpv(1));



%Channels may be closed when the application no longer needs them
%mocha ('close', pv(1)); %close given channel
%mocha ('close', pv); %close given channels
%Close all chanels and release all ca resources before exiting MATLAN
%mocha ('close');

%END (1) Establishing connections to EPICS Process Variables (PVs) 	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(2) Simple single channel operations (and the PVData Object)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (2) Simple single channel operations ');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%Now perform some get/set operations

%Mocha operations will accept either a pv name or the pv handle 
%(as returned from the mocha 'open' method) as the 2nd input argument

%GET value
%by pvName; value (scalar or vector) returned in native data type
[value, status]= mocha ('get', pv(1));
%by Handle; value (scalar or vector) returned in native data type 
[value, status]= mocha ('get', hpv(1));

%by pvName; value (scalar or vector) returned as a MATLAB double 
[value, status]= mocha ('get', pv(1), 'uint8');
%by pvName; value (scalar or vector) returned as a MATLAB uint8 type 
[value, status]= mocha ('get', pv(1), 'double');

%SET 
%value (scalar or vector) can be in any MATLAB data type
[status] = mocha('set', pv(1), value);

%Single channel operations that return data, alarams and timestamps
%pvData struct returned; pvData.val has the native data type
pvData = mocha ('getPV', pv(1));
%pvData struct returned; pvData.val has the double data type
pvData = mocha ('getPV', pv(1), 'double')

pvData.val;
pvData.status; % int32
pvData.alarmStatus;  %int16
pvData.alarmSeverity; %int16
pvData.ts; %[year, month, day, hour, min, sec, nsec]


%Other ways to retrieve alarms and timestamps following a 'get'
 ts=mocha('getTimestamp', pv(1));
[ets]=mocha('getETS', pv(1))  % [secPastEpoch, nsec]


[alarmStatSev] = mocha ('getAlarm', pv(1)); %[alarmStatus, alarmSeverity] 
[alarmStatSev] = mocha ('getAlarmAsString', pv(1)) %[alarmStatus, alarmSeverity] 
	
%END (2) Simple single channel operations 	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(3) Understanding the Status Error Code / MATLAB Exceptions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (3) Understanding the Status Error Code / MATLAB Exceptions');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
[value, status]= mocha ('get', pv(1));
        pvData = mocha ('getPV', pv(1));
        status = pvData.status;
			
%A Status Code of 1 represents a normal completion				
%Status Codes <=480	are generated by the native ca library			
%Status Codes >=600 are generated by the underlying CAFE library				

%A status code is paired to an enumerated name
statusText = mocha ('statusAsText', status);

%Further information concerning the code
statusInfo = mocha ('statusInfo', status);

%Another way to retrieve the status information w.r.t. the previous 
%operation on a given handle is

[status, statusCellArray] = mocha ('getStatus', pv(1))


%Rather than have to check on the status codes, CAFE MATLAB exceptions can be
%enabled (mocha default is to have exceptions dis-enabled)

mocha ('withExceptions', true) %enables MATLAB exceptions for single channel operations

try
	[val, status] = mocha ('get', pv(6)) %pv(6) is dis-connected as channel does not exist
catch ME
  ME
  %A MATLAB exception, MException, comprises the 'identifier' and 'message' properties
  if strfind(ME.identifier,'CAFE_WARN')
		warning(ME.message);
	elseif strfind(ME.identifier, 'CAFE_INFO')
		disp(ME.message);
	elseif strfind(ME.identifer,'CAFE_ERROR')
		error(ME.message);
	else
		disp(ME.message);
	end			
end

mocha ('withExceptions', false) %dis-enable MATLAB exceptions for single channel operations
	
%END (3) Simple single channel operations 	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(4) Waveforms and Arrays
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (4) Waveforms and Arrays');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');

[value, status] = mocha ('get', pv(3)); %Returns all elements of the wf
[pvdata] = mocha ('getPV', pv(3)); %Returns all elements of the wf in pvdata.val

%The default behaviour is for all elements of the wf to be read out and
%presented. However, this number together with an offset can be configured
%by the user

nelemWF = mocha('setNelem', pv(3), 20); 
offsetWF= mocha('setOffset',pv(3), 12);

%nelemWF = mocha('setNelem', pv(3)); %sets to native value
%offsetWF= mocha('setOffset',pv(3), 0); %but remember to set offset to 0 too!

%The subsequent method then returns an array (value) of length 20,
%containing data from the 13th until the 32nd element of the waveform.
[value, status] = mocha ('get', pv(3), 'double')
	
%END (4) Waveforms and Arrays	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(5) Handling Enumerated Data Types
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (5) Handling Enumerated Data Types');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%Consider pv(4) which is an mbbo record with enumerated value/names
%0/off  1/on  2/{transient state}
%with current value 1, i.e., 'on'

%Default behaviour is to present the value as a string:
[value, status] = mocha ('get', pv(4));
%Hence above method will return a value of 'on'
X=[pv(4), 'has value', value];
disp(X);
%Any MATLAB numberic data type, such as 'int8', 'single' could also be specified
[value, status] = mocha ('get', pv(4), 'uint8');
%Hence above method would return a value of 1
X=[pv(4), 'has value', num2str(value)];
disp(X);

%For set, the input value can be any valid numeric or string;
%It will be internally converted to an unsigned short before being sent to the IOC.
[status] = mocha ('set', pv(4), 0); % will send the value 0, i.e. 'off'
[status] = mocha ('set', pv(4), 'off'); % will send the value 0, being the value euivalent of the enumerated name
[status] = mocha ('set', pv(4), '0'); %will send an unsigned short value of 0
[status] = mocha ('set', pv(4), 'anything'); %will complain that this is not a valid enumerated name
[status] = mocha ('set', pv(4), '0anything'); %will convert this to an unsigned short value of 0

%END (5) Handling Enumerated Data Types 	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(6) Multiple Scalar Operations, i.e., operations on several PVs with scalar values 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (6) Multiple Scalar Operations, i.e., operations on several PVs with scalar values');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%GET:
%Messages are collected and transmitted with one single method invocation
%If Handle/PV points to a WF, only the 1st element of the WF is returned.
%If a data type for the retrieved data is not specified, then the native type
%of the first handle/pv is selected, here (pv(1))


pvInput=[pv(1:3),pv(6)];
[val, isAllOK, s] = mocha ('getScalarArray', pvInput, 'double');
% Alternatively 
% [val, isAllOK, s] = mocha ('get', pvInput, 'double'); 
% may be called; 
% mocha('getScalarArray') will then be called internally
[val, isAllOK, s] = mocha ('get', pvInput, 'double')
if isAllOK ~= 1
	for n=1:length(s)
		if s(n) ~= 1
	 		[codes,ctext]=mocha('getStatus', pvInput(n));
	 		X=['GET: Error in Elem:', num2str(n), pvInput(n), ':'];
			Y=[num2str(codes), ctext(1)];
			disp(X); disp(Y);			
		end 
  end 
end


%SET:
val(1)=val(1)+0.001;
[isAllOK, s] = mocha ('setScalarArray', pvInput, val);
% Alternatively 
% [isAllOK, s] = mocha ('set', pvInput, val); 
% may be called; 
% mocha('setScalarArray') will then be called internally


[isAllOK, s] = mocha ('set', pvInput, val);

if isAllOK ~= 1
	for n=1:length(s)
		if s(n) ~= 1
	 		[codes,ctext]=mocha('getStatus', pvInput(n));
	 		X=['SET: Error in Elem.:', num2str(n), pvInput(n), ':'];
			Y=[num2str(codes), ctext(1)];
			disp(X); disp(Y);			
		end 
  end 
end
	
%END (6) Multiple Scalar Operations, i.e., operations on several PVs with scalar values  	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(7) Multiple cell array ('compound') operations, i.e., on several PVS with scalar/vector values
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (7) Multiple cell array ("compound") operations, i.e., on several PVS with scalar/vector values');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');

%GET
%Note the use of the handles; one could equally use the PV array, pv
[val, isAllOK, s] = mocha ('getCellArray', hpv);
%returns a cell array, wherein the elements are of native data type and can be of any size  

%SET
val{1}=1.111;
val{2}=2.222;
val{3}=(1:256); %wf
val{4}='on'; %mbbo
[isAllOK, s] = mocha ('set', hpv, val{:});

%Here, the data type of all returned values is defined to be a string
[val, isAllOK, s] = mocha ('getCellArray', hpv, 'string')

%END (7) Multiple Scalar Operations, i.e., operations on several PVs with scalar values  	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(8) Multiple Struct operations on several PVS returning PVData Objects plus Multiple Sets for Scalars/Vectors
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section (8) Multiple Struct operations on several PVS returning PVData Objects plus Multiple Sets for Scalars/Vectors');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%GET
%returns a sequence of pvData structs 
[pvData, isAllOK] = mocha ('getPVArray', pv)
disp('Display all pvData.val values and statuses:');
%pvData(i) corresponds to the data returned by pv(i)
{pvData.val}
[pvData.status]
%pvData(1:6).ts %timestamps
%[pvData(1:6).alarmStatus] 
%[pvData.alarmSeverity]
disp('Alternatively this same data (values, statuses only) could also be retrieved with getCellArray:');
[val, isAllOK, s] = mocha ('getCellArray', hpv)

%SET
pvData(1).val =0.2345;
pvData(2).val =2.6789;
pvData(3).val =(256:512);
pvData(4).val = 'off';
[isAllOK, s] = mocha ('set', hpv, pvData(1:6).val);
%Check values were set
[val, isAllOK, s] = mocha ('getCellArray', hpv)
disp('Recall we had previously set an offset for wf of value:');
offset=mocha('getOffset',hpv(3))
disp('Setting new offset/nelem for wf has value:');
offset=mocha('setOffset',hpv(3),0)
%nelem =mocha('setNelem',hpv(3), mocha('getnelemnative',hpv(3)) )
nelem =mocha('setNelem',hpv(3),10);
disp('Note that setNelem and setOffset are for readback of waveform only');
%SET
%Another way to set
dd{1} = 3.33;
dd{2} = uint8(4);
dd{3} = [1,2,3,4,5,6,7,8,9,10,11,12,2.6,2.7,2.8,2.9,3.0,3.1,3.2,3.3]; %(2:257); %
dd{4} = 'off';
dd{5} = 'Enable or Dis-enable BPM';
dd{6} = 0;
[isAllOK,s] = mocha ('setMany', pv(1:5), dd{1:5});
%or this will call setMany
dd{4} = 'on';
[isAllOK,s] = mocha ('set', pv(1:5), dd{1:5});
%Check values were set
[pvData, isAllOK] = mocha ('getPVArray', hpv(1:5));
{pvData.val}
pvData(3).val


%END (8) Multiple Struct operations on several PVS returning PVData Objects plus Multiple Sets for Scalars/Vectors
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(9) Asynchronous interactions and retrieving data from Cache
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(9) Asynchronous interactions and retrieving data from Cache');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%GET
%Messages are aggregated until the user decides when to send the messages
%Note that there is no variable for the return data! This will be retrieved
%later through the getCache method
        [s] = mocha ('getAsyn', pv(1));
[isAllOK,s] = mocha ('getAsyn', pv(2:5));
%Now send the messages
mocha('sendNow');  %optional; otherwise 'getCache' will do the send
%or mocha('caFlush');

%getCache methods are used to retrieve the data;
%methods will wait until any outstanding callback is complete or a timeout is reached
[val, s] = mocha('getCache', pv(1), 'double');
 pvData  = mocha('getCache', pv(2));
%get several pvs in one call
[val, isAllOK, s] = mocha('getScalarArrayCache', pv, 'string');
%or getCache will call getScalarArrayCache
[val, isAllOK, s] = mocha('getCache', pv, 'string')
[val, isAllOK, s] = mocha('getCellArrayCache', pv) 
[pvData, isAllOK] = mocha('getPVArrayCache', pv)
%SET
%Set messages are by default asynchronous and non-blocking.
%The user may configure any given handle such that the messages are blocking, 
%meaning a subsequent set method will wait until the server has reported back to the client:
%BLOCKING:
mocha('setPutWait', pv); %policy changed to blocking for all handles pv(1:6)
status = mocha ('set', pv(1), 0.1); %blocks, status may return a timeout error
%NON_BLOCKING - this is the default
mocha('setPutNoWait', pv); %policy changed to non-blocking for all handles pv(1:6)
status = mocha ('set', pv(1), 0.2); %blocks, status will never return a timeout error
%Want to be sure the value got written?
disp('Executing setAndGet; Return val should equal input value, i.e., 0.3');
[val, status]= mocha('setAndGet', pv(1), 0.3) %val will be 0.3 on success.
%The above method initiates a set followed immediately by a get.
%The user should be aware that in the event of other applications writing
%to the same epics channel, the possibility exists that this external write 
%may occur between the set and subsequent get in the above method.
%The data type of the returned value matches that of the input value.

%AGGREGATING ASYNCHRONOUS SET OPERATIONS - Can be ignored as user case is expected to be uncommon
%If required set methods can also be aggregated before being sent asynchronously over the network
dd{1} = double(3.3);
dd{2} = uint8(4);
dd{3} = [11,12,13,14,15,6,7,8,9,10,11,12,2.6,2.7,2.8,2.9,3.0,3.1,3.2,3.3]; %(2:257); %
dd{4} = 'off';
dd{5} = 'Enable or Dis-enable BPM';
dd{6} = 0;

mocha('setPutPrepare', pv(1:6));
s = mocha ('set', pv(1), dd{1});
s = mocha ('set', pv(2), dd{2});
s = mocha ('set', pv(3), dd{3});
s = mocha ('set', pv(4:5), dd{4:5});
s = mocha ('set', pv(6), dd{6});
mocha('sendNow');

%Note that a subsequent set on the same given channel
%will trigger a 'send' to IOC transaction on the pending set 
mocha('setPutPrepare', pv(1:6));
s = mocha ('set', pv(1), dd{1});
s = mocha ('set', pv(1), 0.2); %will first trigger a send of the previous 'set'
[val,s]=mocha('get', pv(1)) %a get method will also initiate a send of all previous messages

%Reset Policy - Remember to reset the policy, e.g., to asynchronous, blocking
mocha('setPutWait', pv);
%or
%Reset Policy - Remember to reset the (default) policy, e.g., to asynchronous, non-blocking, non-aggregating
mocha('setPutNoWait', pv);

%END (9) Asynchronous interactions and retrieving data from Cache
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(10) Synchronous Group Operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(10) Synchronous Group Operations');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%
%Uses the ca_sg functionality which guarantees a set of ca requests have completed, or a timeout is reached
%First define a group; here a simple group of two BPMs
s = mocha('defineGroup', 'gBPM', pv(1:2));
%First define a group; here a collection of non-related pvs
s = mocha('defineGroup', 'gSnapshot', pv(1:5));
%Open a group; optional, otherwise first get/set method will establish the connections
gHandle1 = mocha('openGroup', 'gBPM');
gHandle2 = mocha('openGroup', 'gSnapshot');

%A synchronous group can be regarded as a logical software unity;
%*** A pv within a group will have its own, separate handle *** 
%as revealed from within the following command
mocha('printHandles');
%For example, three ca virual circuits have thus been created for pv(1)
%as this pv exists in its own right, within group 'gBPM' and within group 'gSnapshot'
%Likewise two ca virtual circuits have been created for the waveform in pv(3)
%The various handles for the given pv may be configured independently
%e.g. In the following we will set the nelem for pv(3) (hpv(3)) to 6
% and the the nelem for pv(3) within the group 'gSnapshot' to 12
newNelem=mocha('setNelem', pv(3), 6); %corresponds to handle for pv(3)
%set nelements for handle withing group for pv(3) to 12
handlesWithinGroup=mocha('getHandlesWithinGroup', gHandle2);
newNelem=mocha('setNelem', handlesWithinGroup(3), 12);  %handlesWithinGroup(3) corresponds to pv(3) in group 'gSnapshot'
disp('pv(3) should have been read with 6 elements:')
[val,s]=mocha('get', pv(3)); 
val
disp('pv(3) should have been read with 12 elements:')
[val,s]=mocha('get', handlesWithinGroup(3)); 
val


[pvData1, isAllOK] = mocha('getGroup', 'gBPM'); %2nd argument could equally have been the group handle, i.e., gHandle1
[pvData2, isAllOK] = mocha('getGroup', gHandle2); %2nd argument could equally have been the group name, i.e., 'gSnapshot'

%pvData is an array of returned s
pvData2.val
pvData2.status; % int32
pvData2.alarmStatus;  %int16
pvData2.alarmSeverity; %int16
pvData2.ts; %[year, month, day, hour, min, sec, nsec]

mocha('get',handlesWithinGroup(3))

pvData2(3).val=(1:256); 
[isAllOK,s]=mocha('setGroup', gHandle2, pvData2.val);
%reset to 256
mocha('setNelem', handlesWithinGroup(3), 256)
mocha('get',handlesWithinGroup(3))


%Configuring groups through XML - See documentation/talk for XML format
% mocha('loadXMLGroups', 'test.xml'); 

%List Groups and group Members
disp('listGroups:');
[groups] = mocha('listGroups')
disp('Show members of group gSnapshot:');
[members]= mocha('listGroupMembers', 'gSnapshot')

%This will only CLOSE and remove groups that have already been OPENED!
%%for i =1:length(groups)   
%%mocha('closeGroup', groups{i})
%%end	

%END (10) Synchronous Group Operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%(11) Monitors, either with or without user supplied callbacks (in essence, matlab scripts)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(11) Monitors, either with or without user supplied callbacks (in essence, matlab scripts');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%Simple Monitors
monitorID = mocha ('monitor', pv(1));
%Once the monitor is started, data may be retrieved via one of the cache methods

[val,s] = mocha('getCache', pv(1)); %Scalar or vector returned in native data type
[val,s] = mocha('getCache', pv(1), 'double'); %Scalar or vector returned as a MATLAB double
[val,s] = mocha('getCache', pv(1), 'uint8'); %Scalar or vector returned as a MATLAB uint8


[pvData] = mocha('getPVCache', pv(1)) %PVData Object returned with pvData.val in native data type
[pvData] = mocha('getPVCache', pv(1), 'single'); %PVData Object returned with pvData.val as a MATLAB float
[pvData] = mocha('getPVCache', pv(1), 'int32'); %PVData Object returned as a MATLAB int32

s = mocha('monitorStop', pv(1), monitorID); %Stop monitor for handle with given monitorID
s = mocha('monitorStop', pv(1)); %Stop all monitors for this handle
s = mocha('monitorStop'); %Stop all monitors for all handles


%Monitors with Actions, i,e, callback of MATLAB script

%Need to give the numeric value of the handle as input argument to monActionLocal
monAction=['monActionLocal(', num2str(hpv(1)), ')'] ;
%The above reads monAction = 'monActionLocal(1)'
%Here monActionLocal(1) refers to a Matlab Script monActionLocal.m 
%that takes a single input argument, which is the handle of the monitored channel

monitorID= mocha('monitor',hpv(1),monAction);

%The monAction.m script will typically retrieve data from Cache as follows:
%[val,status]=mocha ('getCache', varargin{1});

mocha('monitorFlushEvent', pv(1)); %will execute the MATLAB script monActionLocal.m if new data has arrived
mocha('monitorFlushEvent'); %will execute all 'actions' for all 'handles' that have received new data

%The above command is used in conjunction with MATLAB timer (see MCA's mcamontimer.m)
obj.t = timer('TimerFcn', 'mocha(''monitorFlushEvent'')', 'Period', 2.0, 'ExecutionMode', 'fixedSpacing');
%Else if double quotes cannot be used in the above; here's a way around the problem:
%obj.t = timer('TimerFcn', 'mocha(402)', 'Period', 2.0, 'ExecutionMode', 'fixedSpacing');
start(obj.t);
disp ('Callback for monitors for 10 seconds')
for n=1:5
	mocha('set', pv(1), n); %See the script be executed
	pause(2);
end	
stop(obj.t);

%END Monitors, either with or without user supplied callbacks (in essence, matlab scripts)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% (12) Control system parameters, i.e. operating limits, engineerimg units
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(12) Control system parameters, i.e. operating limits, engineerimg units');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
[ctrlData] = mocha('getCtrl', hpv(1));
%Usually it is sufficient to use the getCtrlCache method
[ctrlData] = mocha('getCtrlCache', hpv(1));
ctrlData
% This is what an output may look like for an ao/ai record
%{
             		  val: 3.2000
               status: 1
          alarmStatus: 0
        alarmSeverity: 0
            precision: 6
                units: 'mm'
        noEnumStrings: 0
          enumStrings: {1�0 cell}
    upperDisplayLimit: 2
    lowerDisplayLimit: -2
      upperAlarmLimit: NaN
      lowerAlarmLimit: NaN
    upperWarningLimit: NaN
    lowerWarningLimit: NaN
    upperControlLimit: 2
    lowerControlLimit: -2
%}		
[ctrlData] = mocha('getCtrlCache', hpv(4));
ctrlData
% This is what an output may look like for an mbbo/i record
%{
                 val: 'off'
               status: 1
          alarmStatus: 0
        alarmSeverity: 0
            precision: 0
                units: ''
        noEnumStrings: 3
          enumStrings: {'off'  'on'  'monitor'}
    upperDisplayLimit: 0
    lowerDisplayLimit: 0
      upperAlarmLimit: 0
      lowerAlarmLimit: 0
    upperWarningLimit: 0
    lowerWarningLimit: 0
    upperControlLimit: 0
    lowerControlLimit: 0
%}		

%
%END (12) Control system parameters, i.e. operating limits, engineerimg units
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% (13) Retrieve channel medadata, e.g., data type, native no. elements, connection state, read/write access, etc.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(13) Retrieve channel medadata, e.g., data type, native no. elements, connection state, read/write access, etc. ');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');

iStruct=mocha('getInfo', pv(1));
iStruct
% This is what an output may look like:
% The members connectFlag, accessRead, and accessWrite are all of MATLAB data type, logical
%{
   				channelID: '0x7f46fc879ae0'
        connectFlag: 1
           hostName: 'psi-softioc-2.psi.ch:45408'
           dataType: 'DBR_DOUBLE'
          className: 'ai'
         accessRead: 1
        accessWrite: 1
              nelem: 1
    connectionState: 'CA_OP_CONN_UP'
%}		

%
%END (13) Retrieve channel medadata, e.g., data type, native no. elements, connection state, read/write access, etc.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% (14) Setting timeouts for set/get operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(14) Setting timeouts for set/get operations');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
%Timeouts in CAFE are self-regulating and are regulated by 
%the following parameters in defines.h in the CAFE livrary
%const bool              DEFAULT_SELF_GOVERNING_TIMEOUT    = true;
%const unsigned short    DEFAULT_PEND_IO_NO_TRIES          =  2;
%const double            DEFAULT_PEND_IO_INCREMENT_TIME    =  2.0;

%Self governing timeouts may be turned off/on for all handles,
%or on a per handle basis

%Make true (default) for handle hpv(1)
mocha('setselfgoverningtimeout', pv(1), true);

%Make true for all handles
mocha('setselfgoverningtimeout', true);


%Timeouts can be set separately for blocking SET/GET operations 
%either globally or on a per handle basis
[t] = mocha('getTimeout', pv(1));
%t(1) is timeout for a blocking put operation
%t(2) is timeout for a blocking get operation 

%the minimum timeout is returned from among all handles
[t] = mocha('getTimeout');
%t(1) is minimum put timeout from among all handles
%t(2) is minimum get timeout from among all handles

%Timeouts can be set in a number of ways, for all handles
%or on a per handle basis

%set both put and get timeouts for handle hpv(1) to 10.0 seconds
status=mocha('setTimeout', pv(1), 10.0);

%set both put and get timeouts for all handles to 10.0 seconds
status=mocha('setTimeout', 10.0);

%set put timeout for handle hpv(1) to 5.0 seconds
status=mocha('setTimeoutPut', pv(1), 5.0);

%set put timeouts for all handles to 5.0 seconds
status=mocha('setTimeoutPut', 5.0);

%set get timeout for handle hpv(1) to 10.0 seconds
status=mocha('setTimeoutGet', pv(1), 10.0);

%set put timeouts for all handles to 10.0 seconds
status=mocha('setTimeoutGet', 10.0);

%
%END (14) Setting timeouts for set/get operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% (15) Setting synchronous group timeouts for set/get operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(15) Setting synchronous group timeouts for set/get operations');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');

%Self governing timeouts may be turned off/on for all group handles,
%or on a per group handle basis

%Make true (default) for handle hpv(1)
mocha('setSGselfgoverningtimeout', gHandle1, true);

%Make true for all handles
mocha('setSGselfgoverningtimeout', true);


%Timeouts can be set separately for blocking SET/GET operations 
%either globally or on a per grop handle basis
[t] = mocha('getSGTimeout',gHandle1);
%t(1) is timeout for a group put operation
%t(2) is timeout for a grop get operation 

%the minimum timeout is returned from among all handles
[t] = mocha('getSGTimeout');
%t(1) is minimum put timeout from among all group handles
%t(2) is minimum get timeout from among all group handles

%Timeouts can be set in a number of ways, for all gourp handles
%or on a per grop handle basis

%set both put and get timeouts for group handle, gHandle1, to 10.0 seconds
status=mocha('setSGTimeout', gHandle1, 10.0);

%set both put and get timeouts for all group handles to 10.0 seconds
status=mocha('setSGTimeout', 10.0);

%set put timeout for group handle, gHandle1, to 5.0 seconds
status=mocha('setSGTimeoutPut', gHandle1, 5.0);

%set put timeouts for all group handles to 5.0 seconds
status=mocha('setSGTimeoutPut', 5.0);

%set get timeout for group handle, gHandle1, to 10.0 seconds
status=mocha('setTimeoutGet', gHandle1, 10.0);

%set put timeouts for all group handles to 10.0 seconds
status=mocha('setSGTimeoutGet', 10.0);

%
%END (15) Setting synchronous group timeouts for set/get operations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% (16) Special Methods, e.g., setAndGet
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(16) Setting Methods, e.g., setAndGet ');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');


%%Several specialized methods exist within the CAFE library,
%%such as setAndMatch which can set a number of PVs to a given value
%%and then examines a given collection of readback PVS. 
%%Method verifies whether or not the set/readback values agree within 
%%the given tolerance and timeout. Useful for, e.g., ID Gap Scans, 
%%This and other methods can be exposed to mocha on request

%%Another simple method is the setAndGet Method
%%Set a PV and immediately retrieve its value

%The method (previously presente in (9) above, initiates a set and is 
%followed immediately by a get.
%The user should be aware that in the event of other applications writing
%to the same epics channel, the possibility exists that this external write 
%may occur between the set and subsequent get in the above method.

setValue=0.4;
[getValue, status] = mocha ('setAndGet', pv(1), setValue)
%The data type of the returned value matches that of the input value.

%
%END (16) Special Methods, e.g., setAndGet
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% (17) Gracefully terminate CAFE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Section(17) Gracefully terminate CAFE ');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');


%Stops all monitors (if any)
%Closes all channels
%Releases all CA resources
mocha ('close');

%
%END (17) Gracefully terminate CAFE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%





