Три в одном
FB_FileLoad
умеет загружать файл целиком в память. Сам по себе, другие ФБ для работы с
файлами не нужны. Внутри него уже есть FB_FileOpen,
FB_FileRead и FB_FileClose. Два основных вопроса (всего четыре):
- асинхронность? Иначе говоря, долгая загрузка должна быть разбита на несколько циклов.
- Как быстро загрузит большой файл?
- Что с потреблением памяти?
- Вдруг свой будет работать лучше?
Используется библиотека Tc2_System версия 3.4.22.0
Базовое время цикла: 1мс
Время цикла ПЛК задачи: 1мс
ПЛК:
CX5010-1110
Увеличение памяти. Бесплатно
Память ПЛК = 512 Мб. Подсмотреть ее объем можно в панели управления:
Я пытался загрузить файл размером около 32.7Мб, а получил ошибку 0x070A
(1802)
ADSERR_DEVICE_NOMEMORY — не хватает памяти. Причем на старте нехватка памяти в
FB_FileLoad не проверяется, а выясняется только по окончании
марлезонского балету. Получается, что ФБ что-то там грузит до последнего, а
потом внезапно память у него заканчивается. И судя по времени загрузки,
загрузить получается, но что-то в конце не складывается, идет не так как
было задумано и каменный цветок в итоге не выходит. Что с памятью?
Во первых, код захватывает память статически: ему памяти либо хватает, либо
он просто не соберется. Вот что пишет компилятор:
Size of generated code: 58432 bytes
Size of global data: 35024176 bytes
Total allocated memory size for code and data: 64445200 bytes
Здесь все нормально. Возможно что-то выделяется динамически из памяти
роутера? Начинаю увеличивать память роутера: при 100 Мб контроллер все еще
работал, а при 300Мб вообще ничего не запустилось. Остановился на 50 Мб,
должно хватить. И хватило.
Со времен статьи про
New, Delete и память роутера, то есть про динамическую память произошли небольшие изменения. Объем
памяти настраивается все там же: System → Real-Time → Router Memory → Configured Size [MB] -
мегабайты!
...но теперь для активации необходимо не только активировать конфигурацию,
но и перезагрузить контроллер. Подробнее см.
как загружаются параметры конфигурации. Ну и теперь нам показывают чуть больше информации:
Когда памяти стало хватать, а файл стал загружаться без ошибок, я взял
FB_GetRouterStatusInfo
и стал пристально следить за памятью роутера:
Доступно всего, maxMem = 52428800
До старта копирования, maxMemAvail = 52395456
После копирования, maxMemAvail = 52395456
Тоже все нормально, но внезапно я подключил Scope и все встало
на свои места. По вертикальной оси отмечены байты, отмасштабированные в
тысячах, хотя мегабайты должны быть кратны 1024, поэтому есть небольшое
расхождение в итоговых цифрах объема:
Вот эта просадка — в виде красной ямы, происходит в конце загрузки. Система
куда-то грузит файл, затем выделяет под него память роутера, что-то делает,
затем данные появляются в моем статическом массиве, и тут же освобождается
память роутера. Зачем?
Загрузка длится долго, около 30 секунд (см. ниже), буфер заполняется
постепенно, а итоговый массив с данными доступен весь и на всем протяжении
загрузки. Возможно, именно по этой причине, от нас временно пытаются скрыть
недозагруженные куски данных. Я попробовал выделить буфер динамически
— через команду __NEW, ну и получил очередную нехватку памяти,
так как от 50 мб осталось только 17 Мб и буфер под копирование выделять уже
было не из чего.
Timeout
Если ФБ загружает долго (а большие файлы он грузит долго), то при таймауте
ФБ выдает ошибку 0x0745 (1861)
ADSERR_CLIENT_SYNCTIMEOUT. Но почему таймаут срабатывает через в два раза больший промежуток
времени: задаешь 2сек — срабатывает через 4, задаешь 5 сек — срабатывает через 10; 10 сек → 20 сек. Причем код ошибки
nErrId формируется через заданное время, а флаг ошибки
bError устанавливается спустя еще один промежуток таймаута (начиная
от установки кода ошибки). Итого, получаем удвоенное время. Это баг или
фича?
Я поставил таймаут в один час T#1H и этого должно хватить на загрузку. И
хватило.
Тестируем оригинал
Под рукой был большой файл бинарного содержания
/Hard Disk/NK.BIN размером
около ~32.7Mb. Загружаем его несколько раз:
= 26,078 сек. = 26,075 сек. = 26,095 сек.
Исходный код чтобы обратить внимание на таймаут в T#1h и напомнить про тест
памяти:
Пишем свой FileLoad c буферами и чартами
1 Кб = 92,368 сек 4 Кб = 42,063 сек 8 Кб = 33,781 сек 16 Кб = 29,631 сек 32 Кб = 28,251 сек 64 Кб = 26,625 сек <<<< 128 Кб = 26,613 сек 1 Мб = 26,179 сек 4 Мб = 26,115 сек
По результатам строим самый настоящий график из Экселя. Ось Y конечно же
не от нуля: там секунды, а они в 1000 раз больше миллисекунд, поэтому —
норм. По оси абсцисс отложен размер временного буфера или размер блока
байт, которые за раз подгружаются из файла (чтение идет блоками):
Исходный код не зависящий от памяти роутера