Compare commits

...

8 Commits

25 changed files with 1470 additions and 17 deletions

17
.gitignore vendored
View File

@ -1,14 +1,3 @@
# ---> CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json
.cache/
build/
*.zip

18
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
// "defines": ["DO_UNIT_TESTS=1"],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-clang-x64",
"configurationProvider": "ms-vscode.cmake-tools",
"compileCommands": "${workspaceFolder}/build/compile_commands.json"
}
],
"version": 4
}

30
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,30 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/test-kod-bez",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
],
"preLaunchTask": "CMake: build"
}
]
}

32
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,32 @@
{
"files.associations": {
"worker_type.h": "c",
"main.h": "c",
"stdio.h": "c",
"stdint.h": "c",
"stdbool.h": "c",
"unistd.h": "c",
"stdlib.h": "c",
"time.h": "c",
"hdlc_type.h": "c",
"hdlc.h": "c",
"fifo_type.h": "c",
"chrono": "c",
"span": "c",
"worker.h": "c",
"fifo.h": "c",
"string.h": "c",
"*.ipp": "c",
"cstring": "c",
"stddef.h": "c",
"coroutine": "c",
"filo.h": "c",
"filo_type.h": "c",
"array": "c",
"string": "c",
"string_view": "c",
"ranges": "c",
"compare": "c",
"cstdint": "c"
}
}

40
CMakeLists.txt Normal file
View File

@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 4.1 FATAL_ERROR)
set(PROJECT_NAME test-kod-bez)
set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
project(${PROJECT_NAME} LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# set(CMAKE_BUILD_TYPE Release)
add_executable(${PROJECT_NAME} ${SOURCE_DIR}/main.c)
set(HEADERS
${SOURCE_DIR}
${SOURCE_DIR}/hdlc
${SOURCE_DIR}/worker
${SOURCE_DIR}/fifo
)
set(SOURCES
${SOURCE_DIR}/main.c
${SOURCE_DIR}/tools.c
${SOURCE_DIR}/hdlc/hdlc.c
${SOURCE_DIR}/worker/worker.c
${SOURCE_DIR}/worker/worker_tools.c
${SOURCE_DIR}/worker/worker_master.c
${SOURCE_DIR}/worker/worker_slave.c
${SOURCE_DIR}/fifo/fifo.c
)
target_include_directories(${PROJECT_NAME} PRIVATE ${HEADERS})
# target_compile_definitions(${PROJECT_NAME} PUBLIC )
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
target_compile_options(${PROJECT_NAME} PRIVATE -O0 -Wall -Wextra)
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "bin")

View File

@ -1,3 +0,0 @@
# test-kod-bez
Симулятор общения по протоколу HLDC

90
readme.md Normal file
View File

@ -0,0 +1,90 @@
# Тестовое задание
## Описание
Программа демонстрирует взаимодействие клиента с сервером посредством протокола HDLC
## Структура проекта
- **fifo** - исходные коды для работы с fifo
- fifo_type.h - структуры и типы для работы с fifo
- fifo.h
- fifo.c
- **hdlc** - исходные коды для работы с hdlc
- hdlc_type.h - структуры и типы для работы с hdlc
- hdlc.h
- hdlc.c
- main.h
- main.c
- tools.c - вспомагательные функции
- worker - исходные коды обработчиков (хостов)
- worker.h
- worker.c
- worker_master.c - файл с ведущим хостом
- worker_slave.c - файл с ведомым хостом
## Задача
Необходимо разработать программу на языке C без использования сторонних
библиотек и наработок (ChatGPT, исходники с github и т.п.) для реализации
её основных функций. Для реализации вспомогательных функций типа ввода-вывода,
расчёта CRC и т.п. можно использовать библиотечные функции или исходные
тексты библиотек с github или аналогичного ресурса.
Программа должна реализовывать модель системы, состоящей из двух узлов
(ведущего и ведомого), взаимодействующих друг с другом по протоколу
HDLC (https://ru.wikipedia.org/wiki/HDLC) через два байтовых FIFO размером 8 байт.
Один блок FIFO должен обеспечивать передачу байт данных от ведущего к ведомому,
а другой в обратном направлении. Программная реализация FIFO должна обеспечивать
возможность записи и чтения одного байта в FIFO, а также проверку его состояния:
FIFO пуст или FIFO полон. Вся логика работы программы должна быть реализована
в виде общего цикла в функции main (big loop OS) и демонстрировать отправку
ведущим узлом команды в направлении ведомого, ожидание получения ответа от ведомого,
получение и обработку ответа. При этом на экран в текстовом виде должны
выводиться сообщения, позволяющие понять что сейчас происходит и в каком из
узлов моделируемой системы.
Адреса на виртуальной шине HDLC можно выбрать константными.
Управляющее поле пакета должно обеспечивать выбор команды, которая будет
выполняться на стороне ведомого устройства. Размер информационного поля пакета должен
быть не менее 16 байт. При реализации обработки команды на стороне
ведомого не требуется реализовывать какую-то сложную обработку, достаточно например
поменять порядок байт в исходной полученной команде или что-то вроде этого.
Сразу уточню, что по условиям задачи посылка от ведущего к ведомому целиком
не влезет в FIFO между ними. Поэтому по сути необходимо будет реализовать работу в
асинхронном режиме записи в FIFO с опросом его состояния и возвратом управления
в главный цикл при невозможности чтения или записи из/в FIFO.
При оценке будет учитываться как логика работы, так и стиль написания
(имена функций и переменных должны быть осмысленными, комментарии должны
пояснять ключевые решения в коде и т.п.).
Программа должна компилироваться типовым компилятором и не должна требовать
наличия нестандартных библиотек (для реализации достаточно stdio, stdlib и т.п.
стандартных библиотек С).
Апдейт
Протокол нужно реализовать как транспорт для своих тестовых команд. Поэтому в нём должны быть реализованы только самые базовые вещи: фрейминг, проверка контрольных сумм, байт-стаффинг и т.п. Минимальная реализация.
Никаких множественных буферов пакетов не требуется, достаточно одного буфера на приём и одного буфера на передачу на каждом из участников обмена. Ведущий всегда отправляет один пакет данных с командой и параметрами, а далее ждёт получения ответа на свою команду от ведомого. Аналогично и у ведомого - он ждёт получения команды от ведущего, а дальше отправляет ответ ведущему и пока не отправил новые запросы от ведущего не обрабатывает. Никаких очередей команд. FIFO, указанный в задании, используется для эмуляции канала между ведущим и ведомым устройствами, т.к. как RX и TX FIFO UART. В канале предполагается наличие контроля потока и блокировка передачи при заполнении FIFO, т.е. если FIFO оказалось забито, то отправитель данных будет ждать пока оно освободится.
## Функции и их параметры
### worker
- *int WORK_execute(int (*callback)(workerHost_s*), workerHost_s *worker);*
- int WORK_preparePackage(workerHost_s *worker);
- int WORK_sendPackage(workerHost_s *worker);
- int WORK_waitPackage(workerHost_s *worker);
- int WORK_receivePackage(workerHost_s *worker);
- int WORK_checkPackage(workerHost_s *worker);
- int WORK_parsePackage(workerHost_s *worker);
- int WORK_fillBuffer8b(uint8_t *buffer, size_t size);
// tools
int WORK_printBlocks(workerHost_s *worker, uint8_t mode);
int WORK_testFill(uint8_t* buffer, size_t bufferSize);
### tools
- TOOL_workerPrintBlocks
- hdlcHost_s *worker - указатель на машину
- uint8_t mode - режим
- 0 - не выводить ничего
- 1 - вывести содержимое пакета
- 3 - вывести содержимое буфера

93
src/fifo/fifo.c Normal file
View File

@ -0,0 +1,93 @@
#include "fifo.h"
#include "stdio.h"
// if (FIFO_get(fifo, byte) == 0) {
// OK
// } else {
// FIFO ERROR
// }
// Инициализация fifo
int FIFO_init(fifo_s* fifo) {
if (fifo != NULL) {
for (size_t i = 0; i < FIFO_DATA_SIZE; ++i) {
fifo->data[i] = 0;
}
fifo->head = 0;
fifo->tail = 0;
fifo->count = 0;
fifo->state = FIFOS_empty;
fifo->isInitialized = 1;
if (FIFO_isUsable(fifo) == 0) {
return 0;
}
}
return -1;
}
// Заполнение fifo
int FIFO_put(fifo_s* fifo, uint8_t data) {
if (FIFO_isUsable(fifo) == 0) { // Не заполнено
if ((fifo->state == FIFOS_empty) || (fifo->state == FIFOS_ready)) {
fifo->data[fifo->tail] = data;
fifo->tail = (fifo->tail + 1) % FIFO_DATA_SIZE;
if (fifo->count < FIFO_DATA_SIZE - 1) {
fifo->count++;
fifo->state = FIFOS_ready;
} else {
fifo->count = FIFO_DATA_SIZE - 1;
fifo->state = FIFOS_full;
}
return 0;
}
}
return -1;
}
// Получение из fifo
int FIFO_get(fifo_s* fifo, uint8_t *data) {
if (FIFO_isUsable(fifo) == 0) {
if ((fifo->state == FIFOS_full) || (fifo->state == FIFOS_ready)) {
*data = fifo->data[fifo->head];
fifo->data[fifo->head] = 0;
fifo->head = (fifo->head + 1) % FIFO_DATA_SIZE;
if (fifo->count > 1) {
fifo->count--;
fifo->state = FIFOS_ready;
} else {
fifo->count = 0;
fifo->state = FIFOS_empty;
return 1;
}
return 0;
}
return 1;
}
return -1;
}
int FIFO_print(fifo_s* fifo) {
if (FIFO_isUsable(fifo) == 0) {
printf("FIFO: ");
for (size_t i = 0; i < FIFO_DATA_SIZE; i++) {
printf("%d", fifo->data[i]);
}
printf("\n");
return 0;
}
return -1;
}
// Проверка доступности fifo
int FIFO_isUsable(fifo_s* fifo) {
if (fifo != NULL) {
if (fifo->isInitialized == 1) {
return 0;
}
}
return -1;
}

29
src/fifo/fifo.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#ifndef FIFO_H
#define FIFO_H
#include "fifo_type.h"
#include <stddef.h>
// Размер FIFO
#define FIFO_DATA_SIZE 8
// Структура FIFO
typedef struct {
uint8_t data[FIFO_DATA_SIZE];
uint8_t head;
uint8_t tail;
uint8_t count;
fifoState_e state;
uint8_t isInitialized;
} fifo_s;
// Функции FIFO
int FIFO_init(fifo_s* fifo);
int FIFO_put(fifo_s* fifo, uint8_t data);
int FIFO_get(fifo_s* fifo, uint8_t *data);
int FIFO_print(fifo_s* fifo);
int FIFO_isUsable(fifo_s* fifo);
#endif // FIFO_H

16
src/fifo/fifo_type.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#ifndef FIFO_TYPE_H
#define FIFO_TYPE_H
#include <stdint.h>
// Структура состояний FIFO
typedef enum {
FIFOS_empty = 0,
FIFOS_ready,
FIFOS_full,
FIFOS_error
} fifoState_e;
#endif // FIFO_TYPE_H

90
src/filo/filo.c Normal file
View File

@ -0,0 +1,90 @@
#include "filo.h"
#include "stdio.h"
// if (FILO_get(filo, byte) == 0) {
// OK
// } else {
// FILO ERROR
// }
int FILO_isUsable(filo_s* filo);
// Инициализация filo
int FILO_init(filo_s* filo) {
if (filo != NULL) {
for (size_t i = 0; i < FILO_DATA_SIZE; ++i) {
filo->data[i] = 0;
}
filo->head = 0;
filo->state = FILOS_empty;
filo->isInitialized = 1;
if (FILO_isUsable(filo) == 0) {
return 0;
}
}
return -1;
}
// Заполнение filo
int FILO_put(filo_s* filo, uint8_t data) {
if (FILO_isUsable(filo) == 0) { // Не заполнено
if ((filo->state == FILOS_empty) || (filo->state == FILOS_ready)) {
filo->data[filo->head] = data;
if (filo->head < FILO_DATA_SIZE-1) {
filo->head++;
filo->state = FILOS_ready;
} else {
filo->head = FILO_DATA_SIZE-1;
filo->state = FILOS_full;
}
return 0;
}
}
return -1;
}
// Получение из filo
int FILO_get(filo_s* filo, uint8_t *data) {
if (FILO_isUsable(filo) == 0) {
if ((filo->state == FILOS_full) || (filo->state == FILOS_ready)) {
*data = filo->data[filo->head-1];
filo->data[filo->head-1] = 0;
if (filo->head > 1) {
filo->head--;
filo->state = FILOS_ready;
} else {
filo->head = 0;
filo->state = FILOS_empty;
}
return 0;
}
}
return -1;
}
int FILO_print(filo_s* filo) {
if (FILO_isUsable(filo) == 0) {
printf("FILO: ");
for (size_t i = 0; i < FILO_DATA_SIZE; i++) {
printf("%d", filo->data[i]);
}
printf("\n");
return 0;
}
return -1;
}
// Проверка доступности filo
int FILO_isUsable(filo_s* filo) {
if (filo != NULL) {
if (filo->isInitialized == 1) {
return 0;
}
}
return -1;
}

27
src/filo/filo.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#ifndef FILO_H
#define FILO_H
#include "filo_type.h"
#include <stddef.h>
// Размер FILO
#define FILO_DATA_SIZE 8
// Структура FILO
typedef struct {
uint8_t data[FILO_DATA_SIZE];
uint8_t head;
filoState_e state;
uint8_t isInitialized;
} filo_s;
// Функции FILO
int FILO_init(filo_s* filo);
int FILO_put(filo_s* filo, uint8_t data);
int FILO_get(filo_s* filo, uint8_t *data);
int FILO_print(filo_s* filo);
int FILO_isUsable(filo_s* filo);
#endif // FILO_H

16
src/filo/filo_type.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#ifndef FILO_TYPE_H
#define FILO_TYPE_H
#include <stdint.h>
// Структура состояний FILO
typedef enum {
FILOS_empty = 0,
FILOS_ready,
FILOS_full,
FILOS_error
} filoState_e;
#endif // FILO_TYPE_H

39
src/hdlc/hdlc.c Normal file
View File

@ -0,0 +1,39 @@
#include "main.h"
#include "hdlc.h"
#include "hdlc_type.h"
int HDLC_printControlField (hdlcControlField16b_u field);
int HDLC_printControlField (hdlcControlField16b_u field) {
printf ("HDLC: Printing fields:\n");
printf ("HDLC: .. Type:\n");
printf ("HDLC: .. Send sequence: %d\n", field.bits.sendSequence);
printf ("HDLC: .. Poll/Final: %d\n", field.bits.pollFinal);
printf ("HDLC: .. Receive sequence: %d\n", field.bits.receiveSequence);
return 0;
}
hdlcControlField16b_u HDLC_parseControlField (uint16_t value) {
hdlcControlField16b_u fieldUnion;
// fieldUnion.value = TOOL_reverseBits16(value);
fieldUnion.value = value;
HDLC_printControlField(fieldUnion);
return fieldUnion;
}
int HDLC_isControlFieldValid (uint16_t value) {
if ((value == 0xffff) || (value == 0)) {
printf ("HDLC: Wrong control field:\n");
// Так как обрабатывается только вариант с информационным пакетом
return -1;
}
return 0;
}

23
src/hdlc/hdlc.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#ifndef HDLC_H
#define HDLC_H
#include <stdint.h>
#include "hdlc_type.h"
// Cтруктура поля управления
typedef union {
struct __attribute__((packed)) {
uint8_t receiveSequence : 7;
uint8_t pollFinal : 1;
uint8_t sendSequence : 7;
uint8_t type : 1;
} bits;
uint16_t value;
} hdlcControlField16b_u;
int HDLC_isControlFieldValid (uint16_t value);
hdlcControlField16b_u HDLC_parseControlField (uint16_t value);
#endif // HDLC_H

33
src/hdlc/hdlc_type.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#ifndef HDLC_TYPE_H
#define HDLC_TYPE_H
#include "fifo_type.h"
// ----------------------------------------------------------------------------------
// Фрейм HDLC
// Флаг FD | Адрес | Управляющее поле | Информационное поле | FCS | Флаг FD
// 8 бит | кратно 8 | 8 или 16 бит | 0 или более байт | 16 бит | 8 бит
// ----------------------------------------------------------------------------------
#define HDLC_INFO_LENGTH 16
#define HDLC_PACKET_LENGTH 1+2+2+HDLC_INFO_LENGTH+2+1
#define HDLC_PACKET_FLAG 0x7e
#define HDLC_PACKET_ESCAPE 0xab
// Структура пакета (фрейма) HDLC
typedef struct {
// const uint8_t flag;
uint16_t address;
uint16_t control;
uint8_t info[HDLC_INFO_LENGTH];
uint16_t fcs;
// const uint8_t closeFlag;
} hdlcPacket_s;
static const uint8_t hdlcFlag = 0b01111110;
#endif // HDLC_TYPE_H

67
src/main.c Normal file
View File

@ -0,0 +1,67 @@
#include "main.h"
// Перечисление статусов машины состояния
enum bigLoopOsState_e {
OS_first = 0,
OS_masterWorker,
OS_slaveWorker,
OS_wait,
OS_last
} bigLoopOsState;
// Текстовые надписи соответствующие статусам машины состояния
const char* bigLoopOsStateNames[OS_last] = {
"starting",
"master worker",
"slave worker",
"idle"
};
// BIG LOOP OS
int main(void) {
// while(true){WORK_master();TOOL_msleep(1000);}
bigLoopOsState = OS_first;
while(true) {
if (bigLoopOsState >= OS_last) {
bigLoopOsState = OS_masterWorker;
}
printf("OS: Current state is: %s\n", bigLoopOsStateNames[bigLoopOsState]);
switch (bigLoopOsState) {
case OS_first:
printf("\n\n");
break;
case OS_masterWorker:
WORK_master();
printf("\n\n");
break;
case OS_slaveWorker:
WORK_slave();
printf("\n\n");
break;
case OS_wait:
TOOL_msleep(5);
printf("\n\n");
break;
default:
printf("OS: Wrong state, going to first state");
bigLoopOsState = OS_first;
TOOL_msleep(3000);
printf("\033[2J\033[H");
break;
}
bigLoopOsState++;
// Для контроля
TOOL_msleep(10);
}
return 0;
}

26
src/main.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#ifndef MAIN_H
#define MAIN_H
#define DO_UNIT_TESTS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
// Конфигурация
static const uint16_t hdlcMasterAddress = 0b10000000;
static const uint16_t hdlcSlaveAddress = 0b11000000;
static const uint16_t xorByte = 0xaf;
// Вспомогательные функции
uint16_t CRC16_compute(uint8_t *data, size_t length);
uint16_t TOOL_reverseBits16(uint16_t value);
void TOOL_msleep(long milliseconds);
// Потоки ОС
int WORK_master(void);
int WORK_slave(void);
#endif //MAIN_H

77
src/tools.c Normal file
View File

@ -0,0 +1,77 @@
// Файл с общими функциями и конфигурацией
#define _POSIX_C_SOURCE 199309L
#include <time.h>
#if __cplusplus
#pragma message ("plus")
#else
#pragma message ("pure")
#endif
#include "main.h"
// Вычисление контрольной суммы CRC16
uint16_t CRC16_compute(uint8_t *data, size_t length) {
const uint16_t CRC16_poly = 0x8005;
const uint16_t CRC16_initVal = 0xffff;
uint16_t crc = CRC16_initVal;
size_t i, j;
for (i = 0; i < length; i++) {
crc ^= (uint16_t)data[i];
for (j = 0; j < 8; j++) {
if ((crc & 0x0001) != 0) {
crc = (crc >> 1) ^ CRC16_poly;
} else {
crc >>= 1;
}
}
}
return crc;
}
uint16_t TOOL_reverseBits16(uint16_t value) {
uint16_t reversed = 0;
for (int i = 0; i < 16; i++) {
if ((value >> i) & 1) {
reversed |= (1 << (15 - i));
}
}
return reversed;
}
uint16_t TOOL_reverseBitsN(uint16_t value, uint8_t n) {
if ((n > 15) || (n < 2)) {
return -1;
}
uint16_t reversed = 0;
for (int i = 0; i < n; i++) {
if ((value >> i) & 1) {
reversed |= (1 << (n - 1 - i));
}
}
return reversed;
}
void TOOL_msleep(long milliseconds) {
struct timespec time;
time.tv_sec = milliseconds / 1000;
time.tv_nsec = (milliseconds % 1000) * 1000000;
// nanosleep can be interrupted by signals, so we loop if interrupted
while (nanosleep(&time, &time) == -1) {
// If interrupted, remaining time is in ts, so we continue sleeping
// unless an actual error occurred (other than EINTR)
// For simplicity, this example assumes EINTR is the only -1 cause.
}
}

301
src/worker/worker.c Normal file
View File

@ -0,0 +1,301 @@
#include <string.h>
#include "main.h"
#include "worker.h"
#include "hdlc.h"
#include "fifo.h"
extern void WORK_formattedPrintArray(char* str, uint8_t* array, size_t size);
workerHost_s* WORK_workerInit (workerHost_s* worker,
hdlcPacket_s* packet, fifo_s* fifo, uint8_t* buffer) {
if (!worker->isInitialized) {
printf("WORKX: Starting master worker initialization\n");
worker->packet = packet;
worker->fifoWrite = fifo;
worker->fifoRead = NULL;
worker->isInitialized = true;
worker->byteBuffer = buffer;
FIFO_init(worker->fifoWrite);
printf("WORKX: Master initialization done\n");
WORK_printBlocks(worker, 3);
return worker;
}
// Ошибка в пакете, повторная инициализация
worker->isInitialized = false;
return NULL;
}
int WORK_waitClient (fifo_s *clientFifo) {
printf("WORKX: .. Waiting for a client\n");
if (FIFO_isUsable(clientFifo) == 0) {
return 0;
}
return -1;
}
// Подготовка пакета для отправки
int WORK_preparePackage(workerHost_s *worker) {
printf("WORKX: .. Preparing package\n");
// Перемещение структуры в массив на передачу
// Вычитаем байты начала и конца сообщения
size_t structSize = HDLC_PACKET_LENGTH-2;
// size_t structSize = sizeof(hdlcPacket_s);
uint8_t tmpPtr = 0;
uint8_t *packPtr = (uint8_t*)worker->packet;
worker->byteBuffer[tmpPtr++] = HDLC_PACKET_FLAG;
for (size_t s = 0; s < structSize; s++, tmpPtr++) {
// Нашли флаг в теле сообщения
if (packPtr[s] == HDLC_PACKET_FLAG) {
worker->byteBuffer[tmpPtr] = HDLC_PACKET_ESCAPE;
tmpPtr++;
}
worker->byteBuffer[tmpPtr] = packPtr[s];
}
worker->byteBuffer[tmpPtr] = HDLC_PACKET_FLAG;
worker->byteBufferLength = tmpPtr+1;
WORK_formattedPrintArray("prepared bytes:",
worker->byteBuffer, worker->byteBufferLength);
return 0;
}
// Отправка пакета
int WORK_sendPackage(workerHost_s *worker) {
printf("WORKX: .. Sending package\n");
if (worker->byteBufferHead > worker->byteBufferLength) {
return -2;
}
if (FIFO_isUsable(worker->fifoWrite) == 0) {
if (worker->fifoWrite->count < FIFO_DATA_SIZE) {
FIFO_put(worker->fifoWrite, worker->byteBuffer[worker->byteBufferHead]);
if (worker->byteBufferHead < worker->byteBufferLength) {
worker->byteBufferHead++;
return 1;
} else if (worker->byteBufferHead == worker->byteBufferLength) {
// Передан весь буфер через фифо
WORK_formattedPrintArray("sent bytes:",
worker->byteBuffer, worker->byteBufferLength);
return 0;
} else {
worker->byteBufferHead = 0;
return -3;
}
} else {
return -2;
}
return 2;
}
return -1;
}
// Ожидание пакета
int WORK_waitPackage(workerHost_s *worker) {
printf("WORKX: .. Waiting for a package\n");
if (FIFO_isUsable(worker->fifoRead) == 0) {
if (worker->fifoRead->count > 0) {
// Появились данные в fifo
return 0;
}
}
return -1;
}
int WORK_receivePackage(workerHost_s *worker) {
printf("WORKX: .. Receiving package\n");
if (FIFO_isUsable(worker->fifoRead) == 0) {
if (worker->fifoRead->count > 0) {
FIFO_get(worker->fifoRead, &worker->byteBuffer[worker->byteBufferHead]);
if (worker->byteBufferHead < WORK_BYTE_BUFFER_SIZE) {
worker->byteBufferHead++;
return 1;
} else {
worker->byteBufferHead = 0;
return -2;
}
} else {
if (worker->byteBufferHead > 0) {
// Приняли данные. Сейчас fifo пуст, можно обрабатывать пакет
// fifo пуст, можно обрабатывать пакет
WORK_formattedPrintArray("receives bytes:",
worker->byteBuffer, worker->byteBufferLength);
return 0;
}
return 2;
}
}
return -1;
}
int WORK_doGroupPackageBytes(hdlcPacket_s *packet, uint8_t *first, uint8_t *last, size_t bufLen) {
if (last > first) {
bufLen = last - first;
// Так как может быть меньше в теории
if (bufLen <= HDLC_PACKET_LENGTH - 2) {
// Считываем адрес
if (bufLen > 2) {
packet->address = ((uint16_t)*(first+1) << 8) | *(first);
first += 2;
}
// Считываем управляющее поле
if (bufLen > 4) {
packet->control = ((uint16_t)*(first+1) << 8) | *(first);
first += 2;
}
// Считываем контрольную сумму
if (bufLen > 6) {
printf("CRCRCR:%d,%d\n", *last, *(last-1));
packet->fcs = ((uint16_t)*(last) << 8) | *(last-1);
last -= 2;
}
// Считываем информационный пакет
if ((bufLen > 8) && (last > first)) {
int j = 0;
// От переполнения
size_t infoLen = last - first + 1;
if (infoLen > HDLC_INFO_LENGTH) {
return -1;
}
// Заполнение данными
for (uint8_t* i = first; i <= last; i++) {
packet->info[j] = *i;
j++;
}
// Чистка хвоста
for (; j < HDLC_INFO_LENGTH; j++) {
packet->info[j] = 0;
}
}
return 0;
} // bufLen <= HDLC_PACKET_LENGTH - 2
} // (last > first)
return -1;
}
int WORK_parsePackage(workerHost_s *worker) {
printf("WORKX: .. Parsing package\n");
// Поиск пакета в массиве
uint8_t *first = NULL, *last = NULL;
uint8_t tmpArr[WORK_BYTE_BUFFER_SIZE] = {0};
size_t bufPtrI = 0, tmpArrI = 0, bufLen = 0;
// Выносим из принятого массива только нагрузку в буфер
for (bufPtrI = 0; bufPtrI < WORK_BYTE_BUFFER_SIZE; bufPtrI++) {
// Заполняем временный буфер если нашли начало
if (first) {
tmpArr[tmpArrI] = worker->byteBuffer[bufPtrI];
tmpArrI++;
}
// Найден escape-флаг и следующие значение - флаг
if (worker->byteBuffer[bufPtrI] == HDLC_PACKET_ESCAPE) {
if (worker->byteBuffer[bufPtrI+1] == HDLC_PACKET_FLAG) {
tmpArr[tmpArrI-1] = HDLC_PACKET_FLAG;
bufPtrI++;
}
} else
// Найден флаг
if (worker->byteBuffer[bufPtrI] == HDLC_PACKET_FLAG) {
if (!first) {
first = &tmpArr[0];
} else if (!last) {
tmpArrI--;
last = &tmpArr[tmpArrI-1];
break;
}
}
}
WORK_formattedPrintArray("received bytes:", first, tmpArrI);
// Разбор пакета в структуру, если найдены флаги
if (first && last) {
bufLen = last - first + 1;
int ret = WORK_doGroupPackageBytes(worker->packet, first, last, bufLen);
if (ret == 0) {
// memset(worker->byteBuffer, 0, WORK_BYTE_BUFFER_SIZE);
// memcpy(worker->byteBuffer, tmpArr, tmpArrI);
// WORK_formattedPrintArray("parsed bytes:", worker->byteBuffer, bufLen);
WORK_formattedPrintArray("parsed bytes:", tmpArr, bufLen);
WORK_printBlocks(worker, 3);
}
return ret;
} else {
// неправильный пакет
printf("WORKX: .. WRONG PACKET\n");
return -1;
}
return -1;
}
int WORK_checkPackage(workerHost_s *worker) {
printf("WORKX: .. Checking package\n");
int res = 0;
res = HDLC_isControlFieldValid(worker->packet->control);
// Проверка поля управления
if (res != 0) {
return res;
}
printf("WORKX: .. Package is info ...\n");
// Проверка контрольной суммы разобранного пакета
uint16_t crc = worker->packet->fcs;
worker->packet->fcs = 0;
uint16_t crcc = CRC16_compute((uint8_t*) worker->packet, sizeof(hdlcPacket_s));
if (crcc == crc) {
printf("WORKX: .. CRC OK\n");
printf("WORKX: .. Package checked\n");
return 0;
} else {
printf("WORKX: .. WRONG CRC\n");
return -1;
}
return -1;
}
// Вызов функций обработчика хоста
int WORK_execute(int (*callback)(workerHost_s*), workerHost_s *worker) {
if (callback == NULL) {
printf("WORKX: .. Wrong object\n");
return -1;
}
int res = callback(worker);
if (res > 0) {
printf("WORKX: .. Progerss (%d)\n", res);
} else if (res < 0) {
printf("WORKX: .. Error (%d)\n", res);
}
return res;
}

51
src/worker/worker.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#ifndef WORKER_H
#define WORKER_H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "hdlc_type.h"
#include "fifo.h"
// Структура устройства
typedef struct __attribute__((packed)){
hdlcPacket_s *packet;
fifo_s *fifoRead;
fifo_s *fifoWrite;
uint8_t *byteBuffer;
uint8_t byteBufferHead;
size_t byteBufferLength;
bool isInitialized;
} workerHost_s;
#define WORK_BYTE_BUFFER_SIZE 128
workerHost_s* WORK_workerInit (workerHost_s* worker,
hdlcPacket_s* packet, fifo_s* fifo, uint8_t* buffer);
int WORK_execute(int (*callback)(workerHost_s*), workerHost_s *worker);
int WORK_waitClient (fifo_s *clientFifo);
int WORK_preparePackage(workerHost_s *worker);
int WORK_sendPackage(workerHost_s *worker);
int WORK_waitPackage(workerHost_s *worker);
int WORK_receivePackage(workerHost_s *worker);
int WORK_checkPackage(workerHost_s *worker);
int WORK_parsePackage(workerHost_s *worker);
int WORK_fillBuffer8b(uint8_t *buffer, size_t size);
// tools and tests
int WORK_printBlocks(workerHost_s *worker, uint8_t mode);
int WORK_testFill(uint8_t* buffer, size_t bufferSize);
int WORK_testFifo(fifo_s *fifo);
#endif //WORKER_H

117
src/worker/worker_master.c Normal file
View File

@ -0,0 +1,117 @@
#include "main.h"
#include "worker.h"
#include "worker_type.h"
#include <string.h>
static hdlcPacket_s hdlcMasterPacket = {0};
fifo_s hdlcMasterFifo;
extern fifo_s hdlcSlaveFifo;
static workerHost_s hdlcMaster = {0};
static uint8_t masterBuffer[WORK_BYTE_BUFFER_SIZE];
int WORK_master (void) {
static workerMasterHostState_e masterState = WSMASTER_first;
if (masterState >= WSMASTER_last) {
masterState = WSMASTER_first;
}
printf("WMAST: In master worker\n");
int res = 0;
switch (masterState) {
case WSMASTER_first:
printf("WSLAV: Starting master worker\n");
masterState = WSMASTER_initializing;
break;
// Инициализация хоста
case WSMASTER_initializing:
if (WORK_workerInit(&hdlcMaster,
&hdlcMasterPacket, &hdlcMasterFifo, masterBuffer) != NULL) {
masterState = WSMASTER_waitClient;
}
break;
// Ожидание инициализации клиента
case WSMASTER_waitClient:
res = WORK_waitClient(&hdlcSlaveFifo);
if (res == 0) {
// Дождались клиента на линии, сохраняем указатель на его фифо
hdlcMaster.fifoRead = &hdlcSlaveFifo;
masterState = WSMASTER_preparingPackage;
}
break;
// Подготовка пакета
case WSMASTER_preparingPackage:
hdlcMaster.packet->address = hdlcMasterAddress;
hdlcMaster.packet->control = 0b0100110011110000;
WORK_fillBuffer8b(hdlcMaster.packet->info, HDLC_INFO_LENGTH);
hdlcMaster.packet->fcs = 0;
uint16_t crc = CRC16_compute((uint8_t*) hdlcMaster.packet, sizeof(hdlcPacket_s));
hdlcMaster.packet->fcs = crc;
hdlcMaster.byteBufferHead = 0;
res = WORK_execute(WORK_preparePackage, &hdlcMaster);
if (res == 0) {
masterState = WSMASTER_sendingPackage;
}
break;
// Отправка пакета
case WSMASTER_sendingPackage:
res = WORK_execute(WORK_sendPackage, &hdlcMaster);
if (res == 0) {
hdlcMaster.byteBufferHead = 0;
masterState = WSMASTER_waitingPackage;
}
break;
// Ожидание ответа
case WSMASTER_waitingPackage:
res = WORK_execute(WORK_waitPackage, &hdlcMaster);
if (res == 0) {
masterState = WSMASTER_receivingPackage;
}
break;
// Получение пакета
case WSMASTER_receivingPackage:
res = WORK_execute(WORK_receivePackage, &hdlcMaster);
if (res == 0) {
masterState = WSMASTER_parsingPackage;
}
break;
// Парсинг пакета
case WSMASTER_parsingPackage:
res = WORK_execute(WORK_parsePackage, &hdlcMaster);
if (res == 0) {
masterState = WSMASTER_checkingPackage;
}
break;
// Проверка пакета
case WSMASTER_checkingPackage:
res = WORK_execute(WORK_checkPackage, &hdlcMaster);
if (res == 0) {
masterState = WSMASTER_last;
__attribute__((fallthrough));
} else {
break;
}
// Цикл завершён, повторяем
case WSMASTER_last:
printf("WMAST: Job done. Starting from the beginning\n");
masterState = WSMASTER_preparingPackage;
return 0;
break;
}
return res;
}

92
src/worker/worker_slave.c Normal file
View File

@ -0,0 +1,92 @@
#include "main.h"
#include "worker.h"
#include "worker_type.h"
static hdlcPacket_s hdlcSlavePacket = {0};
fifo_s hdlcSlaveFifo;
static workerHost_s hdlcSlave = {0};
static uint8_t slaveBuffer[WORK_BYTE_BUFFER_SIZE];
extern fifo_s hdlcMasterFifo;
int WORK_slave(void) {
static workerSlaveHostState_e slaveState = WSSLAVE_first;
if (slaveState >= WSSLAVE_last) {
slaveState = WSSLAVE_first;
}
printf("WSLAV: In slave worker\n");
int res = 0;
switch (slaveState) {
case WSSLAVE_first:
printf("WSLAV: Starting slave worker\n");
break;
case WSSLAVE_initializing:
if (WORK_workerInit(&hdlcSlave,
&hdlcSlavePacket, &hdlcSlaveFifo, slaveBuffer) != NULL) {
res = 0;
}
break;
case WSSLAVE_waitClient:
res = WORK_waitClient(&hdlcMasterFifo);
if (res == 0) {
hdlcSlave.fifoRead = &hdlcMasterFifo;
}
break;
case WSSLAVE_waitingPackage:
res = WORK_execute(WORK_waitPackage, &hdlcSlave);
break;
case WSSLAVE_receivingPackage:
res = WORK_execute(WORK_receivePackage, &hdlcSlave);
break;
case WSSLAVE_parsingPackage:
res = WORK_execute(WORK_parsePackage, &hdlcSlave);
break;
case WSSLAVE_checkingPackage:
res = WORK_execute(WORK_checkPackage, &hdlcSlave);
break;
case WSSLAVE_preparingPackage:
hdlcSlave.packet->address = hdlcSlaveAddress;
uint8_t tmp = hdlcSlave.packet->info[0];
hdlcSlave.packet->info[0] = hdlcSlave.packet->info[1];
hdlcSlave.packet->info[1] = tmp;
hdlcSlave.packet->fcs = 0;
uint16_t crc = CRC16_compute((uint8_t*) hdlcSlave.packet, sizeof(hdlcPacket_s));
hdlcSlave.packet->fcs = crc;
hdlcSlave.byteBufferHead = 0;
res = WORK_execute(WORK_preparePackage, &hdlcSlave);
break;
case WSSLAVE_sendingPackage:
res = WORK_execute(WORK_sendPackage, &hdlcSlave);
if (res == 0) {
hdlcSlave.byteBufferHead = 0;
slaveState = WSSLAVE_last;
__attribute__((fallthrough));
} else {
break;
}
case WSSLAVE_last:
printf("WSLAV: Job done. Starting from the beginning\n");
slaveState = WSSLAVE_waitClient;
return 0;
break;
} // switch (slaveState)
if (res == 0) {
slaveState++;
}
return res;
}

118
src/worker/worker_tools.c Normal file
View File

@ -0,0 +1,118 @@
#include "worker.h"
#define ARRAY_SIZE(array) sizeof(array)/sizeof(array[0])
static inline void WORK_printArray (uint8_t* array, size_t size) {
for (size_t s = 0; s < size; s++) {
printf("%d, ", array[s]);
}
}
void WORK_formattedPrintArray(const char* str, uint8_t* array, size_t size) {
printf("WORKX: %s (%d bytes):", str, (int)size);
for (size_t s = 0; s < size; s++) {
printf("%d, ", array[s]);
}
printf("\n");
}
// printf("FILL:");for (int i = 0; i < 50; i++) {printf("%d:", worker->byteBuffer[i]);} printf("\n");
// Параметрический вывод пакета worker
int WORK_printBlocks(workerHost_s *worker, uint8_t mode) {
if (worker == NULL) {
return -1;
}
if (mode > 0) {
printf("TOOL: Packet consists of several units:\n");
}
// Вывод содержимого пакета
if (mode & 1) {
printf("TOOL: .. Packet:\n");
printf("TOOL: .... Address: %d \n", worker->packet->address);
// printf("TOOL: .... Flag: %d \n", worker->packet->flag);
printf("TOOL: .... Conrol Field: %d \n", worker->packet->control);
printf("TOOL: .... Info Field: ");
WORK_printArray(worker->packet->info, ARRAY_SIZE(worker->packet->info));
printf("\n");
printf("TOOL: .... FCS: %d \n", worker->packet->fcs);
}
// Вывод содержимого буфера fifo
if (mode & 3) {
size_t size = sizeof(worker->fifoWrite->data) / sizeof(worker->fifoWrite->data[0]);
printf("TOOL: .. FIFO:\n");
printf("TOOL: .... Data (%d items): ", (int)size);
for (size_t i = 0; i < size; i++) {
printf("%02X ", worker->fifoWrite->data[i]);
}
printf("\n");
printf("TOOL: .. Pointer: %d ", worker->fifoWrite->head);
}
return 0;
}
// Тестовое заполнение буфера
int WORK_testFill(uint8_t* buffer, size_t bufferSize) {
bufferSize++;
static uint8_t testbuffer[50] = {
0xaa,0xbb,
HDLC_PACKET_FLAG,
0xa1, 0xa2,
0xc1, 0xc2,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
HDLC_PACKET_ESCAPE, HDLC_PACKET_FLAG,
0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xde,
0xcc, 0xcd,
HDLC_PACKET_FLAG
};
for (int i = 0; i < 50; i++) {
buffer[i] = testbuffer[i];
}
return 0;
}
// Тестирование фифо
int WORK_testFifo(fifo_s *fifo) {
printf("TEST: FIFO START:\n");
FIFO_put(fifo, 4);
FIFO_put(fifo, 3);
FIFO_put(fifo, 8);
FIFO_print(fifo);
uint8_t arr[10] = {0};
for (int a = 0; a < 10; a++) {
FIFO_get(fifo, &arr[a]);
printf("%d:", arr[a]);
}
printf("\n");
printf("TEST: FIFO END:\n");
return 0;
}
// Заполнение массива
int WORK_fillBuffer8b(uint8_t *buffer, size_t size) {
memset(buffer, 0, size);
for (size_t i = 0; i < size; i++) {
buffer[i] = i;
}
buffer[size/2] = HDLC_PACKET_FLAG;
buffer[size/3] = HDLC_PACKET_FLAG;
buffer[size/4] = HDLC_PACKET_FLAG;
buffer[size/5] = HDLC_PACKET_FLAG;
buffer[10] = HDLC_PACKET_FLAG;
buffer[12] = HDLC_PACKET_FLAG;
return 0;
}

42
src/worker/worker_type.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#ifndef WORKER_TYPE_H
#define WORKER_TYPE_H
// Перечисление статусов машины состояния общего обработчика
// typedef enum {
// WORKE_first = 0,
// WORKE_masterWorker,
// WORKE_slaveWorker,
// WORKE_wait,
// WORKE_last
// } workerState_e;
// Перечисление статусов конкретного хоста
typedef enum {
WSMASTER_first = 0,
WSMASTER_initializing,
WSMASTER_waitClient,
WSMASTER_preparingPackage,
WSMASTER_sendingPackage,
WSMASTER_waitingPackage,
WSMASTER_receivingPackage,
WSMASTER_checkingPackage,
WSMASTER_parsingPackage,
WSMASTER_last
} workerMasterHostState_e;
typedef enum {
WSSLAVE_first = 0,
WSSLAVE_initializing,
WSSLAVE_waitClient,
WSSLAVE_waitingPackage,
WSSLAVE_receivingPackage,
WSSLAVE_parsingPackage,
WSSLAVE_checkingPackage,
WSSLAVE_preparingPackage,
WSSLAVE_sendingPackage,
WSSLAVE_last
} workerSlaveHostState_e;
#endif // WORKER_TYPE_H