Буфер с внешним сбросом
Кольцевой буфер с внешним сбросом
Как-то раз мне понадобилось парсить потоковые данные и для этого мне потребовался буфер, который я мог бы очищать по некоторому условию. В Engee такого блока не оказалось, но это не беда, ведь если я знаю что мне надо, то я всегда могу реализовать это или на Си или на Джулии. Посмотрим, что у меня получилось.
Как работает кольцевой буфер с внешним сбросом?
Код буфера - используем Рабочие Переменные
Самый простой подход к реализации чего-либо - самый лучший. Поэтому будем использовать блок C Function.
Для реализации понадобится две переменных-состояния: сам буфер и счетчик. Заведем их как рабочие переменные блока.
Рабочие переменные - это специальные переменные, которые служат как предвыделенная статически область памяти. При настройке этого параметра блока для каждой переменной указывается сколько байт надо выделить. Отдельный плюс - эти переменные свои для каждого экземпляра блока. То есть можно говорить, что это "состояние" блока.
Посмотрим на мои настройки:
Видно, что будет использоваться 2 переменных - 80ти байтовый буфер buffer и однобайтовый счетчик.
Чтобы использовать эти переменные в коде C Function надо обратиться к структуре Work.
В итоге код будет выглядеть так:
uint8_t * ptr = &(Work->buffer[0]);
double* buffer_d_ptr = (double *) ptr;
double* output1_ptr = output1;
if (RESET)
{
memset(buffer_d_ptr, 0, BUFFER_LEN_BYTES);
memset(output1_ptr, 0, BUFFER_LEN_BYTES);
(Work->pos[0]) = 0;
}
else
{
buffer_d_ptr[Work->pos[0]] = byte;
Work->pos[0]++;
memcpy(output1_ptr, buffer_d_ptr, BUFFER_LEN_BYTES);
if (Work->pos[0] > len - 1)
{
Work->pos[0] = 0;
}
}
Как работать с рабочими переменными (магия Си)?
Рабочие переменные всегда имеют тип uint8_t. Но Си прекрасен тем, что позволяет работать с памятью как угодно. А еще можно вспомнить, что любой массив в Си - это просто указатель на кусок памяти, поэтому мы можем интерпретировать его как мы хотим:
uint8_t * ptr = &(Work->buffer[0]);
double* buffer_d_ptr = (double *) ptr;
И ptr и buffer_d_ptr указывают на один и тот же адрес в памяти, но buffer_d_ptr говорит компилятору о том, что по этому адресу будет double. А еще мы можем работать с этим указателем, как с массивом:
buffer_d_ptr[Work->pos[0]] = byte;
Заметьте, что мы вообще не используем динамическое выделение памяти!
Тестируем блок
Создадим тестовую обвязку вида:
запустим симуляцию модели:
sys = engee.open(joinpath(@__DIR__,"cirbuf.engee"))
engee.run(sys)
engee.close(;force=true)
И посмотрим на результаты:
using DataFrames
df = DataFrame(time=zero(Float64),Buffer=[zeros(Float64,16)],RESET = zero(UInt8))
for i in eachindex(workspace_out2)
push!(df,(time=RESETEVT[i][1],
Buffer=BUFFER[i][2],
RESET=RESETEVT[i][2]))
end
show(df, allrows=true, allcols=true)
Выводы
Мы сделали блок циклического буфера с внешним сбросом и научились работать с рабочими переменными, и заодно вспомнили как работать с указателями в Си.