Skip to content

Latest commit

 

History

History
120 lines (86 loc) · 5.32 KB

File metadata and controls

120 lines (86 loc) · 5.32 KB

Serial-Server 使い方

必要ソフト

python3

初期設定

仮想環境の初期設定を行います。

$ source setup.sh

起動方法

まず、仮想環境を立ち上げます。

$ source .venv/bin/activate

その後、本アプリを起動します。

$ python3 serialserver.py

起動するポート番号を変えたい場合は、-pまたは--portオプションを用いてください。デフォルトは7878です。

例.20000ポートで立ち上げたい場合

$ python3 serialserver.py -p 20000

parserの追加方法

このアプリでは、parserを追加することでバイト列を自動で辞書型に変換し、jsonとして出力することができます。

background/parsers/の中にparserという名前のインスタンスを定義したモジュールを置いておくと、自動で読み込みます。 ただし、parserbackground/abstractparser.pyモジュールの中にある、AbstractParserクラスを継承したクラスのインスタンスである必要があります。

注意:parserインスタンスが定義されていないファイルを置いてしまうと、実行時エラーになるので注意してください。parserインスタンスを定義しないモジュールは、background/parsers以外の場所においてください。

例.

C言語で以下の構造体に対応するデータを考えます。

struct {
    int timestamp;
    float value1;
    float value2;
}

この構造体に対するparserは次の通りです。

background/parsers/sampleparser.py

from background.abstractparser import AbstractParser
import struct

class SampleParser(AbstractParser):
    """
    Parser for sample data.
    """
    def __init__(self):
        self.parser=struct.Struct(">Iff")

    @staticmethod
    def get_name() -> str:
        return "sample"

    @staticmethod
    def get_keys() -> list[str]:
        return ["timestamp", "value1", "value2"]

    @staticmethod
    def get_data_length() -> int:
        return 12

    @staticmethod
    def get_id_bytes() -> list[(int, bytes)]:
        return []  # 他のparserと区別する必要がある場合に用います。区別する必要がない場合は、このように空のlistを返すようにしてください。(メソッドを定義しないとエラーになります)

    def parse(self, data: bytes) -> dict:
        if len(data) != self.get_data_length():
            raise ValueError(f"Expected {self.get_data_length()} bytes, got {len(data)} bytes")

        timestamp, value1, value2 = self.parser.unpack(data)
        return {
            "timestamp": timestamp,
            "value1": value1,
            "value2": value2
        }

# 最後にparserという名前のインスタンスを作成しておきます。
# こうすることで、自動的にこのparserを読み込みます。
parser = SampleParser()

ポイントは次の通りです。

  • AbstractParserクラスをimportし、継承してください。このクラスを継承してない場合、実行時エラーになります。
  • get_name(), get_keys(), get_data_length(), get_id_bytes(), parse(self, data: bytes)を定義してください。AbstractParserクラスの中でabstractMethodとして定義されているため、実装しないと実行時エラーになります。
  • get_name()メソッドで定義した名前が、parserの名前として扱われます。APIエンドポイントのparsersparser/<parsername>などで用いられる名前となります。そのため、小文字で統一することを推奨します。
  • get_keys()メソッドで定義したkeyが、parser/<parsername>で帰ってくるkey一覧になります。parse()メソッドでの返り値と異なる値でも定義できてしまうため、実装には十分気をつけてください。
  • get_data_length()で定義した値とget_id_bytes()で定義した値に一致するデータのみがparseされます。こちらもparse()メソッドの実装と異なる値でも定義できてしまうため、実装時には注意するようにしてください。
  • get_id_bytes()メソッドは、(bytesの中の開始インデックス(int), 実際にきて欲しい値(bytes))の配列を返すように実装します。例えば、先頭に必ず0x10が来て、さらに10個目と11個目に0xA0,0xB0ときて欲しい場合は
    return [(0, b'\x10'), (10, b'\xA0\xB0')]
    というふうに記述します。この機能は、正しいデータが来ているかどうか確かめるためと、parserが複数ある場合にどのparserを用いるべきか判別するために用いられます。
  • 複数のparserを用いる場合はget_id_bytes()get_id_bytes()でただ1つのparserのみが条件にあうようにしてください。
  • parse()メソッドはbytes型を受け取りdictとして値を返すような実装になっていれば、中身をどう書いても構いません。上の例ではstructモジュールを用いていますが、bytesから直接読み取るなどの実装を行ってもらっても大丈夫です。

これらのことを守ってparserを定義すれば、対応するデータがきたときに自動的にparseしてくれます。