皆さん、お疲れ様です。相も変わらずCAN通信について勉強してます。今日で7回目ですね。
今回はCAN通信の送受信する内容をclass化しました。
ある程度python-can使えるようになってきたら、「えっとぉ、BUSを繋いでぇ、メッセージ作ってぇ、送ってぇ、、、」っていちいち書くのがめんどくさくなってきました。そこでclass化しました。
IDと送信周期を指定したらそのIDの信号を送信し続けてくれる。受信もシミュレーション中は受信してくれるみたいな動きをするやつです。
これで複数IDのメッセージを簡単に量産できるようになるぞってね。そんな内容です。
具体的にどういう動作するclass?
受信IDと送信IDと送信周期を指定してclass化します。
開始(start関数)すると受信モニター用のスレッドが開始されて受信IDの信号を受信します。これはイベント信号受信時に送信する。みたいな動作を実現させたくてつけた機能です。
開始すると送信用スレッドが開始されて、受信データをそのまま定期的送信し続ける。
受信データが更新されていない時は前回値を保持して、送信する。
みたいな感じです。では実際のコード。
実際のコード
import time
import threading
import can
class ThreadCAN:
def __init__(self, id_rx, id_tx, period):
self.ext_requested = False
self.id_rx = id_rx
self.id_tx = id_tx
self.period = period
#バス接続
self.bus = can.interface.Bus(bustype='vector',
channel=0,
bitrate=500000,
app_name='python-can')
#msgの初期化
self.msg = can.Message(arbitration_id = id_tx,
data = [0,0,0,0,0,0,0,0],
is_extended_id = False)
self.recv_msg = can.Message(arbitration_id = id_rx,
data = [0,0,0,0,0,0,0,0],
is_extended_id = False)
def start(self):
self.exit_requested = False
self.threadsend = threading.Thread(target = self.thread_sendtask)#送信用スレッド
self.threadrecv = threading.Thread(target = self.thread_recvtask)#受信用スレッド
self.threadsend.start()
self.threadrecv.start()
def stop(self):
self.exit_requested = True
if self.threadsend.isAlive():
self.threadsend.join()
if self.threadrecv.isAlive():
self.threadrecv.join()
def thread_sendtask(self):
while self.exit_requested == False:
self.msg = can.Message(arbitration_id = self.id_tx,
#data = [0,0,0,0,0,0,0,0],
data = self.recv_msg.data,
is_extended_id = False)
self.bus.send(self.msg)
time.sleep(self.period)
def thread_recvtask(self):
while self.exit_requested == False:
self.recv_msg = self.bus.recv(timeout=3)
def shutdown(self):
self.stop()
self.bus.shutdown()
if __name__ == '__main__':
#RxID:465 TxID:123 period = 1sec
UnitACAN = ThreadCAN(0x465, 0x123, 1)
UnitACAN.start()
print('Waiting for payload - maximum 5 sec')
t1 = time.time()
#試しに送信 ID465で送信してみる。
bus = can.interface.Bus(bustype='vector',
channel=0,
bitrate=500000,
app_name='python-can')
msg = can.Message(arbitration_id = 0x465,
data= [0,2,3,4,5,6,7,8],
is_extended_id = False)
count = 0
while time.time() - t1 < 10:
msg = can.Message(arbitration_id = 0x465,
data= [count,2,3,4,5,6,7,8],
is_extended_id = False)
bus.send(msg)
count = count + 1
time.sleep(2)
bus.shutdown()
time.sleep(0.2)
print("Exiting")
UnitACAN.shutdown()
軽く解説
init関数で初期化してます。受信ID(rx_id)と送信ID(tx_id)と送信周期(period)[s]を指定します。
以下はまぁいつものpython-canの仮想BUSに繋いで、一応送受信のメッセージ変数を初期化しています。
#バス接続
self.bus = can.interface.Bus(bustype='vector',
channel=0,
bitrate=500000,
app_name='python-can')
#msgの初期化
self.msg = can.Message(arbitration_id = id_tx,
data = [0,0,0,0,0,0,0,0],
is_extended_id = False)
self.recv_msg = can.Message(arbitration_id = id_rx,
data = [0,0,0,0,0,0,0,0],
is_extended_id = False)
start関数が送受信用のスレッドを開始する関数です。exit_requestedはスレッド内のwhile文のループから抜け出すための変数です。スレッドを終わらすときにTrueにします。
def start(self):
self.exit_requested = False
self.threadsend = threading.Thread(target = self.thread_sendtask)#送信用スレッド
self.threadrecv = threading.Thread(target = self.thread_recvtask)#受信用スレッド
self.threadsend.start()
self.threadrecv.start()
んで、classの動作を終わらすのがstop関数。exit_requestedをTrueにしてスレッドのループから抜け出します。if文以下は、ぶっちゃけようわかってない。
def stop(self):
self.exit_requested = True
if self.threadsend.isAlive():
self.threadsend.join()
if self.threadrecv.isAlive():
self.threadrecv.join()
↓ほぼここがメイン。送信用のthread_sendtask関数。まぁやっていることは単純で、受信したデータ(recv_msg.data)を送信して一定周期(period)寝というのを繰り返しています。
受信用のthread_recvtask関数。これも単純でbus.recvデータを保持するために変数に入れています。if文でIDとかを指定しといたら特定のIDだけを保存するみたいなことが出来ると思いますが、今回はめんどくさくなってやってませんね。timeoutは今回受信する信号の周期を2secとするので+1secして3secとしてます。
def thread_sendtask(self):
while self.exit_requested == False:
self.msg = can.Message(arbitration_id = self.id_tx,
#data = [0,0,0,0,0,0,0,0],
data = self.recv_msg.data,
is_extended_id = False)
self.bus.send(self.msg)
time.sleep(self.period)
def thread_recvtask(self):
while self.exit_requested == False:
self.recv_msg = self.bus.recv(timeout=3)
↓最後はシャットダウン用の関数。whileから抜けるstop関数を実行してBUSをシャットダウンして終わりです。
def shutdown(self):
self.stop()
self.bus.shutdown()
以下はシミュレーションの入力を記述しています。
UnitACANというclass名で作って、スタートしています。
while文は10sec間ID465を送り続けるって記述です。先頭データだけをインクリメントしています。実行結果をみて、意図通り動いているか?をわかりやすくするためです。
10sec間送信し続けたら、終わりって感じです。
if __name__ == '__main__':
#RxID:465 TxID:123 period = 1sec
UnitACAN = ThreadCAN(0x465, 0x123, 1)
UnitACAN.start()
print('Waiting for payload - maximum 5 sec')
t1 = time.time()
#試しに送信 ID465で送信してみる。
bus = can.interface.Bus(bustype='vector',
channel=0,
bitrate=500000,
app_name='python-can')
msg = can.Message(arbitration_id = 0x465,
data= [0,2,3,4,5,6,7,8],
is_extended_id = False)
count = 0
while time.time() - t1 < 10:
msg = can.Message(arbitration_id = 0x465,
data= [count,2,3,4,5,6,7,8],
is_extended_id = False)
bus.send(msg)
count = count + 1
time.sleep(2)
bus.shutdown()
time.sleep(0.2)
print("Exiting")
UnitACAN.shutdown()
なので、想定動作としては
・ID465を2sec周期で送り続ける。先頭データだけインクリメントする。
・ID123は1sec周期で送り続ける。データは受信した値をそのまま送信する。
実行結果
では実行結果をBusmasterでモニターした結果を以下に示します。
ぱっと見では良く分からないので、まずはテスト用で送信したID465信号を見てみます。赤枠で囲みました。
周期が2secごとに送信していて、先頭データがインクリメントされています。意図通り問題なしです。
次はclass側の送信データID123です。青枠で囲みました↓。
周期が1secごとに送信していて、受信値を送信しています。意図通りで問題ありません。
はい。今日はそんなとこです。誰かの何かの参考になれば幸いです。
最後までお読みいただきありがとうございました!!!