cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Did you get called away in the middle of writing a post? Don't worry you can find your unfinished post later in the Drafts section of your profile page. X

How to implement Json with step instructions from sequence for label in Vuforia studio ?

IB_10645210
12-Amethyst

How to implement Json with step instructions from sequence for label in Vuforia studio ?

Hello Dear Community, Happy New Year

Please advise how I can implement a Jason file from the Creo Illustrate sequence annotation description for the step instructions in label in Vuforia Studio? 

Thank you

1 ACCEPTED SOLUTION

Accepted Solutions

for the one specific approach here  I want  to provide a simple example.

  1. a project will start manually the playAll service ,by clicking on button which has binding to the playAll service of the modelwidget.
  2. for each step number is called a json file called here properties_step_XX.json. This contains widget and properties which should be set when the step started. 
  3. Syntax of the json file could be something like this  :
    {
    "label-1":{"text":"Step 7"},
    "3DLabel-1":{"visible":true,"text":"The current Step is Step 7","fontColor":"#ff6347"},
    "wayfinder-1":{"selectedWaypointIndex":7,"ribbonColor":"#ff6347"}
    } ​

     

  4. I used some code as shown in the picture of the previous post  

    $scope.jsonData={}; //global $scope variable
    //===========================================================================
    gotJSON=function(data){
     try{
    $scope.jsonData=JSON.parse(data);
     console.warn(  JSON.stringify($scope.jsonData))
       $scope.setMutipleProps($scope.jsonData)
     } 
    catch(wrong){    console.warn(wrong);}
    
    }
    //===========================================================================
    doRead=function (jsonFile){
    fetch(jsonFile)
      .then(response=>response.text())
      .then(data=>gotJSON(data))
    }
    
    //===========================================================================
    $scope.readDataForStepNumber=function(stepNumber) {
    let jsonFile= "app/resources/Uploaded/properties_step_"+stepNumber+".json"
    console.log("jsonFile=",jsonFile)
    doRead(jsonFile)
    }
    //============================================
    function toHex(str) { var ret = ''; 
         for (var i=0; i<str.length; i++) { ret += str.charCodeAt(i).toString(16);}return ret;}
    //===========================================================================
    //the code will read the complette JSON File and
    //assignee it to a jsonData global variable
    //func will set mutliple widget properties according JSON
    $scope.setMutipleProps  = function(obj){
     Object.keys(obj).forEach(function (item)  {
                    for(var prop in obj[item])    {
                              if((item.toString().indexOf('wayfinder') !=-1) && ( prop.toString().indexOf('selectedWaypointIndex') !=-1)) // will set the index one of 4
                                          {  $scope.view.wdg[item][prop]= (parseInt(obj[item][prop])-1)%4;  }        
                         else
                                $scope.view.wdg[item][prop]=obj[item][prop];  
                               $scope.$applyAsync()
                               //print the setting for debugging                 
    							console.log("==>$scope.view.wdg["+item+"]["+prop+"]="+$scope.view.wdg[item][prop] )
                                                  } 
                    							})
                                           };
    //===========================================================================
    //$scope.$on('stepcompleted', function(evt, arg1, arg2, arg3) { //not used
      $scope.$on('stepstarted', function(evt, arg1, arg2, arg3) { 
     var parsedArg3 = JSON.parse(arg3);
       
       $scope.readDataForStepNumber(parsedArg3.stepNumber)
        $scope.$applyAsync()
        }); 
    //===============

     

  5. Tested in Vuforia Studio preview mode - so it set some properties for testing e.g.
  6. 2024-01-12_17-03-13.jpg
  7. demo project attached

 

View solution in original post

7 REPLIES 7

Hello @IB_10645210 ,

what is the goal here? You mention a json file? What is the reason to require a json fil? e.g. You need to save it back to Thingworx repository etc.?

So far I understand you want to have access to the step parameter? 

During a step playing you can access this paramter e.g. in the stepstarted or stepcompleted event ... e.g.

//==================================
$scope.$on('stepstarted', function(evt, arg1, arg2, arg3) { 
 var parsedArg3 = JSON.parse(arg3);

  console.log("stepName="+parsedArg3.stepName);
  console.log("stepDescription="+parsedArg3.stepDescription);
  console.log("nextStep="+parsedArg3.nextStep);
              
  $scope.stepDescription=parsedArg3.stepDescription;
  console.warn("Event: " + evt.name + " arg3 fields, name: " + parsedArg3.stepName + " duration(ms): " + parsedArg3.duration  + " total steps: " + parsedArg3.totalSteps  + " step desc: " + parsedArg3.stepDescription );
}); 

in the javascript info is printed to console and saved to $scope parameter. Similar to the example the info could be also added to a json object. We can save this json object later to json File in Thingworx file repostitory. A local save in project directory so far I know is not allowed because of security reasons.

I am not sure if the step descripton is in the metadata - I belive not because is this informaiton possibly in the PVI file , information related to the sequence. Possibly we can read the sequence pvi file and extract the step description infromation. Need to check this further. Thanks

In the sequence pvi ( file we can find the step_description informaiton

...
...
<galaxy_3di:sequence_step>
      <galaxy_3di:property
        type="string"
        name="step_name_locid"
        value="stepName_9490964569646394174_0"/>
      <galaxy_3di:property
        type="string"
        name="step_name"
        value="Step 1"/>
      <galaxy_3di:property
        type="bool"
        name="step_acknowledge">false</galaxy_3di:property>
      <galaxy_3di:property
        type="string"
        name="step_description_locid"
        value="stepDescription_9167703768846522178_0"/>
      <galaxy_3di:property
        type="string"
        name="step_description"
        value="NOTE FOR STEP1"/>
....
....

so using some xml parser (DOMParser )we could find and extract the step_description informaiton.

the sequence.pvi file is inside the pvz model file. It could be unziped  as file. The pvi file has the same name as the figrure/sequence what we can see in Studio in the sequence property for the modelWidget.  (I-Creo 3D - +<figure name>)

@RolandRaytchev Thank you for response!

I want to simplify my experience in Vuforia Studio.

My current each JS page on VS has 400 lines ( for each case with instruction + "if", "else" for a few  popup dialogs + Wayfinder for some cases) . As I recently learned I can save time on implementing instructions with label and load json file with instructions from Sequence's annotations. Also I can load sequence list from pvi to the dropdown list which is also will save me some time. Please advise  how I can accomplish this.

@IB_10645210  thanks for the feedback! ok the goal is clear. But for me is not clear why is neccessarly is required  to read in this case the pvi file. So it is possible to load any json files. For example the json file with some istructions could be saved in a project folder (e.g. in the Upload folder or subfolder of it) But we can also load json file form any other locations e.g. THINGWORX repository. What  i need to understand here is the context of the simplifcaiton. Could you provide , please, here a piece of code what in this case should be simplificated and possibly with description when it should be executed .... in which context , event so that it could be done then automaticaly. 

For exampe :

....in the following code <some code > we need to check for some.... widgets the following property and when values are >= a value then we need to change value or execute any things. Or when a new steps, or new sequence is set etc.... then some actions should be done ....

 

I remember that in the past we did some attempt for automation using some approach like this:

2024-01-11_16-16-02.jpg

@RolandRaytchev There is my shortest ( linear scenario without if/else) JS View page . My goal is to simplify the  list of cases and automate it /by connecting the json with instructions to the label. Then  separately align Wayfinder JS part  ( I am not sure if I can simplify the Wayfinder code).

 

// $scope, $element, $attrs, $injector, $sce, $timeout, $http, $ionicPopup, and $ionicPopover services are available

//url of the deepLink of Project B
$scope.UriTest='///////////////////////////////////////////////////////'
// the view of the proejct B which should be called
$scope.cVIEW="///////////////////////////////////////////////////////"
// this the function what should be called by the button
//it will call project B (specified by url - and then call view
$scope.callViewProject=function(url,view){

window.location.href='vuforiaview://ptc.com/command////////////////////////////////////////////////////////
}
//----------------
$scope.//////////////////////=()=> {
$scope.callViewProject($scope.UriTest,$scope.cVIEW)
}
// $scope, $element, $attrs, $injector, $sce, $timeout, $http, $ionicPopup, and $ionicPopover services are available


$scope.newstep=function(){
$scope.view.wdg["model-1"].currentStep=$scope.step+1
$scope.step=$scope.step+1;

}
$scope.back=function(){
$scope.step=$scope.step-1;
$scope.view.wdg["model-1"].currentStep=$scope.step
}

$scope.step=0;

$scope.$watch('step', function(val) {
console.log($scope.view.wdg["model-1"].currentStep)
$timeout(function(){
$scope.view.wdg["model-1"].currentStep=$scope.step;
  $scope.app.fn.triggerWidgetService("model-1", "play");
},500); //wait for 1/2 second


switch (val) {
case 1:
$scope.view.wdg['label-1'].text = " ///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].enabled=false
$scope.app.fn.triggerWidgetService("model-1", "play");
$scope.view.wdg['forward'].visible=false;
break;

case 2:
$scope.view.wdg['label-1'].text = "///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].enabled=false
break;
case 3:
$scope.view.wdg['label-1'].text = "///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].enabled=false
break;
case 4:
$scope.view.wdg['label-1'].text = " ///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].enabled=false
break;
case 5:
$scope.view.wdg['label-1'].text = " R///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].selectedWaypointIndex=0;
$scope.view.wdg['wayfinder-1'].showRibbon=true
$scope.view.wdg['wayfinder-1'].showWaypoints=true
$scope.view.wdg['wayfinder-1'].showReticle=true
$scope.view.wdg['wayfinder-1'].enabled=true
  $scope.app.fn.triggerWidgetService("model-1", "play");
break;
case 6:
$scope.view.wdg['label-1'].text = "///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].selectedWaypointIndex=2;
$scope.view.wdg['wayfinder-1'].showRibbon=true
$scope.view.wdg['wayfinder-1'].showWaypoints=true
$scope.view.wdg['wayfinder-1'].showReticle=true
$scope.view.wdg['wayfinder-1'].enabled=true
break;
case 7:
$scope.view.wdg['label-1'].text = "///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].enabled=false
break;
case 8:
$scope.view.wdg['label-1'].text = "///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].enabled=false
break;
case 9:
$scope.view.wdg['label-1'].text = "///////////////////////////////////////////////////////";
$scope.view.wdg['wayfinder-1'].enabled=false
$scope.app.fn.triggerWidgetService("model-1", "stop");


break;
default:
$scope.view.wdg['label-1'].text='done'
break;
}
if(!$scope.view.wdg['model-1']['currentStep']){
$scope.view.wdg['label-1'].text='';
}
else {

$scope.view.wdg['label-1'].text= $scope.view.wdg['model-1']['currentStep']+ '/'+ $scope.view.wdg['model-1']['steps']+' '+ $scope.view.wdg['label-1'].text; //here I am adding step x/n:
}
});

$scope.$on("playstopped", function() {
if ($scope.step!=9){

$scope.app.fn.triggerWidgetService("model-1", "stop");
$scope.view.wdg['forward'].visible=true;}
$scope.view.wdg['backbtn'].visible=true;

$scope.$on("playstarted", function() {
$scope.view.wdg['forward'].visible=false;
  });

  });
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*****************************************************
//This function is triggered when the user clicks 'next'
//for any steps where you need multiple way points, create a new case
//******************************************************

 

$scope.next=function(){
switch ($scope.step) {
case 5:
  if ( $scope.view.wdg['wayfinder-1'].selectedWaypointIndex!=1){
$scope.view.wdg['wayfinder-1'].selectedWaypointIndex= $scope.view.wdg['wayfinder-1'].selectedWaypointIndex+1
}

else{
$scope.newstep();
}
break;

case 6:
if ($scope.view.wdg['wayfinder-1'].selectedWaypointIndex!=5){
$scope.view.wdg['wayfinder-1'].selectedWaypointIndex= $scope.view.wdg['wayfinder-1'].selectedWaypointIndex+1
}
else{
$scope.newstep();
}

break;
default:
$scope.newstep();
break;
}
}
//------------------------------------------------------------
$scope.Test=function(){
var wdgs=$scope.app.view.Home.wdg;
for(var wdg in wdgs)
{
if(hasWdgProp(wdgs[wdg],'selectedWaypointIndex')) //only wayfinder widget has this property
{ let count=0;
let arr_size= wdgs[wdg].waypointsData.length

for (i=0; i<arr_size; i++) {
//4 grids
a1=i % 8
a2= (i -a1) /8
wdgs[wdg].waypointsData[i].position.x=-0.5+0.25*a1
wdgs[wdg].waypointsData[i].position.y=0.1+0.25*a2
wdgs[wdg].waypointsData[i].position.z=0
$scope.$applyAsync();}
}
$scope.$applyAsync();
}
};
//======================================================================

 

Hi @IB_10645210 ,

when I look into the code , I am not sure about the goal because I could find some handling of steps changes but also a wayfinder widget.

First point is to clarify what is the relevant trigger event what need to be used so to decide the setting of the properties. So far I could see the mean ccondition/ is the number of step – means the  event what is checked  should be  the step change. So finishing a step e.g. after step 1 completed then  we need to set the properties for the next step - e.g. step2. In this case we can read a json file with the name e.g. properties_step_2.json read the values and set them . So we will avoid the parts of the code where we change values specific for each step. Is this your requirement?

But... still we need to pay attention that when we set properties on a model Widget or on modelItem Widgets related to the model Widget where we play a step. This could lead to problems with the correct play of the animation of the sequence step. So sometimes also some delay is needed could be helpful before the next  action is colled.

I see also in the code some play and stop tirrgerWidgetServices called what is for me not clear.  So , what should be the primary action?

  • So is the goal to play  all steps (playAll service) and trigger the change of the steps when one step is completed.
  • Or manually change of the step - means  when a step is changed then e.g. properties depended on the current step are set and then e.g.  the play modelwidget servce should be called - is this the intended approach what should be implemented? 

I see that in the code, there are checked different variables also there is a $watch  construct which monitor the $scope.step - variable . It is incremented also by your code - and I am not sure if this is the optimal way and possibly it  could lead to problems. I think we need to concentrate to monitor only one variable or event - event .e.g step completed (the event provided different parameters also the current and next step) or to the modelwidget property  currentstep -  respectively we can bind this to application parameter

 

Another  approach could be  to use the wayfinder widget. In this case  you will start with the Waywidget first point and will go to this point - and when you arrive the point (inside the event radius ) then this could activate some properties , display UI - e.g button for action related to the new position  and play a specific step which is relevant to the current arrived position. And then when you move to the next point then the next actions are called. So means here is the approach related to the current position – depending on that which way points is arrived and  depending on this we will play a specific sequence number. Possibly we could have the same number of sequences as numbers of waypoints , but possibly we need to play more then one sequence on specific location.

I think it will be good if we could concentrate only on one event type/variable as trigger . Using different approaches at the same time could lead possibly to confusion.

Thanks. BR

for the one specific approach here  I want  to provide a simple example.

  1. a project will start manually the playAll service ,by clicking on button which has binding to the playAll service of the modelwidget.
  2. for each step number is called a json file called here properties_step_XX.json. This contains widget and properties which should be set when the step started. 
  3. Syntax of the json file could be something like this  :
    {
    "label-1":{"text":"Step 7"},
    "3DLabel-1":{"visible":true,"text":"The current Step is Step 7","fontColor":"#ff6347"},
    "wayfinder-1":{"selectedWaypointIndex":7,"ribbonColor":"#ff6347"}
    } ​

     

  4. I used some code as shown in the picture of the previous post  

    $scope.jsonData={}; //global $scope variable
    //===========================================================================
    gotJSON=function(data){
     try{
    $scope.jsonData=JSON.parse(data);
     console.warn(  JSON.stringify($scope.jsonData))
       $scope.setMutipleProps($scope.jsonData)
     } 
    catch(wrong){    console.warn(wrong);}
    
    }
    //===========================================================================
    doRead=function (jsonFile){
    fetch(jsonFile)
      .then(response=>response.text())
      .then(data=>gotJSON(data))
    }
    
    //===========================================================================
    $scope.readDataForStepNumber=function(stepNumber) {
    let jsonFile= "app/resources/Uploaded/properties_step_"+stepNumber+".json"
    console.log("jsonFile=",jsonFile)
    doRead(jsonFile)
    }
    //============================================
    function toHex(str) { var ret = ''; 
         for (var i=0; i<str.length; i++) { ret += str.charCodeAt(i).toString(16);}return ret;}
    //===========================================================================
    //the code will read the complette JSON File and
    //assignee it to a jsonData global variable
    //func will set mutliple widget properties according JSON
    $scope.setMutipleProps  = function(obj){
     Object.keys(obj).forEach(function (item)  {
                    for(var prop in obj[item])    {
                              if((item.toString().indexOf('wayfinder') !=-1) && ( prop.toString().indexOf('selectedWaypointIndex') !=-1)) // will set the index one of 4
                                          {  $scope.view.wdg[item][prop]= (parseInt(obj[item][prop])-1)%4;  }        
                         else
                                $scope.view.wdg[item][prop]=obj[item][prop];  
                               $scope.$applyAsync()
                               //print the setting for debugging                 
    							console.log("==>$scope.view.wdg["+item+"]["+prop+"]="+$scope.view.wdg[item][prop] )
                                                  } 
                    							})
                                           };
    //===========================================================================
    //$scope.$on('stepcompleted', function(evt, arg1, arg2, arg3) { //not used
      $scope.$on('stepstarted', function(evt, arg1, arg2, arg3) { 
     var parsedArg3 = JSON.parse(arg3);
       
       $scope.readDataForStepNumber(parsedArg3.stepNumber)
        $scope.$applyAsync()
        }); 
    //===============

     

  5. Tested in Vuforia Studio preview mode - so it set some properties for testing e.g.
  6. 2024-01-12_17-03-13.jpg
  7. demo project attached

 

Top Tags