Community Tip - Stay updated on what is happening on the PTC Community by subscribing to PTC Community Announcements. X
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
Solved! Go to Solution.
for the one specific approach here I want to provide a simple example.
{
"label-1":{"text":"Step 7"},
"3DLabel-1":{"visible":true,"text":"The current Step is Step 7","fontColor":"#ff6347"},
"wayfinder-1":{"selectedWaypointIndex":7,"ribbonColor":"#ff6347"}
}
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()
});
//===============
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:
@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?
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.
{
"label-1":{"text":"Step 7"},
"3DLabel-1":{"visible":true,"text":"The current Step is Step 7","fontColor":"#ff6347"},
"wayfinder-1":{"selectedWaypointIndex":7,"ribbonColor":"#ff6347"}
}
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()
});
//===============