python3
仮想環境の初期設定を行います。
$ source setup.shまず、仮想環境を立ち上げます。
$ source .venv/bin/activateその後、本アプリを起動します。
$ python3 serialserver.py起動するポート番号を変えたい場合は、-pまたは--portオプションを用いてください。デフォルトは7878です。
例.20000ポートで立ち上げたい場合
$ python3 serialserver.py -p 20000このアプリでは、parserを追加することでバイト列を自動で辞書型に変換し、jsonとして出力することができます。
background/parsers/の中にparserという名前のインスタンスを定義したモジュールを置いておくと、自動で読み込みます。
ただし、parserはbackground/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エンドポイントのparsersやparser/<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ときて欲しい場合はというふうに記述します。この機能は、正しいデータが来ているかどうか確かめるためと、parserが複数ある場合にどのparserを用いるべきか判別するために用いられます。return [(0, b'\x10'), (10, b'\xA0\xB0')]
- 複数のparserを用いる場合は
get_id_bytes()とget_id_bytes()でただ1つのparserのみが条件にあうようにしてください。 parse()メソッドはbytes型を受け取りdictとして値を返すような実装になっていれば、中身をどう書いても構いません。上の例ではstructモジュールを用いていますが、bytesから直接読み取るなどの実装を行ってもらっても大丈夫です。
これらのことを守ってparserを定義すれば、対応するデータがきたときに自動的にparseしてくれます。