pySerial でシリアルポートの入力を受け取る

仕事で使うためにシリアルポートからの入力を表示するコンソールのようなものを作りました。TeraTerm の簡易版みたいなものです。

pySerial モジュールを利用することでシリアルポートからのデータの受け取りはものすごく簡単に出来ます。通信に関するいくつかの設定が分かっていれば、 serial モジュールのクラス Serial のインスタンスを作成して、あとはファイルオブジェクトと同じ様にデータを取り出せます。
で、最初は以下のような方法で表示を行っていました。

class MainFrame(resource.xrcMainFrame):
    # スレッド処理関数
    def run(self):
        while self.running:
            buf=self.com.readlines()
            self.text_ctrl.AppendText(buf)

しかしどうもこの方法だとデータが一気に押し寄せてきたときに取りこぼしが出てしまいます。そこで次のように変更しました。

class MainFrame(resource.xrcMainFrame):
    # 受信処理スレッド
    def receive(self):
        while self.running:
            buf=self.com.readlines()
            self.lock.acquire()
            self.buffer.append(buf)
            self.lock.release()
    # スレッド処理関数
    def run(self):
        while self.running:
            self.lock.acquire()
            buf=self.buffer.pop(0)
            self.lock.release()
            self.text_ctrl.AppendText(buf)

リスト buffer を FIFO のキューとして用意し、受信処理とテキストコントロールにテキストを追加していく処理を別スレッドに分けました。このやり方で今のところは上手くいっているようです。

あと、このプログラムは GUI ですが、その終了処理部分は次のようになっています。

@binder(wx.EVT_WINDOW_DESTROY, id=XRCID('MainFrame')
def Exit(self, evt):
    self.running=False      # スレッドの while ループを抜けさせる
    self.thread.join()      # テキストコントロールへの追加スレッドの終了待ち
    self.receiver.join()    # 受信処理スレッドの終了待ち
    self.com.close()        # シリアルポートを閉じる

スレッドの終了を待たないと、最後に受け取ったデータを既に破棄されてしまったテキストコントロールに追加しようとしてエラーが発生する場合があります。