SIMATIC WinCC Open Architecture – это SCADA система разработки ETM(Siemens). В последние годы стала достаточно популярна в России в определенных кругах.  Поскольку в интернете информации о данном продукте не очень много, а на обучениях даются достаточно поверхностные знания и многие моменты не затрагиваются, появилась идея попробовать написать серию статей с интересными на мой взгляд темами.  В WinCC OA встроен C/C++ подобный скриптовый язык CONTROL. О его возможностях сегодня и поговорим. Динамические массивы представлены двумя видами типов данных: vector<type>, где type – тип данных. Тип может быть простым как int или double, так и сложным как строка, структура или же другой вектор. По сути, это тот же вектор, как и в C++ с похожими методами и возможностями.  Динамический массив Dyn_type, где type также тип данных.  В целом оба вида принципиально ничем не отличаются. В документации так дословно сказано об их различиях:  The biggest difference between vectors and dyn_ variables is the index. For vectors this index starts at 0 while dyn_ variables start at 1 Тут также стоит отметить, что возможен такой вариант как vector<void>. В документации про это написано следующее:     The void data type for a vector allows to add any data type. A vector<void> is similar to a dyn_anytype, but a dyn_anytype is a dyn_ containing anytype variables, which themselves point to any other type. In terms of performance and memory consumption, the vector<void> is better, since it directly holds the final pointers to whatever datatype was given. Объект вектора и динамического массива имеют одинаковые методы: Также в языке предусмотрены отдельные функции для работы с динамическими массивами. Такие функции использовать с вектором не получится.  Перечень функций из документации: Этот тип данных представлен одним видом: «mapping».  Из документации: Переменные "mapping" сохраняют произвольные пары ключ-значение. Ключи и значения сохраняются в два массива: один для ключей и один для значений  Таким обозом получается, что тут мы имеем дело с самой простой реализацией и «под капотом» у нас два массива. Попахивает линейной сложностью O(n) выполнения основных операций добавления, поиска и удаления. Никаких тут тебе двоичных деревьев поиска и хеш-таблиц с их логарифмическими O(log n) и константными O(1) сложностями. Методы ассоциативного массива: Функции для работы с mapping: Функция Описание mappingClear Удаляет все записи сопоставления. mappingHasKey Осуществляет проверку наличия "ключа" в сопоставлении. mappingRemove Удаляет запись (сопоставления) с указанным "ключом". mappingGetKey Возвращает ключ с указанным индексом. mappingKeys Возвращает массив со всеми ключами сопоставления. mappingGetValue Возвращает значение (сопоставления) с указанным индексом mappinglen Возвращает размер ассоциативного массива Отдельно стоит отметить наличие такого типа данных как строка: string. По сути, это массив с элементами типа char. Индексирование как и у вектора начинается с 0. Каждый символ можно считать при помощи оператора []. Но тут надо быть осторожным. Строки не могут быть записаны или перезаписаны посимвольно. В остальном со строками удобно работать. Для них реализовано большое количество удобных функций.  Методы строки: Функции для работы со строками Функция Описание dpValToString() Преобразует значение, указанное в параметре val, в строку со строкой форматирования   используемого элемента точки данных dp patternMatch() Осуществляет проверку наличия определенного   шаблона строки sprintf() Выполняет форматирование строки sprintfPL() Выполняет форматирование строки. Аналогично sprintf(), но переходит на текущий язык WinCC OA перед преобразованием sprintfUL() Выполняет форматирование строки аналогично sprintf(), но переходит на текущий язык пользователя ОС Windows перед преобразованием sscanf() Импортирует строку в форматированном виде sscanfPL() Импортирует строку в форматированном виде. Аналогично sscanf(), но   переходит на текущий язык WinCC OA перед преобразованием. sscanfUL() Импортирует строку в форматированном виде. Аналогично sscanf(), но переходит на текущий язык пользователя ОС Windows перед преобразованием strchange() Изменяет содержимое строки в заданном индексе для определенного количества цифр с помощью замещающей строки strexpand Возвращает отформатированную строку strformat Возвращает отформатированную строку strlen() Возвращает длину строки в байтах strltrim() Исключает определенные символы из строки, начиная с левого края strpos() Возвращает позицию строки в другой строке strreplace() Заменяет части строки другой строкой strrtrim() Вырезает определенные символы из строки, начиная справа strsplit() Разделяет строки с помощью символов разграничителей strtolower() Изменяет символы строки на строчные strtoupper() Изменяет символы строки на прописные substr() Вырезает строку или символ определенной длины (в байтах) в другую строку strtok() Обнаруживает первое совпадение строки в другой строке stringEditor() Для редактирование строковых переменных, например, в   текстовых полях, можно использовать функцию "stringEditor()". Это   означает, что строковые переменные не сохраняются в файле На этом с контейнерными типами данных все, но этого вполне достаточно, чтобы реализовать другие, учитывая наличие такого удобного инструмента как умный указатель shared_ptr. Для начала начнем с чего-нибудь попроще, а именно с приоритетной очереди. Вдохновение будем черпать у priority_queue из стандартной библиотеки C++. Данную структуру данных реализовывают в виде пирамиды, где в корне находится элемент с наибольшим приоритетом.  Пирамиду удобно сделать на массиве. Для этого будем использовать следующие правила: индекс корня дерева равен 1    нулевой индекс массива не используем. Это нужно для удобства расчета остальных индексов дерева    индекс левого потомка для родителя i равен 2*i    индекс правого потомка для родителя i равен 2*i + 1    индекс родителя для потомка i равен целочисленному делению i на 2    Массив: i 0 1 2 3 4 5 6 7 8 9 value 0 3 5 10 9 40 15 12 11 16 Попробуем реализовать класс PriorityQueue. Тут здравый смысл говорит, что класс должен быть шаблонным, но в WinCC OA такого понятия нет. Выйти из этой ситуации нам помогут типы данных vector<void> и anytype.   В качестве полей класса запишем вектор, который будет хранить нашу пирамиду, отдельно чисто для сомнительного удобства будем хранить размер кучи и указатель на функцию компаратор. Благо наличие такого указателя разработчики WinCC OA предусмотрели.  Кратко об этом из документации: Идем дальше. Конструктор будет инициализировать поля класса начальными значениями. Для дефолтного значения указателя на компаратор реализуем приватный метод DefaultComp. Также реализуем методы: Top  – выводит элемент очереди с наибольшим приоритетом (элемент вектора с индексом 1) Empty – проверка очереди на пустоту Size – выводит размер очереди Swap – меняет местами две очереди. Тут для удобства дополнительно реализуем приватный метод Sw Получаем такой код: Вставку нового элемента будем осуществлять следующим образом. Элемент добавляем в конец кучи, т.е. в конец нашего массива.    Массив: i 0 1 2 3 4 5 6 7 8 9 10 value 0 3 5 10 9 40 15 12 11 16 6 Далее нам надо сделать так называемое «просеивание вверх», чтобы новый элемент занял свое место в очереди в соответствии со своим значением. Такая вставка будет происходить за O(log n).    Массив: i 0 1 2 3 4 5 6 7 8 9 10 value 0 3 5 10 9 6 15 12 11 16 40  Попробуем реализовать. Для этого добавим в класс PriorityQueue следующие методы: Push – вставка нового элемента SiftUp – просеивание вверх выделим в отдельный приватный метод Код: Удаление элемента осуществляется из корня нашей пирамиды. Чтобы дерево при этом не распалось мы это место затыкаем последним элементом массива, перенеся его в ячейку i=1. Массив: i 0 1 2 3 4 5 6 7 8 9 value 0 40 5 10 9 6 15 12 11 16 После этого нам необходимо сделать «просеивание вниз», чтобы на вершине снова появился элемент с наивысшим приоритетом и все элементы кучи заняли свои места. Такое удаление будет происходить за O(log n). Массив: i 0 1 2 3 4 5 6 7 8 9 value 0 5 6 10 9 40 15 12 11 16 Реализуем. Для этого добавим в класс PriorityQueue следующие методы: Pop – удаление элемента с наибольшим приоритетом    TakeTop – выводит элемент с наибольшим приоритетом одновременно удаляя его из очереди    SiftDown – просеивание вниз выделим в отдельный приватный метод    Код: Для удобства установки приоритета создадим небольшой класс CompareClass    Таким образом сможем провести небольшое тестирование получившейся приоритетной очереди Продолжение следует… но это не точно :)   Инженер-программист