Community Tip - Learn all about PTC Community Badges. Engage with PTC and see how many you can earn! X
Hello Community,
I am having a very strange issue. My experience works well on my pc means vuforia studio but not in view app. I mean it starts and then when i do same thing means run same features which i have built.. works well on pc but not in view app. It just could not finish the code execution and it breaks then its just stay in the same state and not doing anything. There is no error in my console.log even or anything which may impact on view app. Still it does not work and surprisingly sometimes it works but sometimes its not.
Anyone has an idea why it is like that? I have also posted a photo below from log file of view app. i get this FPS many times and afterwards then nothing happens actually.
Hi @MA8731174
so far I now FPS - is for frame per seconds coming when you will try some animation or change the orientations etc where the rendering is called ... so it depends on that what you try to do there.
So some points:
TS article CS344588 https://www.ptc.com/en/support/article/CS344588
where issue is based on some Javascript loop calling takeScreenshot addting large values to array which cause kinde of overflow... etc. The memory amout on device side is lower then on Windows chrome so that possible the difference what I belive will cause the problem .
Thank you @RolandRaytchev for these insights. Actually On button click I am taking signature from user and then save the data in json after reading the values from widgets and then generate the pdf of 6 popups. I have shared my code also below which shows the sequence of the execution on the single button click. Its a bit lengthy operations i would say ...to first take the signature from user, save the data in json from all the widgets (Textfields and checkboxes) ..I have nearly 380 widgets (Textfields and checkboxes) and i save the values of these widgets in json. The function which generates this json is called generateJson which i also added in the code below then i generate a pdf and save it into thingworx. Can you please just see the code may be and give me few more insights what can be the reason of this problem?
HINT: When i see logs while creation of pdf it creates pdf for eg for page 1 then page 2 then page 3 or sometimes page 1 page 2 or page 1 page 2 page 3 page 4 and then just FPS logs will show and nothing happens. the spinner will keeip moving and nothing happens then ....so definitely problem comes in creation of pdf.
DEVICE: IPAD PRO 5th generation with 17.4.1 iOS
MODEL: NO 3D/2D MODEL IN MY EXPERIENCE
ANIMATION: NO
EXECUTION FLOW:-
1) take signature from user
2) generate the json with for loop and save the data on thingworx (generate json have loops with many IF conditions also)
3) create PDF and save it in thingworx
4) save signature images on thingworx
//This function executes on button click and this then let run other functions afterwards the execution
$scope.submitBtnPressed = function () {
const status = $scope.app.params.Status; // take this instead $scope.projectInfo.status;
if (status === PHASES.SCW2Phase1 || status === PHASES.SCW2Phase2 || status === PHASES.SCW2Phase3 ) {
const SignatureHeadinglabelMap = {
[PHASES.SCW2Phase1]: "Verantwortlicher Aufbau EDTCD1: ",
[PHASES.SCW2Phase2]: "Verantwortlicher Aufbau EDTCD2: ",
[PHASES.SCW2Phase3]: "ANLV bzw. delegierter Abnehmer: "
};
$scope.view.wdg['zfSignature'].label = SignatureHeadinglabelMap[status] || "Default Label: ";
$scope.view.wdg['zfSignature'].text = $scope.app.params.UserID;
twx.app.fn.triggerWidgetService('zfSignature', 'takeSignature');
} else if (status === PHASES.SCW2Phase4) {
$ionicPopup.prompt({
title: '<h3>Inbetriebnehmer</h3>',
subTitle: '<h3>Bitte den Namen des Inbetriebnehmer eintragen:</h3>',
template: `
<form>
Vorname:<br>
<input type="text" ng-model="data.firstname" name="firstname"><br>
Nachname:<br>
<input type="text" ng-model="data.lastname" name="lastname">
</form>`,
inputType: 'text',
scope: $scope,
buttons: [
{
text: '<b>OK</b>',
type: 'button-positive',
onTap: function () {
const name = $scope.data.firstname + " " + $scope.data.lastname;
$scope.view.wdg['zfSignature'].label = "Inbetriebnehmer des Versuchs: ";
$scope.view.wdg['zfSignature'].text = name;
$scope.view.wdg['0_4_name'].text = name;
console.log("Es wurde folgender Name eingetragen: " + name);
$scope.customToastmessage("Es wurde folgender Name eingetragen: " + name, 3000);
twx.app.fn.triggerWidgetService('zfSignature', 'takeSignature');
return true;
}
},
{
text: 'Abbrechen',
type: 'button-assertive',
onTap: function () {
console.log("Eingabe abgebrochen");
return false;
}
}
]
}).then(() => {
$scope.data.firstname = "";
$scope.data.lastname = "";
});
}
};
// This runs after the execution of the above function
$scope.$on("signatureTaken", function () {
console.log("Signature Taken!");
triggerPDF = true;
$scope.saveDataBtnPressed();
});
//Then this will run after the execution of the above function
$scope.saveDataBtnPressed = function() {
console.log("save pressed");
twxDS("TestBench_Mobile.Controller", "saveReport", {
"entryTimeStamp" : $scope.view.wdg["0_1"].text,
"Position" : $scope.view.wdg["0_3"].text,
"Location" : $scope.app.params.Location,
"metaData" : JSON.stringify($scope.generateJSON()),
"UserID" : $scope.app.params.UserID,
"changeDate" : + $scope.projectInfo.changeDate
});
}
// this below is the generateJSON function which loops over all the widgets and gets the data from them (textfields and checkboxes)
$scope.generateJSON = function() {
let jsonData = {
"mainValues": [],
"subValues" : [],
"signatures": [],
"checkListVersion": $scope.app.params.checkListVersion
};
// Loop for mainValues
for (let i = 1; ; i++) {
let mainValueQID = "0_" + i;
if (!$scope.view.wdg[mainValueQID]) {
break;
}
let mainValue = {
"QID": mainValueQID,
"label": $scope.view.wdg[mainValueQID].label,
"input": (i === 1) ? $scope.view.wdg["showDate"].text : $scope.view.wdg[mainValueQID].text,
};
if (i === 1) {
mainValue.value = $scope.view.wdg[mainValueQID].text
}
jsonData.mainValues.push(mainValue);
}
// Loop for subValues
for (let i = 1; ; i++) {
var subValueExist = false;
for (let j = 1; ; j++) {
var subValueQID = i + "_" + j;
var widgetID = i + "_" + j + "_1";
console.log('Checking widgetID:', widgetID);
// Check if the widget exists
if (!$scope.view.wdg[widgetID]) {
console.log('No widget found for ID:', widgetID);
break; // Exit the inner loop if the widget doesn't exist
}
console.log('Widget found:', widgetID);
subValueExist = true;
var subValue = {
"QID": subValueQID,
"title": "Name of a title",
"inputs": []
};
// Check the status of subValue
if ($scope.view.wdg[subValueQID + "_io"] && $scope.view.wdg[subValueQID + "_io"].value) {
subValue.status = "io";
}
if ($scope.view.wdg[subValueQID + "_nio"] && $scope.view.wdg[subValueQID + "_nio"].value) {
subValue.status = "nio";
}
if ($scope.view.wdg[subValueQID + "_nv"] && $scope.view.wdg[subValueQID + "_nv"].value) {
subValue.status = "nv";
}
// Check the manager status of subValue
if ($scope.view.wdg[subValueQID + "_Manager_io"] && $scope.view.wdg[subValueQID + "_Manager_io"].value) {
subValue.managerStatus = "Manager_io";
}
if ($scope.view.wdg[subValueQID + "_Manager_nio"] && $scope.view.wdg[subValueQID + "_Manager_nio"].value) {
subValue.managerStatus = "Manager_nio";
}
if ($scope.view.wdg[subValueQID + "_Manager_nv"] && $scope.view.wdg[subValueQID + "_Manager_nv"].value) {
subValue.managerStatus = "Manager_nv";
}
for (let k = 1; ; k++) {
var inputQID = i + "_" + j + "_" + k;
// Check if the widget exists
if (!$scope.view.wdg[inputQID]) {
break;
}
var input = {
"label": $scope.view.wdg[inputQID].label,
"value": $scope.view.wdg[inputQID].text,
"unit": "" // Set the appropriate unit
};
subValue.inputs.push(input);
}
jsonData.subValues.push(subValue);
}
// Exit the outer loop if no subValues were found
if (!subValueExist) {
break;
}
}
for (let i = 1; i < 5; i++) {
if(!$scope.view.wdg['0_' + i + '_sign'].imgsrc){
break;
}
jsonData.signatures.push({
"QID": "0_" + i,
"date": $scope.view.wdg['0_' + i + '_date'].text,
"name": $scope.view.wdg['0_' + i + '_name'].text
});
}
return jsonData;
}
//Then this function will run after the execution of the above function
let signaturePictureName;
let signaturePicture;
let triggerPDF = false;
$scope.$on("saveReport.serviceInvokeComplete", function () {
console.log("Daten wurden gespeicher!");
$scope.customToastmessage("Daten wurden gespeichert!",3000);
if(triggerPDF){
const currentDate = new Date();
const currentDateString = `${currentDate.getDate()}.${(currentDate.getMonth() + 1).toString().padStart(2, '0')}.${currentDate.getFullYear()} ${currentDate.getHours()}:${currentDate.getMinutes().toString().padStart(2, '0')}`;
//$scope.app.params.Status.match(/\d+/) ==> Before it was like that when the status was phase1
$scope.view.wdg['0_' + parseInt($scope.app.params.Status.match(/(?<=Phase)\d+/)) + '_date'].text = currentDateString;
$scope.view.wdg['0_' + parseInt($scope.app.params.Status.match(/(?<=Phase)\d+/)) + '_sign'].imgsrc=$scope.view.wdg['zfSignature'].signatureUrl;
if ($scope.app.params.Status !== PHASES.SCW2Phase4) {
$scope.view.wdg['0_' + parseInt($scope.app.params.Status.match(/(?<=Phase)\d+/)) + '_name'].text = $scope.app.params.UserID;
}
signaturePicture = $scope.view.wdg['0_' + parseInt($scope.app.params.Status.match(/(?<=Phase)\d+/)) + '_sign'].imgsrc;
if ($scope.app.params.Status == PHASES.SCW2Phase1) {
signaturePictureName = "Signature_mechanic_EDTCD1.jpg";
}
if ($scope.app.params.Status == PHASES.SCW2Phase2) {
signaturePictureName = "Signature_mechanic_EDTCD2.jpg";
}
if ($scope.app.params.Status == PHASES.SCW2Phase3) {
signaturePictureName = "Signature_manager.jpg";
}
if ($scope.app.params.Status == PHASES.SCW2Phase4) {
signaturePictureName = "Signature_customer.jpg";
}
if($scope.app.params.Status == PHASES.SCW2Phase1 ||$scope.app.params.Status == PHASES.SCW2Phase2 || $scope.app.params.Status == PHASES.SCW2Phase4) {
$scope.printPDF_MESSAGE();
}
if($scope.app.params.Status == PHASES.SCW2Phase3) {
$ionicPopup.show({
cssClass: 'custom-popup',
title: 'Fertig?',
subTitle: 'Ist der Prüfaufbau abgeschlossen oder steht noch die Unterschrift des Inbetriebnehmer aus? Bitte schließen Sie die Anwendung nicht, bis Sie die Meldung "Upload war erfolgreich" erhalten haben. Nach Erhalt dieser Meldung werden Sie automatisch ausgeloggt.',
buttons: [{
text: 'Inbetriebnehmer nötig',
type: 'button-energized',
onTap: function() {
this.hide(); // Popup manuell schließen
$scope.printPDF_MESSAGE();
return 'cancel';
}
}, {
text: 'Fertig',
type: 'button-balanced',
onTap: function() {
this.hide(); // Popup manuell schließen
// Extract the number at the end of the string, increment it by 2, and replace the old number in the string with the new one.
$scope.view.wdg['0_4_date'].text = "——";
$scope.view.wdg['0_4_name'].text = "Kein Inbetriebnehmer vorhanden";
$scope.app.params.Status = $scope.app.params.Status.replace(/\d+$/, match => parseInt(match) + 1);
customerPresent = false;
$scope.printPDF_MESSAGE();
return 'ok';
}
}, ]
})
}
}
});
//This will run after the execution of the above function
$scope.printPDF_MESSAGE = function () {
$ionicPopup.alert({
title: "WICHTIG!",
template: `<div>
Bitte schließen Sie die Applikation nicht, während die PDF erstellt wird! Das korrekte Format ist nur gewährleistet, wenn Sie das Tablet richtig ausrichten.
Dies erfolgt <span style="font-size: 24px; color: red; font-weight: bold;">nur</span> im
</div>
<br>
<div style="font-size: 52px; color: red; font-weight: bold;">Querformat(!)</div></br>
`,
scope: $scope,
buttons: [{
text: `<b>PDF erstellen</b>`,
type: 'button-positive',
onTap: function() {
$scope.view.wdg["loading-screen"].visible = true;
$timeout(function () {
$scope.printPDF();
}, 500);
}
}]
});
};
//Afterwards this code will execute which generates the PDF for the popups which are called pages
let myscript = document.createElement('script');
myscript.src="app/resources/Uploaded/jspdf.js";
document.head.appendChild(myscript);
myscript.onload = async function(){
// code to be executed when the script has finished loading
//...
console.log("stop here");
}
let html2canvas = document.createElement('script');
html2canvas.src="app/resources/Uploaded/html2canvas.js";
document.head.appendChild(html2canvas);
html2canvas.onload = function(){
// code to be executed when the script has finished loading
//...
}
let PDFLib = document.createElement('script');
PDFLib.src="app/resources/Uploaded/pdf-lib.js";
document.head.appendChild(PDFLib);
PDFLib.onload = async function(){
// code to be executed when the script has finished loading
//...
}
$scope.printPDF = async function() {
let popupArray = $scope.app.params.Page5Visibility === true? ["page_0", "page_1", "page_2", "page_3", "page_4", "page_5"] : ["page_0", "page_1", "page_2", "page_3", "page_4"];
if (customerPresent && $scope.app.params.Status === PHASES.SCW2Phase4) { // For SCW2Phase4 and customer present, use only page_0
popupArray = [
"page_0",
/* "page_1",
"page_2",
"page_3",
"page_4",
"page_5"*/];
}
let helper1 = document.querySelectorAll("div.twx-popup-container.ng-hide, ion-spinner.ng-hide");
helper1.forEach(box => { box.classList.remove("ng-hide")} );
// Wait for 100ms before proceeding
// await new Promise(resolve => setTimeout(resolve, 100));
let attributeContainer = [];
const pdfDoc = await window.PDFLib.PDFDocument.create();
for (const popupId of popupArray) {
console.log(PHASES.SCW2Phase3);
console.log($scope.app.params.Status);
console.log("page: " + popupId + " erstellt");
attributeContainer.push(document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).getAttribute("style"));
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", "width: 100%; overflow: visible;");
let mergedElements = document.createElement('DIV');
mergedElements.setAttribute('style', 'font-family: prometo !important; letter-spacing: 0.01px; overflow: visible');
mergedElements.append(document.querySelector(`twx-widget[widget-id='${popupId}']`).cloneNode(true));
let pdfHeight = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetHeight + 25,
pdfWidth = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetWidth + 20;
let pdf = new jspdf.jsPDF('l', 'px', [pdfWidth, pdfHeight]);
pdf.addFileToVFS('prometo-normal.ttf', font);
pdf.addFont('prometo-normal.ttf', 'prometo', 'normal');
pdf.setFont("prometo", 'normal');
let donorPdfBytes;
await pdf.html(mergedElements, {
// this warning is wrong - everything is fine
callback: receivedPDF => { donorPdfBytes = receivedPDF.output("datauristring"); },
x: 5,
y: 5
});
let donorPdfDoc = await window.PDFLib.PDFDocument.load(donorPdfBytes);
let docLength = donorPdfDoc.getPageCount();
for (let k = 0; k < docLength; k++) {
const [donorPage] = await pdfDoc.copyPages(donorPdfDoc, [k]);
pdfDoc.addPage(donorPage);
/* console.log("jamal page "+ donorPage);*/
}
}
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
let mergedPDF = pdfDataUri.substring(pdfDataUri.indexOf(',') + 1);
helper1.forEach(box => { box.classList.add("ng-hide"); });
popupArray.forEach((popupId, i) => {
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", attributeContainer[i]);
});
const currentDate = new Date();
const currentDateString = `${currentDate.getFullYear()}-${(currentDate.getMonth() + 1).toString().padStart(2, '0')}-${currentDate.getDate().toString().padStart(2, '0')}_${currentDate.getHours().toString().padStart(2, '0')}-${currentDate.getMinutes().toString().padStart(2, '0')}_`;
let helperName;
if ($scope.app.params.Status == PHASES.SCW2Phase1) {
helperName = "mechanic_EDTCD1";
}
if ($scope.app.params.Status == PHASES.SCW2Phase2) {
helperName = "mechanic_EDTCD2";
}
if ($scope.app.params.Status == PHASES.SCW2Phase3) {
helperName = "manager";
}
if ($scope.app.params.Status == PHASES.SCW2Phase4) {
if(!customerPresent) {
helperName = "manager";
} else {
helperName = "Inbetriebnehmer";
}
}
let pdfName = $scope.view.wdg["0_5"].text + "-" + $scope.view.wdg["0_6"].text + "_" + currentDateString + helperName + ".pdf";
let pdfPath = $scope.projectInfo.repositoryRelativePath + "/" + pdfName;
twxDS("TestBench_Mobile.Controller", "savePDF", { "content": mergedPDF, "path": pdfPath });
console.log(" PDF with the name '" + pdfName + "' created");
};
$scope.$on("savePDF.serviceInvokeComplete", function () {
console.log("Save PDF done");
$scope.customToastmessage("pdf wurde erfolgreich gespeichert.",3000);
$scope.lockEntries(true);
twxDS("TestBench_Mobile.Controller", "saveSignatureImage", { "path": $scope.projectInfo.repositoryRelativePath, "name": signaturePictureName, "content": signaturePicture });
})
$scope.$on("saveSignatureImage.serviceInvokeComplete", function () {
console.log("Signature saving done");
$scope.app.params.Status = $scope.app.params.Status.replace(/\d+$/, match => parseInt(match) + 1);
twxDS("TestBench_Mobile.Controller", "submitReport", {
"entryTimeStamp" : $scope.view.wdg["0_1"].text,
"Position" : $scope.view.wdg["0_3"].text,
"Location" : $scope.app.params.Location,
"status" : $scope.app.params.Status,
"metaData" : JSON.stringify($scope.generateJSON()),
"UserID" : $scope.app.params.UserID,
"changeDate" : + $scope.projectInfo.changeDate
});
})
$scope.$on("submitReport.serviceInvokeComplete", function () {
console.log("Submit durchgeführt");
$scope.view.wdg["loading-screen"].visible = false;
$scope.customToastmessage("submit war erfolgreich", 3000);
// TODO : disable buttons? or instantly restartApp - because people can abuse the system if not
$timeout(function () {
$scope.restartApp();
}, 3500);
});
Hi @MA8731174 ,
thanks for the feedback! I see that is quite complex workflow with several callbacks - so I think that this is and syncornisation issue so that the different function and events are called assyncronoussly at all and possibly there is accessing of the data form a call which is not finished yet.
So what you can do .
So first to understand better the problem you can split described step and call they with different buttons.
the entry point in you script is that
$scope.submitBtnPressed()
where called first the signature twx.app.fn.triggerWidgetService('zfSignature', 'takeSignature');
//## >> then next point is the
$scope.$on("signatureTaken", function () {
console.log("Signature Taken!");
triggerPDF = true;
$scope.saveDataBtnPressed(); //-> goes in the next item
});
>>> how is the signatureTaken defined as evetnt?
so that when this is that you expect to received from the servcie called takeSignature and the service in added in the External data section then possibly the name is something like this:
$scope.$root.$on("signatureTaken-complete", function (event,args) {
console.warn(JSON.stringify(event))// is the data received
console.warn(JSON.stringify(args))// what are the args?
// ... what you do after it comm back from service
$timeout($scope.saveDataBtnPressed,500)
})
....
2.) when 1.) is ok then check the next in extra button: to generate the Json - So this is involved then in the function saveDataBrnPressed
- I see the saveDataBrnPressed will call twxDS - no idea what this method do - so posswibly some interaction with TWX? There is called the $scope.generteJSON() inside the arguments of the twxDS call. So possibly you can define a syncronisation that the twxDS method is called when the scope.genrateJSON() is completed - e.g. by call promise concept : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
and in the then () call the twxDS call
3.) call the method for PDF generation in extra button when you ensure that every thing was completed
-- I see that
$scope.$on("saveReport.serviceInvokeComplete", function () {
what same as
$scope.$root.$on("saveReport-complete", function (event,args) {
when the saveRport is added in the External data secton. But acctualy I could not see this service "saveReport" called anywere... that I belived that is involed by some service which called by the twxDS() -> TestBench_mobile.Controller ... what ever that will do
4.) in extra button when PDF egenerated saved it to twx when 3 complete
5.) call in button to save the singature image when 4 complete
- I think such way to split the workflow in steps possibly will help you to understand where is the issue - you can try to merge some calls later and see when the issue occurs
-another point is that you callede some forms inside your continous workflow process, means inside or betweens the function/events/ - there is also some timout expiring when calling of the $ionicPopup.
Possibly you can try to simplify the workflow making it more straight away meass by calling all data at the begining and when the data is collected there to call the next step. So possibly extra step in UI via stage button to involve this - because I do not see a difference in the UI logic when you call $ionicPopup inside a call backs or events or start every step with separte button when the previous step is completed.
Unfortunately I did not have the time to check and understand your code very detailed y and therefore possibly my suggestion are incomplete. Thanks BR.
Hey @RolandRaytchev
I appreciate your feedback and time. I would like to give you some more insights yes i would like to try your suggested approach to run the things once the previous operation finished but if i see the sequence of implementation its already behaving in that way. The problem occurs only in the pdf generation and i feel like there memory execssive or overflow somehow in it.
VUFORIA STUDIO (BROWSER)
If i run the same code on browser it takes longer may be around 40 seconds to generate the pdf but it never fails means my code always works. It has never happened that the process fail in browser that means my code is somehow right and sequence of flow is also fine.
VIEW APP (IPAD)
Now come to the ipad in view app there the process takes less time to finish which is good that its fast but sometimes it fails and it only happens while generating the PDF. For eg: sometimes it just generates page 1 page 2 then suddenly process fails. It is somehow related to memory or loop.
IMPORTANT: Currently i have around 6 popup (pages) for generation of PDF. I did one interesting test yesterday i decrease these 6 pages to 3 pages and then in IPAD this test never fails. It was always working. What do you think the cause can be? I have added my code below the function which generates the PDF for me.. Would you please have an overview of it. I have also put logs screenshot and there you can see when process stops not doing anything then FPS: 30.0 is coming on and on... There is no animation or model in my expereince it is simple checklist which user fills and then signs then generates PDF and send it to backend on thingworx... Would you please see my code what actually is the problem.. Decreasing the pages makes the app running perfectly fine in VIEW APP and definitly faster then even before.
$scope.printPDF = async function() {
let popupArray = $scope.app.params.Page5Visibility === true? ["page_0", "page_1", "page_2","page_3","page_4", "page_5"] : ["page_0", "page_1", "page_2","page_3","page_4"];
if (customerPresent && $scope.app.params.Status === PHASES.SCW2Phase4) {
popupArray = [
"page_0",
];
}
let helper1 = document.querySelectorAll("div.twx-popup-container.ng-hide, ion-spinner.ng-hide");
helper1.forEach(box => { box.classList.remove("ng-hide")} );
let attributeContainer = [];
const pdfDoc = await window.PDFLib.PDFDocument.create();
for (const popupId of popupArray) {
console.log(PHASES.SCW2Phase3);
console.log($scope.app.params.Status);
console.log("page: " + popupId + " erstellt");
attributeContainer.push(document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).getAttribute("style"));
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", "width: 100%; overflow: visible;");
let mergedElements = document.createElement('DIV');
mergedElements.setAttribute('style', 'font-family: prometo !important; letter-spacing: 0.01px; overflow: visible');
mergedElements.append(document.querySelector(`twx-widget[widget-id='${popupId}']`).cloneNode(true));
let pdfHeight = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetHeight + 25,
pdfWidth = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetWidth + 20;
let pdf = new jspdf.jsPDF('l', 'px', [pdfWidth, pdfHeight]);
pdf.addFileToVFS('prometo-normal.ttf', font);
pdf.addFont('prometo-normal.ttf', 'prometo', 'normal');
pdf.setFont("prometo", 'normal');
let donorPdfBytes;
await pdf.html(mergedElements, {
// this warning is wrong - everything is fine
callback: receivedPDF => { donorPdfBytes = receivedPDF.output("datauristring"); },
x: 5,
y: 5
});
let donorPdfDoc = await window.PDFLib.PDFDocument.load(donorPdfBytes);
let docLength = donorPdfDoc.getPageCount();
for (let k = 0; k < docLength; k++) {
const [donorPage] = await pdfDoc.copyPages(donorPdfDoc, [k]);
pdfDoc.addPage(donorPage);
}
}
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
let mergedPDF = pdfDataUri.substring(pdfDataUri.indexOf(',') + 1);
helper1.forEach(box => { box.classList.add("ng-hide"); });
popupArray.forEach((popupId, i) => {
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", attributeContainer[i]);
});
const currentDate = new Date();
const currentDateString = `${currentDate.getFullYear()}-${(currentDate.getMonth() + 1).toString().padStart(2, '0')}-${currentDate.getDate().toString().padStart(2, '0')}_${currentDate.getHours().toString().padStart(2, '0')}-${currentDate.getMinutes().toString().padStart(2, '0')}_`;
let helperName;
if ($scope.app.params.Status == PHASES.SCW2Phase1) {
helperName = "mechanic_EDTCD1";
}
if ($scope.app.params.Status == PHASES.SCW2Phase2) {
helperName = "mechanic_EDTCD2";
}
if ($scope.app.params.Status == PHASES.SCW2Phase3) {
helperName = "manager";
}
if ($scope.app.params.Status == PHASES.SCW2Phase4) {
if(!customerPresent) {
helperName = "manager";
} else {
helperName = "Inbetriebnehmer";
}
}
let pdfName = $scope.view.wdg["0_5"].text + "-" + $scope.view.wdg["0_6"].text + "_" + currentDateString + helperName + ".pdf";
let pdfPath = $scope.projectInfo.repositoryRelativePath + "/" + pdfName;
twxDS("TestBench_Mobile.Controller", "savePDF", { "content": mergedPDF, "path": pdfPath });
console.log(" PDF with the name '" + pdfName + "' created");
};
One Additional thing in my function printPDF there is this piece of code which give me warning but still it works and maybe that i causing a problem. I have attached a screenshot below about this warning when i hover on it and without hover so that you can see the code chunk...
NEW UPDATE: So now i have refactor the code and put callback function outside the loop and now warning is gone! I have put so many console.logs to see whats a point of failure actually. it seems the main bottleneck and failure point is the pdf.html method .This is likely due to the limited resources and processing power of the iPad compared to a PC. The point of failure is this small snippet in below function which is this
let donorPdfBytes = await new Promise(resolve => {
pdf.html(mergedElements, {
callback: receivedPDF => resolve(receivedPDF.output("datauristring")),
x: 5,
y: 5
});
});
On here when it is generating pages its just stops in middle may be after creating page 0 and page 1. It would just stop then. On PC its working well.
$scope.printPDF = async function() {
let popupArray = $scope.app.params.Page5Visibility === true ? ["page_0", "page_1", "page_2", "page_3", "page_4", "page_5"] : ["page_0", "page_1", "page_2", "page_3", "page_4"];
if (customerPresent && $scope.app.params.Status === PHASES.SCW2Phase4) { // For SCW2Phase4 and customer present, use only page_0
popupArray = ["page_0"];
}
let helper1 = document.querySelectorAll("div.twx-popup-container.ng-hide, ion-spinner.ng-hide");
helper1.forEach(box => { box.classList.remove("ng-hide") });
let attributeContainer = [];
const pdfDoc = await window.PDFLib.PDFDocument.create();
const processPopup = async (popupId) => {
console.log(PHASES.SCW2Phase3);
console.log($scope.app.params.Status);
console.log("page: " + popupId + " erstellt");
attributeContainer.push(document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).getAttribute("style"));
console.log("problem cause 1");
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", "width: 100%; overflow: visible;");
console.log("problem cause 2");
let mergedElements = document.createElement('DIV');
console.log("problem cause 3");
mergedElements.setAttribute('style', 'font-family: prometo !important; letter-spacing: 0.01px; overflow: visible');
console.log("problem cause 4");
mergedElements.append(document.querySelector(`twx-widget[widget-id='${popupId}']`).cloneNode(true));
console.log("problem cause 5");
let pdfHeight = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetHeight + 25,
pdfWidth = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetWidth + 20;
console.log("problem cause 6");
let pdf = new jspdf.jsPDF('l', 'px', [pdfWidth, pdfHeight]);
console.log("problem cause 7");
pdf.addFileToVFS('prometo-normal.ttf', font);
console.log("problem cause 8");
pdf.addFont('prometo-normal.ttf', 'prometo', 'normal');
console.log("problem cause 9");
pdf.setFont("prometo", 'normal');
console.log("problem cause 10");
let donorPdfBytes = await new Promise(resolve => {
pdf.html(mergedElements, {
callback: receivedPDF => resolve(receivedPDF.output("datauristring")),
x: 5,
y: 5
});
});
console.log("problem cause 11");
let donorPdfDoc = await window.PDFLib.PDFDocument.load(donorPdfBytes);
console.log("problem cause 12");
let docLength = donorPdfDoc.getPageCount();
console.log("problem cause 13");
for (let k = 0; k < docLength; k++) {
const [donorPage] = await pdfDoc.copyPages(donorPdfDoc, [k]);
pdfDoc.addPage(donorPage);
/* console.log("jamal page "+ donorPage);*/
}
};
for (const popupId of popupArray) {
await processPopup(popupId);
}
console.log("problem cause 14");
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
console.log("problem cause 15");
let mergedPDF = pdfDataUri.substring(pdfDataUri.indexOf(',') + 1);
console.log("problem cause 16");
helper1.forEach(box => { box.classList.add("ng-hide"); });
console.log("problem cause 17");
popupArray.forEach((popupId, i) => {
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", attributeContainer[i]);
});
console.log("problem cause 18");
const currentDate = new Date();
console.log("problem cause 19");
const currentDateString = `${currentDate.getFullYear()}-${(currentDate.getMonth() + 1).toString().padStart(2, '0')}-${currentDate.getDate().toString().padStart(2, '0')}_${currentDate.getHours().toString().padStart(2, '0')}-${currentDate.getMinutes().toString().padStart(2, '0')}_`;
let helperName;
if ($scope.app.params.Status == PHASES.SCW2Phase1) {
helperName = "mechanic_EDTCD1";
}
if ($scope.app.params.Status == PHASES.SCW2Phase2) {
helperName = "mechanic_EDTCD2";
}
if ($scope.app.params.Status == PHASES.SCW2Phase3) {
helperName = "manager";
}
if ($scope.app.params.Status == PHASES.SCW2Phase4) {
if (!customerPresent) {
helperName = "manager";
} else {
helperName = "Inbetriebnehmer";
}
}
let pdfName = $scope.view.wdg["0_5"].text + "-" + $scope.view.wdg["0_6"].text + "_" + currentDateString + helperName + ".pdf";
let pdfPath = $scope.projectInfo.repositoryRelativePath + "/" + pdfName;
twxDS("TestBench_Mobile.Controller", "savePDF", { "content": mergedPDF, "path": pdfPath });
console.log(" PDF with the name '" + pdfName + "' created");
};
Hi @MA8731174 ,
thanks for sharing the resutls . I see that you have a lot of progress!
It seems that removing /reducing the of the popup will be helpful. Is it possible the change the design so that first collect all required informaiton via calling poupup one time and then staring the workflow without interruption.
I believe that PDF lib is used from the extension. I checked and see that there are 2 different version of this extension:
Custom PDF Widget extension (PDFViewer_0.3.zip Tested on Vuforia 8.5.x and 9.0.0) or Custom PDF Widget extension (PDFViewer_0.5.1.zip Tested on Vuforia 9.0.3). Which version did you test /use? I remember that somebody mentioned that the second one is more stable. Do you use the second version?
Unfortunately to be able to say more about this problem respectively to report this to R&D team I need to check it myself and try to reproduce it. Unfortunately this will be possible first next week there I hope could check it more detailed.
Thanks , BR
Hi @RolandRaytchev Thank you for your constant support and feedback! I have got a root problem which was causing this whole issue and my problem is resolved now 🙂
This reduces the peak memory usage because only one page is processed at a time.
My Code below for generating PDF from POPUPs
async function generatePagePDF(popupId, pdfWidth, pdfHeight, font) {
const pdf = new jspdf.jsPDF('l', 'px', [pdfWidth, pdfHeight]);
// If font is necessary, uncomment the following lines
pdf.addFileToVFS('prometo-normal.ttf', font);
pdf.addFont('prometo-normal.ttf', 'prometo', 'normal');
pdf.setFont("prometo", 'normal');
const mergedElements = document.createElement('DIV');
mergedElements.setAttribute('style', 'font-family: prometo !important; letter-spacing: 0.01px; overflow: visible');
mergedElements.append(document.querySelector(`twx-widget[widget-id='${popupId}']`).cloneNode(true));
const donorPdfBytes = await new Promise((resolve, reject) => {
pdf.html(mergedElements, {
callback: receivedPDF => resolve(receivedPDF.output("datauristring")),
x: 5,
y: 5,
html2canvas: {
scale: 1, // Reduce the scale if necessary to reduce memory usage
}
});
});
return donorPdfBytes;
}
$scope.printPDF = async function() {
let popupArray = $scope.app.params.Page5Visibility === true ? ["page_0", "page_1", "page_2", "page_3", "page_4", "page_5"] : ["page_0", "page_1", "page_2", "page_3", "page_4"];
if (customerPresent && $scope.app.params.Status === PHASES.SCW2Phase4) { // For SCW2Phase4 and customer present, use only page_0
popupArray = ["page_0"];
}
let helper1 = document.querySelectorAll("div.twx-popup-container.ng-hide, ion-spinner.ng-hide");
helper1.forEach(box => { box.classList.remove("ng-hide") });
let attributeContainer = [];
const pdfDoc = await window.PDFLib.PDFDocument.create();
for (const popupId of popupArray) {
try {
console.log(PHASES.SCW2Phase3);
console.log($scope.app.params.Status);
console.log("page: " + popupId + " erstellt");
attributeContainer.push(document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).getAttribute("style"));
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", "width: 100%; overflow: visible;");
let pdfHeight = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetHeight + 25,
pdfWidth = document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup .gridLayout`).offsetWidth + 20;
let donorPdfBytes = await generatePagePDF(popupId, pdfWidth, pdfHeight, font);
let donorPdfDoc = await window.PDFLib.PDFDocument.load(donorPdfBytes);
let docLength = donorPdfDoc.getPageCount();
for (let k = 0; k < docLength; k++) {
const [donorPage] = await pdfDoc.copyPages(donorPdfDoc, [k]);
pdfDoc.addPage(donorPage);
}
attributeContainer.pop();
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", attributeContainer[attributeContainer.length - 1]);
// await new Promise(resolve => setTimeout(resolve, 200)); // Adding delay to manage resources
} catch (error) {
console.error('Error processing page ' + popupId + ':', error);
}
}
try {
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
let mergedPDF = pdfDataUri.substring(pdfDataUri.indexOf(',') + 1);
helper1.forEach(box => { box.classList.add("ng-hide"); });
popupArray.forEach((popupId, i) => {
document.querySelector(`twx-widget[widget-id='${popupId}'] .twx-popup`).setAttribute("style", attributeContainer[i]);
});
const currentDate = new Date();
const currentDateString = `${currentDate.getFullYear()}-${(currentDate.getMonth() + 1).toString().padStart(2, '0')}-${currentDate.getDate().toString().padStart(2, '0')}_${currentDate.getHours().toString().padStart(2, '0')}-${currentDate.getMinutes().toString().padStart(2, '0')}_`;
let helperName;
if ($scope.app.params.Status == PHASES.SCW2Phase1) {
helperName = "mechanic_EDTCD1";
} else if ($scope.app.params.Status == PHASES.SCW2Phase2) {
helperName = "mechanic_EDTCD2";
} else if ($scope.app.params.Status == PHASES.SCW2Phase3) {
helperName = "manager";
} else if ($scope.app.params.Status == PHASES.SCW2Phase4) {
helperName = customerPresent ? "Inbetriebnehmer" : "manager";
}
let pdfName = $scope.view.wdg["0_5"].text + "-" + $scope.view.wdg["0_6"].text + "_" + currentDateString + helperName + ".pdf";
let pdfPath = $scope.projectInfo.repositoryRelativePath + "/" + pdfName;
twxDS("TestBench_Mobile.Controller", "savePDF", { "content": mergedPDF, "path": pdfPath });
console.log(" PDF with the name '" + pdfName + "' created");
} catch (error) {
console.error('Error saving PDF:', error);
}
};
In order to inculde libraries in vuforia studio for fonts and PDF generation.
let myscript = document.createElement('script');
myscript.src="app/resources/Uploaded/jspdf.js";
document.head.appendChild(myscript);
myscript.onload = async function(){
// code to be executed when the script has finished loading
//...
console.log("stop here");
}
let html2canvas = document.createElement('script');
html2canvas.src="app/resources/Uploaded/html2canvas.js";
document.head.appendChild(html2canvas);
html2canvas.onload = function(){
// code to be executed when the script has finished loading
//...
}
let PDFLib = document.createElement('script');
PDFLib.src="app/resources/Uploaded/pdf-lib.js";
document.head.appendChild(PDFLib);
PDFLib.onload = async function(){
// code to be executed when the script has finished loading
//...
}
HI @MA8731174 thanks for the feedback! that is really great news. I am heapy to hear that is working now. Was not really easy issue 😊... but possibly interessing point with helpful results! BR
Hi @RolandRaytchev Unfortunately the issue is still there. 😞 Is it possible for you that your IOT team will reproduce it on the vuforia view app. The extension which you have shared with me that was pdf viewer. For me it is different i have library installed to make the pdf in javascript and i am generating pdf from all 6 popups and then sending it to backend thats it. It was working for few days and now issue is back. For me its unbelieveable that why view app behaves like that. Nothing has been changed so far still its not working now. It would be nice if you would spare some time for the issue so that we can find the root cause. I am ready to share my code or further information if you need any. I am just taking the classes of popups which were assigned already to already from Develoeprs of ptc and i am just generating a pdf.