cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
91 stars 0 forks source link

Получение списка доступных последовательных портов (Boost.Asio) #567

Open ksrp1984 opened 1 year ago

ksrp1984 commented 1 year ago

<Описание вашей идеи> Метод получения доступных последовательных портов в системе <Примеры, где ваша идея будет полезна. Чем больше примеров и чем большую аудиторию они охватывают - тем лучше> Будет полезно в любом приложение работающее с COM портами <Код c реализацией вашей идеи, если есть> В качестве референса можно использовать реализацию в QT: "QList QSerialPortInfo::availablePorts()"(qserialportinfo_unix.cpp, qserialportinfo_win.cpp)

Полезные ссылки:

kov-serg commented 1 year ago

Чтоб далеко не бегать:

int enum_comports( void (*handler)(void* ctx, const char* name, const char* desc), void *ctx );

#ifdef WIN32

#include <windows.h>

int enum_comports( void (*handler)(void* ctx, const char* name,const char* desc), void *ctx ) {
    const char* path1="HARDWARE\\DEVICEMAP\\SERIALCOMM";
    LSTATUS st; DWORD nvalues=0; HKEY key=0;

    st=RegOpenKeyExA(HKEY_LOCAL_MACHINE,path1,0,KEY_READ,&key);
    if (st) {
        // fprintf(stderr,"no com ports. unable to open %s\n",path1);
        return 1;
    }
    st=RegQueryInfoKeyA(key,0,0,0,0,0,0,&nvalues,0,0,0,0);
    if (st) {
        // fprintf(stderr,"unable to get values count\n");
        nvalues=256;
    }
    for(int i=0;i<(int)nvalues;i++) {
        enum { name_max=256, value_max=256 };
        char name[name_max], value[value_max];
        DWORD name_len=name_max, value_len=value_max, val_type=0;
        name[0]=0; value[0]=0;
        st=RegEnumValueA(key,i,name,&name_len,0,&val_type,(LPBYTE)value,&value_len);
        if (st) {
            if (st==ERROR_NO_MORE_ITEMS) break;
            // fprintf(stderr,"error enum values %d\n",st);
            break;
        }
        if (val_type==REG_SZ ||
            val_type==REG_EXPAND_SZ ||
            val_type==REG_MULTI_SZ) {
                const char* desc=name;
                const char* prefix="\\Device\\";
                int i=0; while(prefix[i] && *desc==prefix[i]) { i++; desc++; }
                handler(ctx,value,desc);
        } else {
            // fprintf(stderr,"unexpected value type %d\n",val_type);
        }
    }
    RegCloseKey(key);
    return 0;
}

#else // linux

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>

#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <linux/serial.h>
#include <string>
using std::string;

static string comport_get_driver(const string& dir) {
    struct stat st;
    string devicedir = dir;

    devicedir += "/device";
    if (lstat(devicedir.c_str(), &st)==0 && S_ISLNK(st.st_mode)) {
        enum { buffer_max=1024 }; char buffer[buffer_max];
        memset(buffer, 0, sizeof(buffer));
        devicedir += "/driver";
        if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0) {
            return basename(buffer);
        }
    }
    return "";
}

static int comport_probe(const char *name) {
    struct serial_struct serinfo;
    int rc=1;
    int fd=open(name, O_RDWR | O_NONBLOCK | O_NOCTTY);
    if (fd>=0) {
        if (ioctl(fd, TIOCGSERIAL, &serinfo)==0) {
            if (serinfo.type!=PORT_UNKNOWN) rc=0;
        }
        close(fd);
    }
    return rc;
}

int enum_comports( void (*handler)(void* ctx, const char* name,const char* desc), void *ctx ) {
    int n; struct dirent **namelist;
    const char* sysdir = "/sys/class/tty/";

    n = scandir(sysdir, &namelist, NULL, NULL);
    if (n<0) perror("scandir");
    else {
        while (n--) {
            if (strcmp(namelist[n]->d_name,"..") && strcmp(namelist[n]->d_name,".")) {
                string devicedir = sysdir;
                devicedir += namelist[n]->d_name;
                string driver = comport_get_driver(devicedir.c_str());
                if (driver.size() > 0) {
                    string devfile = string("/dev/") + basename(devicedir.c_str());
                    bool invalid=0;
                    if (driver == "serial8250") {
                        invalid=comport_probe(devfile.c_str());
                    }
                    if (!invalid) handler(ctx,devfile.c_str(),driver.c_str());
                }
            }
            free(namelist[n]);
        }
        free(namelist);
    }
    return 1;
}

#endif
GitSparTV commented 1 year ago

А много ли приложений пользуется COM портами? Звучит слишком ОС-зависимо и тем более у нас нет понятия устройств и прочего, в какую библиотеку это войдёт?

ksrp1984 commented 1 year ago

В промышленности последовательные порты повсеместно используются. Логичнее всего чтобы вошло в boost asio, т.к. в стандартной библиотеке ничего подобного нет. В boost же работа с последовательными портами реализована, а вот функции для получения доступных портов нет. По поводу "ОС-зависимо" - в Qt же реализовали, недавно собрал на Ubuntu одно свое приложение, которое изначально разрабатывалось под Windows, проблем не обнаружил, без каких либо изменений в коде приложение собралось и работало на Linux, список всех портов отображался корректно. Понятно что внутри в Qt код разный под разные платформы, но пользователь библиотеки об этом знать не должен.

GitSparTV commented 1 year ago

Не знал, что сюда и для буста идеи можно кидать

ksrp1984 commented 1 year ago

Если подскажите можно куда перенести

incoder1 commented 1 year ago

Зачем это в стандарте? Это функции операционной системы. Какие COM порты скажем в Rabsbery Pi ? Получится что нет портов - зничит и нельзя раработать полностьтю удовлятеворяющую стандарту стандартную библиотеку.

kov-serg commented 1 year ago

Rasbery PI обычные последовательные порты, даже в arduino они есть.

ksrp1984 commented 1 year ago

Зачем это в стандарте? Это функции операционной системы. Какие COM порты скажем в Rabsbery Pi ? Получится что нет портов - зничит и нельзя раработать полностьтю удовлятеворяющую стандарту стандартную библиотеку. А какие потоки, исключения, динамическая память на 8 битном МК (а вот последовательные порты даже там есть)? По этой логике много чего в стандарте лишнее. И речь все же не про стандартную библиотеку, а про boost в котором уже реализована работа с последовательными портами.

ksrp1984 commented 12 months ago

Не знал, что сюда и для буста идеи можно кидать

Вроде бы планируется в C++ добавить Networking, не знаю подразумевает ли он работу с последовательными портами, но логично было бы что ДА.

ksrp1984 commented 12 months ago

Зачем это в стандарте? Это функции операционной системы. Какие COM порты скажем в Rabsbery Pi ? Получится что нет портов - зничит и нельзя раработать полностьтю удовлятеворяющую стандарту стандартную библиотеку.

Для популярных ОС можно полноценно реализовать этот функционал. Для систем без ОС можно просто оставить стандартный интерфейс который должен реализовать пользователь.
Rabsbery Pi вродебы на Linux работает? Какие там могут быть проблемы?