3

半二重シリアル ポートで完全非同期 IO を設計する際のアドバイスが必要です。現在、セマフォとミューテックスによって制御されるリーダースレッドと多くのライタースレッドがあります。ここで、スレッドを排除して同期を簡素化したいと考えています。主な問題は、シリアル ポート IO の動作がおかしくなることです。

readIO操作が実際に完了するまで、writeシステムコールがスレッドの呼び出しのみをブロックするようにするだけです。readデフォルトではブロッキングシステムコールであると想定しています。-1のリターンとして取得していますがread。私が説明していない奇妙なEBUSYエラーがあります。現在のコード:

bool SerialManager::initialize(const PortType& portType, const size_t& number)
{
// Open Serial port (/dev/ttyS2 in this case)
fd = open(portName.str().c_str(), O_RDWR ); //O_NOCTTY
if (fd < 0) // if open is not successful
{
    cerr << ERROR << "Unable to open `" << portName << "'." << endl;
    return false;
}
else
{
    cout << INFO << "Port " << portName.str() << " successfully opened."
            << endl;
    cout << INFO << "Configuring port..." << endl;
    fcntl(fd, F_SETFL,~O_NONBLOCK);
    struct termios port_settings; // structure to store the port settings in
    cfsetispeed(&port_settings, B38400); // set baud rate
    cfsetospeed(&port_settings, B38400); // set baud rate
    port_settings.c_cflag |= CLOCAL | CREAD;
    port_settings.c_cflag &= ~CRTSCTS; // disable H/W flow control
    port_settings.c_lflag &= ~( ISIG | // disable SIGxxxx signals
            IEXTEN | // disable extended functions
            ECHO | ECHOE); // disable all auto-echo functions
    port_settings.c_lflag &= ~ICANON ; // raw mode
    port_settings.c_oflag &= ~OPOST; // raw output
    port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // disable S/W flow control;
    port_settings.c_cc[VTIME] = 20; // wait 0.1 second to get data
    port_settings.c_cc[VMIN] = 0;

    port_settings.c_cflag = (port_settings.c_cflag &= ~CSIZE) | CS8; // set data byte size
    port_settings.c_cflag &= ~CSTOPB; // set stop bit 1
    port_settings.c_cflag &= ~PARENB; // set no parity
    port_settings.c_iflag |= IGNPAR; // ignore parity
    port_settings.c_iflag &= ~(INPCK | ISTRIP | PARMRK);

    // Set
    if (tcsetattr(fd, TCSANOW, &port_settings) < 0)
    {
        cerr << ERROR << "Unable to configure serial port." << endl;
        return false;
    }
    else
    {
        cout << INFO << "Port `" << portName.str()
                << "' configuration was successful." << endl;
            return true;
    }
}
}

データを書き込むには:

int SerialManager::asyncWriteData(const byte* data, const size_t& size)
{
    int writeSize = write(fd, data, size);
    return writeSize;
}

読み取り用:

void SerialManager::asyncRead(byte* buffer, const size_t& size, bool& ok)
{
    byte temp[256];
    ssize_t packetSize = read(fd, temp, 256);
    if (packetSize > 0)
    {
        for (size_t i = 0; i < size; ++i)
            buffer[i] = temp[i];
        ok = true;
    }
    cout << errno << endl;
    perror("Error occured: "); // <=== Here I'm getting EBUSY (code 16)
    ok = false;
}

SerialManager外部でクラスを使用する:

....
word checksum = this->id + 0x2C;
checksum = ~checksum;
// Send read command
byte command[] =
{ 0xff, // heading
        0xff, // ~
        this->id, // id of actuator
        0x04, // length
        0x02, // instruction: read
        0x24, // start address: present position
        0x02, // data length
        static_cast<byte>(checksum) //checksum
        };
SerialManager::lockPort(); // lock a mutex to avoid collitions
int numbytes = SerialManager::asyncWriteData(command, 8);
if (numbytes < 0)
{
    cerr << ERROR << "Could not write to serial port." << endl;
    return 0;
}
cout << INFO << numbytes << " bytes has been written." << endl;
for (size_t i = 0; i < 8; ++i)
{
    cout << hex << setfill('0') << setw(2) << (int) command[i] << ' ';
}
cout << endl;

byte* data = new byte[8];
bool ok;
// Here I need to make sure data write is completed before start reading
SerialManager::asyncRead(data, 8, ok);
if (ok)
{
    word position = data[5] + (static_cast<word>(data[6]) << 8);
    return position;
}
else
{
    cerr << ERROR << "Unable to read data from serial port..." << endl;
    return -1;
}
SerialManager::unlockPort(); // Unlock previously locked mutex
....

アップデート:

意味のない読者スレッドを削除しました。送信を制御できない半二重回線があるためです。同期 IO には 2 つの問題があります。

  1. コントローラーからアクチュエーターに非常に長いデータを送信すると、データがポートにある間、最初のアクチュエーターが応答しなくなります。 初め

  2. 別のアクチュエータのデータが完全に送信されていない間にアクチュエータが応答する場合があります 2番

また、問題はafterEBUSYを追加することで解決されます。これはどういうわけか私が必要なものです。(ブロッキング):fsyncwritewrite

int SerialManager::asyncWriteData(const byte* data, const size_t& size)
{
    ssize_t packetSize = write(fd, data, size);
    if (packetSize > 0)
    {
        fsync(fd);
    }
    return packetSize;
} 

男 fsync から:

fsync() は、ファイル記述子 fd によって参照されるファイル (つまり、変更されたバッファ キャッシュ ページ) のすべての変更されたコア内データを、そのファイルが存在するディスク デバイス (または他の永続的なストレージ デバイス) に転送 (「フラッシュ」) します。転送が完了したことをデバイスが報告するまで、呼び出しはブロックされます。また、ファイルに関連付けられたメタデータ情報もフラッシュします

4

1 に答える 1

1

これがあなたの質問に答えているとは思えませんが、役立つと思います。

void SerialManager::asyncRead(byte*, const size_t&, bool&) には、2 つのバグがあります。

  1. read がエラーになったとき (戻り値 < 0) だけでなく、read が成功を返したときに、errno の値を使用しています (および perror を使用してそれに基づくメッセージを出力しています)。成功した場合、errno は特定の値を持つように指定されません。したがって、ガベージ データを効果的に使用しています。あなたが得ている EBUSY は、おそらく何の意味もありません。errno は、特定のエラーが発生した場合にのみ特定の値を持つように定義されています。Linux システムの 'man errno' を参照してください。私の Debian システムでは、関連する部分で次のように述べられています。またはほとんどのライブラリ関数から NULL); 成功した関数は errno を変更できます。". 「しなければならない」ではなく、「許可されている」ことに注意してください。

  2. 一時バッファに読み込み、呼び出し元が渡した宛先バッファにバイト単位でコピーします。これは、CPU サイクルとメモリ帯域幅の無意味な浪費です。呼び出し元のバッファを直接読み取ることができます。(何らかの奇妙な理由で) 一時バッファに読み込んで別の場所にコピーする必要がある場合でも、独自のバイトごとのループを使用せずに、memcpy(3) またはおそらく memmove(3) を使用して別の場所にコピーする必要があります。

設計上、シリアルポートが1つしかないのに複数のリーダーやライターを搭載する意味がわかりません。並列処理を実現しようとしていますか? シリアルポートが 1 つしかないのに、どうしてできるのかわかりません。並列処理ではなく非同期処理が必要な場合は、fcntl(fd, F_SETFL, existing_flags & O_ASYNC) を調べる必要があります (ただし、シグナルはかなり古くて醜いです)。または、単に fd を非ブロックに設定してから、内部イベントで select() を呼び出す必要があります。アプリケーションのループを処理し、利用可能な I/O (存在する場合) を処理します。

お役に立てれば!

于 2013-11-28T13:07:12.393 に答える