Данный CODESYS форум содержит архивную копию русской ветви только для чтения. Для создания сообщений пожалуйста используйте актуальную международную платформу CODESYS Forum. Close

Динамические массивы

ZRomik
2014-11-01
2014-12-23
  • ZRomik

    ZRomik - 2014-11-01

    Доброго времени суток!

    По работе у меня чаще всего однотипные объекты, которые различаются только количеством сигналов. В общем-то шаблон типового проекта есть, но надоедает каждый раз ручками проставлять границы массивов, связи между объектами и т.д. Давно задумывался о сабже и вот выдалась пара свободных дней. Хочу предложить на суд общественности вариант реализации:

    Сам контейнер:
    FUNCTION_BLOCK TCollection IMPLEMENTS ICollection
    VAR_INPUT
    //идентификатор коллекции для системы
    sCollectionName: STRING(10);
    //размер элемента коллекции
    udiObjectSize: UDINT;
    END_VAR
    VAR
    //указатель на начало области памяти
    hMemPtr: POINTER TO BYTE := 0;
    //количество элементов в коллекции
    iCount: INT;
    //результат выполнения операции
    hResult: SysTypes.RTS_IEC_RESULT;
    //флаг ошибки
    xError: BOOL;
    //максимальное количество элементов в коллекции
    udiCapacity: UDINT := 0;
    END_VAR
    VAR CONSTANT
    //максимальное значение, на которое можно увеличивать память
    byMaxGrowSize: BYTE := 16;
    END_VAR

    (добавляет элемент в коллекцию)
    METHOD AddObject
    VAR_INPUT
    (интерфейс добавляемого объекта)
    pObject : IObject;
    END_VAR
    VAR
    //адрес элемента в памяти
    hMem: POINTER TO IObject;
    END_VAR
    IF (hMemPtr = 0) OR (udiCapacity = iCount) THEN
    Grow();
    END_IF
    hMem := hMemPtr + iCount * udiObjectSize;
    hMem^ := pObject;
    iCount := iCount + 1;

    (возвращает запрошенный элемент коллекции)
    METHOD GetObject
    VAR_INPUT
    (индекс элемента в коллекции)
    udiIndex : UDINT;
    END_VAR
    VAR_IN_OUT
    //интерфейс объекта
    pObject: IObject;
    END_VAR
    VAR
    //указатель на память
    hMem: POINTER TO IObject;
    END_VAR
    IF (iCount <= 0) OR (hMemPtr = 0) THEN
    RETURN;
    END_IF
    hMem := hMemPtr + udiIndex * udiObjectSize;
    pObject := hMem^;

    //выделяет память под элементы коллекции
    METHOD PRIVATE Grow
    VAR
    udiNeedSize: UDINT;
    END_VAR
    //сбрасываем ошибку
    xError := FALSE;
    //рассчитываем необходимый размер памяти
    udiNeedSize := (iCount + byMaxGrowSize) * udiObjectSize;
    IF hMemPtr = 0 THEN
    hMemPtr := SysMem.SysMemAllocData(szComponent := sCollectionName, udiSize := udiNeedSize, pResult := ADR(hResult));
    ELSE
    hMemPtr := SysMem.SysMemReallocData(szComponent := sCollectionName, pMemory := hMemPtr, udiSize := udiNeedSize, pResult := ADR(hResult));
    END_IF
    xError := hResult <> 0;
    udiCapacity := udiCapacity + byMaxGrowSize;

    Использование:
    PROGRAM PLC_PRG
    VAR
    obj: TOpticalSensor1;
    objptr: POINTER TO TOpticalSensor1;
    coll: TCollection;
    ww: WORD := 1;
    count: INT;
    END_VAR
    objptr := __NEW(TOpticalSensor1);
    objptr^(wID := ww);
    ww := ww + 1;
    coll.AddObject(pObject := objptr^);

    FOR count := 0 TO coll.Count() - 1 DO
    coll.GetObject(udiIndex := count, pObject := objintf);
    IF objintf <> 0 THEN
    objintf.Execute();
    END_IF
    END_FOR

    Вот как-то так. Нарисовано на коленке, в перерывах между танками и евой. Жду критики.

     
  • ZRomik

    ZRomik - 2014-11-14

    Неужели никто ничего не скажет?

     
  • Yegor

    Yegor - 2014-12-23

    А где проверка корректности индекса в GetObject? Ещё в подобных классах принята возможность задавать начальный размер. По вкусу можно сделать размер опережающего наращивания зависимым от него. Ну и без удаления не труъ)