Large file sending by using react-native-ble-plx - android

I'm trying to write in a characteristic that sends a binary file as the value, this value is large, it's 19215 bytes which is not a problem because i negociated the mtu using :
device.connect({ requestMTU: 260 })
i've divided the file into 240 bytes for each element and each time encode the element to base64 and i use the function writeCharacteristicWithResponseForDevice() in order to write that element,
the issue is i successfully wrote the whole file 19215 bytes using a loop to write each time an element, but while i tried to read the characteristic i can only read the last written element
Example:
this.manager.writeCharacteristicWithResponseForDevice(device.id,"1111", "40E1ED56-EC4A-4DC6-A6BD-30377F186B77", base64.encode(element1)
this.manager.writeCharacteristicWithResponseForDevice(device.id,"1111", "40E1ED56-EC4A-4DC6-A6BD-30377F186B77", base64.encode(element2))
this.manager.writeCharacteristicWithResponseForDevice(device.id,"1111", "40E1ED56-EC4A-4DC6-A6BD-30377F186B77", base64.encode(element3))
when i read the Characteristic using
device.readCharacteristicForService("1111", "40E1ED56-EC4A-4DC6-A6BD-30377F186B77")
i get as a value :
console.log(base64.decode(characteristic.value) => element3
it should be element1+ element2 + element3
-
here is my code for writing :
WriteIncaracteristic() {
// this.state.fileSize is calculated in another function
const nbPackages = Math.floor(this.state.fileSize/240) + 1
var fileArray = []
for (let i = 0; i <= nbPackages; i++) {
// this.state.fileContent is created in another function
fileArray.push(this.state.fileContent.slice(0, 240))
this.state.fileContent = this.Remove(this.state.fileContent, 0, 240)
}
for (let j = 0; j <= fileArray.length; j++) {
if (fileArray[j]) {
const device = this.state.myDevice
this.manager.writeCharacteristicWithResponseForDevice(device.id,"1111","40E1ED56-EC4A-4DC6-A6BD-30377F186B77", base64.encode(fileArray[j]))
.then((characteristic) => {
console.log(base64.decode(characteristic.value));
return
}) .catch((error) => {
this.createTAlert("writing", error.message )
});
}}
}
here is the function to read characteristic:
ReadCaracteristic() {
const device = this.state.myDevice
device.readCharacteristicForService("1111", "40E1ED56-EC4A-4DC6-A6BD-30377F186B77")
.then((characteristic) => {
console.log(base64.decode(characteristic.value));
console.log(characteristic );
return
}) .catch((error) => {
// Handle errors
this.createTAlert("Reading", error.message )
});
}
Can anybody help please by providing a working example in how to send large package files, because the problem can be while writing, the function erases the older value maybe.
Thanks

Related

Protractor - What expected condition to use here?

I have 3 tabs, that I need to test if they are clicked or not, but I can't seem to find a good expected condition to use. Right now I am using a browser. sleep with higher times, since the tab takes a couple of seconds to load.
The tabs:
Code:
let locator = {
upcomingTabButton : element(by.css('div.toolbar-content.toolbar-content-md ion-segment ion-segment-button:nth-child(1)')),
pendingTabButton : element(by.css('div.toolbar-content.toolbar-content-md ion-segment ion-segment-button:nth-child(2)')),
completedTabButton : element(by.css('div.toolbar-content.toolbar-content-md ion-segment ion-segment-button:nth-child(3)')),
activeTab : element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')),
upcomingActiveTab: element(by.cssContainingText('ion-segment ion-segment-button.segment-button.segment-activated', 'UPCOMING')),
};
describe('When x tab button is pressed, ', function () {
it('if x = PENDING tab, then it should be activated', () => {
browser.sleep(1000);
browser.wait(EC.elementToBeClickable(locator.pendingTabButton),5000);
locator.pendingTabButton.click();
browser.sleep(10000);
expect(element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')).getText()).toEqual('PENDING');
});
it('if x = UPCOMING tab, then it should be activated', () => {
browser.wait(EC.elementToBeClickable(locator.upcomingTabButton),5000);
locator.upcomingTabButton.click();
//browser.wait(EC.presenceOf(locator.upcomingActiveTab),5000);
browser.sleep(10000);
expect(element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')).getText()).toEqual('UPCOMING');
});
it('if x = COMPLETED tab, then it should be activated', () => {
browser.wait(EC.elementToBeClickable(locator.completedTabButton),5000);
locator.completedTabButton.click();
//browser.wait(EC.presenceOf(locator.activeTab),5000);
browser.sleep(10000);
expect(element(by.css('ion-segment ion-segment-button.segment-button.segment-activated')).getText()).toEqual('COMPLETED');
});
});
I tried some expected conditions but nothing worked since I need to see when the tab is selected and I didn't found any exp condition like that.
I am using the browser.sleep(10000) 10 seconds, but that's, not a good way to use.
Can anyone help me here?
The tabs content takes like 5/6 seconds to load
/**
* wait until some element is present
*
*/
this.waitElement = function(ElmFinder){
var i = 0;
var _retryOnErr = function(err) {
console.log(' <<< warning: wait retrying iteration: ' + i + ' >>> ');
browser.sleep(500);
return false;
};
browser.wait(function() {
i++;
return ElmFinder.isDisplayed().then(function(present) {
if (present) {
return true;
} else {
console.log("element is not present");
return _retryOnErr();
}
}, _retryOnErr);
}, 120*1000, 'Error waiting for element present: ').
then(function(waitRetValue) {
return waitRetValue; // usually just `true`
}, function(err) {
throw err + ' after ' + i + ' iterations.';
});
};

Uploading chunked file using Ionic4 and Angular HttpClient fails after some successful uploads with net::ERR_FILE_NOT_FOUND

i'm trying to upload a large file (500+Mb, but could be even bigger) to our php server, using an app written in Ionic4+Angular+Cordova, on an emulator with Android 10.
I set up a system to upload the file in chunks.
It reads the file choosen by the user using THIS PLUGIN, chunk by chunk (5Mb per chunk).
Then it proceeds to send it to our server performing a POST request with Content-type multipart/form-data.
The file goes to server, server saves it, says "OK", then the app proceeds to send the following chunk.
Everything works fine, for the first 25/29 chunks.
Then, the POST request fails with
POST http://192.168.1.2/work/path/to/webservices/uploadChunks.php net::ERR_FILE_NOT_FOUND
I tried:
starting at another point in the file instead of byte 0 - got the same error
reading the file chunk by chunk, without making any POST request- could cycle the whole 500Mb file
reading the file chunk by chunk and making the POST requests, but not sending the chunks with them - could execute every single call without any error, through the end of the file
reading the file chunk by chunk and sending them to ANOTHER webservice - got the same error
reading the file chunk by chunk and performing a POST request to another webservice, with content-type application/json and putting the formData object into the request body (not sure this is a valid test tho) - could execute every single call without any error, through the end of the file
Checking out memory snapshots taken in chrome inspector during different chunks upload did not show any sign of memory leak.
The case was tested on a rather old device, where the same procedure caused the app to exit, without signaling any error (not even in logcat apparently).
Here is the piece of code used to chunk and send the file:
const generatedName = 'some_name_for_file';
// Path obtained from fileChooser plugin
let path_to_file = 'content://com.android.externalstorage.documents/document/primary%3ADownload%2Ffilename.avi'
const min_chunk_size = (5 * 1024 * 1024);
// Converting path to file:// path
this.filePath.resolveNativePath(path_to_file).then((resolvedPath) => {
return this.fileAPI.resolveLocalFilesystemUrl(resolvedPath);
}, (error) => {
console.log('ERROR FILEPATH');
console.log(error);
return Promise.reject('Can not access file.<br>Code : F - ' + error);
}).then(
(entry) => {
path_to_file = entry.toURL();
console.log(path_to_file);
(entry as FileEntry).file((file) => {
//Getting back to the zone
this.ngZone.run(() => {
// Re-computing chunk size to be sure we do not get more than 10k chunks (very remote case)
let file_chunk_size = file.size / 10000;
if (file_chunk_size < min_chunk_size) {
file_chunk_size = min_chunk_size;
}
//Total number of chunks
const tot_chunk = Math.ceil(file.size / file_chunk_size);
const reader = new FileReader();
let retry_count = 0; //Counter to check on retries
const readFile = (nr_part: number, part_start: number, length: number) => {
// Computing end of chunk
const part_end = Math.min(part_start + length, file.size);
// Slicing file to get desired chunk
const blob = file.slice(part_start, part_end);
reader.onload = (event: any) => {
if (event.target.readyState === FileReader.DONE) {
let formData = new FormData();
//Creating blob
let fileBlob = new Blob([reader.result], {
type: file.type
});
formData.append('file', fileBlob, generatedName || file.name);
formData.append('tot_chunk', tot_chunk.toString());
formData.append('nr_chunk', nr_part.toString());
// UPLOAD
const sub = this.http.post('http://192.168.1.2/path/to/webservice/uploadChunk.php', formData).subscribe({
next: (response: any) => {
console.log('UPLOAD completed');
console.log(response);
retry_count = 0;
if (response && response.status === 'OK') {
//Emptying form and blob to be sure memory is clean
formData = null;
fileBlob = null;
// Checking if this was the last chunk
if (part_end >= file.size) {
// END
callback({
status: 'OK'
});
} else {
// Go to next chunk
readFile(nr_part + 1, part_end, length);
}
//Clearing post call subscription
sub.unsubscribe();
} else {
//There was an error server-side
callback(response);
}
},
error: (err) => {
console.log('POST CALL ERROR');
console.log(err);
if (retry_count < 5) {
setTimeout(() => {
retry_count++;
console.log('RETRY (' + (retry_count + 1) + ')');
readFile(nr_part, part_start, length);
}, 1000);
} else {
console.log('STOP RETRYING');
callback({status:'ERROR'});
}
}
});
}
};
//If for some reason the start point is after the end point, we exit with success...
if (part_start < part_end) {
reader.readAsArrayBuffer(blob);
} else {
callback({
status: 'OK'
});
}
};
//Start reading chunks
readFile(1, 0, file_chunk_size);
});
}, (error) => {
console.log('DEBUG - ERROR 3 ');
console.log(error);
callback({
status: 'ERROR',
code: error.code,
message: 'Can not read file<br>(Code: 3-' + error.code + ')'
});
});
}, (error) => {
console.log('ERROR 3');
console.log(error);
return Promise.reject('Can not access file.<br>Code : 3 - ' + error);
}
);
I can not figure out what is going wrong. Can someone help me debug this, or knows what could be going on?
Thank you very much.
I still do not know what caused this issue, but i resolved using a PUT request instead of a POST request, sending the raw chunk, and putting additional data in custom headers (something like "X-nr-chunk" or "X-tot-chunk"). Upload completed fine without the error message.
I also used the cordova-advanced-http plugin, but i do not think it made a difference here, since it did not work with the POST request, like the other method (httpClient).
This has been tested on android only for now, not on iOS. I'll report if there is any problem. For now i consider this solved, but if you know what may have caused this problem, please share your thoughts.
Thanks everyone.

NODE JS Api working from Postman results in timeout from App

I have a Node js API written in express framework.
I am sending some data over all my api does is calculate number of Packages to make For example :- 100/10 = 10 packages to make.
Loops and creates packages in sales force and firebase one by one.
Works fine from postman.
Problem:
When i try to hit the api from my app it works fine when the package count is <= 10. when > 10 ,Lets say 25 it calculates packages and run a loop of 25 and creates packages,crashes after 11th iteration and restarts the route, calculate again 25 packages to create and resulting in "Over weight error".
1- thought it was from android error may be i was hitting two request one after an other ( this was not the case ).
2- Tried sending header "Connection" "Keep-Alive" ( as Postman does ) not working.
3- tried to put up the timeout in the below code it did not work either ( tried variations of time out like 0, 50,000 ms )
else {
console.log('=====By Item=============');
const supplierFinishedGood = {
Name: parentBatchDoc['itemName'],
Supplier_Product__c: parentBatchDoc['id'],
Package_Size__c: 'a090S000001ZQ5kQAG', // Hard coded PackageSize in 'Gram' as per SALESFORCE
On_Hand_Amount__c: childBatch['batchWeight']
}
console.log('=====By Item============= 2');
const SupplierFinishedProductID = await createSupplierFinishedProduct(supplierFinishedGood, bearerToken);
const Quantity_Packaged__c = Math.floor((childBatch['batchWeight'] - childBatch['batchTestAmount']) / noOfPackage);
console.log('=====By Item============= 3');
//console.log('Quantity_Packaged__c ==== Remaining_Grams_Available_for_Packaging__c', Quantity_Packaged__c, parentBatchSalesforce['Remaining_Grams_Available_for_Packaging__c']);
for (let index = 0; index < noOfPackage; index++) {
if (parentBatchSalesforce['Remaining_Grams_Available_for_Packaging__c'] > Quantity_Packaged__c) {
let package = {
Batch__c: childId,
Product__c: SupplierFinishedProductID,
Inventory_Location__c: 'a030S000003x7M7QAI', //Hard coded InventoryLocation 'StorageFinished' as per SALESFORCE
Number_Of_Items__c: noOfItemInPackage,
Quantity_Packaged__c: Quantity_Packaged__c,
Date_Packaged__c: datePackaged,
Expiration_Date__c: expirationDate
};
console.log('Before creating apcaktge ', index);
const packageID = await createPackage(package, bearerToken);
console.log('After creating package ', index, parentBatchSalesforce['Remaining_Grams_Available_for_Packaging__c']);
package['parentBatchId'] = parentId;
package['status'] = 'Ready to checkout';
package['uid'] = packageID;
const packageFBResponse = await db.collection('packages').doc(packageID).set(package, { merge: true });
reponseBody.push(packageID);
} else {
console.log('======Over
Weight====');
}
Above code is what produces the error.
There is a If condition before this it works fine i have tested it
as it has some other scenario.
End result should not be a timeout error.
API should create all the packages and return the result.
I have found a workaround. I am Playing around batch status and my code returns me the IDs which i need and packages are created.
This is not the ultimated best solution it is a work around. i am still open to know what the actual problem is.
parentBatchDoc['childBatches'][childId]['batchStatus'] = "In Processing";
const parentBatchDocResponse = await db.collection('batches').doc(parentId).set(parentBatchDoc, { merge: true });
if(parentBatchDoc['childBatches'][childId]['batchStatus'] !== "In Processing"){
===================Rest of the case of =======================
}

How to take data from native storage, add to an array then add back to native storage

I am building an app using ionic/cordova. I need to be able to scan a barcode, then add a timestamp (achieved this) to the devices native storage.
I tried just adding scan data to the native storage, but found that it just overwrites existing scan data. I then tried this using an array, but again noticed if I change page, when the page loads again the array is empty, so that it replaces existing data with the new empty array.
What I "think" I need to do is -
Create an empty array
Take existing data native storage and add to this array
Add new scan data to this array
Then add the array back to native storage.
What I am currently stuck on is taking the data out of native storage and adding to an array.
My code is -
export class ScanSession {
scans: any [];
constructor(private barcodeScanner: BarcodeScanner, private
nativeStorage: NativeStorage) {
this.scans = [];
}
ScanCode() : any{
this.barcodeScanner.scan().then((barcodeData) => {
this.nativeStorage.getItem('scans')
.then(
data => console.log(data),
error => console.error(error)
);
this.scans.push(data);
let inputString = "testData";
//let ts = new Date();
if( barcodeData.text == inputString){
//this.scans.push(ts);
this.nativeStorage.setItem('scans', (JSON.stringify(this.scans)))
//(JSON.stringify(this.scans)
.then(
() => console.log('Stored item!'),
error => console.error('Error storing item', error)
);
//console.log("Success");
//console.log(this.scans);
//this.nativeStorage.getItem('scans')
//.then(
// data => console.log(data),
// error => console.error(error)
// );
} else {
console.log("Doesnt Match");
}
}, (err) => {
});
};
}
Your missing JSON.parse as your using JSON.stringify, you need to unstringify, if you get what i mean.
Change
this.nativeStorage.getItem('scans')
To
JSON.parse(this.nativeStorage.getItem('scans'))

Select camera on Android Chrome

I came across several questions on this subject. I'm trying to select the rear camera on an Android device running Chrome.
So, after some reading :
var selector = document.getElementById('video-source-selector');
navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
var videoDevices = devices.map(function (item) {
if(item.kind === 'videoinput'){
return item;
}
}).filter(function( element ) {
return element !== undefined;
});
var max = videoDevices.length;
videoDevices.forEach(function(device, i) {
var html = '';
var div = document.createElement('div');
if(i === max-1){ // last element reached
html += '<option value="'+device.deviceId+'" selected>'+ device.label +'</option>';
}
else {
html += '<option value="'+device.deviceId+'">'+ device.label +'</option>';
}
div.innerHTML = html;
selector.appendChild(div.childNodes[0]);
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
});
selector.addEventListener("change", function(){
console.log(selector.value); // Works as supposed : returns the ID of the selected device
});
Then, as I'm using Three.js in this app, I'm binding this ID to Jerome Etienne three extension WebcamGrabbing (https://github.com/jeromeetienne/threex.webar):
var videoGrabbing = new THREEx.WebcamGrabbing(selector.value);
Then I had to modify THREEx.WebcamGrabbing class this way (I removed the irrelevant parts):
THREEx.WebcamGrabbing = function(sourceDeviceId){
...
console.log('webcamgrabbing : ', sourceDeviceId); // returns the expected ID
var constraints = {
video: {
optional: [{
sourceId: sourceDeviceId
}]
}
}
// try to get user media
navigator.getUserMedia( constraints, function(stream){
domElement.src = URL.createObjectURL(stream);
}, function(error) {
console.error("Cant getUserMedia()! due to ", error);
});
...
}
But still, Chrome on Android is still giving me the stream of the face camera, whatever device I select...
What do I miss?
EDIT : Based on this topic (GetUserMedia - facingmode), I came up with some logs to see what's happening here :
var constraints = {
audio: false,
video: { facingMode: { exact: "environment" } }
}
console.log('Try to get stream with constraints:', constraints);
navigator.getUserMedia( constraints, function(stream){
var videoTracks = stream.getVideoTracks();
console.log('Got stream with constraints:', constraints); // Ok
console.log('Using video device: ' + videoTracks[0].label); // > Using video device: camera 0, facing back
for(var i = 0; i < videoTracks.length; i++){
console.log('Found video device with contraints : ', videoTracks[i].label); // Found video device with contraints : camera 0, facing back
}
domElement.src = URL.createObjectURL(stream);
}, function(error) {
console.error("Cant getUserMedia()! due to ", error);
});
An alternative way to select the back camera on chrome is to use the enumerateDevices method.
First get all the video input id's:
navigator.mediaDevices.enumerateDevices().then(function(devices) {
devices.forEach(function(device) {
if(device.kind=="videoinput"){
//If device is a video input add to array.
}
});
Then the first element of the array will contain the id of the front camera, the second element will contain the id of the back camera.
Finally put the id of the camera that you want to use
navigator.getUserMedia({audio: false, video: { sourceId: VideoId } }, successCallback, errorCallback);

Categories

Resources