Поддельная последовательная связь под Linux

У меня есть приложение, в котором я хочу имитировать соединение между устройством и "модемом". Устройство будет подключено к последовательному порту и будет разговаривать с программным модемом.

В целях тестирования я хочу иметь возможность использовать макет программного обеспечения для проверки отправки и получения данных.

Пример кода Python

device = Device()
modem  = Modem()
device.connect(modem)

device.write("Hello")
modem_reply = device.read()

Теперь, в моем последнем приложении, я просто передам /dev/ttyS 1 или COM1 или что-то другое для использования приложения. Но как я могу сделать это в программном обеспечении? Я использую Linux, а приложение написано в Python.

Я попытался сделать FIFO (mkfifo ~/my_fifo), и это действительно работает, но тогда мне понадобится один FIFO для записи и один для чтения. Я хочу открыть ~/my_fake_serial_port и читать и писать на него.

Я также lpayed с модулем pty, но не могу заставить это работать. Я могу получить дескриптор ведущего и подчиненного файлов из pty.openpty(), но попытка их чтения или записи вызывает сообщение об ошибке IOError Bad File Descriptor.

Update

Комментарии указали мне на вопрос SO Есть ли какая-то программа, например COM0COM в linux?, которая использует socat для установки виртуального последовательного соединения. Я использовал его так:

socat PTY,link=$HOME/COM1 PTY,link=$HOME/COM2

Для всех вас, спасибо за предоставленную мне ценную информацию. Я решил согласиться с ответом Винай Саипса, потому что это решение, к которому я пошел, пока не появилось предложение соната. Кажется, он работает достаточно хорошо.

Ответы

Ответ 1

Вероятно, лучше использовать pyserial для связи с последовательным портом, и вы можете просто создать макетную версию serial.Serial класс, который реализует read, readline, write и любые другие методы, которые вам нужны.

Ответ 2

Вы находитесь на правильном пути с псевдотерминалами. Для этого вашему макетному программному устройству необходимо сначала открыть псевдотерминальный мастер - это файловый дескриптор, с которого он будет считывать и записывать, когда он разговаривает с серийным программным обеспечением, которое вы тестируете. Затем ему необходимо предоставить доступ и разблокировать псевдотерминальный ведомый и получить имя подчиненного устройства. Затем он должен распечатать имя ведомого устройства где-нибудь, чтобы вы могли сообщить другому программному обеспечению, чтобы он открыл это как его последовательный порт (то есть, это программное обеспечение будет открывать имя, например /dev/pts/0 вместо /dev/ttyS1).

Затем программное обеспечение симулятора просто считывает и записывает с главной стороны псевдотерминала. В C это будет выглядеть так:

#define _XOPEN_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    int pt;

    pt = open("/dev/ptmx", O_RDWR | O_NOCTTY);
    if (pt < 0)
    {
        perror("open /dev/ptmx");
        return 1;
    }

    grantpt(pt);
    unlockpt(pt);

    fprintf(stderr, "Slave device: %s\n", ptsname(pt));

    /* Now start pretending to be a modem, reading and writing "pt" */
    /* ... */
    return 0;
}

Надеюсь, это достаточно просто, чтобы преобразовать в Python.

Ответ 3

Здесь pythonic версия pts-emulated (caf's) последовательной связи:

from serial import Serial

driver = MyDriver()  # what I want to test
peer = serial.Serial()
driver.port.fd, peer.fd = posix.openpty()
driver.port._reconfigurePort()
peer.setTimeout(timeout=0.1)
peer._reconfigurePort()
driver.start()

# peer.write("something")
# driver.get_data_from_serial()

Он имеет некоторые преимущества перед издевательским Serial, а именно, что используется Serial-код и используются некоторые артефакты последовательного порта.

Если вы хотите проверить открытие последовательных портов, вы можете поменять местами ведущие и подчиненные и использовать os.ttyname(salve_fd) как имя последовательного порта. Однако я не могу ручаться за побочные эффекты обмена мастером и подчиненными. Наиболее примечательным является то, что вы можете закрыть и снова открыть ведомый, но fi вы также закроете главные подчиненные матрицы.

Это работает как шарм, если ваш тестовый код работает в одном и том же процессе. Я еще не сгладил перегибы с несколькими/отдельными процессами.