node.js - pngjs error: "Stream not writable" randomly - javascript

I am working with pngjs through many of it's methods. Most of the time, they work fine. However, like in the following example, I get an error: "Stream is not writable"
var fs = require('fs'),
PNG = require('pngjs').PNG;
var dst = new PNG({width: 100, height: 50});
fs.createReadStream('http://1.1m.yt/hry7Eby.png') //download this picture in order to examine the code.
.pipe(new PNG())
.on('parsed', function(data) {
console.log(data);
});
This case is not singular, I get this error on 1 random png image once a day, through all of pngjs methods, and that error obviously crashes my app.
(note: you can't use the http link I gave you with a readStream, you will have to download & rename it and do something like):
fs.createReadStream('1.png')
Thank you for your time and effort.

This seems to be a bug in the library, though I'm wary of saying so as I'm no expert in PNGs. The parser seems to complete while the stream is still writing. It encounters the IEND, and so calls this:
ParserAsync.prototype._finished = function() {
if (this.errord) {
return;
}
if (!this._inflate) {
this.emit('error', 'No Inflate block');
}
else {
// no more data to inflate
this._inflate.end();
}
this.destroySoon();
};
If you comment out the this.destroySoon(); it finishes the image correctly, instead of eventually calling this function:
ChunkStream.prototype.end = function(data, encoding) {
if (data) {
this.write(data, encoding);
}
this.writable = false;
// already destroyed
if (!this._buffers) {
return;
}
// enqueue or handle end
if (this._buffers.length === 0) {
this._end();
}
else {
this._buffers.push(null);
this._process();
}
};
...which would otherwise end up setting the stream.writeable to false, or, if you comment that out, to pushing a null value into the _buffers array and screwing up the ChunkStream._processRead.
I'm fairly certain this is a synchronicity problem between the time the zlib parser takes to complete and the time the stream takes to complete, since if you do this synchronously it works fine:
var data = fs.readFileSync('pic.png');
var png = PNG.sync.read(data);
var buff = PNG.sync.write(png);
fs.writeFileSync('out2.png', buff);

Related

javascript streamreader only displaying second chunk in #log-box.append

I am struggling through learning JQuery/Javascript and have a web application using the chrome "experimental" web serial API. When I enter a command and get a response back, this string is broken into 2 pieces in a random place, usually in the first third:
<p0><iDCC-EX V-0.2.1 / MEGA / STANDARD_MOTOR_SHIELD G-9db6d36>
All the other return messages are shorter and also wrapped in "<" and ">" brackets.
In the code below. The log window only ever shows the second chunk, even in the "ChunkTransformer() routine that simultaneously displays it properly in the devtools console log.
How can I get all my return messages to appear as one string? It is ok if the chunks are split as separate return values by the brackets as long as they display in the log. I think the <p0> is not displaying because the log window thinks it is a special character. It would not even display here until I wrapped in in a code tag. So I think I have at least two issues.
async function connectServer() {
try{
port = await navigator.serial.requestPort(); // prompt user to select device connected to a com port
await port.open({ baudRate: 115200 }); // open the port at the proper supported baud rate
// create a text encoder output stream and pipe the stream to port.writeable
const encoder = new TextEncoderStream();
outputDone = encoder.readable.pipeTo(port.writable);
outputStream = encoder.writable;
// send a CTRL-C and turn off the echo
writeToStream('\x03', 'echo(false);');
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable
// test why only getting the second chunk in the log
.pipeThrough(new TransformStream(new ChunkTransformer()));
// get a reader and start the non-blocking asynchronous read loop to read data from the stream.
reader = inputStream.getReader();
readLoop();
return true;
} catch (err) {
console.log("User didn't select a port to connect to")
return false;
}
}
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
if (value) {
displayLog(value);
}
if (done) {
console.log('[readLoop] DONE'+done.toString());
displayLog('[readLoop] DONE'+done.toString());
reader.releaseLock();
break;
}
}
}
class ChunkTransformer {
transform(chunk, controller) {
displayLog(chunk.toString()); // only shows last chunk!
console.log('dumping the raw chunk', chunk); // shows all chunks
controller.enqueue(chunk);
}
}
function displayLog(data){
$("#log-box").append("<br>"+data+"<br>");
$("#log-box").animate({scrollTop: $("#log-box").prop("scrollHeight"), duration: 1}, "fast");
}
First Step:
Modify the displayLog() function in one of the following ways
With Animate:
function displayLog(data){
$("#log-box").append("<br>"+data+"<br>");
$("#log-box").animate({scrollTop: $("#log-box").prop("scrollHeight")}, "fast");
}
Without Animate:
function displayLog(data){
$("#log-box").append("<br>"+data+"<br>");
$("#log-box").scrollTop( $("#log-box").prop("scrollHeight"));
}
OR Just for your understanding:
function displayLog(data){
$("#log-box").append("<br>"+data+"<br>");
scrollHeight = $("#log-box").prop("scrollHeight");
$("#log-box").scrollTop(scrollHeight);
}

Twitter bot image posting bug

Ok so I've had a lot of fun setting up a twitterbot through these tutorials and I have had no problems up to image posting. I have been following along these tutorials: https://www.youtube.com/playlist?list=PLRqwX-V7Uu6atTSxoRiVnSuOn6JHnq2yV and was stuck on 15.6 (https://www.youtube.com/watch?v=mUoIPmZ4KwA) where we post images. It's takes kind of a while to watch all of it but you might be able to skip the simple tweeting text and follow response videos. Now, I understand about 80% of what this guy is saying, as I am a little new to JS compared to some of you guys who are much more experienced than me, so some of my code is a little repetitive, but I wanted to make sure everything runs.
var imageTweet = function(txt) {
var tweet = {
status:txt
}
//This is for uploading the image to twitter but not actually tweeting it
//Might want to define the b64_img
var b64_img = 0;
var imageProcessing = function(){
var filename = "dankykang.jpg";
var params_img = {
encoding:'base64'
}
b64_img = fs.readFileSync(filename, params_img)
}
imageProcessing();
var upload_params = {
media_data: b64_img
}
T.post('media/upload', upload_params, tweeted_img);
function upload_img(err, data, response){
//This part does the tweet
var id = data.media_id_string;
var tweet = {
//for text in the image tweet
status:"testing a new way to tweet an image...",
//This calls the image from the imageProcessing function
media_ids:[id]
}
T.post('statuses/update', tweet, imgstatustweeted);
function imgstatustweeted(){
if (err){
console.log("The status didn't work")
} else {
console.log("Status worked")
}
}
imgstatustweeted();
}
upload_img();
function tweeted_img(err, data, response){
if (err){
console.log("Image not posted");
} else {
console.log("Image posted");
}
}
tweeted_img();
}
imageTweet();
I follow most of his steps with some name changes, and I don't think I am missing anything... but when I run bot.js it keeps giving me an error on data.media_id_string; [above var tweet = {...} and below function upload_img(...)]
it says TypeError: Cannot read property 'media_id_string' of undefined. But according to the tutorial I'm following, I think this has to to with the npm which was set up. So I don't know why it runs fine on his end but doesn't run fine on my end. If I'm not mistaken its not a syntax problem.

Update HTML object with node.js and javascript

I'm new to nodejs and jquery, and I'm trying to update one single html object using a script.
I am using a Raspberry pi 2 and a ultrasonic sensor, to measure distance. I want to measure continuous, and update the html document at the same time with the real time values.
When I try to run my code it behaves like a server and not a client. Everything that i console.log() prints in the cmd and not in the browesers' console. When I run my code now i do it with "sudo node surveyor.js", but nothing happens in the html-document. I have linked it properly to the script. I have also tried document.getElementsByTagName("h6").innerHTML = distance.toFixed(2), but the error is "document is not defiend".
Is there any easy way to fix this?
My code this far is:
var statistics = require('math-statistics');
var usonic = require('r-pi-usonic');
var fs = require("fs");
var path = require("path");
var jsdom = require("jsdom");
var htmlSource = fs.readFileSync("../index.html", "utf8");
var init = function(config) {
usonic.init(function (error) {
if (error) {
console.log('error');
} else {
var sensor = usonic.createSensor(config.echoPin, config.triggerPin, config.timeout);
//console.log(config);
var distances;
(function measure() {
if (!distances || distances.length === config.rate) {
if (distances) {
print(distances);
}
distances = [];
}
setTimeout(function() {
distances.push(sensor());
measure();
}, config.delay);
}());
}
});
};
var print = function(distances) {
var distance = statistics.median(distances);
process.stdout.clearLine();
process.stdout.cursorTo(0);
if (distance < 0) {
process.stdout.write('Error: Measurement timeout.\n');
} else {
process.stdout.write('Distance: ' + distance.toFixed(2) + ' cm');
call_jsdom(htmlSource, function (window) {
var $ = window.$;
$("h6").replaceWith(distance.toFixed(2));
console.log(documentToSource(window.document));
});
}
};
function documentToSource(doc) {
// The non-standard window.document.outerHTML also exists,
// but currently does not preserve source code structure as well
// The following two operations are non-standard
return doc.doctype.toString()+doc.innerHTML;
}
function call_jsdom(source, callback) {
jsdom.env(
source,
[ 'jquery-1.7.1.min.js' ],
function(errors, window) {
process.nextTick(
function () {
if (errors) {
throw new Error("There were errors: "+errors);
}
callback(window);
}
);
}
);
}
init({
echoPin: 15, //Echo pin
triggerPin: 14, //Trigger pin
timeout: 1000, //Measurement timeout in µs
delay: 60, //Measurement delay in ms
rate: 5 //Measurements per sample
});
Node.js is a server-side implementation of JavaScript. It's ok to do all the sensors operations and calculations on server-side, but you need some mechanism to provide the results to your clients. If they are going to use your application by using a web browser, you must run a HTTP server, like Express.js, and create a route (something like http://localhost/surveyor or just http://localhost/) that calls a method you have implemented on server-side and do something with the result. One possible way to return this resulting data to the clients is by rendering an HTML page that shows them. For that you should use a Template Engine.
Any DOM manipulation should be done on client-side (you could, for example, include a <script> tag inside your template HTML just to try and understand how it works, but it is not recommended to do this in production environments).
Try searching google for Node.js examples and tutorials and you will get it :)

converting array buffers to string

I'm getting some weird results when converting an array buffer to a string then displaying the output in a div.
I'm getting some GPS data from the USB port in a chrome packaged app. It converts the array buffer received from the port into a string and outputs. The functions are:
var onReceiveCallback = function(info) {
if (info.connectionId == connectionId && info.data) {
$(".output").append(ab2str(info.data));
}
};
/* Interprets an ArrayBuffer as UTF-8 encoded string data. */
var ab2str = function(buf) {
var bufView = new Uint8Array(buf);
var encodedString = String.fromCharCode.apply(null, bufView);
return decodeURIComponent(escape(encodedString));
};
I have a start and stop button to obviously start and stop the reading of data from the gps device. When I start it the first time it works and outputs as expected, something like:
$GPGGA,214948.209,,,,,0,0,,,M,,M,,*41 $GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,01,07,,,33*7F
$GPRMC,214948.209,V,,,,,0.00,0.00,270814,,,N*4C
$GPGGA,214949.209,,,,,0,0,,,M,,M,,*40 $GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,01,07,,,34*78
$GPRMC,214949.209,V,,,,,0.00,0.00,270814,,,N*4D
but then when I stop it, and restart it, although I clear the output div, the output data seems to be mixing in with the previous result. Like:
$$GPGPGGGGAA,,221155115544..202099,,,,,,,,,0,0,0,0,,,,,,MM,,,,MM,,,,**4455
$$GGPPGGSSAA,,AA,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,**11EE
$$GGPGPGSSVV,,11,,11,,0022,,0077,,,,,,3344,1,177,,,,,,3311**77FF
$$GGPPRRMMCC,,212155115544..220099,,VV,,,,,,,,,,00..0000,,00..0000,,227700881144,,,,,,NN*4*488
$$GPGGPGGGAA,,221155115555..220099,,,,,,,,,,00,,00,,,,,,MM,,,,MM,,,,**4444
$$GGPPGGSSAA,,AA,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,**11EE
$G$GPPGGSSVV,,11,,11,,0022,,0077,,,,,,331,1,1177,,,,,,2255**77FF
$$GGPPRRMMCC,2,21155115555..220099,,VV,,,,,,,,,,00..0000,,00..0000,,227700881144,,,,,,N*N*4499
Its like a buffer or variable isnt being emptied, or something else crazy that I cant figure out. Any pointers appreciated.
edit:
this is the 'start' function which clears the output div and reconnects:
// when the start button is clicked
$( "#start" ).click(function() {
if ( deviceId == 0 ) {
console.log("Please select a device");
return;
}
else {
$(".output").empty();
serial.connect(deviceId, {bitrate: 9600}, onConnect);
}
});
I have found this technique unreliable in my own code, although I don't remember if the problem was similar to one you report:
var ab2str = function(buf) { // not reliable
var bufView = new Uint8Array(buf);
var encodedString = String.fromCharCode.apply(null, bufView);
return decodeURIComponent(escape(encodedString));
};
So, I have done it this way, with code taken from one of the Google Chrome App examples (tcpserver):
function ab2str(buf, callback) {
var bb = new Blob([new Uint8Array(buf)]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
}
Note that this version isn't an exact replacement, since it's asynchronous.
Now, starting with Chrome Version 38 (now in beta), you can do it this way:
function ab2str(buf) {
var dataView = new DataView(buf);
var decoder = new TextDecoder('utf-8');
return decoder.decode(dataView);
}
As I always run the beta and am preparing examples for a forthcoming book, I am now doing it the newest way. Give that a try and see if your problem goes away. If not, my suggestion to examine info.data is still a good one, I think.
UPDATE: I've just checked out this reverse function, which you may also find handy at some point:
function str2ab(buf) {
var encoder = new TextEncoder('utf-8');
return encoder.encode(buf).buffer;
}

Javascript Variable Sometimes Undefined

I know this question has been asked several times, but I couldn't seem to find a solution that worked for me in any of the previous questions. I have a variable that gets set when my HTML page is done loading, but sometimes when my code tries to access that variable, it says that it is undefined. I'm not sure why, since I believe I am waiting for everything to load properly. This exception seems to happen randomly, as most of the time all the code runs fine. Here's a simplified version of my code:
var globalVar;
function initStuff(filePath) {
// I wait till the HTML page is fully loaded before doing anything
$(document).ready(function(){
var video = document.getElementById("videoElementID");
// My parseFile() function seems to run smoothly
var arrayOfStuff = parseFile(filePath);
if (arrayOfStuff == null) {
console.error("Unable to properly parse the file.");
} else {
setGlobalVariable(arrayOfStuff);
video.addEventListener("play", updateVideoFrame, false);
}
});
}
function setGlobalVariable(arrayOfStuff) {
window.globalVar = arrayOfStuff;
}
function updateVideoFrame() {
// A bunch of other code happens first
// This is the line that fails occasionally, saying
// "window.globalVar[0].aProperty.anArray[0] is undefined"
var test = window.globalVar[0].aProperty.anArray[0].aProperty;
}
The only thing that I can think of that might be causing this problem is some sort of synchronicity issue. I don't see why that would be the case, though. Help please!
Edit:
In case the asynchronicity issue is coming from my parseFile(xmlFile) method, here is what I'm doing there. I thought it couldn't possibly be causing the issue, since I force the method to happen synchronously, but in case I'm wrong, here it is:
function parseKML(xmlFile) {
var arrayOfStuff = new Array();
// Turn the AJAX asynchronicity off for the following GET command
$.ajaxSetup( { async : false } );
// Open the XML file
$.get(xmlFile, {}, function(xml) {
var doc = $("Document", xml);
// Code for parsing the XML file is here
// arrayOfStuff() gets populated here
});
// Once I'm done processing the XML file, I turn asynchronicity back on, since that is AJAX's default state
$.ajaxSetup( { async : true } );
return arrayOfStuff;
}
The first thing you should do in your code is figure out which part of:
window.globalVar[0].aProperty.anArray[0]
is undefined.
Since you have multiple chained property references and array references, it could be many different places in the chain. I'd suggest either set a breakpoint right before your reference it examine what's in it or use several console.log() statement sto output each nested piece of the structure in order to find out where your problem is.
console.log("globalVar = " + globalVar);
console.log("globalVar[0] = " + globalVar[0]);
console.log("globalVar[0].aProperty = " + globalVar[0].aProperty);
console.log("globalVar[0].aProperty.anArray = " + globalVar[0].aProperty.anArray);
console.log("globalVar[0].aProperty.anArray[0] = " + globalVar[0].aProperty.anArray[0]);
If the problem is that globalVar isn't yet set, then you have a timing problem or an initialization problem.
If the problem is that one of the other properties isn't set, then you aren't initializing globalVar with what you think you are.
You may also want to write your code more defensibly so it fails gracefully if some of your data isn't set properly.
You need to use defensive programming.
http://www.javascriptref.com/pdf/ch23_ed2.pdf
Example:
var video = document.getElementById("videoElementID") || 0;
-
if( video && video.addEventListener ){
video.addEventListener("play", updateVideoFrame, false);
}
Here's another version of your code.
window.globalVar = globalVar || [];
function setGlobalVariable(arrayOfStuff) {
window.globalVar = arrayOfStuff;
}
function updateVideoFrame() {
// A bunch of other code happens first
// This is the line that fails occasionally, saying
// "window.globalVar[0].aProperty.anArray[0] is undefined"
if( window.globalVar ){
var g = window.globalVar || [];
var d = (g[0] || {})["aProperty"];
// etc...
}else{
console.error( "test error." );
}
}
function initStuff(filePath) {
// I wait till the HTML page is fully loaded before doing anything
$(document).ready(function () {
var video = $("#videoElementID");
// My parseFile() function seems to run smoothly
var arrayOfStuff = parseFile(filePath) || [];
if (arrayOfStuff == null || video == null ) {
console.error("Unable to properly parse the file.");
} else {
setGlobalVariable(arrayOfStuff);
video.bind("play", updateVideoFrame);
}
});
}

Categories

Resources