Code covered by the BSD License  

Highlights from
simlog

image thumbnail

simlog

by

 

13 Jan 2011 (Updated )

GUI to enable easy logging of multiple signals in Simulink. Log a complete subsystem with one click.

Editor's Notes:

This file was selected as MATLAB Central Pick of the Week

simlog.m
function simlog
% SIMLOG Gui for easy and quick logging of signals in Simulink.
% 
% Enable/disable logging of signals in the current subsystem + plot logged
% signals.
% 
%   1. Select signals, 1 or more, in model. (If subsystems or blocks are
%       selected, they will not be logged, use subsystem logging instead).
%   2. Click the desired button among the signal buttons (add/remove/plot)
% 
% Enable/disable logging for entire subsystems
% 
%   1. Select 1 or more subsystem
%   2. Click the desired button among the subsystem buttons (add/remove)
% 
%   A number of checkboxes allows for inclusion/exclusion of certain block
%   types.
% 
% After execution of the Simulink model the logged data can be plotted by
% selecting a logged signal in the model and clicking the plot button.
% Data can also be exported to a variable in the WS for analysis with the
% export button.
%
% tstool starts the simulink built-in Time Series Tools for analysing the
% logged data
% 
% Note: input signals for some blocks, like the Merge block, can not be
% logged. Simlog will automatically disable logging for input signals
% connected to Merge blocks if a Merge block is included in a Subsystem
% add. If Simulink reports an error stating that inputs to a specific
% Merge block can not be logged, simply select the Merge block and click
% Subsystem add, simlog will then trace the inputs back and disable logs.
%
% Version: New function (2007-08-28), RLA
%          v0.5 number added to end of log name to make unique even for
%          subsignals in Mux blocks 2007-08-31, RLA
%          v0.6 GNC Core pass, 17510 signals logged, 2007-08-31, RLA
%          Fixed problems with signals with more than 2 dimensions, export
%          & plot. 080529, RLA
%          Fixed error when exporting signal that was not logged,110207, RLA
% 
% Author: Robin Larsson (robin.larsson@ssc.se)
% Copyright: Swedish Space Corporation
% All rights reserved.

%% build gui
gui_width  = 125;
gui_height = 239+5;

version=0.7;

p = get(0,'screensize');

f=findall(0,'type','figure','tag','simlog');
if isempty(f)
    f=figure;
end
clf(f)
set(f,'Name','Simlog0.7','menubar','none','tag','simlog','units','pixels','Color','white',...
    'numbertitle','off','position',[0+10 p(4)-gui_height-20-10 gui_width gui_height],...
    'resize','off','Visible','off','pointer','hand','HandleVisibility','off');

panel_prop={'BackgroundColor','white','parent',f,'FontSize',10,'units','pixels'};
button_props = {'style','pushbutton','Units','pixels','FontSize',8};
check_props = {'style','checkbox','Units','pixels','FontSize',8,'BackgroundColor','white'};

signalsPanel = uipanel('Title','Selected signals',panel_prop{:},'position',[2 2 gui_width-2 95]);
tstoolPanel = uipanel(panel_prop{:},'position',[60 2 gui_width-60 31]);
subsystemPanel = uipanel('Title','SubSystem',panel_prop{:},'position',[2 96 gui_width-2 148]);

addB = uicontrol(button_props{:},'parent',signalsPanel,'position',[5 56 45 20],'string','add','callback',@addLog,'TooltipString','Enable logging for selected signals');
removeB = uicontrol(button_props{:},'parent',signalsPanel,'position',[55 56 60 20],'string',' remove ','callback',@removeLog,'TooltipString','Disable logging for selected signals');

plotB = uicontrol(button_props{:},'parent',signalsPanel,'position',[5 28+2 45 20],'string','plot','callback',@plotExportLog,'TooltipString','Plot logged data for selected signals');
gridC = uicontrol(check_props{:},'parent',signalsPanel,'position',[56 28+3 45 20],'string','grid','TooltipString','Enable grid in plots');
exportB = uicontrol(button_props{:},'parent',signalsPanel,'position',[5 5 45 20],'string','export','callback',@plotExportLog,'TooltipString','Export logged data for selected signals');

tstoolB = uicontrol(button_props{:},'parent',tstoolPanel,'position',[9 5 45 20],'string','tstool','callback',@tsLog,'TooltipString','Launch the Time Series Tools');

addSubB = uicontrol(button_props{:},'parent',subsystemPanel,'position',[5 5 45 20],'string','add','callback',@addLog,'TooltipString','Enable logging for signals inside the selected subsystems');
removeSubB = uicontrol(button_props{:},'parent',subsystemPanel,'position',[55 5 60 20],'string',' remove ','callback',@removeLog,'TooltipString','Disable logging for signals inside the selected subsystems');

conC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[3 30 70 20],'string','Constant','TooltipString','Include Constant blocks');
selC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[3 50 60 20],'string','Selector','TooltipString','Include Selector blocks');
froC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[3 70 50 20],'string','From','TooltipString','Include From blocks');
inpC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[3 90 50 20],'string','Inport','TooltipString','Include Inports');
subC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[3 110 60 20],'string','SubSys','TooltipString','Include SubSystems');

muxC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[65 110 40 20],'string','Mux','TooltipString','Include Mux blocks');
demC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[65 90 55 20],'string','Demux','TooltipString','Include Demux blocks');
buCC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[65 50 55 20],'string','busSel','TooltipString','Include Bus Selectors');
buSC = uicontrol(check_props{:},'parent',subsystemPanel,'position',[65 70 55 20],'string','busCre','TooltipString','Include Bus Creators');

set(f,'Visible','on')

%% open tstool
    function tsLog(src,evt)
        if evalin('base','exist(''logsout'')')
            evalin('base','tstool(logsout)')
        else
            disp('No log data in base WS, run simulation with logging turned on.')
        end
    end
%% turn on log
    function addLog(src,evt)
        if strcmp(get_param(bdroot,'ShowTestPointIcons'),'off');
            set_param(bdroot,'ShowTestPointIcons','on')
        end
        if src == addB
            sys=find_system(gcs,'FindAll','on','searchdepth',1,'followlinks','on','LookUnderMasks','all','selected','on','type','line');
            sysP=zeros(length(sys),1);
            for i=1:length(sysP)
                sysP(i)=get(sys(i),'SrcPortHandle');
            end
        elseif src == addSubB
            sys=find_system(gcs,'FindAll','on','searchdepth',1,'followlinks','on','LookUnderMasks','all','selected','on');
            sys=sys(sys~=get_param(gcs,'handle'));
            sysP=find_system(sys,'FindAll','on','followlinks','on','LookUnderMasks','all','type','port','porttype','outport');
        end

        banList={'Ground','If','SwitchCase'};
        banListSub={'Constant','Mux','SubSystem','Inport','From','Selector','Demux','BusCreator','BusSelector'};

        Ib=[get(conC,'Value') get(muxC,'Value') get(subC,'Value') get(inpC,'Value') get(froC,'Value') get(selC,'Value') get(demC,'Value') get(buCC,'Value') get(buSC,'Value')];
        banListSub=banListSub(Ib==0);
    
        nrPorts=length(sysP);
        
        questA='Yes';
        if nrPorts > 500
            questA = questdlg(['Enable logging for ' mat2str(nrPorts) ' possible signals?']);
        end
        
        if strcmp(questA,'Yes')
            waitB=-1;
            if nrPorts > 500
                waitB=waitbar(0,['Trying to add ' mat2str(nrPorts) ' signals...']);
                updateBar=round(nrPorts/100);
            end
            for i=1:nrPorts
                if nrPorts > 500 && mod(i,updateBar) == 0
                    waitbar(i/nrPorts)
                end
                parentB=get(sysP(i),'Parent');
                parentBType=get_param(parentB,'blocktype');

                if ~any(strcmp(parentBType,banList)) && (~any(strcmp(parentBType,banListSub)) || src == addB)  %problems with same signal logging names for Mux on inputs
                    portP=sysP(i);
                    set(portP,'DataLoggingNameMode','Custom')
                    portNr=get(portP,'PortNumber');
                    parentBname=get_param(parentB,'name');
                    I=isspace(parentBname);
                    parentBname(I)='_'; %remove spaces

                    log_name=genvarname([parentBname(1:min(length(parentBname),40)) '_Port' mat2str(portNr) 'nr' mat2str(i)]);

                    set(portP,'DataLoggingName',log_name)
                    set(portP,'DataLoggingLimitDataPoints','off')

                    set(portP,'DataLogging','on')
                else
                    k=1;
                end
            end
            if ishandle(waitB)
                close(waitB)
            end
            if src == addSubB
                % remove logs conected to signals connected to merge blocks
                sysMerge = find_system(sys,'FindAll','off','followlinks','on','LookUnderMasks','all','blocktype','Merge');
                for i=1:length(sysMerge)
                    disp(['Removing blocks connected to Merge nr: ' mat2str(i) '/' mat2str(length(sysMerge))])
                    disableLogToThisBlock(sysMerge(i))
                end

                %remove logs connected to if control signals
                sysAction=find_system(sys,'FindAll','on','followlinks','on','LookUnderMasks','all','type','port','PortType','ifaction');
                for i=1:length(sysAction)
                    disp(['Removing blocks connected to "if action" port nr: ' mat2str(i) '/' mat2str(length(sysAction))])
                    disableLogToThisBlock(sysAction(i))
                end

                sysSub=find_system(sys,'followlinks','on','LookUnderMasks','all','blocktype','SubSystem');
                for i=1:length(sysSub)
                    subName=get(sysSub(i),'name');
                    if length(subName)>63
                        subName(subName(:)==10)=32; %replace new row with ' '
                        disp(['Removing blocks in Subsystems with too long names nr: ' subName])
                        sysP=find_system(sysSub(i),'FindAll','on','followlinks','on','LookUnderMasks','all','type','port','porttype','outport','DataLogging','on');
                        for i=1:length(sysP)
                            set(sysP(i),'DataLogging','off')
                            set(sysP(i),'TestPoint','off')
                        end
                    end
                end
                sysLog=find_system(sys,'FindAll','on','followlinks','on','LookUnderMasks','all','type','port','porttype','outport','DataLogging','on');
                if length(sysLog) == 1
                    disp(['There are now ' mat2str(length(sysLog)) ' signal logged in this subsystem'])
                else
                    disp(['There are now ' mat2str(length(sysLog)) ' signals logged in this subsystem'])
                end
            end
        end

        
    end

%% turn off log
    function removeLog(src,evt)
        if src == removeB
            sys=find_system(gcs,'FindAll','on','searchdepth',1,'followlinks','on','LookUnderMasks','all','selected','on','type','line');
            sysP=zeros(length(sys),1);
            for i=1:length(sysP)
                sysP(i)=get(sys(i),'SrcPortHandle');
            end
        elseif src == removeSubB
            sys=find_system(gcs,'FindAll','on','searchdepth',1,'followlinks','on','LookUnderMasks','all','selected','on');
            %sys=find_system(sys,'FindAll','on','followlinks','on','LookUnderMasks','all','type','line');
            sysP=find_system(sys,'FindAll','on','followlinks','on','LookUnderMasks','all','type','port','porttype','outport','DataLogging','on');
        end

        for i=1:length(sysP)
            set(sysP(i),'DataLogging','off')
            set(sysP(i),'TestPoint','off')
        end
        if length(sysP) == 1
            disp(['Disabled logging for ' mat2str(length(sysP)) ' signal.'])
        else
            disp(['Disabled logging for ' mat2str(length(sysP)) ' signals.'])
        end
    end
%% plot/export log
    function plotExportLog(src,evt)
        sys=find_system(gcs,'FindAll','on','searchdepth',1,'followlinks','on','LookUnderMasks','all','selected','on','type','line');
        tmpPort=0;
        if evalin('base','exist(''logsout'',''var'')')
            logs=evalin('base','logsout');
            simlog_export=struct;
            if length(sys)>0
                for i=1:length(sys)
                    portP=get(sys(i),'SrcPortHandle');
                    if tmpPort~= portP
                        tmpPort=portP;
                        portNr=get(portP,'PortNumber');
                        parentB=get(get(sys(i),'SrcPortHandle'),'parent');
                        parentB(parentB(:)==10)=32; %replace new row with ' '
                        I=find(parentB=='/',1);
                        parentB=parentB(I+1:end); %remove model name

                        tmp=logs;
                        I=find(parentB=='/',1); %get subsystem
                        data_logged=true;
                        try
                            while ~isempty(I)
                                tmp=tmp.(parentB(1:I-1));
                                parentB=parentB(I+1:end);
                                I=find(parentB=='/',1); %get subsystem
                            end
                            I=isspace(parentB);
                            parentB(I)='_'; %remove spaces

                            fnames=fieldnames(tmp);
                            varName=genvarname([parentB(1:min(length(parentB),40)) '_Port' mat2str(portNr)]);
                            I=strmatch(varName,fnames);
                            if length(I)==1
                                tmp=tmp.(fnames{I(1)});
                            elseif length(I)>1
                                tmp=tmp.(fnames{I(1)});
                                disp(['more than one logged signal matches ' varName '. Consider using tstool instead to be sure!'])
                            else
                                error('no field name')
                            end
                        catch
                            disp([parentB '_Port' mat2str(portNr) ' has not been logged!'])
                            data_logged=false;
                        end
                        if data_logged
                            if strcmp(class(tmp),'Simulink.Timeseries')
                                if src == plotB
                                    plotTimeSeries(tmp)
                                elseif src == exportB
                                    simlog_export.(tmp.Name)=exportTimeSeries(tmp);
                                end
                            elseif strcmp(class(tmp),'Simulink.TsArray')
                                if src == plotB
                                    plotTsArray(tmp)
                                elseif src == exportB
                                    simlog_export.(tmp.Name)=exportTsArray(tmp);
                                end
                            end
                        end
                    end
                end
                if src == exportB
                    assignin('base','simlog_export',simlog_export)
                    disp([mat2str(length(fieldnames(simlog_export))) ' signals exported to simlog_export.'])
                end
            else
                disp('Select one or more signals')
            end
        else
            disp('No log data in workspace')
        end
    end
%% plot timeseries
    function plotTimeSeries(tmp,busName)
        figure
        if ndims(tmp.Data)>2
            %time is last dim
            s=size(tmp.Data);
            %only 2D plot, get number of elements per time
            nrElem=1;
            for iel=1:length(s)-1
               nrElem=nrElem*s(iel);
            end
            plot(tmp.Time,reshape(tmp.Data,nrElem,s(end))','.');
        else
            plot(tmp.Time,tmp.Data,'.');
        end
        if get(gridC,'value')
            grid on
        end
        if nargin==2
            title([busName '.' tmp.Name],'Interpreter','None');
        else
            title(tmp.Name,'Interpreter','None');
        end
    end
%% plot tsArray
    function plotTsArray(tmp,busName)
        if nargin==2
            busName=[busName '.' tmp.Name];
        else
            busName=tmp.name;
        end
        for i=1:length(tmp.Members)
            tmpi=tmp.(tmp.Members(i).name);
            if strcmp(class(tmpi),'Simulink.Timeseries')
                plotTimeSeries(tmpi,busName)
            elseif strcmp(class(tmpi),'Simulink.TsArray')
                plotTsArray(tmpi,busName)
            end
        end
    end
%% export timeseries
    function out=exportTimeSeries(tmp)
        out=[];
        out.Time=tmp.Time;
        out.Data=tmp.Data;
%         if ndims(tmp.Data)==3
%             s=size(tmp.Data);
%             I=find(s~=1);
%             if size(I)==1
%                 out.Data=reshape(tmp.Data,s(I(1)),1);
%             else
%                 out.Data=reshape(tmp.Data,s(I(1)),s(I(2)));
%             end
%         else
%             out.Data=tmp.Data;
%         end
    end
%% export tsArray
    function out=exportTsArray(tmp)
        out=[];
        for i=1:length(tmp.Members)
            tmpi=tmp.(tmp.Members(i).name);
            if strcmp(class(tmpi),'Simulink.Timeseries')
                out.(tmp.Members(i).name)=exportTimeSeries(tmpi);
            elseif strcmp(class(tmpi),'Simulink.TsArray')
                out.(tmp.Members(i).name)=exportTsArray(tmpi);
            end
        end
    end
%% remove logs connected to this block
    function disableLogToThisBlock(startBlock,inportNr)
        if strcmpi(get(startBlock,'Type'),'block')
            lh=get(startBlock,'LineHandles');
        elseif strcmpi(get(startBlock,'Type'),'port')
            lh.Inport=get(startBlock,'Line');
        end
        if nargin==2 % one port specified (ex. when exiting a subsystem)
            currentPort=get(lh.Inport(inportNr),'SrcPortHandle');
            % Do things
            set(currentPort,'DataLogging','off')
            set(currentPort,'TestPoint','off')

            findNext(currentPort)
        else
            for i=1:length(lh.Inport)
                currentPort=get(lh.Inport(i),'SrcPortHandle');
                % Do things
                set(currentPort,'DataLogging','off')
                set(currentPort,'TestPoint','off')

                findNext(currentPort)
            end
        end
    end
%% Find next start block
    function findNext(currentPort)

        currentBlock=get_param(get(currentPort,'parent'),'handle');
        bType=get(currentBlock,'BlockType');

        switch bType
            case 'SubSystem'
                portNr=get(currentPort,'PortNumber');
                nextBlock=find_system(currentBlock,'followlinks','on','LookUnderMasks','all','SearchDepth',1,'BlockType','Outport','Port',mat2str(portNr));
                disableLogToThisBlock(nextBlock)
            case 'Inport'
                exitPortNr=eval(get(currentBlock,'port'));
                nextBlock=get_param(get(currentBlock,'parent'),'handle');
                disableLogToThisBlock(nextBlock,exitPortNr)
            case {'Selector','Mux','Demux'}
                nextBlock=currentBlock;
                disableLogToThisBlock(nextBlock)
        end
    end
end

Contact us