aboutsummaryrefslogtreecommitdiff
path: root/testing/yup-comm/protocol/data.c
diff options
context:
space:
mode:
authorJan Sucan <sucanjan@fit.cvut.cz>2019-06-04 14:34:27 +0200
committerJan Sucan <sucanjan@fit.cvut.cz>2019-06-04 14:34:27 +0200
commitdc8703206e3f0f69605c56d0e1127f7e17f3476a (patch)
tree166823a741dc420c10d54250cb53d1e3a6b74faf /testing/yup-comm/protocol/data.c
Initial commit
Diffstat (limited to 'testing/yup-comm/protocol/data.c')
-rw-r--r--testing/yup-comm/protocol/data.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/testing/yup-comm/protocol/data.c b/testing/yup-comm/protocol/data.c
new file mode 100644
index 0000000..c93ef6d
--- /dev/null
+++ b/testing/yup-comm/protocol/data.c
@@ -0,0 +1,312 @@
+/* Author: Jan Sucan */
+
+#include "data.h"
+#include "frame.h"
+
+#include <stdbool.h>
+
+#define DATA_PROTOCOL_VERSION 0x0
+
+#define DATA_ACK_RETRY_COUNT 4
+
+
+yup_retcode_t data_is_data_frame (const frame_t * const frame);
+yup_retcode_t data_is_starting_frame (const frame_t * const frame);
+yup_retcode_t data_is_ack_for_frame (const frame_t * const ack, const frame_t * const frame);
+void data_send_ack (uint16_t relation_id, uint16_t seq_number);
+void data_send_rej_for_frame(const frame_t * const frame, uint8_t err_code);
+
+
+yup_retcode_t
+data_is_data_frame(const frame_t * const frame)
+{
+ yup_retcode_t yr = DATA_OK;
+
+ if (frame_get_data_length(frame) == 0) {
+ yr = DATA_DATA_HAS_NO_PAYLOAD;
+ } else if (frame_is_flag_ack_set(frame)) {
+ yr = DATA_DATA_HAS_REJ_FLAG;
+ } else if (frame_is_flag_rej_set(frame)) {
+ yr = DATA_DATA_HAS_REJ_FLAG;
+ }
+
+ return yr;
+}
+
+yup_retcode_t
+data_is_starting_frame(const frame_t * const frame)
+{
+ yup_retcode_t yr = DATA_OK;
+
+ if ((yr = data_is_data_frame(frame)) != DATA_OK) {
+ ;
+ } else if (frame_get_seq_number(frame) != 0) {
+ yr = DATA_STARTING_HAS_NONZERO_SEQ_NUMBER;
+ }
+
+ return yr;
+}
+
+yup_retcode_t
+data_is_ack_for_frame(const frame_t * const ack, const frame_t * const frame)
+{
+ yup_retcode_t yr = DATA_OK;
+
+ if (frame_get_data_length(ack) != 0) {
+ yr = DATA_ACK_HAS_PAYLOAD;
+ } else if (!frame_is_flag_ack_set(ack)) {
+ yr = DATA_ACK_HAS_NOT_ACK_FLAG;
+ } else if (frame_is_flag_lor_set(ack)) {
+ yr = DATA_ACK_HAS_LOR_FLAG;
+ } else if (frame_is_flag_rej_set(ack)) {
+ yr = DATA_ACK_HAS_REJ_FLAG;
+ } else if (frame_get_relation_id(ack) != frame_get_relation_id(frame)) {
+ yr = DATA_ACK_RELATION_ID_MISMATCH;
+ } else if (frame_get_seq_number(ack) != frame_get_seq_number(frame)) {
+ yr = DATA_ACK_SEQ_NUMBER_MISMATCH;
+ }
+
+ return yr;
+}
+
+void
+data_send_ack(uint16_t relation_id, uint16_t seq_number)
+{
+ frame_t ack;
+
+ frame_init(&ack, DATA_PROTOCOL_VERSION);
+ frame_set_flag_ack(&ack);
+ frame_set_relation_id(&ack, relation_id);
+ frame_set_seq_number(&ack, seq_number);
+
+ frame_send(&ack);
+}
+
+void
+data_send_rej_for_frame(const frame_t * const frame, uint8_t err_code)
+{
+ frame_t rej;
+
+ frame_init(&rej, frame_get_protocol_version(frame));
+ frame_set_flag_rej(&rej);
+ frame_set_relation_id(&rej, frame_get_relation_id(frame));
+ frame_set_seq_number(&rej, frame_get_seq_number(frame));
+ frame_set_data(&rej, &err_code, sizeof(err_code));
+
+ frame_send(&rej);
+}
+
+
+typedef enum data_receive_automaton_states {
+ DATA_R_WAITING_FOR_STARTING_FRAME,
+ DATA_R_START_RELATION,
+ DATA_R_SAVE,
+ DATA_R_ACK_LAST_FRAME,
+ DATA_R_WAITING_NEXT_DATA_FRAME,
+ DATA_R_END
+} data_receive_automaton_state_t;
+
+typedef enum data_send_automaton_states {
+ DATA_S_START_RELATION,
+ DATA_S_PEPARE_DATA_FRAME,
+ DATA_S_SEND_DATA_FRAME,
+ DATA_S_WAIT_ACK,
+ DATA_S_END
+} data_send_automaton_state_t;
+
+yup_retcode_t
+data_receive(uint8_t * const buf, size_t bufsize, size_t * const bytes_read)
+{
+ uint16_t relation_id = 0;
+ uint16_t sequence_num = 0;
+ size_t data_index = 0;
+ data_receive_automaton_state_t state = DATA_R_WAITING_FOR_STARTING_FRAME;
+
+ // Kontrola argumentov
+ if ((buf == NULL) || (bufsize == 0)) {
+ return DATA_ERROR;
+ }
+
+ frame_t f;
+
+ while (state != DATA_R_END) {
+
+ yup_retcode_t r;
+ uint8_t l;
+
+ switch (state) {
+ case DATA_R_WAITING_FOR_STARTING_FRAME:
+ // Na prijem startovacieho ramca sa bude cakat donekonecna
+ r = frame_receive(&f, DATA_PROTOCOL_VERSION);
+ if (r != FRAME_OK) {
+ // Chyba pri prijme ramca, posle sa naspat REJ so spravou o chybe
+ data_send_rej_for_frame(&f, r);
+ } else if ((r = data_is_starting_frame(&f)) == DATA_OK) {
+ // Mame platny startovaci ramec, zahajime relaciu
+ state = DATA_R_START_RELATION;
+ }
+ break;
+
+ case DATA_R_START_RELATION:
+ // Od startovacieho ramca zacne plynut cas pre timeout relacie
+ relation_id = frame_get_relation_id(&f);
+ sequence_num = frame_get_seq_number(&f);
+ data_index = 0;
+ state = DATA_R_SAVE;
+ break;
+
+ case DATA_R_SAVE:
+ if ((bufsize - data_index) < frame_get_data_length(&f)) {
+ // Data sa nezmestia do bufferu
+ // Pocka sa na dalsi paket, co sa bude hodit
+ state = DATA_R_WAITING_NEXT_DATA_FRAME;
+ } else {
+ // Data sa ulozia
+ frame_get_data(&f, buf + data_index, &l);
+ // Ulozia sa informacie o naposledy prijatom ramci
+ sequence_num = frame_get_seq_number(&f);
+ data_index += frame_get_data_length(&f);
+ // Ramec sa potvrdi
+ state = DATA_R_ACK_LAST_FRAME;
+ }
+ break;
+
+ case DATA_R_ACK_LAST_FRAME:
+ // Potvrdime naposledy prijaty ramec
+ data_send_ack(relation_id, sequence_num);
+ // Na posledny ramec relacie sa uz neocakava odpoved
+ // Je bezpecne pristupovat k ramcu f, ak ma nastaveny LOR nemoze byt prepisany dalsim ramcom
+ if (frame_is_flag_lor_set(&f)) {
+ state = DATA_R_END;
+ } else {
+ // Pockame na prijem dalsieho datoveho ramca
+ state = DATA_R_WAITING_NEXT_DATA_FRAME;
+ }
+ break;
+
+ case DATA_R_WAITING_NEXT_DATA_FRAME:
+ r = frame_receive(&f, DATA_PROTOCOL_VERSION);
+ if (r == FRAME_TIMEOUT) {
+ // Pri timeoute prijmu prijmu sa nebude opakovane posielat ACK
+ // Timeout relacie vyprsal
+ state = DATA_R_END;
+ } else if (r != FRAME_OK) {
+ // Chyba pri prijme datoveho ramca
+ data_send_rej_for_frame(&f, r);
+ } else if ((r = data_is_data_frame(&f)) != DATA_OK) {
+ // Datovy ramec prijaty v poriadku, ale s nespavnym obsahom
+ data_send_rej_for_frame(&f, r);
+ } else if ((frame_get_seq_number(&f) == 0) && (frame_get_relation_id(&f) != relation_id)) {
+ // Jedna sa o zaciatok novej relacie
+ state = DATA_R_START_RELATION;
+ } else if (frame_get_relation_id(&f) != relation_id) {
+ // Obycajny datovy ramec, ale v odlisnej relacii
+ data_send_rej_for_frame(&f, (uint8_t) DATA_DATA_RELATION_ID_MISMATCH);
+ } else if (frame_get_seq_number(&f) != (sequence_num + 1)) {
+ // Obycajny datovy ramec v spravnej relacii, ale nenavazujuci do sekvencie
+ data_send_rej_for_frame(&f, (uint8_t) DATA_DATA_SEQ_NUMBER_MISMATCH);
+ } else {
+ // Spravny navazujuci datovy ramac, ulozia sa jeho data
+ state = DATA_R_SAVE;
+ }
+ break;
+
+ case DATA_R_END:
+ // Sem sa nedostaneme, len sa umlci prekladac, ze stav nie je osetreny vo switch
+ break;
+ }
+
+ }
+
+ // Pocet skutocne prijatych bajtov
+ *bytes_read = data_index;
+
+ return DATA_OK;
+}
+
+yup_retcode_t
+data_send(const uint8_t * const buf, size_t bufsize)
+{
+ static uint16_t relation_id = 0;
+
+ uint16_t sequence_num = 0;
+ size_t data_index = 0;
+ size_t ack_retry_count = DATA_ACK_RETRY_COUNT;
+
+ data_send_automaton_state_t state = DATA_S_START_RELATION;
+
+ // Kontrola argumentov
+ if ((buf == NULL) || (bufsize == 0)) {
+ return DATA_ERROR;
+ }
+
+ frame_t f, a;
+
+ while (state != DATA_S_END) {
+
+ yup_retcode_t r;
+
+ switch (state) {
+ case DATA_S_START_RELATION:
+ ++relation_id;
+ sequence_num = 0;
+ data_index = 0;
+ ack_retry_count = DATA_ACK_RETRY_COUNT;
+ state = DATA_S_PEPARE_DATA_FRAME;
+ break;
+
+ case DATA_S_PEPARE_DATA_FRAME:
+ // Pripravi sa ramec s datami
+ frame_init(&f, DATA_PROTOCOL_VERSION);
+ frame_set_relation_id(&f, relation_id);
+ frame_set_seq_number(&f, sequence_num);
+ size_t len = ((bufsize - data_index) >= FRAME_MAX_DATA_BYTES) ? FRAME_MAX_DATA_BYTES : (bufsize - data_index);
+ frame_set_data(&f, buf + data_index, len);
+ data_index += len;
+ // Je to posledny ramec?
+ if (data_index >= bufsize) {
+ frame_set_flag_lor(&f);
+ }
+ state = DATA_S_SEND_DATA_FRAME;
+ break;
+
+ case DATA_S_SEND_DATA_FRAME:
+ // Odosle sa pripraveny ramec
+ frame_send(&f);
+ state = DATA_S_WAIT_ACK;
+ break;
+
+ case DATA_S_WAIT_ACK:
+ r = frame_receive(&a, DATA_PROTOCOL_VERSION);
+ if (r == FRAME_TIMEOUT) {
+ // Neprijal sa ACK, znovu sa odoslu pipravene data
+ if (ack_retry_count-- > 0) {
+ state = DATA_S_SEND_DATA_FRAME;
+ } else {
+ // Bol dosiahnuty maximalny pocet pokusov o znozvu odoslanie dat
+ return DATA_ERROR;
+ }
+ } else if (r != FRAME_OK) {
+ // Chyba pri prijme ACK ramca
+ data_send_rej_for_frame(&a, r);
+ } else if ((r = data_is_ack_for_frame(&a, &f)) != DATA_OK) {
+ // ACK ramec prijaty v poriadku, ale s nespravnym obshom
+ data_send_rej_for_frame(&a, r);
+ } else if (frame_is_flag_lor_set(&f)) {
+ // Datovy ramec, ktory som odoslal, bol posledny
+ state = DATA_S_END;
+ } else {
+ // Naposledy vyslany datovy ramec bol potvrdeny protistranou, odosle sa dalsi
+ sequence_num++;
+ state = DATA_S_PEPARE_DATA_FRAME;
+ }
+ break;
+
+ case DATA_S_END:
+ // Sem sa nedostaneme, len sa umlci prekladac, ze stav nie je osetreny vo switch
+ break;
+ }
+ }
+
+ return DATA_OK;
+}