1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "bin.h"
#define WS_PROTOCOL_CMD_MAX_ARGUMENTS (1)
#define WS_PROTOCOL_CMD_BUFFER_LEN (40)
#define WS_PROTOCOL_CMD_AMOUNT (1)
#define WS_PROTOCOL_C_NEWLINE (0x0a)
#define WS_PROTOCOL_C_SPACE (0x20)
#define WS_PROTOCOL_C_NULL (0x00)
/**
* @brief parsed cmd struct, holds arguments similar to argc and argv provided
* to `int main()`
*/
typedef struct {
int argc; /** argument count */
char* argv[]; /** argument array, null terminated strings */
} ws_s_protocol_parsed_cmd;
/**
* @brief holds parser state variables for `ws_protocol_parse_byte` function.
* each incoming tcp request should get it's own parser 'instance'
*/
typedef struct {
ws_s_protocol_parsed_cmd* target; /** parsed cmd reference */
bool valid; /** command still valid flag */
char* cmd; /** raw cmd */
uint16_t cmd_len; /** raw cmd string length */
uint16_t arg_len; /** amount of arguments */
uint16_t args_len[]; /** array of argument lengths */
} ws_s_protocol_parser_state;
/** @brief return values for command handlers */
typedef enum {
WS_PROTOCOL_CMD_RETURN_OK = 0,
WS_PROTOCOL_CMD_RETURN_ERROR = 1,
} ws_e_protocol_cmd_return_value;
/** @brief request response data struct */
typedef struct {
ws_e_protocol_cmd_return_value success;
ws_s_bin* msg;
} ws_s_protocol_response;
/**
* @brief allocate parser struct
*
* @return pointer to newly allocated struct
*/
ws_s_protocol_parser_state* ws_protocol_parser_alloc();
/** @brief deallocate parser struct, automatically frees all child pointers */
void ws_protocol_parser_free(ws_s_protocol_parser_state* state);
/**
* @brief initialize ws_s_protocol_parsed_cmd struct pointer of
* ws_s_protocol_parser_state (internal only)
*/
void ws_protocol_cmd_init(ws_s_protocol_parser_state* state);
/** @brief deallocate ws_s_protocol_parsed_cmd struct pointer (internal only) */
void ws_protocol_cmd_free(ws_s_protocol_parsed_cmd* cmd);
/**
* @brief parse incoming data byte by byte until a finished command is detected
*
* @remark [server]
*
* @param state parser state object, each incoming request should have it's own parser state
* @param input input byte
*/
void ws_protocol_parse_byte(ws_s_protocol_parser_state* state, char input);
/**
* @brief parse incoming data chunk
*
* @remark [server]
*
* @param state parser state object, each incoming request should have it's own parser state
* @param input input byte array
* @param length input byte array length
*/
void ws_protocol_parse_bytes(ws_s_protocol_parser_state* state, char* input, unsigned int length);
/**
* @brief handle complete command
*
* this function gets called when ws_protocol_parse_byte has detected a
* finished command. this function decides which command handler gets called,
* given that argv[0] contains a valid command. command argument parsing is
* handled by the command handler function.
*
* @remark [server]
*
* @return response
*
* @param parsed_cmd cmd parsed into ws_s_protocol_parsed_cmd struct
*/
ws_s_protocol_response* ws_protocol_parse_finished(ws_s_protocol_parsed_cmd* parsed_cmd);
/**
* @brief create a `last-records` request command
* @remark [client]
* @return ws_s_bin containing the command string
*/
ws_s_bin* ws_protocol_req_last_records(unsigned int record_amount);
/**
* @brief `last-records` response handler
*
* @remark [server]
*
* gets fired when the weather station receives a complete `last-records`
* command, and returns the response string
*
* @param parsed_cmd complete parsed command from ws_protocol_parse_*
* @param response response struct with uninitialized pointer to msg
*/
void ws_protocol_res_last_records(ws_s_protocol_parsed_cmd* parsed_cmd, ws_s_protocol_response* response);
/**
* @brief data sender wrapper
*
* this function should be implemented in the source files of each target
* platform, as the send interface will be different on desktop and on the
* stm32.
*/
void ws_protocol_send_data(ws_s_bin* data);
/** @brief cmd codes (used to call handlers) */
typedef enum {
WS_PROTOCOL_CMD_UNKNOWN = -1,
WS_PROTOCOL_CMD_LAST_RECORDS = 0,
} ws_e_protocol_cmd;
/** @brief response handlers, called when a command is parsed */
static void (*g_ws_protocol_res_handlers[WS_PROTOCOL_CMD_AMOUNT])(ws_s_protocol_parsed_cmd*, ws_s_protocol_response*) = {
[WS_PROTOCOL_CMD_LAST_RECORDS] = &ws_protocol_res_last_records
};
|