%% Real-Time Control with OPC Toolbox
% This example shows how to implement an online model predictive controller
% application using the OPC client supplied with the OPC Toolbox(TM). 
%
% The example uses the Matrikon(TM) Simulation OPC server to simulate the
% behavior of an industrial process on Windows(R) operating system.

% Copyright 1990-2014 The MathWorks, Inc.

%% Download the Matrikon(TM) OPC Simulation Server from "www.matrikon.com"
% Download and install the server and set it running either as a service 
% or as an application. 
%
% This example needs OPC Toolbox.
if ~mpcchecktoolboxinstalled('opc')
    disp('The example needs OPC Toolbox.')
end

%% Establish a Connection to the OPC Server
% Use OPC Toolbox commands to connect to the Matrikon OPC Simulation
% Server.
if mpcchecktoolboxinstalled('opc')
    % Clear any existing opc connections.
    opcreset 
    % Flush the callback persistent variables.
    clear mpcopcPlantStep; 
    clear mpcopcMPCStep;
    try
        h = opcda('localhost','Matrikon.OPC.Simulation.1');
        connect(h);
    catch ME
        disp('The Matrikon(TM) OPC Simulation Server must be running on the local machine.')
        return
    end
end

%% Configure Plant OPC I/O
% In practice, the plant would be a physical process, and the OPC tags
% which define its I/O would already have been created on the OPC server.
% However, in this case, since a simulation OPC server is used, the plant
% behavior must be simulated. To do so, you define tags for the
% plant manipulated and measured variables and create a callback function
% (|mpcopcPlantStep|) to simulate plant response to changes in the
% manipulated variables. Two OPC groups are required, one to represent the
% two manipulated variables to be read by the plant simulator and another
% to write back the two measured plant outputs storing the results of the
% plant simulation.
if mpcchecktoolboxinstalled('opc')
    % Build an opc group for 2 plant inputs and initialize them to zero.
    plant_read = addgroup(h,'plant_read');
    imv1 = additem(plant_read,'Bucket Brigade.Real8', 'double');
    writeasync(imv1,0);
    imv2 = additem(plant_read,'Bucket Brigade.Real4', 'double');
    writeasync(imv2,0);
    % Build an opc group for plant outputs.
    plant_write = addgroup(h,'plant_write');
    opv1 = additem(plant_write,'Bucket Brigade.Time', 'double');
    opv2 = additem(plant_write,'Bucket Brigade.Money', 'double');
    plant_write.WriteAsyncFcn = []; % Suppress command line display.
end

%% Specify MPC Controller to Control Simulated Plant
% Create plant model.
plant_model = ss([-.2 -.1; 0 -.05],eye(2,2),eye(2,2),zeros(2,2));
disc_plant_model = c2d(plant_model,1);

%%
% We assume no model mismatch, a control horizon 6 steps, and
% prediction horizon of 20 steps.
mpcobj = mpc(disc_plant_model,1,20,6);
mpcobj.weights.ManipulatedVariablesRate = [1 1];

%%
% Build an internal MPC object structure so that the MPC object
% is not rebuilt during each callback execution.
state = mpcstate(mpcobj);
y1 = mpcmove(mpcobj,state,[1;1]',[1 1]'); 

%% Build OPC I/O for MPC Controller 
% Build two OPC groups, one to read the two measured plant outputs and the
% other to write back the two manipulated variables.
if mpcchecktoolboxinstalled('opc')
    % Build an opc group for MPC inputs.
    mpc_read = addgroup(h,'mpc_read');
    impcpv1 = additem(mpc_read,'Bucket Brigade.Time', 'double');
    writeasync(impcpv1,0);
    impcpv2 = additem(mpc_read,'Bucket Brigade.Money', 'double');
    writeasync(impcpv2,0);
    impcref1 = additem(mpc_read,'Bucket Brigade.Int2', 'double');
    writeasync(impcref1,1);
    impcref2 = additem(mpc_read,'Bucket Brigade.Int4', 'double');
    writeasync(impcref2,1);
    % Build an opc group for mpc outputs.
    mpc_write = addgroup(h,'mpc_write');
    additem(mpc_write,'Bucket Brigade.Real8', 'double');
    additem(mpc_write,'Bucket Brigade.Real4', 'double');
    % Suppress command line display.
    mpc_write.WriteAsyncFcn = []; 
end

%% Build OPC Groups to Trigger Simulator and Controller
% Build two OPC groups based on the same external OPC timer to trigger
% execution of both plant simulation and MPC execution when the contents
% of the OPC time tag change.
if mpcchecktoolboxinstalled('opc')
    gtime = addgroup(h,'time');
    time_tag = additem(gtime,'Triangle Waves.Real8');
    gtime.UpdateRate = 1;
    gtime.DataChangeFcn = {@mpcopcPlantStep plant_read plant_write disc_plant_model};
    gmpctime = addgroup(h,'mpctime');
    additem(gmpctime,'Triangle Waves.Real8');
    gmpctime.UpdateRate = 1;
    gmpctime.DataChangeFcn = {@mpcopcMPCStep mpc_read mpc_write mpcobj};
end

%% Log Data from Plant Measured Outputs
% Log the plant measured outputs from tags 'Bucket Brigade.Money' and
% 'Bucket Brigade.Money'.
if mpcchecktoolboxinstalled('opc')
    mpc_read.RecordsToAcquire = 40;
    start(mpc_read);
    while mpc_read.RecordsAcquired < mpc_read.RecordsToAcquire
       pause(3)
       fprintf('Logging data: Record %d / %d',mpc_read.RecordsAcquired,mpc_read.RecordsToAcquire)
    end
    stop(mpc_read);
end

%% Extract and Plot Logged Data
if mpcchecktoolboxinstalled('opc')
    [itemID, value, quality, timeStamp, eventTime] = getdata(mpc_read,'double'); 
    plot((timeStamp(:,1)-timeStamp(1,1))*24*60*60,value)
    title('Measured Outputs Logged from Tags Bucket Brigade.Time,Bucket Brigade.Money')
    xlabel('Time (secs)');
end

%%
% <<../mpcopcresult.png>>
    
%%
