classdef MatMust < handle %MATMUST A library to get deal with ESA webmust REST API % MATMUST embed the first properties POSToptions GEToptions loginstatus url urlsub userProjects end properties (SetAccess = private) tockenfilename tockenstruct end properties (Transient) end methods function obj = MatMust(varargin) % Initialize the values obj.POSToptions = weboptions('RequestMethod', 'POST', 'ArrayFormat', 'json', 'Timeout',30, 'MediaType','application/json'); obj.GEToptions = weboptions('RequestMethod', 'GET', 'ArrayFormat', 'repeating', 'Timeout',30, 'MediaType','application/x-www-form-urlencoded'); obj.url = 'https://bepicolombo.esac.esa.int/webclient-must/'; obj.urlsub = 'mustlink/'; obj.tockenfilename = 'auth.json'; obj.loginstatus = false; %check the url if(nargin > 0) if isfield(varargin{1},'url') obj.url = varargin{1}.url; end end %check url [~,status] = urlread(obj.url); if ~status error('invalid URL') end if(MatMust_logincheck(obj)) disp('++++ MATMUST initialization ++++'); disp('++++ Access token valid. User logged in ++++'); disp('++++++++++++++++++++++++++++++++++++++++++++'); return end end function e = MatMust_logincheck(obj) f = fopen(obj.tockenfilename); e = false; % check if user is already logged in if f > 0 raw = fread(f,inf); tockenfilejson = jsondecode(char(raw)'); if isfield(tockenfilejson,'token') & isfield(tockenfilejson,'expiresAt') if length(split(tockenfilejson.token,'.')) == 3 & datetime(datenum(tockenfilejson.expiresAt, 'yyyy-mm-dd HH:MM:SS'), 'ConvertFrom','datenum', 'TimeZone','utc') > datetime(datetime(now, 'ConvertFrom','datenum', 'TimeZone','local'), 'TimeZone','utc') obj.loginstatus = true; obj.tockenstruct = tockenfilejson; MatMust_getUserLinkedProjects(obj); e=true; else disp('++++ MATMUST initialization ++++'); disp('++++ invalid or exiperd access token. User not logged in yet ++++'); disp('++++ User login needed - use MatMust_login to access ++++'); disp('+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'); end end else disp('++++ MATMUST initialization ++++'); disp('++++ cannot find access token. User not logged ++++'); disp('++++ User login needed - use MatMust_login to access ++++'); disp('+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'); end end function t = MatMust_getTockenStatus(obj) t = obj.loginresponse; end function t = MatMust_getUserProjects(obj) if obj.loginstatus query = 'usermanagement/projects'; obj.GEToptions.HeaderFields = {'Authorization', obj.tockenstruct.token}; try response = webread([obj.url,obj.urlsub, query], obj.GEToptions); t = response ; catch ME fprintf('+++ WARNING\n%s - %s\n', ME.identifier, ME.message) end end end function t = MatMust_getUserLinkedProjects(obj, varargin) % Get the project to witch the logged in User belongs and have access to. % if 'dump' option is set a screenslash is done userInfo = MatMust_getUserInfo(obj); obj.userProjects = userInfo.projects; t = obj.userProjects; if nargin > 1 if strcmp(varargin{1},'dump') fprintf('+++++++++++++ PROJECTS LIST +++++++++++++\n'); for i = 1:numel(t) fprintf('\tname: %s\n\tdescription: %s\n\tid: %i\n', t(i).name, t(i).description, t(i).id); fprintf('+++++++++++++++++\n'); end end end end function t = MatMust_getUserInfo(obj) if obj.loginstatus query = 'usermanagement/userinfo'; obj.GEToptions.HeaderFields = {'Authorization', obj.tockenstruct.token}; try response = webread([obj.url,obj.urlsub, query], obj.GEToptions); t = response ; catch ME fprintf('+++ WARNING\n%s - %s\n', ME.identifier, ME.message) end end end function data = MatMust_getDataFromName(obj, ds, parname, dateStart, dateStop, varargin ) % data = MatMust_getDataFromName(obj, ds, parname, dateStart, dateStop, options ) % MatMust_getDataFromName get the data name parameter from the project name ds % in the time interval dateStart, dateStop. % % inputs % ------ % ds = 'string' -> name of the project i.e.'BEPICRUISE' % parname = 'string' -> a parameter name i.e. 'NCAD7EB7' % dateStart = 'yyyy-mm-dd HH:MM:SS' format date UTC time % dateStop = 'yyyy-mm-dd HH:MM:SS' format date UTC time % 'dump','plot' optional parameter to dump and plot the data % 'calibrated' optional parameter that specify that you want % calibrated data % intialize data output data = []; if ~(MatMust_logincheck(obj)) return end %validate ds if isfield(obj.userProjects,'name') if ~any(strcmp({obj.userProjects.name},ds)) disp('++++ Project not in Available User List, check ds name or check you access rigth ++++'); return end end if any(ismember(varargin,'calibrated')) calibrate = 'true'; else calibrate = 'false'; end query = ['dataproviders/',ds,'/parameters/data']; obj.GEToptions.HeaderFields = {'Authorization', obj.tockenstruct.token}; if isa(parname,'cell') parname = char(join(parname,',')); end args = {'key', 'name',... 'values', (parname),... 'from', (dateStart),... 'to', (dateStop),... 'calibrate', calibrate,... 'mode', 'SIMPLE'}; try response = webread([obj.url,obj.urlsub, query], args{:} ,obj.GEToptions); catch ME fprintf('+++ WARNING\n%s - %s\n', ME.identifier, ME.message) return end if isa(response,'struct') data{1} = response; else data = response; end %refine date string to adapt MATLAB date format for i=1:numel(data{1}) if isempty(data{1}(i).data) fprintf('+++ WARNING\n%s for %s\n', 'No Data existing in time span', data{1}.name) continue end data_par = data{1}(i).data; t = num2cell(datenum(datetime('1970-01-01', 'TimeZone','utc'))+(str2num(str2mat(data_par.date))/1000/86400)); [data{1}(i).data.dateMAT] = deal(t{:}); clear t end if nargin > 5 if any(ismember(varargin,'dump')) fprintf('+++++++++++++ DATA RESULT LIST +++++++++++++\n'); for i=1:numel(data{1}) if isempty(data{i}(i).data) continue end data_par = data{i}.data; fprintf('%s-%s: %s -> %s\n', data{1}(i).subsystem, data{1}(i).type, data{1}(i).name, data{1}(i).description ); %TODO filter for several kind of dataType 'DOUBLE', %'SIGNED_SMALL_INT', 'STRING' switch data{1}(i).dataType case 'DOUBLE' format = '%f'; case {'SIGNED_SMALL_INT', 'UNSIGNED_INT'} format = '%i'; case 'STRING' format = '%s'; otherwise format = '%s'; end format = ['%s - ',format,'\n']; for i = 1:numel(data_par) fprintf(format,datestr(data_par(i).dateMAT,'yyyy-mm-dd HH:MM:SS.FFF'), data_par(i).value ); end fprintf('\n++++++++++++++\n\n'); end fprintf('+++++++++++++++++++++++++++++++++++++++++\n'); end if any(ismember(varargin,'plot')) for i=1:numel(data{1}) if isempty(data{1}(i).data) continue end data_par = data{1}(i).data; figure title(sprintf('%s-%s: %s -> %s\n', data{1}(i).subsystem, data{1}(i).type, data{1}(i).name, data{1}(i).description )) if strcmp(calibrate,'true') switch class(data_par(1).calibratedValue) case 'char' indx = [1,find(diff([data_par.value])~=0)]+1; ylabels = string; ylabels([unique([data_par.value],'stable')]) = replace({data_par(indx).calibratedValue},'_',' '); plot([data_par.dateMAT], [data_par.value], '-x' ); set(gca,'ytick',unique([data_par.value],'sorted')) set(gca,'yticklabel',ylabels) if exist('dataFigShowUpdatefcn')==2 set(datacursormode(gcf),'UpdateFcn',@dataFigShowUpdatefcn) end otherwise plot([data_par.dateMAT], [data_par.calibratedValue], '-x' ); end else plot([data_par.dateMAT], [data_par.value], '-x' ); end xlabel('time'); if isfield(data{1}(i), 'unit') ylabel(data{1}(i).unit); end title([data{1}(i).description,' - ',data{1}(i).name]); datetick('x'); grid on end end end end function t = MatMust_searchTMparFromName(obj, namequery, dataproviders, varargin) % +++ % MatMust_searchTMparFromName % Search a parameter into the requested dataprovider (es. BEPICRUISE) % MatMust_searchTMparFromName(obj, namequery, dataproviders, varargin) % % dataproviders = dataprovider IdName. % namequery = The query to perform (ex. '*HGA' ) % varargin = You can specify several options in varagin: % ex: 'dump' -> print to screen the parameters found % 'Description' -> Extend search in Parameters Descriptions % 'Id' -> Extend Search in Parameter Id % 'Unit' -> Extend Search in Unit name space % if obj.loginstatus query = 'metadata/treesearch'; obj.GEToptions.HeaderFields = {'Authorization', obj.tockenstruct.token}; if isa(dataproviders,'cell') dataproviders = char(join(dataproviders,',')); end try optindx = strcmp(varargin,'Description')+strcmp(varargin,'Unit')+strcmp(varargin,'Id'); if any(find(optindx)) field = join(varargin(find(optindx)),','); response = webread([obj.url,obj.urlsub, query], 'field',['name,',char(field)],'text',namequery,'dataproviders', dataproviders ,obj.GEToptions); else response = webread([obj.url,obj.urlsub, query], 'field','name','text',namequery,'dataproviders', dataproviders ,obj.GEToptions); end t = response ; catch ME fprintf('+++ WARNING\n%s - %s\n', ME.identifier, ME.message) end if nargin > 3 if any(find(strcmp(varargin,'dump'))) fprintf('+++++++++++++ PARAMETERS QUERY RESULT LIST +++++++++++++\n'); found = false; for i = 1:numel(t) if numel(t(i).children) > 0 found = true; fprintf('\n\n\t+++++++++++++ %s LIST +++++++++++++\n', t(i).type); for j = 1:numel(t(i).children) par = t(i).children(j); fprintf('\ttitle: %s\n\tname: %s\n\tdescription: %s\n\tid: %s\n', par.title, par.sortingField, par.label, par.id); fprintf(' +++++++++++++++++\n'); end end end if ~found, fprintf('++++ Nothing found ++++\n'); end fprintf('++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n'); end end end end function MatMust_login(obj, varargin ) % login % https://bepicolombo.esac.esa.int/webclient-must/mustlink/api-docs/index.html#/Authentication/login % input: % "username": "string", % "password": "string", % "maxDuration": false % output: % "token": "string", % "issuer": "string", % "subject": "string", % "issuedAt": "string", % "expiresAt": "string", % "expirationDate": "yyyy-mm-ddTHH:MM:SS.FFFZ" if ~obj.loginstatus % validation input input_validation = struct('username', 'char', 'password', 'char', 'maxDuration', 'logical' ); exit = false; if(nargin > 0) inputs = varargin{1}; e = obj.MatMust_inputvalidator(inputs, input_validation); if ~e exit = false; return else exit = true; end else exit = false; return end json_POST = inputs; query = 'auth/login/'; obj.tockenstruct = webwrite([obj.url,obj.urlsub, query],json_POST,obj.POSToptions); if isfield(obj.tockenstruct,'token') disp('+++ Succesfully logged in +++') obj.loginstatus = true; else disp('+++ Error in loggin +++') obj.loginstatus = false; end %store into a file if obj.loginstatus f = fopen(obj.tockenfilename, 'w'); text = jsonencode(obj.tockenstruct); fprintf(f,'%s',text); fclose(f); end %store the UserAvailable Projects in class Property MatMust_getUserLinkedProjects(obj); else disp('user already loggen in') end end function exit = MatMust_inputvalidator(obj, input, validation) % For now, every validation_input field is treated as required % TODO: set add a subfield required at validataion json exit = false; val_fields = fields(validation); for i = 1:numel(val_fields) val_field = validation.(val_fields{i}); if isfield(input, val_fields{i}) if isa(input.(val_fields{i}), val_field) exit = true; else exit = false; end else exit = false; end end end end end