| |||||||||||||||
Solving a non-fixed Mosel model using a REST webservice, from Mosel Description In this example, the Xpress Executor should first be configured with the runzippedsubmodel.mos model. Then, you
run the blendxe.mos model locally; this uses REST webservice requests to send the blend3c model and blend.csv file to the Xpress
Executor and remotely solve the model using this, then downloads and displays the results. This demonstrates
how the Xpress Executor can be used to run many different models. This example
requires a local installation of Xpress.
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
Data Files blendxe.mos (!****************************************************** Xpress Executor Example Model ============================= file blendxe.mos ```````````````` Demonstrates executing the 'blend3c' model using an Xpress Executor configured to run a submodel extracted from a zipfile, which also contains the input data. It will then display the results. Assumes that you have an Xpress Executor component already configured with the runzippedsubmodel.bim model. (c) 2015-2017 Fair Isaac Corporation author: J. Farmer, Jul. 2017 *******************************************************!) model "Blend XE" uses "mmhttp", "mmssl", "mmsystem", "mmxml", "mmjobs" parameters ! You should set the DMP_XE_REST_ENDPOINT, DMP_SOLUTION_CLIENT_ID and DMP_SOLUTION_SECRET parameters ! to point to the component you want to test. ! The REST endpoint of the Xpress Executor DMP component ! Obtain this by clicking "View Links" for the Xpress Executor component on the DMP UI DMP_XE_REST_ENDPOINT="" ! The client ID of solution containing the Xpress Executor DMP component ! Obtain this through the DMP UI DMP_SOLUTION_CLIENT_ID="" ! The secret of the solution containing the Xpress Executor DMP component ! Obtain this through the DMP UI DMP_SOLUTION_SECRET="" ! The root DMP manager URL. This will be different depending on which instance of DMP you are using. DMP_MANAGER_URL="https://manager-svc.prd-platform.ficoanalyticcloud.com" ! The remote model file MODELFILE="../model/blend3c.mos" ! The input file for the remote model INPUTFILE="../data/blend.csv" ! File into which to save the results RESULTFILE="blendresults.csv" end-parameters declarations ! XML document containing final status of execution executionStatus: xmldoc ! Array of parameters modelParameterNames: set of string modelParameters: dynamic array(modelParameterNames) of text ! Array of input files to include in zip (not including submodel bim) ! In this array, key is the local file, value is path of file in the zipfile modelInputSourceFiles: set of string modelInputFiles: dynamic array(modelInputSourceFiles) of text ! Token used to authorise access to the Xpress Executor component bearerToken: text end-declarations forward procedure authoriseDmpComponent forward procedure createInputZip(modelfile:text,modelInputFiles:array(modelInputSourceFiles) of text) forward function startExecution(inputzipfile:text,modelParameters:array(modelParameterNames) of text) : xmldoc forward procedure waitForCompletion(executionStatus:xmldoc) forward procedure deleteExecution(executionStatus:xmldoc) forward function getURLFromPath(path:text) : text forward function getNodeValue(doc:xmldoc,path:text) : text forward function getNodeValue(doc:xmldoc,path:string) : text forward function fetchPathFromComponent(path:text,destfile:text) : integer public declarations ! Temporary variables used in assembling HTTP requests httpPostBody: text httpResponseBody: text inputBase64: text runLog: text end-declarations ! Configure Mosel to use TLS1.2 ciphers with HTTPS to allow us to talk to DMP setparam("https_ciphers","TLSv1.2+HIGH:\!SSLv2:\!aNULL:\!eNULL:\!3DES:@STRENGTH") ! Log into DMP writeln("Requesting authorization token from DMP") authoriseDmpComponent ! Execute our model writeln("Initiating model execution") modelParameters("INPUTFILE"):="blend.csv" ! Read inputs from the file blend.csv modelParameters("RESULTFILE"):="result" ! In Xpress Executor, we can only access model results from a file called "result" modelInputFiles(INPUTFILE) := "blend.csv" ! Include blend.csv in the zipfile we upload createInputZip(MODELFILE,modelInputFiles) executionStatus := startExecution("input.zip",modelParameters) ! Model will be executing asynchronously; wait for it to complete. writeln("Waiting for completion of execution") waitForCompletion(executionStatus) ! Check execution was successful writeln("Processing model results") ! In event of failure, echo the remote model status, exit code & run log to aid with troubleshooting if getNodeValue(executionStatus,"/jsv/status")<>"OK" or getNodeValue(executionStatus,"/jsv/exitCode")<>"0" then writeln("Execution failed!") writeln("Execution status: ", getNodeValue(executionStatus,"/jsv/status")) writeln("Execution exit code: ", getNodeValue(executionStatus,"/jsv/exitCode")) writeln writeln("Execution log:") ! Fetch the remote execution log as it may contain error messages from the model if fetchPathFromComponent( getNodeValue(executionStatus,"/jsv/runLogPath"), "text:runLog" )<>200 then writeln("Execution log not available") end-if writeln(runLog) exit(1) end-if ! Download results file if fetchPathFromComponent( getNodeValue(executionStatus,"/jsv/resultPath"), RESULTFILE )<>200 then writeln("Model results are not available!") exit(1) end-if ! Parse results and display to user declarations ORES=1..2 use: array(ORES) of real end-declarations initializations from "mmsheet.csv:"+RESULTFILE use as "[A1:B2]" end-initializations writeln writeln("Solution:") forall(o in ORES) writeln(" use(" + o + "): ", use(o)) ! Delete execution from component deleteExecution( executionStatus ) ! Use the client ID and secret from the solution to request a token that we can use to ! authorise access to the Xpress Executor component procedure authoriseDmpComponent declarations payloadDoc: xmldoc responseCode: integer end-declarations ! Assemble payload to send to DMP reset(payloadDoc) rootElem := addnode(payloadDoc, 0, XML_ELT, "jsv") setattr(payloadDoc, rootElem, "jst", "obj") clientIdElem := addnode(payloadDoc, rootElem, XML_ELT, "clientId") setattr(payloadDoc, clientIdElem, "jst", "str") setvalue(payloadDoc, clientIdElem, DMP_SOLUTION_CLIENT_ID) secretElem := addnode(payloadDoc, rootElem, XML_ELT, "secret") setattr(payloadDoc, secretElem, "jst", "str") setvalue(payloadDoc, secretElem, DMP_SOLUTION_SECRET) ! Post to server jsonsave(payloadDoc, "text:httpPostBody") reset(payloadDoc) responseCode := httppost( DMP_MANAGER_URL + "/registration/rest/client/token", "text:httpPostBody", "text:httpResponseBody", "Content-Type: application/json" ); reset(httpPostBody) ! Check response if responseCode<>200 then writeln("ERROR: Failed to authorise with DMP (HTTP response: "+responseCode+")") writeln(httpResponseBody) exit(1) end-if ! Store bearer token bearerToken := httpResponseBody reset(httpResponseBody) end-procedure ! Build the input zipfile from the given files procedure createInputZip(modelfile:text,modelInputFiles:array(modelInputSourceFiles) of text) declarations zipdir, workdir: string end-declarations ! Create temporary directory workdir := getparam("workdir") zipdir := workdir + "/inputdir" makedir(zipdir) if getsysstat<>0 then writeln("Failed to create ",zipdir) exit(1) end-if ! Compile or copy model into directory if endswith(modelfile,".mos") then if compile("s",string(modelfile),zipdir+"/submodel.bim")<>0 then writeln("ERROR: Failed to compile ",modelfile) exit(1) end-if else fcopy(modelfile,zipdir+"/submodel.bim") if getsysstat<>0 then writeln("ERROR: Failed to copy ",modelfile," to ",zipdir,"/submodel.bim") exit(1) end-if end-if ! Copy other input files into directory forall( sourceFileName in modelInputSourceFiles | exists(modelInputFiles(sourceFileName)) ) do fcopy( sourceFileName, zipdir+"/"+modelInputFiles(sourceFileName) ) if getsysstat<>0 then writeln("ERROR: Failed to copy ",sourceFileName," to ",zipdir,"/",modelInputFiles(sourceFileName) ) exit(1) end-if end-do ! Ensure old zipfile if present if getfstat("input.zip")<>0 then removefiles("input.zip") end-if ! Create zip newzip(0, "input.zip", zipdir, [text("submodel.bim")]+ sum(sourceFileName in modelInputSourceFiles | exists(modelInputFiles(sourceFileName))) [modelInputFiles(sourceFileName)] ) if getsysstat<>0 then writeln("ERROR: Failed to create zipfile" ) exit(1) end-if ! Clean up removefiles( SYS_RECURS, zipdir, "*" ) removedir(zipdir) end-procedure ! Start execution of model, using the given input data files. Returns initial execution status function startExecution(inputzipfile:text,modelParameters:array(modelParameterNames) of text) : xmldoc declarations payloadDoc: xmldoc responseDoc: xmldoc responseCode: integer headers: text paramstr: text end-declarations ! Assemble payload to send to DMP reset(payloadDoc) rootElem := addnode(payloadDoc, 0, XML_ELT, "jsv") setattr(payloadDoc, rootElem, "jst", "obj") ! Create parameters string forall( paramName in modelParameterNames | exists(modelParameters(paramName)) ) do setmodpar(paramstr, paramName, modelParameters(paramName)) end-do ! Set parameters in JSON doc paramsElem := addnode(payloadDoc, rootElem, XML_ELT, "parameters") setattr(payloadDoc, paramsElem, "jst", "obj") paramElem := addnode(payloadDoc, paramsElem, XML_ELT, "jsv") setattr(payloadDoc, paramElem, "name", "SUBMODEL_PARAMS") setattr(payloadDoc, paramElem, "jst", "str") setvalue(payloadDoc, paramElem, paramstr) ! Set input data fcopy( inputzipfile, "mmssl.base64:text:inputBase64" ) if getsysstat<>0 then writeln("Error encoding ",inputzipfile) exit(1) end-if inputDataElem := addnode(payloadDoc, rootElem, XML_ELT, "inputBase64") setattr(payloadDoc, inputDataElem, "jst", "str") setvalue(payloadDoc, inputDataElem, inputBase64 ) reset(inputBase64) ! Assemble HTTP headers headers += "Content-Type: application/json\n" headers += "Accepts: application/json\n" headers += "Authorization: Bearer "+bearerToken+"\n" ! Post to server jsonsave(payloadDoc, "mmsystem.text:httpPostBody") reset(payloadDoc) responseCode := httppost( DMP_XE_REST_ENDPOINT, "text:httpPostBody", "text:httpResponseBody", headers ); reset(httpPostBody) ! Check response if responseCode<>200 then writeln("ERROR: Failed to start model execution (HTTP response: "+responseCode+")") writeln(httpResponseBody) exit(1) end-if ! Parse response JSON jsonload(responseDoc, "text:httpResponseBody", 0) reset(httpResponseBody) returned := responseDoc end-function ! Return URL of given path on Xpress Executor component function getURLFromPath(path:text) : text declarations url: text end-declarations ! Strip the path off the endpoint URL url := DMP_XE_REST_ENDPOINT lastSlash := 0 forall (i in 1..3) do nextSlash := findtext(url,"/",lastSlash+1) if nextSlash=0 then writeln("Failed to parse URL!") exit(1) end-if lastSlash := nextSlash end-do deltext(url, lastSlash, getsize(url)) ! Append given path url += path ! And done returned := url end-function ! Return content of given XPATH expression in given document. Aborts model if was not found. function getNodeValue(doc:xmldoc,path:string) : text declarations nodeId: integer end-declarations nodeId := getnode(doc,path) if nodeId=-1 then writeln("Failed to find node '",path,"' in document") exit(1) end-if returned := getvalue(doc,nodeId) end-function function getNodeValue(doc:xmldoc,path:text) : text returned := getNodeValue(doc,string(path)) end-function ! Fetch the given path from the Xpress Executor component and save content to the given destination file. Returns response code. function fetchPathFromComponent(path:text,destfile:text) : integer declarations responseCode: integer headers: text url: text end-declarations reset(headers) headers += "Accepts: application/json\n" headers += "Authorization: Bearer "+bearerToken+"\n" url := getURLFromPath( path ) responseCode := httpget( url, string(destfile), headers ) returned := responseCode end-function ! Wait for model execution to complete; updates the supplied execution status document procedure waitForCompletion(executionStatus: xmldoc) declarations responseCode: integer end-declarations ! Loop while status indicates execution not completed status := getNodeValue(executionStatus,"/jsv/status") while (status="NOT_COMPLETED" or status="NOT_LOADED") do ! Wait for half a second sleep(500) ! Refresh status responseCode := fetchPathFromComponent( getNodeValue(executionStatus,"/jsv/statusPath"), "text:httpResponseBody" ) if responseCode<>200 then writeln("ERROR: Encountered HTTP error code ",responseCode," when querying execution status") writeln(httpResponseBody) ! Write out response body, in case it contains an error message. exit(1) end-if jsonload(executionStatus, "text:httpResponseBody", 0) reset(httpResponseBody) status := getNodeValue(executionStatus,"/jsv/status") end-do end-procedure ! Delete the execution from the service procedure deleteExecution(executionStatus: xmldoc) declarations responseCode: integer headers: text end-declarations reset(headers) headers += "Accepts: application/json\n" headers += "Authorization: Bearer "+bearerToken+"\n" responseCode := httpdel( getURLFromPath(getNodeValue(executionStatus,"/jsv/statusPath")), "null:", headers ) if responseCode<>200 then writeln("Warning: Received HTTP response ",responseCode," when deleting execution") end-if end-procedure end-model | |||||||||||||||
© Copyright 2024 Fair Isaac Corporation. |