serialport / node-serialport

Access serial ports with JavaScript. Linux, OSX and Windows. Welcome your robotic JavaScript overlords. Better yet, program them!
https://serialport.io
MIT License
5.82k stars 1.01k forks source link

serialport doesn't recognize unpluged device #1361

Closed goodhoko closed 5 years ago

goodhoko commented 7 years ago

Summary of Problem

When I try to write to port of device that has been physically unplugged from computer error callback is not called and application just do nothing.

Steps and Code to Reproduce the Issue

var port = new SerialPort('COM2'); //success
//unplug the device
port.write('data');
port.drain((err) => { 
    if(err){
         console.log(err)
    }
}

the callback just isn't called.

reconbot commented 7 years ago

if the write errors, an error event will happen on the port object, the port will close. Drain should also error, but are you getting the other events?

anwarhamr commented 6 years ago

I'm having the same issue on Mac connected to arduino using firmata. I get connected, have data events but error,close are never called.
I've even added a setInterval port.write('data'); that never fails and just continues on.
Other things to note: I'm using this in Electron but have testing it in a vanilla node env with: node 8.7.0 serialport 6.0.4

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in a week no further activity occurs. Feel free continue the discussion or ask for a never-stale label to keep it open. If this is a support issue, consider sharing it on stack overflow to get more eyes on your problem.

goodhoko commented 5 years ago

@reconbot I've tried again and no, the error event does not happen on the port object.

reconbot commented 5 years ago

@anwarhamr I recommend opening your own issue, you have a very different environment @goodhoko can you try the latest serialport (7.0.2) and run with debug mode on?

goodhoko commented 5 years ago

@reconbot will do.

goodhoko commented 5 years ago

@reconbot Ok finally got to my windows rig.

Here's a code I used for testing:

// this is here only to construct a message to which this specific defice will respond so we can see some response when the device is connected
// no need to pay any attention to this class
class PosnetPrinter {
    constructor(websocket) {
        this.STX = String.fromCharCode(2);
        this.ETX = String.fromCharCode(3);
        this.TAB = String.fromCharCode(9);
        this.LF  = String.fromCharCode(10);
    }

    crc16ccit(s) {
        var crcTable = [
            0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5,
            0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
            0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
            0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
            0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
            0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
            0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
            0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
            0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
            0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
            0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5,
            0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
            0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
            0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
            0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
            0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
            0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03,
            0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
            0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6,
            0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
            0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
            0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
            0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1,
            0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
            0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
            0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
            0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
            0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
            0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447,
            0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
            0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2,
            0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
            0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
            0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
            0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
            0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
            0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0,
            0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
            0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
            0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
            0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba,
            0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
            0x2e93, 0x3eb2, 0x0ed1, 0x1ef0]
        var crc = 0x0
        var j, i, c
        for (i = 0; i < s.length; i++) {
            c = s.charCodeAt(i)
            if (c > 255) {
                console.log('char out of range: "' + c + '"')
                throw new RangeError()
            }
            j = (c ^ (crc >> 8)) & 0xFF
            crc = crcTable[j] ^ (crc << 8)
        }
        return ((crc ^ 0) & 0xFFFF)
    }

    buildCommand(command, parameters) {
        command = command + this.TAB
        if (parameters){
            Object.keys(parameters).map((objectKey, index) => {
                command = command + objectKey + parameters[objectKey] + this.TAB
            })
        }
        return this.STX + command + '#' + this.crc16ccit(command).toString(16) + this.ETX
    }
}

// main code

const SerialPort = require('serialport')
const helper = new PosnetPrinter()

var port = new SerialPort('COM3', {/*
    baudRate: 9600,
    dataBits: 8,
    parity: 'none',
    stopBits: 1,
    rtscts: true
*/}, err => {
    if(err){
        console.log('Can\'t open port: "' + err +'"')
        return
    }
})

port.on('data', data => {
    console.log('response: ' + data)
})

port.on('error', function (error) {
    console.log('port error: ' + error)
})

setInterval(() => {
    var message = helper.buildCommand('scomm')
    console.log('message:' + message)
    port.write(message, error => {
        if(error){
            console.log('write error: ' + error)
        }
        console.log('written')
    })
    port.drain(error =>{
        if(error){
            console.log('drain error: ' + error)
        }
        console.log('drained')
    })
}, 1000)

There are basically two issues, the first one less important: 1) When the device is not connected at all the open callback is called with error which is correct. However further calling write or drain won't emit any error and won't call write neither drain callback with error. I guess this is ok as it can be handled from open callback, however would be nice if writes and drains called on closed (not open) port failed.

2) If the device is available when constructing the port and later disconnected during the interval still running no callback is called. The output can look something like this:

port open
message:scomm  #c42b
written
response: scomm        fsN     tzN     ts0     hrT     nuXYZ 1800000056        #F3D8
drained
message:scomm  #c42b
written
drained
response: scomm        fsN     tzN     ts0     hrT     nuXYZ 1800000056        #F3D8
// unplug the device
message:scomm  #c42b
message:scomm  #c42b
message:scomm  #c42b
message:scomm  #c42b

This could be handled by setting a timeout manually, but that's not nice at all. .)


setup info:

Thanks for your time and effort!

HipsterBrown commented 5 years ago

would be nice if writes and drains called on closed (not open) port failed

I believe this is the case because the write and drain methods queue calling the underlying bindings until the 'open' event is emitted -> https://github.com/node-serialport/node-serialport/blob/master/packages/stream/stream.js#L311

I suppose there could be an internal flag for when an error has prevented opening the port so those methods could callback with the error as soon as possible. /cc: @reconbot

disconnected during the interval still running no callback is called This could be handled by setting a timeout manually

Can you show an example of what you mean by the timeout?

goodhoko commented 5 years ago

@HipsterBrown sure, what I meant is something like this:

function callback(err) {
    if(error){
        console.log('write error: ' + error)
    }
    console.log('written')
}

var timeout = setTimeout(() => {
    callback('port write timed out')
}, 1500);

port.write(message, error => {
    clearTimeout(timeout)
    callback(error)
})

not nice at all though.

goodhoko commented 5 years ago

forgot to do that in debug mode. Will do.

goodhoko commented 5 years ago

So I've run code from my previous comment in debug mode:

$env:DEBUG='serialport/*'; node .\index.js

and it gave exactly the same output. Am I missing something? I was expecting at least some additional info to be printed.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in a week no further activity occurs. Feel free continue the discussion or ask for a never-stale label to keep it open. If this is a support issue, consider sharing it on stack overflow to get more eyes on your problem.

goodhoko commented 5 years ago

@reconbot idk, can we maybe have at least some comment on this before closing it? .)

reconbot commented 5 years ago

Sorry looks like nobody responded to stale bot. Most people don’t let us know when they’ve solved (or abandoned) a support issue so the bot closes up after a while (and a warning).

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in a week no further activity occurs. Feel free continue the discussion or ask for a never-stale label to keep it open. If this is a support issue, consider sharing it on stack overflow to get more eyes on your problem.