序文
マイクロコントローラーを使用しているとき、私はしばしばバイナリプロトコルに出くわしました。特に複数のコントローラーがある場合。または、Bluetooth Low Energyが使用されており、特性内のバイナリデータを処理するコードを記述する必要があります。コードに加えて、明確なドキュメントが常に必要です。
常に疑問が生じます-プロトコルを何らかの方法で記述し、すべてのプラットフォームのコードとドキュメントを生成することは可能ですか?IDLはこれを支援することができます。
1.IDLとは
IDLの定義は非常に単純で、すでにウィキペディアに掲載されています
IDL、または インターフェイス定義言語 (英語。 インターフェイスの説明言語 および インターフェイス定義言語) -C ++の言語でのクラスの説明と構文的に類似した、 インターフェイス を説明 するための仕様言語。
IDLで最も重要なことは、インタラクションインターフェイス、API、プロトコルを適切に記述している必要があるということです。他のエンジニアのドキュメントとして役立つように十分に明確である必要があります。
ボーナスもあります-ドキュメント、構造、コードの生成。
2.動機
IDL. , - QFace (https://github.com/Pelagicore/qface), swagger ( IDL, API development tool). : https://www.protlr.com/.
Swagger REST API. . cbor ( json ).
QFace , "" embedded, . , enum-.
, .
IDL "" , . QFace. - .
2.1 QFace
IDL, , :
module <module> <version>
import <module> <version>
interface <Identifier> {
<type> <identifier>
<type> <operation>(<parameter>*)
signal <signal>(<parameter>*)
}
struct <Identifier> {
<type> <identifier>;
}
enum <Identifier> {
<name> = <value>,
}
flag <Identifier> {
<name> = <value>,
}
jinja2. :
{% for module in system.modules %}
{%- for interface in module.interfaces -%}
INTERFACE, {{module}}.{{interface}}
{% endfor -%}
{%- for struct in module.structs -%}
STRUCT , {{module}}.{{struct}}
{% endfor -%}
{%- for enum in module.enums -%}
ENUM , {{module}}.{{enum}}
{% endfor -%}
{% endfor %}
. "" "", . sly IDL .
3. sly
sly - .
. . :
class CalcLexer(Lexer):
# Set of token names. This is always required
tokens = { ID, NUMBER, PLUS, MINUS, TIMES,
DIVIDE, ASSIGN, LPAREN, RPAREN }
# String containing ignored characters between tokens
ignore = ' \t'
# Regular expression rules for tokens
ID = r'[a-zA-Z_][a-zA-Z0-9_]*'
NUMBER = r'\d+'
PLUS = r'\+'
MINUS = r'-'
TIMES = r'\*'
DIVIDE = r'/'
ASSIGN = r'='
LPAREN = r'\('
RPAREN = r'\)'
Lexer, tokens
- . - , .
- . . - -/ . - . - .
( ):
class CalcParser(Parser):
# Get the token list from the lexer (required)
tokens = CalcLexer.tokens
# Grammar rules and actions
@_('expr PLUS term')
def expr(self, p):
return p.expr + p.term
@_('expr MINUS term')
def expr(self, p):
return p.expr - p.term
@_('term')
def expr(self, p):
return p.term
@_('term TIMES factor')
def term(self, p):
return p.term * p.factor
@_('term DIVIDE factor')
def term(self, p):
return p.term / p.factor
@_('factor')
def term(self, p):
return p.factor
@_('NUMBER')
def factor(self, p):
return p.NUMBER
@_('LPAREN expr RPAREN')
def factor(self, p):
return p.expr
. @_
, . sly .
.
: https://sly.readthedocs.io/en/latest/sly.html
4.
yml . sly . . - jinja2 .
, .
, :
@_('term term')
def term(self, p):
t0 = p.term0
t1 = p.term1
t0.extend(t1)
return t0
, , ";"(SEPARATOR
):
@_('enum_def SEPARATOR')
def term(self, p):
return [p.enum_def]
@_('statement SEPARATOR')
def term(self, p):
return [p.statement]
@_('interface SEPARATOR')
def term(self, p):
return [p.interface]
@_('struct SEPARATOR')
def term(self, p):
return [p.struct]
. (term
term
) .
:
@_('STRUCT NAME LBRACE struct_items RBRACE')
def struct(self, p):
return Struct(p.NAME, p.struct_items, lineno=p.lineno)
@_('decorator_item STRUCT NAME LBRACE struct_items RBRACE')
def struct(self, p):
return Struct(p.NAME, p.struct_items, lineno=p.lineno, tags=p.decorator_item)
@_('struct_items struct_items')
def struct_items(self, p):
si0 = p.struct_items0
si0.extend(p.struct_items1)
return si0
@_('type_def NAME SEPARATOR')
def struct_items(self, p):
return [StructField(p.type_def, p.NAME, lineno=p.lineno)]
@_('type_def NAME COLON NUMBER SEPARATOR')
def struct_items(self, p):
return [StructField(p.type_def, p.NAME, bitsize=p.NUMBER, lineno=p.lineno)]
@_('decorator_item type_def NAME SEPARATOR')
def struct_items(self, p):
return [StructField(p.type_def, p.NAME, lineno=p.lineno, tags=p.decorator_item)]
@_('decorator_item type_def NAME COLON NUMBER SEPARATOR')
def struct_items(self, p):
return [StructField(p.type_def, p.NAME, bitsize=p.NUMBER, lineno=p.lineno, tags=p.decorator_item)]
- (struct
) (struct_items
). :
(
type_def
), (NAME
), (SEPARATOR
)
(
type_def
), , (COLON
), (NUMBER
- , ),
(
decorator_item
), , ,
, , , (
COLON
), (NUMBER
- ),
QFace ( protlr) - . - alias.
@_('DECORATOR ALIAS NAME COLON expr struct SEPARATOR')
def term(self, p):
return [Alias(p.NAME, p.expr, p.struct), p.struct]
:
enum Opcode {
Start = 0x00,
Stop = 0x01
};
@alias Payload: Opcode.Start
struct StartPayload {
...
};
@alias Payload: Opcode.Stop
struct StopPayload {
...
};
struct Message {
Opcode opcode: 8;
Payload<opcode> payload;
};
, opcode
= Opcode.Start
(0x00) - payload
StartPayload
. opcode
= Opcode.Stop
(0x01) - payload
StopPayload
. .
- . - , git. . flag enum, . , , .
python- . . .
- Solvable
. , . , SymbolType
( ). , reference. jinja enum . Solvable
solve
. .. .
solve :
def solve(self, scopes: list):
scopes = scopes + [self]
for i in self.items:
if isinstance(i, Solvable):
i.solve(scopes=scopes)
, solve
- scopes
. . :
struct SomeStruct {
i32 someNumber;
@setter: someNumber;
void setInteger(i32 integer);
};
- someNumber
, SomeStruct.someNumber
.
QFace - , . .
examples/uart - , html . uart . , put_u32 - MCU.
: https://gitlab.com/volodyaleo/volk-idl
P.S.
これはHabrに関する私の最初の記事です。このトピックが興味深いかどうかにかかわらず、フィードバックをいただければ幸いです。Embedded用のkodo + dokoバイナリプロトコルジェネレータの良い例があれば、コメントを読んでおくと面白いでしょう。または、バイナリプロトコルを記述するための同様のシステムを実装するいくつかの成功した実践。
このプロジェクトでは、仕事のスピードにはあまり注意を払いませんでした。私は「問題をより早く解決する」ためにいくつかのことをしました。すでにさまざまなプロジェクトに適用できる実用的なコードを入手することがより重要でした。