Kata pengantar
Saat bekerja dengan mikrokontroler, saya sering menemukan protokol biner. Apalagi bila ada banyak pengontrol. Atau bluetooth energi rendah digunakan dan perlu menulis kode untuk memproses data biner dalam karakteristik. Selain kodenya, dokumentasi yang jelas selalu dibutuhkan.
Pertanyaannya selalu muncul - apakah mungkin untuk mendeskripsikan protokol dan menghasilkan kode dan dokumentasi untuk semua platform? IDL dapat membantu dalam hal ini.
1. Apakah IDL
Definisi IDL cukup sederhana dan sudah disajikan di wikipedia
IDL , atau Interface Definition Language ( Engl. Interface the Description the Language and Interface Definition the Language ) - bahasa spesifikasi untuk mendeskripsikan antarmuka , secara sintaksis mirip dengan deskripsi kelas dalam bahasa C ++ .
Hal terpenting dalam IDL adalah harus menggambarkan dengan baik antarmuka interaksi, API, protokol. Ini harus cukup jelas untuk dijadikan dokumentasi bagi teknisi lain.
Bonus juga - generasi dokumentasi, struktur, kode.
2. Motivasi
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.
Ini adalah artikel pertama saya tentang Habr. Saya akan senang menerima tanggapan - apakah topik ini menarik atau tidak. Jika ada yang memiliki contoh yang baik dari generator protokol biner kodo + doko untuk Embedded, akan menarik untuk dibaca di komentar. Atau beberapa praktik sukses dalam mengimplementasikan sistem serupa untuk mendeskripsikan protokol biner.
Dalam proyek ini, saya tidak terlalu memperhatikan kecepatan kerja. Saya melakukan beberapa hal untuk "memecahkan masalah lebih cepat". Lebih penting untuk mendapatkan kode yang berfungsi yang sudah dapat Anda coba terapkan ke proyek yang berbeda.