Nodejs, как получить доступ к пути SerialPort в событии «данные»

Я использую библиотеку SerialPort в nodejs, чтобы вывести список доступных портов и отправить данные на каждый из них. Если какой-либо из них возвращает OK, я хотел бы узнать, какой порт возвращает его:

SerialPort.list().then(ports => {
            ports.forEach(port => {
                    var rtuSocket = new SerialPort(port.path, { baudRate: 9600 }, (err, data) => {
                        rtuSocket.on('data', (err, data) => {
                            console.log(rtuSocket.path)
                            console.log("[RTU][CONNECTED]")
                        })
                        rtuSocket.write("AT")
                    })
            })
        })

Очевидно, что rtuSocket будет другой переменной к моменту возврата данных. Есть ли способ узнать, какой порт возвращает данные внутри .on(data)?


person Jay    schedule 22.10.2020    source источник


Ответы (1)


Вам нужен какой-то тайм-аут, чтобы определить, не отвечает ли порт. Я также настоятельно рекомендую вам проверить данные ответа и отправить что-то большее, чем простая AT-команда, потому что многие другие устройства реагируют на AT-команды, такие как контроллеры RGB для игровых ПК и некоторые телефоны Android, настроенные как модемы.

Что-то вроде следующего должно работать:

function checkPort (path, callback) { // callback(error, path)
    let sock = new SerialPort(path, { baudRate: 9600 }, (err, data) => {
        let timer = setTimeout(() => {
            callback(new Error('Timeout'));
        }, 100); // 100ms should be enough. If not increase it

        sock.on('data', (err, data) => {
            clearTimeout(timer); // clear the timer

            if (err) {
                return callback(err);
            }
            else {
                return callback(null, path);
            }
        })
        sock.write("AT");
    })
}

Теперь вы можете проверить, какой порт подключен:

function findConnectedPort (callback) {
    SerialPort.list().then(ports => {

        portCount = ports.length;

        ports.forEach(port => {
            checkPort(port.path, (err, foundPath) => {
                if (err) {
                    // Ignore errors or deal with them how you want
                }
                else {
                    // Great! Got response. Return the path:

                    if (portCount > 0) { // prevent calling callback
                                         // more than once
                       
                        callback(null, foundPath);
                    }
                    portCount = 0;
                }

                portCount--;

                if (portCount <= 0) {
                    // Well, none of the ports responded so we
                    // return an error:

                    callback(new Error('Not found'));
                }
            })
        })
    })
}

Теперь, чтобы найти подключенный порт, просто выполните:

findConnectedPort((err,path) => {
    if (err) {
        console.error(err);
    }
    else {
        console.log("RTU CONNECTED at " + path);

        // Do what you need with the port here...
    }
})

Обещания и асинхронность/ожидание

Пока код выше работает. Для асинхронного кода, который требует большого количества условной логики, подобной этой, я считаю, что использовать async/await гораздо проще. Для этого вам нужно преобразовать свой код, чтобы он возвращал обещания:

function checkPort (path) {
    return new Promise((resolve, reject) => {
        let sock = new SerialPort(path, { baudRate: 9600 }, (err, data) => {
            let timer = setTimeout(() => {
                reject(new Error('Timeout'));
            }, 100); // 100ms should be enough. If not increase it

            sock.on('data', (err, data) => {
                clearTimeout(timer); // clear the timer

                if (err) {
                    return reject(err);
                }
                else {
                    return resolve(path);
                }
            })
            sock.write("AT");
        })
    });
}

Теперь цикл for легче понять, хотя теперь вы не можете использовать forEach или map или filter или любые методы массива. Вам нужно использовать for или while, если вы хотите использовать async/await:

async function findConnectedPort () {
    let ports = await SerialPort.list();

    for (let i=0; i<ports.length; i++) {
        let port = ports[i];

        try {
            // return the first successful result:
            return await checkPort(port.path);
        }
        catch (err) {
            // ignore errors or handle them how you like
            // just remember that we use an error to signal
            // a timeout which simply means no device
            // responded
        }
    }

    // we didn't find anything. In this case I prefer to return
    // nothing instead of returning an error:

    return null;
}

Теперь вы можете просто получить подключенный порт, выполнив:

async function main () {

    let connectedPath = await findConnectedPort();

    if (connectedPath === null) {
        console.log('No devices found!');
    }
    else {
        // Do what you need with the port here...
    }
}

main();
person slebetman    schedule 23.10.2020