Thread処理と非同期処理をやってみた(Python)

並行処理としてマルチプロセス処理、並列処理としてThread処理、非同期処理がある。 Python multiprocessing vs threading vs asyncio - JX通信社エンジニアブログ

今回はThread処理、非同期処理について実行した備忘録となる。

方法

Threadクラスを継承する

Python3でマルチスレッド処理を試してみる - Corgi Lab. ~備忘録のための技術ブログ~

Threadクラスのインスタンスをつくる

Python3でマルチスレッド処理を試してみる - Corgi Lab. ~備忘録のための技術ブログ~

import time
import threading

def proc():
    for i in range(0,5):
        time.sleep(1)
        print("count", i)

if __name__ == '__main__':
    th1 = threading.Thread(target=proc)
    th2 = threading.Thread(target=proc)
    th1.start()
    th2.start()

クラスの中で更にスレッド処理させようとも試みたがうまく行かなかった。

非同期I/Oを利用する

非同期I/Oでやってみたコードが下記。 iPerf Client / Serverを実行し、結果をリアルタイムに取得する。

# 5.2. プロセス生成: SSHClientConnection.create_process()
# https://blog.hoxo-m.com/entry/asyncssh

import asyncio, asyncssh, sys, getpass

async def async_ssh(cmd):
    pw = "password"
    async with asyncssh.connect("localhost", username='user',password=pw) as conn:
        if "-c" in cmd: await asyncio.sleep(1)
        async with conn.create_process(cmd, stdout=sys.stdout) as process:
            await process.wait()

try:
    iperf_srv = "iperf -s -u -i1 -t5"
    iperf_cli = "iperf -u -c localhost -i1 -t5"
    gather = asyncio.gather(
        async_ssh(iperf_srv),
        async_ssh(iperf_cli)
    )
    asyncio.get_event_loop().run_until_complete(gather)   
    #loop = asyncio.get_event_loop()
    
except (OSError, asyncssh.Error) as exc:
    sys.exit('SSH connection failed: ' + str(exc))

孫クラスからThreadクラスのinit()をコール

ベースクラスとしてThreadクラスを持たせ、その孫クラスにてスレッドを実行する場合を書く。

Threadクラスを継承するaクラス、aクラスを継承するbクラス、bクラスを継承するcクラスといった構成にて、 それぞれIinit()コンストラクタが設定されている場合、各々init()をオーバーライトしているため、 最下のcクラスからThreadクラスのinit()をコールする事ができない。

この場合、Threadクラスのinit()をコールするためには、各クラスで上位クラスのコンストラクタをコールすればよい。

#a.py
import threading

class a(threading.Thread):
    def __init__(self):
        print('a')
        super().__init__()
#b.py
from a import a

class b(a):
    def __init__(self):
        print('b')
        super().__init__()
#c.py
from b import b
import threading
import time

class c(b):
    def __init__(self,name):
        print('c',name)
        self.__name = name
        super().__init__()

    def run(self):
        while 1:
            print('c-loop',self.__name)
            time.sleep(1)

if __name__ == '__main__' :
    c1=c('c1')
    c1.start()
    c2=c('c2')
    c2.start()

余談

余談だが、クラス図の作成はpylintを用いて作成した。

pyreverse -o png . -A -f ALL

オプションを設定しないと全クラスやプライベート変数を表示できないため、-Aで全クラス、-fでプライベート変数を表示する。 参考:Python でパッケージやクラス間の依存関係を可視化してみた.[Pyreverse] - 人生ゲーム - 限界理系大学院生の日々の呟き

参考

ESP32 無線関係資料

ESP32のWIFI_MODE_APSTA(Client AP共用モード)を調べていると、1228pageに渡る"Kolban's book on ESP32"と称される秘伝の書に辿り着いた。仙人の巻物感ある Leanpub: Publish Early, Publish Often

AP STA BT 共用方法

WiFI AP起動後にSTAを立ち上げる。

#include "WiFi.h"
...
     WiFi.mode(WIFI_AP);
     WiFi.softAP(AP_SSID);
     WiFi.begin(STA_SSID, STA_PASS);

Bluetooth + STAも可能。

#include "WiFi.h"
...
      WiFi.begin(STA_SSID, STA_PASS);
      btStart();

詳細は、Arduino IDE > Examples > WiFi > WiFiBlueToothSwitch を参照。

Web server

同期式

ESP32 Web Server - Arduino IDE | Random Nerd Tutorials

非同期

VS codeショートカット

VS Code の便利なショートカットキー - Qiita

C++ headerとcppファイルによるコンパイル

headerとcppファイルを分割してコンパイルする方法がわからなかったので備忘録として記録する。

C++にてheaderとcppファイルによるコンパイルを行うには、分割コンパイルとリンクを行う必要がある。 student.hとstudent.cppによりクラスを作成し、mainからstudentインスタンスを呼び出す事を考える。

student.h

using namespace std;

class student{
        int ID;
        //string name;

public:
        student(int id=100);

        int getID();
};

student.cpp

#include "student.h"
#include <iostream>

student::student(int id){
        ID = id;
}

int student::getID(){
        return ID;
}
#include "student.h"
#include <iostream>

using namespace std;

main.cpp

int main(){
        //student s;
        //cout << s.getID() << endl;

        student s(5);
        cout << s.getID() << endl;
        return 0;
}

二つのcppファイルをそれぞれ分割コンパイルし、オブジェクトファイルを生成し、最後にオブジェクトファイルをリンクする。

g++ -c student.cpp -o student.o
g++ -c main.cpp -o main.o 
g++ -o main main.o student.o main.o
 ```

./main を実行すると正常動作しているのがわかる。

M5StickC Magic Wandからの学び

M5StickCのジェスチャでLEDを変色させるMagic Wandのコードで学びがあったため記載しておく。

プロジェクトは下記から確認できる。 それにしても認識は早い。これは機械学習を使っていないためという事もある。

https://m5stack.hackster.io/shasha-liu/magic-wand-752f52 GitHub - m5stack/MagicWand: IMU sensor apply to Motion capture

実際ジェスチャという点ではMotion Controller | ProtoPedia と似ており、 Magic WandはRoll/Pitch/Yawを相補フィルターによって求めている。

相補フィルターは、加速度センサ、ジャイロセンサから取得した値に係数を乗じて角度を算出する方法だ。

新入社員が制作実習でドローンの自作に挑戦した話。(3) | 組込み技術ラボ

尚、Motion ControllerはIMU FilterにMahony Filterを利用している。

さて気になる所だが、コードでTaskSchedulerライブラリ#include <TaskScheduler.h> が呼ばれていたため、動作を調べてみた。 https://github.com/arkhipenko/TaskScheduler

利点としては、センサやLED等をTask割当してスケジューリング実行したい場合にTask割当をシンプルに記載できるところか。 定常的にTaskを実行する処理は、Taskにコールバック関数を登録してloop関数中のrunner.execute()から常にTaskを呼び出す流れでかける。

下記はコード一例だ。

#include <TaskScheduler.h>

void led_blink();

Scheduler runner;

Task M5LED(5000,TASK_FOREVER,&led_blink);
bool statusLED = false;

void setup(){
  Serial.begin(9600);
  runner.addTask(M5LED);
  M5LED.enable(); 
  pinMode(10,OUTPUT);
}

void loop(){
  runner.execute();
}

void led_blink(){
  statusLED = !statusLED;
  digitalWrite(10,statusLED);
  Serial.println(statusLED);
}

vTask...はCPU割当が可能だがTask Schedulerはできそうにない。

GNURadioで生成した.pyファイルを自動起動する

Windows10をddでクローンしてGTP-NVME-SSD128GBからGTP-NVME-SSD1TBへ移行する

結論

(更新) 結論から言うと、NVMe接続のスロットにSATA接続のm.2 SSDは接続できない。 価格.com - 『2.5インチSSDではなくM.2のSSDの換装ないし増設について』 Lenovo ThinkCentre M75q-1 Tiny 価格.com限定 AMD Ryzen 5 PRO 3400GE・8GBメモリー・256GB SSD搭載 パフォーマンス 11A4CTO1WW のクチコミ掲示板

概要

PCであるThinkCenter M75-q1のnvmeSSD128GBを換装する際、SSD1からSSD2へ換装できなかった。 原因と解決策を記載しておく。

今回利用したSSDは下記。最終的にSSD1からSSD3へ換装した事となる。

  1. Samsunq MZ-ALQ1280 128GB M-Key nvme
  2. Kingston RBU-SNS8152S3 256GB B&M-Key SATA?  今回使えなかった
  3. Kingston SKC2500M8 1TB M-key nvme
原因

M2インターフェイスはBkeyとMkeyがあり、SSD2は両方に対応するB & M keyだが、PCが認識できていなかった事がSSD2が使えない原因だった。 B/M keyには注意する必要がある。尚、B keyは6pin、M keyは5pinとなる。
参考 M.2 SSDのインターフェース - パソコンの選び方と買い方

SSD1(中央) と 問題だったSSD2(右)
USB-M2変換でSSD1からSSD2へコピーする際の接続写真。

解決策

そのため、B keyであるSSD3へ換装する事とした。 B keyを二つ認識できる機器を持ち合わせないため、B&M M2変換 を利用して、下記順序でSSD3へddでデータコピーした。 SSD1(B 128GB) → SSD2(B&M 256GB) へdd SSD2(B&M 256GB) → SSD3(B 1TB) へdd

手持ちのB&M USB to M2変換がB keyに対応していれば一気にSSD1からSSD3へ変換できるはず。

変換方法
  1. UbuntuのUSB Live diskを用意して起動する。
  2. diskと検索すると認識diskのDeviceが出てくるため、接続しているSSDのDeviceを調べる。
  3. ddでコピーする。
    128GBから256GBへ変換する際は下記で行った。
    dd if=/dev/nvme0n1 of=/dev/sda bs=64K conv=noerror,sync status=progress
  4. 変換したSSDがPCのM2インターフェイスから認識できるか確認する
    これが重要。認識できていないと勿論windows bootできない。
    SSD2が認識できているものと思っていたため何回もハマってしまった。
  5. window回復パーティションを削除し、Cドライブパーティションを拡張する。
    筆者は念のためディスクの最後にfdiskとddを使い回復パーティションを移しておいた。使えるのかは不明。  最終的に下記構成とした。Cドライブを300GB程度にしているのは、故障時のコピー容量を抑えるため。容量は必要に応じて増やす予定。

最終的にwindows 10のディスクをnvmeSSD128GBからnvmeSSD1TBへ換装できた。

余談

Windowsだけでやる場合は、AOMEIはMBR形式なら無料版でできる。GTP形式はProfessional版 5480円で可能。Windows向けのバックアップフリーソフト | AOMEI Backupper Standard

WindowsでもWSL2やVirtualboxLinuxが使えるのでddでコピーするのが良い。今回はその容量すらなかったためGuesut Ubuntuを利用した。Windowsのみでddでコピーする方法の需要があればコメント下さい。

HC-SR04が未検知状態で異常動作する

未検知状態はエコーが返ってこない状態を指す。

室内では周辺のものがあるためエコーが返るため正常動作するが、
室外(見通し)だと室内同様に検知できなかった。
未検知状態では回路の違いにより、正常・異常動作するものがある事が分かった。注意する事。 Ultrasonic distance sensor HC-SR04 lack of timeout problem - #71 by DaveEvans - Sensors - Arduino Forum

左:正常 右:異常 左右センサ共にどこで購入したか不明。今後管理していきたい...。

異常状態

  • 異常なものはタイムアウトが機能しない、たまにフリーズする。
  • 結果、センサの前に手を置いた場合に距離(赤色)が正しく測定できない事が頻発する。赤色が一定値になるはずだが、10~200cmとばたついている。
  • 異常なセンサでは数十ms間隔でサンプリングできないため、品選びに注意する必要がある。
    異常。赤色が距離。

正常状態

正常なものは距離(赤色)が常に測定できる。

正常。赤色が距離

int Trig = 13;
int Echo = 12;
unsigned long Duration;
float Distance;
unsigned long startMillis,stopMillis,millisPrevious;
unsigned long millisPerRead = 10; //10ms以下は正常に超音波センシングできない

void setup() {
  Serial.begin(115200);
  delay(10);

  pinMode(2, OUTPUT);
  pinMode(Trig,OUTPUT);
  pinMode(Echo,INPUT);
  startMillis = millis();
}

void loop() {

  if ( (millis() - millisPrevious) > millisPerRead ) {
    millisPrevious = millis();
    digitalWrite(Trig,LOW);
    delayMicroseconds(2);
    digitalWrite(Trig,HIGH);
    delayMicroseconds(10);
    digitalWrite(Trig,LOW);
    Duration = pulseIn(Echo,HIGH,millisPerRead*1000); // ★340m/s x 0.007s x 0.5 ≒ 110cm
    if(Duration==0){
      Distance = 200;
    }
    else if (Duration>0) {
      Distance = (Duration/2.0)*340*100/1000000; // ultrasonic speed is 340m/s = 34000cm/s = 0.034cm/us 
    } 

    Serial.print(millis() - millisPrevious);
    Serial.print(",");
    Serial.println(Distance);
    
  }  
}

備考