diff --git a/MATLAB/hri_comm.m b/MATLAB/hri_comm.m index cb32e62..2f4a086 100644 --- a/MATLAB/hri_comm.m +++ b/MATLAB/hri_comm.m @@ -1,463 +1,484 @@ classdef hri_comm < handle % HHRIBOARD HHRI board class. % Manages the communication between MATLAB on PC and the HHRI board, % and allows remote operation. properties(Access = private) hSerial packetBuffer rxCurrentMessageType rxBytesCount firstHalfByte rxTimer pendingPingback pendingGetVarsList pendingGetVar logfile varsList streamId varsIdsToStream streamPacketSize streamBuffer currentStreamBufferIndex gotVarsListCallbackFunc boardOfflineCallbackFunc plotDecimCounter end properties(Hidden = true, Access = private, Constant = true) %% Messages sent by MATLAB to the board. PC_MESSAGE_DO_NOTHING = 0; % Do nothing. PC_MESSAGE_PING = 1; PC_MESSAGE_GET_VARS_LIST = 2; % Request the device to send the variables list. PC_MESSAGE_SET_STREAMED_VAR = 3; % Set the variables to be streamed. PC_MESSAGE_GET_VAR = 4; % Request the device to send the selected value. PC_MESSAGE_SET_VAR = 5; % Set the selected variable. %% Messages sent by the board to MATLAB. STM_MESSAGE_PINGBACK = 0; % Response to a ping request. STM_MESSAGE_VAR = 1; % Variable state. STM_MESSAGE_STREAMING_PACKET = 2; % Streaming packet. STM_MESSAGE_DEBUG_TEXT = 3; % Debug text message. STM_MESSAGE_VARS_LIST = 4; % Monitored variables list. STM_MESSAGE_START_INFO = 5; % Notification that the board has (re)started. %% Other constants. SYNCVAR_NAME_SIZE = 20; % Max size of a SyncVar name, including the '\0' trailing character. STREAMING_BUFF_LEN = 1000; % [bytes]. READ_BYTES_PERIOD = 0.100; % [s]. VAR_ACCESSES = {'readonly', 'writeonly', 'read+write'}; VAR_TYPES = {'bool', 'uint8', 'int8', 'uint16', 'int16', ... 'uint32', 'int32', 'uint64', 'int64', 'single', ... 'double'}; end methods function hbh = hri_comm(comPortName, gotVarsListCallbackFunc) % HHRIBOARD Class constructor. hbh.hSerial = serial(comPortName, ... 'BaudRate', 1843200, ... 'Parity', 'none', ... 'StopBits', 1, ... 'FlowControl', 'none', ... 'Timeout', 0.5, ... 'InputBufferSize', 100000, ... 'ReadAsyncMode', 'continuous'); hbh.packetBuffer = zeros(1, 1000, 'uint8'); hbh.rxCurrentMessageType = 0; hbh.rxBytesCount = 0; hbh.pendingPingback = 0; hbh.logfile = 0; hbh.rxTimer = 0; hbh.streamId = 0; hbh.gotVarsListCallbackFunc = gotVarsListCallbackFunc; hbh.plotDecimCounter = 1; end function open(hbh) % OPEN Open the serial link, and start receiving data. % Open the serial link. fopen(hbh.hSerial); flushinput(hbh.hSerial); % Setup the callback function, called periodically to read the % bytes sent by the board. hbh.rxTimer = timer('Period', hbh.READ_BYTES_PERIOD, ... 'TimerFcn', @(~,~)readBytes(hbh), ... 'ExecutionMode', 'fixedRate', ... 'BusyMode', 'drop'); start(hbh.rxTimer); % Stop the streaming, if it was running. setStreamedVars(hbh, []); % Get the remote variables list, if the board is OK. if ping(hbh) requestVarsList(hbh); end end function close(hbh) % CLOSE Close the serial link. stop(hbh.rxTimer); if strcmp(hbh.hSerial.Status, 'open') fclose(hbh.hSerial); delete(hbh); end end function delete(hbh) % DELETE Class destructor. close(hbh); end function ok = ping(hbh) % PING Check if the board is responding to a ping request. hbh.pendingPingback = 1; sendPacket(hbh, hbh.PC_MESSAGE_PING, []); startTime = tic; while hbh.pendingPingback && toc(startTime) < 1.0 pause(0.1); end ok = (hbh.pendingPingback == 0); end % function ok = getLinkStatus(hbh) % % end function startLogging(hbh) % STARTLOGGING Start the logging to a text file. % wmh WalkiMotorboard object to set. + hri_constants; % Load the parameters file. + + % Get the directory of this file, to avoid problems when + % changing the current MATLAB directory. + [baseDirectory, ~, ~] = fileparts(mfilename('fullpath')); + addpath(baseDirectory); % If the "logs" directory does not exist, create it. - if ~exist('logs/', 'dir') - mkdir('logs/'); + logsDirectory = [baseDirectory '/' 'logs/']; + if ~exist(logsDirectory, 'dir') + mkdir(logsDirectory); end % Open the logfile to write. - hbh.logfile = fopen(['logs/log_' ... - datestr(now, 'yyyy-mm-dd_HH_MM_SS') ... + if strcmp(LOGFILE_NAMING, 'date') + % Use the current date/time to get a unique filename. + filenameSuffix = datestr(now, 'yyyy-mm-dd_HH_MM_SS'); + elseif strcmp(LOGFILE_NAMING, 'counter') + % List the existing logfiles, and look for the highest + % number, in order to determine the next number. + logfilesNames = dir([logsDirectory 'log_*.csv']); + logfilesNames = {logfilesNames.name}; + a = regexp(logfilesNames, 'log_(?\d{6}).csv', 'tokens'); + a(cellfun(@isempty, a)) = []; + maxNumber = max(cellfun(@(x) str2double(x{1}), a)); + filenameSuffix = sprintf('%06i', maxNumber+1); + end + + hbh.logfile = fopen([logsDirectory 'log_' filenameSuffix ... '.csv'], 'w'); + % Build the header. header = 'timestamp [us];'; for i=hbh.varsIdsToStream header = [header hbh.varsList(i).name ';']; %#ok end header(end) = []; fprintf(hbh.logfile, [header '\n']); end function stopLogging(hbh) % STOPLOGGING Stop the logging to a text file. % wmh WalkiMotorboard object to set. if hbh.logfile ~= 0 fclose(hbh.logfile); hbh.logfile = 0; end end function requestVarsList(hbh) sendPacket(hbh, hbh.PC_MESSAGE_GET_VARS_LIST, []); end function varsList = getVarsList(hbh) sendPacket(hbh, hbh.PC_MESSAGE_GET_VARS_LIST, []); hbh.pendingGetVarsList = 1; while hbh.pendingGetVarsList pause(0.1); end varsList = hbh.varsList; end function setVar(hbh, varId, newValue) % SETVAR Set a board variable to the desired value. % wmh WalkiMotorboard object to set. % varId the selected variable to set. % newValue the new desired value of the selected variable. The % given value will be cast to the correct type. % Send a packet to set the value of the variable. if strcmp(hbh.varsList(varId).type, 'bool') newValue = cast(newValue, 'uint8'); else newValue = cast(newValue, hbh.varsList(varId).type); end data = [varId-1 typecast(newValue, 'uint8')]; sendPacket(hbh, hbh.PC_MESSAGE_SET_VAR, data); end function varValue = getVar(hbh, varId) % REQUESTVAR Get the variable value from the board. % This function blocks until the variable has been received. % wmh WalkiMotorboard object to set. % varId the ID of the selected variable to get. hbh.pendingGetVar = 1; sendPacket(hbh, hbh.PC_MESSAGE_GET_VAR, varId-1); while(hbh.pendingGetVar) pause(0.01); end varValue = hbh.varsList(varId).value{:}; end function dispVarsList(hbh) for i=1:length(hbh.varsList) fprintf('#%i %s %s (size %i) %s\n', i, ... hbh.varsList(i).name, ... hbh.varsList(i).type, ... hbh.varsList(i).size, ... hbh.varsList(i).access); end end function setStreamedVars(hbh, varsIdsToStream) % Allocate the buffer. hbh.streamBuffer = zeros(1000, length(varsIdsToStream)); hbh.currentStreamBufferIndex = 1; % Build the communication packet. hbh.streamId = hbh.streamId + 1; hbh.streamPacketSize = 1 + 4; % ID + timestamp. hbh.varsIdsToStream = varsIdsToStream; data = zeros(1, 2 + length(varsIdsToStream)); data(1) = uint8(length(varsIdsToStream)); data(2) = uint8(hbh.streamId); for i=1:length(varsIdsToStream) data(2+i) = uint8(varsIdsToStream(i)-1); hbh.streamPacketSize = hbh.streamPacketSize + ... hbh.varsList(varsIdsToStream(i)).size; end sendPacket(hbh, hbh.PC_MESSAGE_SET_STREAMED_VAR, data); % TODO: remove. % This avoids a bug when removing a variable while streaming. pause(0.1); end function vars = getStreaming(hbh) vars = hbh.streamBuffer(1:hbh.currentStreamBufferIndex-1, :); hbh.currentStreamBufferIndex = 1; end end methods(Static) function boardComPort = getHriComPort() hwinfo = instrhwinfo('Serial'); ports = hwinfo.AvailableSerialPorts; for i=1:length(ports) port = ports{i}; h = hri_comm(port, 0); open(h); if ping(h) boardComPort = port; delete(h); return; end delete(h); clear h; end boardComPort = ''; end end methods(Access = private) function sendPacket(wmh, type, data) % Build the packet. txBuffer = zeros(1, 1 + length(data) * 2, 'uint8'); txBuffer(1) = bitset(type, 8); % Set bit 8 to 1 ("start byte"). for i = 1:length(data) b = data(i); txBuffer(i*2+0) = bitshift(b, -4); % MSB. txBuffer(i*2+1) = bitshift(bitshift(b, 4), -4); % LSB. end % Send the packet through the serial link. fwrite(wmh.hSerial, txBuffer); % TODO: set to 'async'? end function readBytes(hbh) try hri_constants; % Read the available bytes from the serial link. nAvailableBytes = hbh.hSerial.BytesAvailable; if nAvailableBytes == 0 return; end rxBytes = fread(hbh.hSerial, nAvailableBytes, 'uint8'); % Cache the most accessed members. rxBytesCountC = hbh.rxBytesCount; packetBufferC = hbh.packetBuffer; % Process each byte, one by one. for rxByte = rxBytes' if bitget(rxByte, 8) == 1 % Start byte. hbh.rxCurrentMessageType = bitset(rxByte, 8, 0); rxBytesCountC = 0; else % "Normal" byte. rxBytesCountC = rxBytesCountC + 1; end if rem(rxBytesCountC, 2) == 1 % Got first half byte. hbh.firstHalfByte = rxByte; else % Got nothing, or the second half byte. dataBytesReady = floor(rxBytesCountC / 2); % Accumulate the incoming byte in the RX packet buffer. if dataBytesReady > 0 packetBufferC(dataBytesReady) = ... bitshift(hbh.firstHalfByte, 4) + ... % MSB. bitshift(bitshift(rxByte, 4), -4); % LSB. end % If enough data bytes have been received, interpret % them. switch hbh.rxCurrentMessageType case hbh.STM_MESSAGE_PINGBACK hbh.pendingPingback = 0; case hbh.STM_MESSAGE_VAR if dataBytesReady >= 1 varId = packetBufferC(1) + 1; varSize = hbh.varsList(varId).size; if dataBytesReady == 1 + varSize if strcmp(hbh.varsList(varId).type, 'bool') hbh.varsList(varId).value = {packetBufferC(2) ~= 0}; else hbh.varsList(varId).value = {typecast(packetBufferC(2:1+varSize), hbh.varsList(varId).type)}; end end hbh.pendingGetVar = 0; end case hbh.STM_MESSAGE_STREAMING_PACKET if dataBytesReady == hbh.streamPacketSize if packetBufferC(1) == hbh.streamId timestamp = cast(typecast(packetBufferC(2:5), 'uint32'), 'double'); p = 6; for varId=hbh.varsIdsToStream if strcmp(hbh.varsList(varId).type, 'bool') hbh.varsList(varId).value = {packetBufferC(p) ~= 0}; p = p + 1; else varSize = hbh.varsList(varId).size; hbh.varsList(varId).value = {typecast(packetBufferC(p:p+varSize-1), hbh.varsList(varId).type)}; p = p + varSize; end end % Add the values into the streaming buffer. streamBufferLine = cellfun(@(x)cast(x{:}, 'double'), {hbh.varsList(hbh.varsIdsToStream).value}); if hbh.plotDecimCounter >= PLOT_DOWNSAMPLING hbh.plotDecimCounter = 1; hbh.streamBuffer(hbh.currentStreamBufferIndex,:) = streamBufferLine; hbh.currentStreamBufferIndex = hbh.currentStreamBufferIndex + 1; % If the streaming buffer is full, restart. if hbh.currentStreamBufferIndex >= hbh.STREAMING_BUFF_LEN hbh.currentStreamBufferIndex = 1; warning('The streaming buffer was full, and has been reseted.'); end else hbh.plotDecimCounter = hbh.plotDecimCounter + 1; end % Log to file, if enabled. if hbh.logfile ~= 0 fprintf(hbh.logfile, '%f;', [timestamp streamBufferLine]); fprintf(hbh.logfile, '\n'); end end end case hbh.STM_MESSAGE_DEBUG_TEXT if dataBytesReady > 0 && ... packetBufferC(dataBytesReady) == 0 message = char(... packetBufferC(... 1:find(packetBufferC == 0)-1)); message(message == 13) = []; fprintf([message '\n']); end case hbh.STM_MESSAGE_VARS_LIST if dataBytesReady > 0 nVars = double(packetBufferC(1)); hbh.varsList = []; if dataBytesReady == 1 + nVars * (hbh.SYNCVAR_NAME_SIZE + 3) j = 2; for i=1:nVars name = packetBufferC(j:j+hbh.SYNCVAR_NAME_SIZE); hbh.varsList(i).name = char(name(1:find(name == 0, 1, 'first')-1)); j = j + hbh.SYNCVAR_NAME_SIZE; hbh.varsList(i).type = hbh.VAR_TYPES{packetBufferC(j)+1}; j = j + 1; hbh.varsList(i).access = hbh.VAR_ACCESSES{packetBufferC(j)+1}; j = j + 1; hbh.varsList(i).size = double(packetBufferC(j)); j = j + 1; if strcmp(hbh.varsList(i).type, 'bool') hbh.varsList(i).value = 0; else hbh.varsList(i).value = {cast(0, hbh.varsList(i).type)}; end end hbh.pendingGetVarsList = 0; if isa(hbh.gotVarsListCallbackFunc, 'function_handle') hbh.gotVarsListCallbackFunc(hbh.varsList); end end end case hbh.STM_MESSAGE_START_INFO if dataBytesReady == 0 disp('The board has (re)started.'); requestVarsList(hbh); end end end end % De-cache the cached variables. hbh.rxBytesCount = rxBytesCountC; hbh.packetBuffer = packetBufferC; catch e % Having this variable in global allows accessing it from % the main workspace. global err; %#ok err = e; rethrow(e); end end end end \ No newline at end of file diff --git a/MATLAB/hri_constants.m b/MATLAB/hri_constants.m index 9289653..e27cf6c 100644 --- a/MATLAB/hri_constants.m +++ b/MATLAB/hri_constants.m @@ -1,9 +1,12 @@ %% HRI_GUI CONSTANTS %% GUI-related constants. -N_VARS_MAX = 25; -PLOT_SAMPLING_FREQS = [2 10]; +N_VARS_MAX = 25; % Maximum number of variables. +PLOT_SAMPLING_FREQS = [2 10]; % Plot frequencies proposed by the combobox [Hz]. %% Plot-related constants. -PLOT_N_SAMPLES = 1000; -PLOT_DOWNSAMPLING = 10; \ No newline at end of file +PLOT_N_SAMPLES = 1000; % Width (in samples) of the plot window. +PLOT_DOWNSAMPLING = 10; % Downsampling factor before plotting. + +%% Logfiles options. +LOGFILE_NAMING = 'date'; % 'date' or 'counter'. \ No newline at end of file diff --git a/MATLAB/hri_gui.m b/MATLAB/hri_gui.m index ce155b5..fae1607 100644 --- a/MATLAB/hri_gui.m +++ b/MATLAB/hri_gui.m @@ -1,416 +1,422 @@ function varargout = hri_gui(varargin) % HRI_GUI MATLAB code for hri_gui.fig % HRI_GUI, by itself, creates a new HRI_GUI or raises the existing % singleton*. % % H = HRI_GUI returns the handle to a new HRI_GUI or the handle to % the existing singleton*. % % HRI_GUI('CALLBACK',hObject,eventData,handles,...) calls the local % function named CALLBACK in HRI_GUI.M with the given input arguments. % % HRI_GUI('Property','Value',...) creates a new HRI_GUI or raises the % existing singleton*. Starting from the left, property value pairs are % applied to the GUI before hri_gui_OpeningFcn gets called. An % unrecognized property name or invalid value makes property application % stop. All inputs are passed to hri_gui_OpeningFcn via varargin. % % *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one % instance to run (singleton)". % % See also: GUIDE, GUIDATA, GUIHANDLES % Edit the above text to modify the response to help hri_gui % Last Modified by GUIDE v2.5 26-Dec-2015 20:20:49 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @hri_gui_OpeningFcn, ... 'gui_OutputFcn', @hri_gui_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); else gui_mainfcn(gui_State, varargin{:}); end % End initialization code - DO NOT EDIT % --- Executes just before hri_gui is made visible. function hri_gui_OpeningFcn(hObject, eventdata, handles, varargin) %#ok % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to hri_gui (see VARARGIN) % Choose default command line output for hri_gui handles.output = hObject; % UIWAIT makes hri_gui wait for user response (see UIRESUME) % uiwait(handles.main_window); hri_constants; handles.streamedVars = []; handles.varsWidgets = []; handles.plotDataCurrentIndex = 1; handles.plotData = []; handles.plotFig = findobj('Tag', 'plot_figure'); handles.dataStreamingCheckbox = findobj('Tag', 'data_streaming_checkbox'); +% Add the current directory, in order to ensure the callbacks work, even if +% the current directory is changed by user. +[baseDirectory, ~, ~] = fileparts(mfilename('fullpath')); +addpath(baseDirectory); + % Create the variables widgets. lineHeight = 1 / N_VARS_MAX; y = 1 - lineHeight; for i=1:N_VARS_MAX % Create the UI controls at the right location. handles.varsWidgets(i).checkbox = uicontrol('Parent', handles.vars_panel, ... 'Style', 'checkbox', ... 'Units', 'normalized', ... 'HandleVisibility', 'callback', ... 'Position', [0 y 3/6 lineHeight], ... 'Visible', 'off'); handles.varsWidgets(i).get = uicontrol('Parent', handles.vars_panel, ... 'Units', 'normalized', ... 'HandleVisibility', 'callback', ... 'Position', [3/6 y 1/6 lineHeight], ... 'String', 'Get', ... 'Visible', 'off'); handles.varsWidgets(i).edit = uicontrol('Parent', handles.vars_panel, ... 'Style', 'edit', ... 'Units', 'normalized', ... 'HandleVisibility', 'callback', ... 'Position', [4/6 y 1/6 lineHeight], ... 'String', '', ... 'Visible', 'off'); handles.varsWidgets(i).set = uicontrol('Parent', handles.vars_panel, ... 'Units', 'normalized', ... 'HandleVisibility', 'callback', ... 'Position', [5/6 y 1/6 lineHeight], ... 'String', 'Set', ... 'Visible', 'off'); y = y - lineHeight; end if isempty(varargin) % Find which serial port corresponds to the board. comPort = hri_comm.getHriComPort(); if isempty(comPort) warning('The board is not detected.'); handles.hbh = []; guidata(hObject, handles); return; else fprintf('The board was detected on %s.\n', comPort); end else comPort = varargin{1}; end % Open the serial link with the board. -handles.hbh = hri_comm(comPort, @(varsList) hri_gui_UpdateVarsList(hObject, varsList)); +handles.hbh = hri_comm(comPort, ... + @(varsList) hri_gui_UpdateVarsList(hObject, varsList)); open(handles.hbh); % Set the plot figure update timer. handles.plotFreqPopup = findobj('Tag', 'plot_freq_popup'); plotFreq = PLOT_SAMPLING_FREQS(handles.plotFreqPopup.Value); handles.plotTimer = timer('Period', 1.0 / plotFreq, ... 'TimerFcn', @(~,~)updatePlot(hObject), ... 'ExecutionMode', 'fixedRate', ... 'BusyMode', 'drop'); start(handles.plotTimer); % guidata(hObject, handles); function hri_gui_UpdateVarsList(hObject, varsList) hri_constants; handles = guidata(hObject); handles.varsList = varsList; % Stop data streaming and logging, if running. handles.streamedVars = []; set(handles.data_streaming_checkbox, 'Value', 0); set(handles.log_to_file_checkbox, 'Value', 0); % for i=1:length(handles.varsList) % Modify the widgets to they match the associated variable. set(handles.varsWidgets(i).checkbox, 'String', handles.varsList(i).name); set(handles.varsWidgets(i).checkbox, 'Value', 0); set(handles.varsWidgets(i).checkbox, 'Visible', 'on'); set(handles.varsWidgets(i).checkbox, 'ForegroundColor', 'black'); if ~strcmp(varsList(i).access, 'writeonly') set(handles.varsWidgets(i).get, 'Visible', 'on'); end set(handles.varsWidgets(i).edit, 'Visible', 'on'); set(handles.varsWidgets(i).edit, 'String', ''); if ~strcmp(varsList(i).access, 'readonly') set(handles.varsWidgets(i).set, 'Visible', 'on'); end % Set the callbacks. handles.varsWidgets(i).checkbox.Callback = @(h,~)onToggleVarCheckbox(h,i); if handles.varsWidgets(i).get ~= 0 handles.varsWidgets(i).get.Callback = @(h,~)onGetVar(h,i); end if handles.varsWidgets(i).set ~= 0 handles.varsWidgets(i).set.Callback = @(h,~)onSetVar(h,i); end end % Hide the other widgets. for i=length(handles.varsList)+1:N_VARS_MAX set(handles.varsWidgets(i).checkbox, 'Visible', 'off'); set(handles.varsWidgets(i).get, 'Visible', 'off'); set(handles.varsWidgets(i).edit, 'Visible', 'off'); set(handles.varsWidgets(i).set, 'Visible', 'off'); end % Update handles structure. guidata(hObject, handles); function onToggleVarCheckbox(hObject, varID, ~) % Get handles structure. handles = guidata(hObject); % Add or remove the variable ID associated to the checkbox. if handles.varsWidgets(varID).checkbox.Value == 0 handles.streamedVars(handles.streamedVars == varID) = []; else handles.streamedVars = sort([handles.streamedVars varID]); end % Change the streaming of the board, if it currently running. guidata(hObject, handles); if handles.dataStreamingCheckbox.Value ~= 0 setupStreaming(hObject, handles.streamedVars); end function setupStreaming(hObject, streamedVars) hri_constants; handles = guidata(hObject); % Stop the streaming, if running. handles.log_to_file_checkbox.Value = 0; % Request the board to change the streamed vars. setStreamedVars(handles.hbh, streamedVars); % Pre-allocate the plot points buffer. % TODO: keep data for the variable that remain the same. if ~isempty(streamedVars) handles.plotData = nan(PLOT_N_SAMPLES, length(handles.streamedVars)); if ~isempty(handles.plotData) handles.plotFig = plot(1:PLOT_N_SAMPLES, handles.plotData); end end % Color the variables labels to match the lines colors. % The standard MATLAB legend is too slow for live plots. colors = get(handles.plot_figure, 'ColorOrder'); for i = 1:N_VARS_MAX streamVarIndex = find(i == streamedVars, 1); if ~isempty(streamVarIndex) set(handles.varsWidgets(i).checkbox, ... 'ForegroundColor', colors(rem(streamVarIndex-1,7)+1, :)); else set(handles.varsWidgets(i).checkbox, ... 'ForegroundColor', 'black'); end end guidata(hObject, handles); function onGetVar(hObject, varID) % Get handles structure. handles = guidata(hObject); % Get the variable value from the board. varValue = getVar(handles.hbh, varID); % Display the value on the associated edit box widget. set(handles.varsWidgets(varID).edit, 'String', varValue); function onSetVar(hObject, varID) % Get handles structure. handles = guidata(hObject); % Get the desired variable value from the associated edit box widget. varValue = str2double(handles.varsWidgets(varID).edit.String); % Set the variable value on the board. setVar(handles.hbh, varID, varValue); function updatePlot(hObject) hri_constants; try handles = guidata(hObject); catch return; end if ~isfield(handles, 'plotData') || isempty(handles.plotData) return; end vars = getStreaming(handles.hbh); if isempty(vars) || (size(vars, 2) ~= size(handles.plotData, 2)) return; end % If the plot buffer is full, restart. if handles.plotDataCurrentIndex + size(vars,1) > PLOT_N_SAMPLES - 1 handles.plotDataCurrentIndex = 1; end range = handles.plotDataCurrentIndex:handles.plotDataCurrentIndex+size(vars,1)-1; handles.plotData(range, :) = vars; handles.plotData(handles.plotDataCurrentIndex+size(vars,1), :) = nan; handles.plotDataCurrentIndex = handles.plotDataCurrentIndex + size(vars,1); for i=1:size(handles.plotData, 2) set(handles.plotFig(i), 'ydata', handles.plotData(:,i)); end guidata(hObject, handles); % --- Outputs from this function are returned to the command line. function varargout = hri_gui_OutputFcn(hObject, eventdata, handles) %#ok % varargout cell array for returning output args (see VARARGOUT); % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Get default command line output from handles structure varargout{1} = handles.output; % --- Executes on button press in data_streaming_checkbox. function data_streaming_checkbox_Callback(hObject, ~, handles) %#ok % hObject handle to data_streaming_checkbox (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) if get(hObject, 'Value') == 0 % Stop the streaming. setupStreaming(hObject, []); % Stop the logging. handles.log_to_file_checkbox.Value = 0; guidata(hObject, handles); stopLogging(handles.hbh); else setupStreaming(hObject, handles.streamedVars); end % --- Executes on button press in clear_plot_button. function clear_plot_button_Callback(hObject, ~, handles) %#ok % hObject handle to clear_plot_button (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) handles.plotData = nan(size(handles.plotData)); handles.plotDataCurrentIndex = 1; for i=1:size(handles.plotData, 2) set(handles.plotFig(i), 'ydata', handles.plotData(:,i)); end guidata(hObject, handles); % --- Executes on button press in log_to_file_checkbox. function log_to_file_checkbox_Callback(hObject, ~, handles) %#ok % hObject handle to log_to_file_checkbox (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) if get(hObject,'Value') == 0 % Stop the logging. stopLogging(handles.hbh); % Unlock the streaming checkboxes of the variables list. for j=1:length(handles.varsList)+1 handles.varsWidgets(j).checkbox.Enable = 'on'; end else % Start the logging. startLogging(handles.hbh); % Lock the streaming checkboxes of the variables list. for j=1:length(handles.varsList)+1 handles.varsWidgets(j).checkbox.Enable = 'off'; end end % Update handles structure. guidata(hObject, handles); function main_window_CloseRequestFcn(hObject, ~, ~) %#ok handles = guidata(hObject); if isfield(handles, 'plotTimer') stop(handles.plotTimer); delete(handles.plotTimer); handles = rmfield(handles, 'plotTimer'); end if isfield(handles, 'hbh') delete(handles.hbh); handles = rmfield(handles, 'hbh'); end guidata(hObject, handles); delete(hObject); close(gcf); % --- Executes on selection change in plot_freq_popup. function plot_freq_popup_Callback(hObject, ~, handles) %#ok % hObject handle to plot_freq_popup (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) hri_constants; % Get the constants. currentIndex = hObject.Value; plotFreq = PLOT_SAMPLING_FREQS(currentIndex); stop(handles.plotTimer); set(handles.plotTimer, 'Period', 1.0 / plotFreq); start(handles.plotTimer); diff --git a/MATLAB/hri_load_logfile.m b/MATLAB/hri_load_logfile.m index d6f27a7..c36feac 100644 --- a/MATLAB/hri_load_logfile.m +++ b/MATLAB/hri_load_logfile.m @@ -1,26 +1,36 @@ function variables = hri_load_logfile(filename) %HRI_LOAD_LOGFILE Loads a HRI logfile to a MATLAB structure. % Loads the filename with the given filename. A MATLAB structure is % generated, each named field is an array of the history of the variable % value. %% Read the header to get the variables names. fid = fopen(filename, 'r'); line = fgetl(fid); varNames = textscan(line, '%s', 'Delimiter', ';'); varNames = varNames{:}; +%% + +% Replace the special characters by '_'. +for i = 1:length(varNames) + varName = varNames{i}; + varName((varName < 'a' | varName > 'z') & ... + (varName < 'A' | varName > 'Z') & ... + (varName < '0' | varName > '9')) = '_'; + varNames{i} = varName; +end + fclose(fid); %% Read the variables values over time. -A = dlmread('logs/log_2015-12-27_22_40_19.csv', ';', 1, 0); +A = dlmread(filename, ';', 1, 0); %% Create a structure with a field per variable. for i=1:length(varNames) variables.(varNames{i}) = A(:,i); end -end - +end \ No newline at end of file