私はシリアルポートをエミュレートするためにLinuxのttyサブシステムを使用しています。アナログシリアルポートは、物理シリアルポートが必要なアプリケーションで使用されます。私の場合、アプリケーションはDockerで実行されますが、Dockerはこの問題とは関係がないと思います。ただし、シミュレートされたシリアルポートを管理するアプリケーションがロックされている問題が発生しましたwrite
。通話がブロックされないようにするか、いつブロックされるかを検出する方法があるかどうか疑問に思います。
シミュレートされたシリアルポートを管理し、それらの間でブロードキャストを実行するPythonアプリケーションがあります。つまり、あるアプリケーションがそのシリアルポートに書き込むと、ネットワーク上の他のすべてのシリアルポートが書き込み情報を受け取ります。アプリケーションは次のとおりです。
#!/usr/bin/env python3
import os
import pty
import selectors
import signal
import tty
# read the configuration
all_apps = ...
selector = selectors.DefaultSelector()
# create the emulated serial ports
master_fds = { }
for app in all_apps:
app_master, app_slave = pty.openpty()
tty.setraw(app_master)
master_fds[app] = app_master
selector.register(app_master, selectors.EVENT_READ, app_master)
# make the symlink that gets picked up by the application
os.symlink(os.ttyname(app_slave), "/run/my-app/{}/ttyS0".format(app))
# exit on SIGINT or SIGTERM
run_flag = True
def signal_handler(sig, stack):
global run_flag
run_flag = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# main loop
while run_flag:
events = selector.select(timeout = 1)
for key,mask in events:
fd = key.data
# read up to 1024 bytes of data from the app that sent the message
data = os.read(fd, 1024)
for app_fd in master_fds.values():
# don't broadcast to the application that sent the message
if fd == app_fd:
continue
# send the message to each application
os.write(app_fd, data)
# cleanup
for key in master_fds.keys():
os.remove("/run/my-app/{}/ttyS0".format(key))
os.close(master_fds[key])
通常、このスクリプトはうまく機能します。アプリケーションが起動したら、ptyデバイスをシリアルポートに依存させ、アプリケーション間でメッセージをブロードキャストします。ただし、ある時点でスクリプトが中断される可能性があります。私はスクリプトが呼び出されていると思い、os.write
アプリケーションがptyマスターの1つに送信されたときにのみ呼び出されます。この状態では、スクリプトはシグナルに応答しません(ループの実行中にのみrun_flagをチェックしてos.write
ループをブロックするため)。
私にとって理解できる唯一の説明は、少なくとも1つのアプリケーションがptyのスレーブ側でデータを正しく読み取ることができないことです。これが真であれば、カーネルの一部のptyサポートバッファがいっぱいになり、呼び出しがバッファをオーバーフローするwrite
ため、バッファが十分に使い果たされるまで呼び出しはブロックされます(これは決して発生しません)。
cat
ポートのスレーブ側で実行して、エミュレートされたシリアルポートを消費することが時々可能であることがわかりました。ただし、os.write
通話が通常ブロックされると、人の介入なしにブロックされないように十分に信頼できるアプリケーションが必要です。
ptyマスターへの呼び出しをブロックしないように、ptyサポートバッファの残りの容量を測定する方法(私のPythonスクリプトなど)はありますかwrite
?
答え1
解決策を見つけたようですが、必ずしも理想的なものかどうかはわかりません。
ptyマスターを非ブロックモードに設定するようにスクリプトを編集できます。
import fcntl
...
for app in all_apps:
app_master, app_slave = pty.openpty()
flags = fcntl.fcntl(app_master, fcntl.F_GETFL)
flags |= os.O_NONBLOCK
fnctl.fcntl(app_master, fcntl.F_SETFL, flags)
その後、ホストに書き込むたびに書き込みが正常にブロックされたら、キャプチャしてBlockingIOError
続行できます。
# (try to) send the message to each application
try:
os.write(app_fd, data)
except BlockingIOError:
print("Caught BlockingIOError")
これはアプリケーションが誤動作してシリアルポートを読み取ることができないという基本的な問題を解決しませんが、このソリューションを使用すると、スクリプトは少なくともその操作を続けることができます。欠点は、一部のデータが失われる可能性があることです(ポートを消費しないアプリケーションにブロードキャスト)。