質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

Q&A

解決済

2回答

1252閲覧

デコレータによってコルーチンを高階関数の戻り値にしたい

takusan189

総合スコア5

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

0グッド

1クリップ

投稿2020/02/06 02:09

前提・実現したいこと

pyserial_asyncioを使った通信プログラムを作っています。TCP socketとのゲートウェイを作る為、ノンブロッキングIO処理を行っています。

今回問題となっているのは、そのシリアル通信側。

相手機器とは幾つかのメッセージシーケンスを持っていて、それぞれに受信時の相手側が送りつける終了文字が異なります。その受信シーケンスを終了させる条件を定義する関数を用意しました。

受信バッファ読み出し部は共通のため、それをreceive関数としてシーケンス関数にデコレートしました。

python

1import asyncio 2import serial_asyncio 3import functools 4 5class MachineCommunication(): 6 7 def __init__(self,port,baudrate): 8 self.__reader = None 9 self.__writer = None 10 self.__port = port 11 self.__baudrate = baudrate 12 13 async def connect(self): 14 self.__reader, self.__writer = await serial_asyncio.open_serial_connection(url=self.__port, 15 baudrate=int(self.__baudrate)) 16 async def send(self, msg): 17 self.__writer.write((msg + '\r').encode()) 18 19 def receive(Function): 20 @functools.wraps(Function) 21 async listener(self): 22 msg = '' 23 while Function(msg) 24 msg = await self.__reader.readuntil(b'\r') # pyserial_asyncioのストリームリーダ 25 msg = msg.rstrip().decode() 26 return listener 27 28 @receive 29 def sequence_A(self,msg): 30 if msg == '受信を終了する文字': 31 return False 32 else: 33 メッセージをさばく処理 34 return True 35 36 @receive 37 def sequence_B(self,msg): 38 if msg == '受信を終了する文字': 39 return True 40 else: 41 メッセージをさばく処理 42 return False

発生している問題・エラーメッセージ

問題は、どうやって上記のsequence_A, sequence_B を呼び出す時にrecieveをawaitさせるか?という事です。
これらの関数を呼び出す時はコルーチンではないため、
await sequence_A
などとは定義できません。しかし、awaitさせないと、

  1. sendを使ってメッセージ送信
  2. seqence_Aを使って受信待ち
  3. sendを使って次のメッセージ送信
  4. seqence_Bを使って受信待ち
  5. 終了

のそれぞれ2,4 を待ってくれません。

試したこと

デコレータを使わず、recieve関数をコルーチンにして呼び出す形にすればうまくいきました。
しかし、すっきり書きたいので、デコレータでうまく行く方法を探しています。

補足情報(FW/ツールのバージョンなど)

Python3.7を使っています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takusan189

2020/02/06 05:51

ご回答ありがとうございます。やってみたのですが、やはり受信を待ってはくれませんでした。 Sequence_A,Bは終了文字監視のみなので、そこで待つのではなくて、 async def listener(self): asincio.sleep(1) の様に受信処理部分にタイマを設けてもこの時間待ってくれない、という症状です。 プライベート変数についてアドバイスありがとうございます。先月からPython始めたばかりの初心者であまり精通しておらず… モジュール読み込みさせない事が目的なんてすね?
guest

回答2

0

ベストアンサー

asyncio.run すればいいだけでは?
シリアル通信環境がないので、ダミー関数で実験

py

1import asyncio 2import functools 3import time 4 5class MachineCommunication: 6 7 def __init__(self): 8 print('__init__') 9 10 async def connect(self): 11 print('connect') 12 13 async def send(self, msg): 14 print('send', msg) 15 16 def receive(Function): 17 @functools.wraps(Function) 18 async def listener(self): 19 print('listener sleeping...') 20 await asyncio.sleep(1) 21 print('listener sleeping... done.') 22 Function(self, '') 23 def wait(self): 24 asyncio.run(listener(self)) 25 return wait 26 27 @receive 28 def sequence_A(self, msg): 29 print('sequence_A') 30 if msg == '受信を終了する文字': 31 return False 32 else: 33 # メッセージをさばく処理 34 return True 35 36 @receive 37 def sequence_B(self, msg): 38 print('sequence_B') 39 if msg == '受信を終了する文字': 40 return True 41 else: 42 # メッセージをさばく処理 43 return False 44 45m = MachineCommunication() 46asyncio.run(m.send('a')) 47m.sequence_A() 48asyncio.run(m.send('b')) 49m.sequence_B()

実験結果

__init__ send a listener sleeping... listener sleeping... done. sequence_A send b listener sleeping... listener sleeping... done. sequence_B

listener を返すのであれば、以下のように await するのが一般的ですけど。

python

1import asyncio 2import functools 3import time 4 5class MachineCommunication: 6 7 def __init__(self): 8 print('__init__') 9 10 async def connect(self): 11 print('connect') 12 13 async def send(self, msg): 14 print('send', msg) 15 16 def receive(Function): 17 @functools.wraps(Function) 18 async def listener(self): 19 print('listener sleeping...') 20 await asyncio.sleep(1) 21 print('listener sleeping... done.') 22 Function(self, '') 23 return listener 24 25 @receive 26 def sequence_A(self, msg): 27 print('sequence_A') 28 if msg == '受信を終了する文字': 29 return False 30 else: 31 # メッセージをさばく処理 32 return True 33 34 @receive 35 def sequence_B(self, msg): 36 print('sequence_B') 37 if msg == '受信を終了する文字': 38 return True 39 else: 40 # メッセージをさばく処理 41 return False 42 43async def amain(): 44 m = MachineCommunication() 45 await m.send('a') 46 await m.sequence_A() 47 await m.send('b') 48 await m.sequence_B() 49 50def main(): 51 asyncio.run(amain()) 52 53if __name__ == '__main__': 54 main()

それと、プライベート変数はアンダースコア1個にしましょう。
理由はPEP8参照: https://pep8-ja.readthedocs.io/ja/latest/

投稿2020/02/06 04:05

編集2020/02/06 08:18
shiracamus

総合スコア5406

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takusan189

2020/02/06 06:12

ご回答ありがとうございます。やってみたのですが、やはり受信を待ってはくれませんでした。 Sequence_A,Bは終了文字監視のみなので、そこで待つのではなくて、 async def listener(self): asincio.sleep(1) の様に受信処理部分にタイマを設けてもこの時間待ってくれない、という症状です。 プライベート変数についてアドバイスありがとうございます。先月からPython始めたばかりの初心者であまり精通しておらず… モジュール読み込みさせない事が目的なんてすね?
shiracamus

2020/02/06 06:41 編集

wait関数が増えてることに気付いてますかね? asyncio.sleep処理を追加した処理に書き換えてみました。 コピペして動作確認してみてください。 アンダースコア2個は、サブクラスでの名前衝突回避のためだけに使いましょう、とPEP8にかかれています。
takusan189

2020/02/06 13:18

いろいろお手間おかけしまして申し訳ありません。コピーペーストしたコードでしたら正常に動作いたしました。 本番製品コードは会社のPCで今日は確認できませんが、明日もう一度みなおしてみます。現象としては、await m.sequence_A() の箇所にはawaitが付けられないよ、という様なエラーがでていたと思います。 def sequence_A() にはデコレータによってコルーチン化されているものの、async句が無いのでコルーチンと見なされずにawaitが付けられないのかと思い質問させていただきました。 ご丁寧にありがとうございました。
guest

0

参考までに、検証したコードを記載します。

python

1import asyncio 2import functools 3 4class MachineCommunication: 5 6 def __init__(self): 7 print('__init__') 8 9 async def connect(self): 10 print('connect') 11 12 async def send(self, msg): 13 print('send', msg) 14 15 def receive(Function): 16 @functools.wraps(Function) 17 async def listener(self): 18 msg = '' 19 while Function(self,msg): 20 msg = 'START' 21 await asyncio.sleep(1) 22 msg = 'END' 23 return listener 24 25 @receive 26 def sequence_A(self, msg): 27 print('sequence_A') 28 if msg == 'END': 29 print('A処理完了') 30 return False 31 else: 32 # メッセージをさばく処理 33 print('A処理中') 34 return True 35 36 @receive 37 def sequence_B(self, msg): 38 print('sequence_B') 39 if msg == 'END': 40 print('B処理完了') 41 return False 42 else: 43 # メッセージをさばく処理 44 print('B処理中') 45 return True 46 47async def amain(): 48 m = MachineCommunication() 49 await m.send('a') 50 await m.sequence_A() 51 await m.send('b') 52 await m.sequence_B() 53 54def main(): 55 loop = asyncio.get_event_loop() 56 loop.run_until_complete(amain()) 57 58if __name__ == '__main__': 59 main()

実行結果は以下の通りでした。

__init__ send a sequence_A A処理中 sequence_A A処理完了 send b sequence_B B処理中 sequence_B B処理完了

投稿2020/02/06 13:23

takusan189

総合スコア5

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問