struct-frame 0.0.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of struct-frame might be problematic. Click here for more details.
- struct-frame/__init__.py +3 -0
- struct-frame/__main__.py +358 -0
- struct-frame/base.py +80 -0
- struct-frame/boilerplate/c/struct_frame.h +103 -0
- struct-frame/boilerplate/c/struct_frame_gen.h +2 -0
- struct-frame/boilerplate/c/struct_frame_parser.h +101 -0
- struct-frame/boilerplate/c/struct_frame_types.h +69 -0
- struct-frame/boilerplate/ts/struct_frame.ts +65 -0
- struct-frame/boilerplate/ts/struct_frame_gen.ts +7 -0
- struct-frame/boilerplate/ts/struct_frame_parser.ts +98 -0
- struct-frame/boilerplate/ts/struct_frame_types.ts +80 -0
- struct-frame/c_gen.py +184 -0
- struct-frame/ts_gen.py +186 -0
- struct_frame-0.0.11.dist-info/METADATA +33 -0
- struct_frame-0.0.11.dist-info/RECORD +17 -0
- struct_frame-0.0.11.dist-info/WHEEL +4 -0
- struct_frame-0.0.11.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "stdbool.h"
|
|
4
|
+
#include "stdint.h"
|
|
5
|
+
|
|
6
|
+
typedef struct _msg_id_len_t {
|
|
7
|
+
bool valid;
|
|
8
|
+
uint8_t len;
|
|
9
|
+
uint8_t msg_id;
|
|
10
|
+
} msg_id_len_t;
|
|
11
|
+
|
|
12
|
+
typedef struct _parser_functions_t {
|
|
13
|
+
bool (*get_msg_id_len)(msg_id_len_t *, uint8_t);
|
|
14
|
+
bool (*validate_packet)(uint8_t *, msg_id_len_t *);
|
|
15
|
+
} parser_functions_t;
|
|
16
|
+
|
|
17
|
+
typedef struct _struct_frame_config {
|
|
18
|
+
uint8_t has_crc;
|
|
19
|
+
uint8_t has_len;
|
|
20
|
+
uint8_t start_byte;
|
|
21
|
+
parser_functions_t *parser_funcs;
|
|
22
|
+
} struct_frame_config;
|
|
23
|
+
|
|
24
|
+
enum ParserState { LOOKING_FOR_START_BYTE = 0, GETTING_LENGTH_MSG_AND_ID = 1, GETTING_PAYLOAD = 2 };
|
|
25
|
+
|
|
26
|
+
typedef struct _struct_frame_buffer {
|
|
27
|
+
// Used for framing and parsing
|
|
28
|
+
struct_frame_config config;
|
|
29
|
+
uint8_t *data;
|
|
30
|
+
size_t max_size;
|
|
31
|
+
size_t size;
|
|
32
|
+
bool in_progress;
|
|
33
|
+
|
|
34
|
+
// Used for framing
|
|
35
|
+
size_t crc_start_loc;
|
|
36
|
+
|
|
37
|
+
// Used for parsing
|
|
38
|
+
enum ParserState state;
|
|
39
|
+
size_t payload_len;
|
|
40
|
+
msg_id_len_t msg_id_len;
|
|
41
|
+
|
|
42
|
+
} struct_buffer;
|
|
43
|
+
|
|
44
|
+
typedef struct _buffer_parser_result_t {
|
|
45
|
+
struct_frame_config config;
|
|
46
|
+
bool found;
|
|
47
|
+
bool valid;
|
|
48
|
+
uint8_t *msg_loc;
|
|
49
|
+
size_t r_loc;
|
|
50
|
+
bool finished;
|
|
51
|
+
msg_id_len_t msg_id_len;
|
|
52
|
+
} buffer_parser_result_t;
|
|
53
|
+
|
|
54
|
+
// https://github.com/serge-sans-paille/frozen
|
|
55
|
+
// https://www.npmjs.com/package/typed-struct
|
|
56
|
+
|
|
57
|
+
#define default_parser {0, 0, 0x90}
|
|
58
|
+
|
|
59
|
+
#define zero_initialized_parser_result {default_parser, false, false, 0, 0, false, {0, 0}};
|
|
60
|
+
|
|
61
|
+
#define CREATE_DEFAULT_STRUCT_BUFFER(name, size) \
|
|
62
|
+
uint8_t name##_buffer[size]; \
|
|
63
|
+
struct_buffer name = { \
|
|
64
|
+
default_parser, name##_buffer, size, 0, false, 0, LOOKING_FOR_START_BYTE, 0, {false, 0, 0}}
|
|
65
|
+
|
|
66
|
+
typedef struct checksum_t {
|
|
67
|
+
uint8_t byte1;
|
|
68
|
+
uint8_t byte2;
|
|
69
|
+
} checksum_t;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Struct } from 'typed-struct';
|
|
2
|
+
import * as sf_types from './struct_frame_types';
|
|
3
|
+
|
|
4
|
+
function fletcher_checksum_calculation(buffer: Uint8Array, data_length: number): sf_types.checksum_t {
|
|
5
|
+
const checksum: sf_types.checksum_t = { byte1: 0, byte2: 0 };
|
|
6
|
+
|
|
7
|
+
for (let i = 0; i < data_length; i++) {
|
|
8
|
+
checksum.byte1 += buffer[i];
|
|
9
|
+
checksum.byte2 += checksum.byte1;
|
|
10
|
+
}
|
|
11
|
+
return checksum;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function msg_encode(buffer: sf_types.struct_frame_buffer, msg: any, msgid: number) {
|
|
15
|
+
buffer.data[buffer.size++] = buffer.config.start_byte;
|
|
16
|
+
buffer.crc_start_loc = buffer.size;
|
|
17
|
+
buffer.data[buffer.size++] = msgid;
|
|
18
|
+
|
|
19
|
+
if (buffer.config.has_len) {
|
|
20
|
+
buffer.data[buffer.size++] = msg.getSize();
|
|
21
|
+
}
|
|
22
|
+
const rawData = Struct.raw(msg);
|
|
23
|
+
for (let i = 0; i < rawData.length; i++) {
|
|
24
|
+
buffer.data[buffer.size++] = rawData[i]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (buffer.config.has_crc) {
|
|
28
|
+
const crc = fletcher_checksum_calculation(buffer.data.slice(buffer.crc_start_loc), buffer.crc_start_loc + rawData.length);
|
|
29
|
+
buffer.data[buffer.size++] = crc.byte1;
|
|
30
|
+
buffer.data[buffer.size++] = crc.byte2;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function msg_reserve(buffer: sf_types.struct_frame_buffer, msg_id: number, msg_size: number) {
|
|
35
|
+
throw new Error('Function Unimplemented');
|
|
36
|
+
|
|
37
|
+
if (buffer.in_progress) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
buffer.in_progress = true;
|
|
41
|
+
buffer.data[buffer.size++] = buffer.config.start_byte;
|
|
42
|
+
|
|
43
|
+
buffer.data[buffer.size++] = msg_id;
|
|
44
|
+
if (buffer.config.has_len) {
|
|
45
|
+
buffer.data[buffer.size++] = msg_size;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const ret = Buffer.from(buffer.data, buffer.size, msg_size);
|
|
49
|
+
buffer.size += msg_size;
|
|
50
|
+
return ret;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
export function msg_finish(buffer: sf_types.struct_frame_buffer) {
|
|
56
|
+
throw new Error('Function Unimplemented');
|
|
57
|
+
|
|
58
|
+
if (buffer.config.has_crc) {
|
|
59
|
+
const crc = fletcher_checksum_calculation(buffer.data.slice(buffer.crc_start_loc), buffer.crc_start_loc - buffer.size);
|
|
60
|
+
buffer.data[buffer.size++] = crc.byte1;
|
|
61
|
+
buffer.data[buffer.size++] = crc.byte2;
|
|
62
|
+
buffer.size += 2
|
|
63
|
+
}
|
|
64
|
+
buffer.in_progress = false;
|
|
65
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { get_message_length } from './struct_frame_gen';
|
|
2
|
+
import * as sf_types from './struct_frame_types';
|
|
3
|
+
|
|
4
|
+
function parse_default_format_validate(buffer: Uint8Array, msg_id_len: sf_types.msg_id_len_t): boolean {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function parse_default_format_char_for_len_id(c: number, msg_id_len: sf_types.msg_id_len_t): boolean {
|
|
9
|
+
msg_id_len.msg_id = c;
|
|
10
|
+
msg_id_len.len = get_message_length(c);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const default_parser_functions: sf_types.parser_functions_t = { get_msg_id_len: parse_default_format_char_for_len_id, validate_packet: parse_default_format_validate };
|
|
15
|
+
|
|
16
|
+
function parse_char_for_start_byte(config: sf_types.struct_frame_config, c: number): sf_types.parser_functions_t | undefined {
|
|
17
|
+
if (config.start_byte == c) {
|
|
18
|
+
return default_parser_functions;
|
|
19
|
+
}
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function parse_char(pb: sf_types.struct_frame_buffer, c: number): boolean {
|
|
24
|
+
let parse_func_ptr: sf_types.parser_functions_t | undefined = undefined;
|
|
25
|
+
switch (pb.state) {
|
|
26
|
+
case sf_types.ParserState.LOOKING_FOR_START_BYTE:
|
|
27
|
+
parse_func_ptr = parse_char_for_start_byte(pb.config, c);
|
|
28
|
+
if (parse_func_ptr) {
|
|
29
|
+
pb.config.parser_funcs = parse_func_ptr;
|
|
30
|
+
pb.state = sf_types.ParserState.GETTING_LENGTH_MSG_AND_ID;
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
|
|
34
|
+
case sf_types.ParserState.GETTING_LENGTH_MSG_AND_ID:
|
|
35
|
+
if (pb.config.parser_funcs && pb.config.parser_funcs.get_msg_id_len(c, pb.msg_id_len)) {
|
|
36
|
+
pb.state = sf_types.ParserState.GETTING_PAYLOAD;
|
|
37
|
+
pb.size = 0;
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
|
|
41
|
+
case sf_types.ParserState.GETTING_PAYLOAD:
|
|
42
|
+
pb.data[pb.size] = c;
|
|
43
|
+
pb.size++;
|
|
44
|
+
if (pb.size >= pb.msg_id_len.len) {
|
|
45
|
+
pb.msg_data = Buffer.from(pb.data, 0, pb.size)
|
|
46
|
+
pb.state = sf_types.ParserState.LOOKING_FOR_START_BYTE;
|
|
47
|
+
if (pb.config.parser_funcs) {
|
|
48
|
+
return pb.config.parser_funcs.validate_packet(pb.data, pb.msg_id_len);
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function parse_buffer(buffer: Uint8Array, size: number, parser_result: sf_types.buffer_parser_result_t): boolean {
|
|
62
|
+
let state: sf_types.ParserState = sf_types.ParserState.LOOKING_FOR_START_BYTE;
|
|
63
|
+
let parse_func_ptr: sf_types.parser_functions_t | undefined;
|
|
64
|
+
for (let i = parser_result.r_loc; i < size; i++) {
|
|
65
|
+
switch (state) {
|
|
66
|
+
case sf_types.ParserState.LOOKING_FOR_START_BYTE:
|
|
67
|
+
parse_func_ptr = parse_char_for_start_byte(parser_result.config, buffer[i]);
|
|
68
|
+
if (parse_func_ptr) {
|
|
69
|
+
state = sf_types.ParserState.GETTING_LENGTH_MSG_AND_ID;
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case sf_types.ParserState.GETTING_LENGTH_MSG_AND_ID:
|
|
74
|
+
if (parse_func_ptr && parse_func_ptr.get_msg_id_len(buffer[i], parser_result.msg_id_len)) {
|
|
75
|
+
state = sf_types.ParserState.GETTING_PAYLOAD;
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case sf_types.ParserState.GETTING_PAYLOAD:
|
|
80
|
+
parser_result.msg_data = Buffer.from(buffer, i, (i + parser_result.msg_id_len.len));
|
|
81
|
+
parser_result.r_loc = i + parser_result.msg_id_len.len;
|
|
82
|
+
parser_result.found = true;
|
|
83
|
+
if (parse_func_ptr && parse_func_ptr.validate_packet(parser_result.msg_data, parser_result.msg_id_len)) {
|
|
84
|
+
parser_result.valid = true;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
parser_result.valid = false;
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
default:
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
export class msg_id_len_t {
|
|
3
|
+
valid = false;
|
|
4
|
+
len = 0;
|
|
5
|
+
msg_id = 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type GetMsgIdLenType = (c: number, msg_id_len: msg_id_len_t) => boolean;
|
|
9
|
+
export type ValidatePacketType = (buffer: Uint8Array, msg_id_len: msg_id_len_t) => boolean;
|
|
10
|
+
|
|
11
|
+
export interface parser_functions_t {
|
|
12
|
+
get_msg_id_len: GetMsgIdLenType;
|
|
13
|
+
validate_packet: ValidatePacketType;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface struct_frame_config {
|
|
17
|
+
has_crc: number;
|
|
18
|
+
has_len: number;
|
|
19
|
+
start_byte: number;
|
|
20
|
+
parser_funcs?: parser_functions_t;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export enum ParserState {
|
|
24
|
+
LOOKING_FOR_START_BYTE = 0,
|
|
25
|
+
GETTING_LENGTH_MSG_AND_ID = 1,
|
|
26
|
+
GETTING_PAYLOAD = 2
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const basic_frame_config: struct_frame_config = { has_crc: 0, has_len: 0, start_byte: 0x90 };
|
|
30
|
+
|
|
31
|
+
export class struct_frame_buffer {
|
|
32
|
+
// Used for framing and parsing
|
|
33
|
+
config: struct_frame_config = basic_frame_config;
|
|
34
|
+
data: Uint8Array;
|
|
35
|
+
size = 0;
|
|
36
|
+
in_progress = false;
|
|
37
|
+
|
|
38
|
+
// Used for framing
|
|
39
|
+
crc_start_loc = 0;
|
|
40
|
+
|
|
41
|
+
// Used for parsing
|
|
42
|
+
state: ParserState = ParserState.LOOKING_FOR_START_BYTE;
|
|
43
|
+
payload_len = 0;
|
|
44
|
+
msg_id_len: msg_id_len_t = new msg_id_len_t();
|
|
45
|
+
msg_data: Buffer = Buffer.allocUnsafe(0);
|
|
46
|
+
|
|
47
|
+
constructor(public max_size: number, buffer?: Uint8Array) {
|
|
48
|
+
if (buffer) {
|
|
49
|
+
this.data = buffer;
|
|
50
|
+
} else {
|
|
51
|
+
this.data = new Uint8Array(max_size);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class buffer_parser_result_t {
|
|
57
|
+
config: struct_frame_config = basic_frame_config;
|
|
58
|
+
found = false;
|
|
59
|
+
valid = false;
|
|
60
|
+
msg_data: Buffer = Buffer.allocUnsafe(0);
|
|
61
|
+
r_loc = 0;
|
|
62
|
+
finished = false;
|
|
63
|
+
msg_id_len: msg_id_len_t = new msg_id_len_t();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// https://github.com/serge-sans-paille/frozen
|
|
67
|
+
// https://www.npmjs.com/package/typed-struct
|
|
68
|
+
|
|
69
|
+
//#define default_parser { 0, 0, 0x90 }
|
|
70
|
+
//
|
|
71
|
+
//#define zero_initialized_parser_result { default_parser, false, false, 0, 0, false, { 0, 0} };
|
|
72
|
+
//
|
|
73
|
+
//#define CREATE_DEFAULT_STRUCT_BUFFER(name, size) \
|
|
74
|
+
// uint8_t name##_buffer[size]; \
|
|
75
|
+
// struct_buffer name = { default_parser, name##_buffer, size, 0, 0, false, 0, 0, 0, 0, NULL }
|
|
76
|
+
|
|
77
|
+
export interface checksum_t {
|
|
78
|
+
byte1: number;
|
|
79
|
+
byte2: number;
|
|
80
|
+
}
|
struct-frame/c_gen.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# kate: replace-tabs on; indent-width 4;
|
|
3
|
+
|
|
4
|
+
from .base import version, NamingStyleC, CamelToSnakeCase, pascalCase
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
StyleC = NamingStyleC()
|
|
8
|
+
|
|
9
|
+
c_types = {"uint8": "uint8_t",
|
|
10
|
+
"int8": "int8_t",
|
|
11
|
+
"uint16": "uint16_t",
|
|
12
|
+
"int16": "int16_t",
|
|
13
|
+
"uint32": "uint32_t",
|
|
14
|
+
"int32": "int32_t",
|
|
15
|
+
"bool": "bool",
|
|
16
|
+
"float": "float",
|
|
17
|
+
"double": "double"}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EnumCGen():
|
|
21
|
+
@staticmethod
|
|
22
|
+
def generate(field):
|
|
23
|
+
leading_comment = field.comments
|
|
24
|
+
|
|
25
|
+
result = ''
|
|
26
|
+
if leading_comment:
|
|
27
|
+
for c in leading_comment:
|
|
28
|
+
result = '%s\n' % c
|
|
29
|
+
|
|
30
|
+
enumName = '%s%s' % (pascalCase(field.package), field.name)
|
|
31
|
+
result += 'typedef enum %s' % (enumName)
|
|
32
|
+
|
|
33
|
+
result += ' {\n'
|
|
34
|
+
|
|
35
|
+
enum_length = len(field.data)
|
|
36
|
+
enum_values = []
|
|
37
|
+
for index, (d) in enumerate(field.data):
|
|
38
|
+
leading_comment = field.data[d][1]
|
|
39
|
+
|
|
40
|
+
if leading_comment:
|
|
41
|
+
for c in leading_comment:
|
|
42
|
+
enum_values.append(c)
|
|
43
|
+
|
|
44
|
+
comma = ","
|
|
45
|
+
if index == enum_length - 1:
|
|
46
|
+
# last enum member should not end with a comma
|
|
47
|
+
comma = ""
|
|
48
|
+
|
|
49
|
+
enum_value = " %s_%s = %d%s" % (CamelToSnakeCase(field.name).upper(),StyleC.enum_entry(d), field.data[d][0], comma)
|
|
50
|
+
|
|
51
|
+
enum_values.append(enum_value)
|
|
52
|
+
|
|
53
|
+
result += '\n'.join(enum_values)
|
|
54
|
+
result += '\n}'
|
|
55
|
+
|
|
56
|
+
result += ' %s;\n' % (enumName)
|
|
57
|
+
|
|
58
|
+
result += 'typedef uint8_t %s_t;' % (enumName)
|
|
59
|
+
return result
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class FieldCGen():
|
|
63
|
+
@staticmethod
|
|
64
|
+
def generate(field):
|
|
65
|
+
result = ''
|
|
66
|
+
|
|
67
|
+
var_name = field.name
|
|
68
|
+
type_name = field.fieldType
|
|
69
|
+
if type_name in c_types:
|
|
70
|
+
type_name = c_types[type_name]
|
|
71
|
+
else:
|
|
72
|
+
type_name = '%s%s' % (pascalCase(field.package), type_name)
|
|
73
|
+
if field.isEnum:
|
|
74
|
+
type_name = '%s_t' % type_name
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
result += ' %s %s%s;' % (type_name, var_name, "")
|
|
78
|
+
|
|
79
|
+
leading_comment = field.comments
|
|
80
|
+
if leading_comment:
|
|
81
|
+
for c in leading_comment:
|
|
82
|
+
result = c + "\n" + result
|
|
83
|
+
|
|
84
|
+
return result
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class MessageCGen():
|
|
88
|
+
@staticmethod
|
|
89
|
+
def generate(msg):
|
|
90
|
+
leading_comment = msg.comments
|
|
91
|
+
|
|
92
|
+
result = ''
|
|
93
|
+
if leading_comment:
|
|
94
|
+
for c in msg.comments:
|
|
95
|
+
result = '%s\n' % c
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
structName = '%s%s' % (pascalCase(msg.package), msg.name.title())
|
|
99
|
+
result += 'typedef struct %s {' % structName
|
|
100
|
+
|
|
101
|
+
result += '\n'
|
|
102
|
+
|
|
103
|
+
size = 1
|
|
104
|
+
if not msg.fields:
|
|
105
|
+
# Empty structs are not allowed in C standard.
|
|
106
|
+
# Therefore add a dummy field if an empty message occurs.
|
|
107
|
+
result += ' char dummy_field;'
|
|
108
|
+
else:
|
|
109
|
+
size = msg.size
|
|
110
|
+
|
|
111
|
+
result += '\n'.join([FieldCGen.generate(f) for key, f in msg.fields.items()])
|
|
112
|
+
result += '\n}'
|
|
113
|
+
result += ' %s;\n\n' % structName
|
|
114
|
+
|
|
115
|
+
defineName = '%s_%s' % (CamelToSnakeCase(msg.package).upper(), CamelToSnakeCase(msg.name).upper())
|
|
116
|
+
result += '#define %s_MAX_SIZE %d;\n' % (defineName, size)
|
|
117
|
+
|
|
118
|
+
if msg.id:
|
|
119
|
+
result += '#define %s_MSG_ID %d\n' % (defineName, msg.id)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
funcName = defineName.lower()
|
|
123
|
+
result += 'MESSAGE_HELPER(%s, %s, %d, %d);\n\n' % (funcName, structName,
|
|
124
|
+
size, msg.id)
|
|
125
|
+
|
|
126
|
+
return result + '\n'
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def get_initializer(msg, null_init):
|
|
130
|
+
if not msg.fields:
|
|
131
|
+
return '{0}'
|
|
132
|
+
|
|
133
|
+
parts = []
|
|
134
|
+
for field in msg.fields:
|
|
135
|
+
parts.append(field.get_initializer(null_init))
|
|
136
|
+
return '{' + ', '.join(parts) + '}'
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class FileCGen():
|
|
140
|
+
@staticmethod
|
|
141
|
+
def generate(package):
|
|
142
|
+
yield '/* Automatically generated struct frame header */\n'
|
|
143
|
+
yield '/* Generated by %s at %s. */\n\n' % (version, time.asctime())
|
|
144
|
+
|
|
145
|
+
yield '#pragma once\n'
|
|
146
|
+
yield '#pragma pack(1)\n'
|
|
147
|
+
|
|
148
|
+
yield '#include "struct_frame.h"\n'
|
|
149
|
+
|
|
150
|
+
#include additional header files if available in the future
|
|
151
|
+
|
|
152
|
+
if package.enums:
|
|
153
|
+
yield '/* Enum definitions */\n'
|
|
154
|
+
for key, enum in package.enums.items():
|
|
155
|
+
yield EnumCGen.generate(enum) + '\n\n'
|
|
156
|
+
|
|
157
|
+
if package.messages:
|
|
158
|
+
yield '/* Struct definitions */\n'
|
|
159
|
+
# Need to sort messages to make sure dependecies are properly met
|
|
160
|
+
|
|
161
|
+
for key, msg in package.sortedMessages().items():
|
|
162
|
+
yield MessageCGen.generate(msg) + '\n'
|
|
163
|
+
yield '\n'
|
|
164
|
+
|
|
165
|
+
# Add default initializers if needed
|
|
166
|
+
#if package.messages:
|
|
167
|
+
# yield '/* Initializer values for message structs */\n'
|
|
168
|
+
# for key, msg in package.messages.items():
|
|
169
|
+
# identifier = '%s_%s_init_default' % (package.name, StyleC.struct_name(msg.name))
|
|
170
|
+
# yield '#define %-40s %s\n' % (identifier, MessageCGen.get_initializer(msg, False))
|
|
171
|
+
# for key, msg in package.messages.items():
|
|
172
|
+
# identifier = '%s_%s_init_zero' % (package.name, StyleC.struct_name(msg.name))
|
|
173
|
+
# yield '#define %-40s %s\n' % (identifier, msg.get_initializer(True))
|
|
174
|
+
# yield '\n'
|
|
175
|
+
|
|
176
|
+
if package.messages:
|
|
177
|
+
yield 'uint8_t get_message_length(uint8_t msg_id){\n switch (msg_id)\n {\n'
|
|
178
|
+
for key, msg in package.sortedMessages().items():
|
|
179
|
+
name = '%s_%s' % (CamelToSnakeCase(msg.package).upper(), CamelToSnakeCase(msg.name).upper())
|
|
180
|
+
yield ' case %s_MSG_ID: return %s_MAX_SIZE;\n' % (name, name)
|
|
181
|
+
|
|
182
|
+
yield ' default: break;\n } return 0;\n}'
|
|
183
|
+
yield '\n'
|
|
184
|
+
|
struct-frame/ts_gen.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# kate: replace-tabs on; indent-width 4;
|
|
3
|
+
|
|
4
|
+
from .base import version, NamingStyleC
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
StyleC = NamingStyleC()
|
|
8
|
+
|
|
9
|
+
ts_types = {
|
|
10
|
+
"int8_t": 'Int8',
|
|
11
|
+
"uint8_t": 'UInt8',
|
|
12
|
+
"int16_t": 'Int16LE',
|
|
13
|
+
"uint16_t": 'UInt16LE',
|
|
14
|
+
"bool": 'Boolean8',
|
|
15
|
+
"double": 'Float64LE',
|
|
16
|
+
"float": 'Float32LE',
|
|
17
|
+
"int32_t": 'Int32LE',
|
|
18
|
+
"uint32_t": 'UInt32LE',
|
|
19
|
+
"uint64_t": 'BigInt64LE',
|
|
20
|
+
"int64_t": 'BigUInt64LE',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class EnumTsGen():
|
|
24
|
+
@staticmethod
|
|
25
|
+
def generate(field, packageName):
|
|
26
|
+
leading_comment = field.comments
|
|
27
|
+
result = ''
|
|
28
|
+
if leading_comment:
|
|
29
|
+
for c in leading_comment:
|
|
30
|
+
result = '%s\n' % c
|
|
31
|
+
|
|
32
|
+
result += 'export enum %s%s' % (packageName, StyleC.enum_name(field.name))
|
|
33
|
+
|
|
34
|
+
result += ' {\n'
|
|
35
|
+
|
|
36
|
+
enum_length = len(field.data)
|
|
37
|
+
enum_values = []
|
|
38
|
+
for index, (d) in enumerate(field.data):
|
|
39
|
+
leading_comment = field.data[d][1]
|
|
40
|
+
|
|
41
|
+
if leading_comment:
|
|
42
|
+
for c in leading_comment:
|
|
43
|
+
enum_values.append(c)
|
|
44
|
+
|
|
45
|
+
comma = ","
|
|
46
|
+
if index == enum_length - 1:
|
|
47
|
+
# last enum member should not end with a comma
|
|
48
|
+
comma = ""
|
|
49
|
+
|
|
50
|
+
enum_value = " %s = %d%s" % (
|
|
51
|
+
StyleC.enum_entry(d), field.data[d][0], comma)
|
|
52
|
+
|
|
53
|
+
enum_values.append(enum_value)
|
|
54
|
+
|
|
55
|
+
result += '\n'.join(enum_values)
|
|
56
|
+
result += '\n}'
|
|
57
|
+
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class FieldTsGen():
|
|
62
|
+
@staticmethod
|
|
63
|
+
def generate(field, packageName):
|
|
64
|
+
result = ''
|
|
65
|
+
isEnum = False
|
|
66
|
+
# isEnum = field.pbtype in ('ENUM', 'UENUM')
|
|
67
|
+
var_name = StyleC.var_name(field.name)
|
|
68
|
+
type_name = field.fieldType
|
|
69
|
+
if type_name in ts_types:
|
|
70
|
+
type_name = ts_types[type_name]
|
|
71
|
+
else:
|
|
72
|
+
type_name = '%s_%s' % (packageName, StyleC.struct_name(type_name))
|
|
73
|
+
|
|
74
|
+
if isEnum:
|
|
75
|
+
result += ' .UInt8(\'%s\', typed<%s>())' % (var_name, type_name)
|
|
76
|
+
else:
|
|
77
|
+
result += ' .%s(\'%s\')' % (type_name, var_name)
|
|
78
|
+
|
|
79
|
+
leading_comment = field.comments
|
|
80
|
+
if leading_comment:
|
|
81
|
+
for c in leading_comment:
|
|
82
|
+
result = c + "\n" + result
|
|
83
|
+
|
|
84
|
+
return result
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
# Generation of messages (structures)
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class MessageTsGen():
|
|
93
|
+
@staticmethod
|
|
94
|
+
def generate(msg, packageName):
|
|
95
|
+
leading_comment = msg.comments
|
|
96
|
+
|
|
97
|
+
result = ''
|
|
98
|
+
if leading_comment:
|
|
99
|
+
for c in msg.comments:
|
|
100
|
+
result = '%s\n' % c
|
|
101
|
+
|
|
102
|
+
struct_name = '%s_%s' % (packageName, StyleC.type_name(msg.name))
|
|
103
|
+
result += 'export const %s = new typed_struct.Struct(\'%s\') ' % (
|
|
104
|
+
struct_name, struct_name)
|
|
105
|
+
|
|
106
|
+
result += '\n'
|
|
107
|
+
|
|
108
|
+
size = 1
|
|
109
|
+
if not msg.fields:
|
|
110
|
+
# Empty structs are not allowed in C standard.
|
|
111
|
+
# Therefore add a dummy field if an empty message occurs.
|
|
112
|
+
result += ' .UInt8(\'dummy_field\');'
|
|
113
|
+
else:
|
|
114
|
+
size = msg.size
|
|
115
|
+
|
|
116
|
+
result += '\n'.join([FieldTsGen.generate(f, packageName) for key, f in msg.fields.items()])
|
|
117
|
+
result += '\n .compile();\n\n'
|
|
118
|
+
|
|
119
|
+
result += 'export const %s_max_size = %d;\n' % (struct_name, size)
|
|
120
|
+
|
|
121
|
+
if msg.id:
|
|
122
|
+
result += 'export const %s_msgid = %d\n' % (msg.name, msg.id)
|
|
123
|
+
|
|
124
|
+
result += 'export function %s_encode(buffer: struct_frame_buffer, msg: any) {\n' % (
|
|
125
|
+
msg.name)
|
|
126
|
+
result += ' msg_encode(buffer, msg, %s_msgid)\n}\n' % (msg.name)
|
|
127
|
+
|
|
128
|
+
result += 'export function %s_reserve(buffer: struct_frame_buffer) {\n' % (
|
|
129
|
+
msg.name)
|
|
130
|
+
result += ' const msg_buffer = msg_reserve(buffer, %s_msgid, %s_max_size);\n' % (
|
|
131
|
+
msg.name, msg.name)
|
|
132
|
+
result += ' if (msg_buffer){\n'
|
|
133
|
+
result += ' return new %s(msg_buffer)\n }\n return;\n}\n' % (
|
|
134
|
+
msg.name)
|
|
135
|
+
|
|
136
|
+
result += 'export function %s_finish(buffer: struct_frame_buffer) {\n' % (
|
|
137
|
+
msg.name)
|
|
138
|
+
result += ' msg_finish(buffer);\n}\n'
|
|
139
|
+
return result + '\n'
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def get_initializer(msg, null_init):
|
|
143
|
+
if not msg.fields:
|
|
144
|
+
return '{0}'
|
|
145
|
+
|
|
146
|
+
parts = []
|
|
147
|
+
for field in msg.fields:
|
|
148
|
+
parts.append(field.get_initializer(null_init))
|
|
149
|
+
return '{' + ', '.join(parts) + '}'
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class FileTsGen():
|
|
153
|
+
@staticmethod
|
|
154
|
+
def generate(package):
|
|
155
|
+
yield '/* Automatically generated struct frame header */\n'
|
|
156
|
+
yield '/* Generated by %s at %s. */\n\n' % (version, time.asctime())
|
|
157
|
+
|
|
158
|
+
yield 'const typed_struct = require(\'typed-struct\')\n'
|
|
159
|
+
yield 'const ExtractType = typeof typed_struct.ExtractType;\n'
|
|
160
|
+
yield 'const type = typeof typed_struct.ExtractType;\n\n'
|
|
161
|
+
|
|
162
|
+
yield "import { struct_frame_buffer } from './struct_frame_types';\n"
|
|
163
|
+
|
|
164
|
+
yield "import { msg_encode, msg_reserve, msg_finish } from './struct_frame';\n\n"
|
|
165
|
+
|
|
166
|
+
#include additional header files here if available in the future
|
|
167
|
+
|
|
168
|
+
if package.enums:
|
|
169
|
+
yield '/* Enum definitions */\n'
|
|
170
|
+
for key, enum in package.enums.items():
|
|
171
|
+
yield EnumTsGen.generate(enum, package.name) + '\n\n'
|
|
172
|
+
|
|
173
|
+
if package.messages:
|
|
174
|
+
yield '/* Struct definitions */\n'
|
|
175
|
+
for key, msg in package.sortedMessages().items():
|
|
176
|
+
yield MessageTsGen.generate(msg, package.name) + '\n'
|
|
177
|
+
yield '\n'
|
|
178
|
+
|
|
179
|
+
if package.messages:
|
|
180
|
+
yield 'export function get_message_length(msg_id : number){\n switch (msg_id)\n {\n'
|
|
181
|
+
for key, msg in package.sortedMessages().items():
|
|
182
|
+
name = StyleC.type_name(msg.name)
|
|
183
|
+
yield ' case %s_msgid: return %s_max_size;\n' % (name, name)
|
|
184
|
+
|
|
185
|
+
yield ' default: break;\n } return 0;\n}'
|
|
186
|
+
yield '\n'
|