среда, 29 апреля 2026 г.

ATTENTION, FAKE!

(этот пост является переводом предыдущего поста на английский язык)

Eight years ago, I posted on my blog that a Vietnamese citizen was selling a so-called "miracle program" called All PLC and HMI Crack, which had a ridiculously ugly green interface that made it instantly recognizable:


Versions 4.2 and 2.2 are actually identical

The program goes by many names: "PLC HMI Password Crack," "PLC - HMI Password Decryption," "PLC HMI Password Unlock," "Crack All PLC HMI," "PLC HMI Keyword," and so on. I initially doubted that it worked, but various users started telling me in the comments that it was not fake and could actually crack something. So, after all these years, I suddenly found this program available for free, downloaded it, and tried to crack some basic things (for example, opening a password-protected S7-200 project). And it actually worked. However, there is an important thing to understand: similar cracks have been around for a long time and are no big secret. Still, at least we have confirmation that the program is real — and that is exactly what attracts potential buyers. It seems that if the old version is real, the new ones must be too.

Then I found an entire website, unlockplcbd.com, dedicated to this scam. Look at these wonderful ad brochures that the developer is showing off:

Advertising PDF #1

Advertising PDF #2

  


 

He really tried! He clearly hired a designer, because given the program's shabby green appearance, the author could not have made such PDFs himself. However, these PDFs have some obvious flaws that alone should give the scam away. For example, if you open these two documents, you might notice one very strange detail: the description of version 5.3 claims it can crack Siemens S7-200 Smart controllers, but for some reason, version 5.7 no longer includes those controllers — though it does include the S7-300, S7-400, and even the S7-1200. Where did the S7-200 Smart go? Probably the scammer simply forgot to copy that mention when designing the new brochure. The version 5.3 brochure also mentions Koyo controllers, but they are also missing from version 5.7. These strange, illogical moments should raise immediate suspicion.
 

Now let's move on to our hero of the day. Here he is:

 

As you can see, the phone number is published right in the program: +8801758432841. This is done deliberately so you have no doubt that it is the author communicating with you on WhatsApp — not some other scammer. Although, in fact, the author is the scammer.

Now, the most important part: what is the scammer selling? Nothing! After receiving the money, he sends a RAR archive containing the same old program that he wrote many years ago. Here is how the scam works: he originally sold this program, but at some point, buyers leaked it online, making it freely available. Instead of losing money, the scammer turned this situation into a profit. He started creating advertising PDFs for non-existent new versions of the program. His old software — now freely available because it was leaked — became a kind of "demo version" for these fake new releases.

So now, the scammer does not need to develop anything. He simply creates fake brochures for non-existent versions, collects payments, and vanishes — leaving the victim with an old, freely available program. 

Here is his program, which you can easily find online: link 
(Mirror: link)
And here is the program this scammer sent me after payment: link 
(Mirror: link)

As you can see, there is no difference — the version he sold is 4.2, though it is actually no different from version 2.2. What is more, the scammer did not even bother to fix the "About" window, so in version 4.2, that window shows version 2.3, and the software name does not match either:

 

The versions 5.3 and 5.7 that the scammer advertises in his brochures do not exist. There are only fake screenshots on his website:

 

All his advertising brochures and pseudo-screenshots are a scam. Furthermore, for those unwilling to pay him 500,thescammeroffersa500,thescammeroffersa300 option that supposedly cracks just one controller model, for example, the Siemens S7-1200. Naturally, that is also a scam.

In addition to his fancy website, the author posts videos on YouTube and spreads false information about his program on other sites.

Don't trust scammers — save your money )).

ВНИМАНИЕ, FAKE!

8 лет назад в моем блоге было сообщение о том, что некий вьетнамский гражданин продает чудо-программу под названием All PLC and HMI Crack с идиотским салатовым интерфейсом, по которому эта прога сразу идентифицируется:

 

Версии программ 4.2 и 2.2. По факту - абсолютно идентичные


У программы много названий: "PLC HMI Password Crack", "PLC - HMI Password Descryption", "PLC HMI Password Unlock", "Crack All PLC HMI", "PLC HMI Keyword" и т.п. Я изначально выражал сомнения в работоспособности данной программы, но разные пользователи начали писать мне в комментариях, что программа не фейк и что-то взламывает. И вот спустя столько лет я вдруг случайно нахожу данную программу в бесплатном доступе, скачиваю ее и пробую взломать некоторые элементарные вещи (например, открыть запороленный проект к S7-200). И оно действительно работает. Правда, тут надо понимать один важный аспект: подобные взломы давно уже реализованы и не являются тайной. Но однако, по крайней мере появилось подтверждение, что программа настоящая. И именно это является приманкой для потенциальных покупателей! Кажется, что если старая версия настоящая, то и новые тоже. Далее нахожу целый сайт unlockplcbd.com, посвященный всей этой афере. Глядите-ка какие замечательные рекламные буклеты демонстрирует разработчик данного ПО: 

 Рекламный PDF №1

 Рекламный PDF №2

 


Да уж, расстарался! Явно нанял дизайнера, ибо учитывая убогий салатовый внешний вид ПО, нарисовать такой PDF автор точно бы не смог. Правда есть явные косяки с этими PDF, которые сами по себе уже должны выдать аферу. Например, открыв эти два документа, вы можете заметить одну очень странную деталь: в описании программы v5.3 заявлено, что она якобы может вскрывать контроллеры Siemens S7-200 Smart, но в версии 5.7 по какой-то причине эти контроллеров уже нет, но зато есть S7-300, S7-400 и даже S7-1200. Куда же подевались S7-200 Smart?.. Ну, видимо, мошенник просто забыл скопировать упоминание о нем, когда верстал новый рекламный буклет. Также в буклете на версию 5.3 упомянуты контроллеры Koyo, а в версии 5.7 их почему-то уже нет. Т.е. эти странные и нелогичные моменты сразу должны вызвать подозрения.

Теперь переходим собственно к нашему герою дня. Вот он:

 


Как видим, номер телефона опубликован прямо в программе: +8801758432841. Это сделано специально, чтобы у вас не было сомнений насчет того, действительно ли с вами в WhatsApp общается автор, а не мошенники. Хотя на самом деле сам автор и есть мошенник.

Теперь самое главное: ну а что же жулик продает? А ничего! Получив деньги, он отправляет RAR-архив, содержащий ту же самую старую программу, которую он написал много лет назад. Вот как работает мошенничество. Изначально он продавал свою программу (старую), но в какой-то момент покупатели выложили её в интернет, сделав общедоступной. Вместо того чтобы потерять деньги, вьетнамский жулик превратил эту ситуацию в прибыль. Он начал создавать рекламные PDF-файлы для несуществующих новых версий программы. Его старое ПО при этом — общедоступное, потому что оно было выложено в сеть — стало своего рода демо-версией для этих поддельных новых релизов.

Таким образом, теперь мошеннику не нужно ничего разрабатывать, достаточно лишь рисовать PDF с рекламой несуществующих программ, брать деньги и сваливать.

Вот его программа, которую вы легко найдете в интернете: ссылка 

А вот программа, которую этот жулик скинул мне после оплаты: ссылка  

Как вы можете заметить, разницы нет никакой, а версия проданной программы является 4.2, хотя на самом деле ничем не отличается от версии 2.2. Мало того, жулик даже не озаботился тем, чтобы исправить окно "О программе", поэтому в версии 4.2 это окно показывает версию 2.3, да и название ПО тоже не совпадает:

 

 

Никаких версий 5.3 и 5.7, которые жулик рекламирует в своих буклетах, не существует. Есть только фальшивые скриншоты у него на сайте:

 




Все его рекламные буклеты и псевдо-скриншоты - это обман. Мало того, для тех, кто не хочет платить ему $500, жулик предлагает вариант за $300, который будет взламывать только одну модель контроллера, например, Siemens S7-1200. Естественно, что это тоже всё обман.

Помимо красивого сайта автор выкладывает ролики на Youtube и публикует лживую информацию на других сайтах о своей программе.

Не доверяйте жуликам, экономьте деньги )). 

пятница, 9 января 2026 г.

Класс QString для C++Builder

ссылка для скачивания

 

Реализация Qt-подобного строкового класса для C++Builder

Если совсем кратко говорить, то суть в том, чтобы использовать Borland'овский стандартный класс UnicodeString через методы QString из Qt. Т.е. речь идет не о чем-то новом в плане разработки, а лишь об унификации стандартов: если уже есть опыт с QString и нет желания вникать в особенности UnicodeString, то можно использовать этот класс. Также использование классов с одинаковыми методами может быть удобно с точки зрения переносимости кода между разными средами разработки. Упрощенные реализации QChar и QStringList сделаны лишь в том контексте, что они требуются классу QString, не более того.

    QChar — упрощённый аналог Qt::QChar:

  •         Хранит один символ (wchar_t).
  •         Методы для проверки типа символа (isDigit, isLetter и т.д.).
  •         Преобразование регистра (toUpper, toLower).


    QString — основная строка:

  •         Внутреннее хранение через UnicodeString
  •         Поддерживает создание из разных источников: char*, wchar_t*, std::string, std::wstring.
  •         Реализует большинство методов Qt-строки:
  •             Поиск, разделение, замена, регистр.
  •             Подстроки (mid, left, right).
  •             Числовые преобразования (toInt, toDouble).
  •             Кодировки (UTF-8, Latin1, ISO-8859-1).
  •             Форматирование (arg, number). arg() реализована лишь частично (!)
  •             Операторы сравнения и конкатенации.


    QStringList — список строк:

  •         Наследуется от std::vector<QString>.
  •         Методы: join, contains, filter.
  •         Перегруженный operator<< для добавления строк. 

 

  1. Использует встроенные возможности C++Builder (UnicodeString, UTF8String, AnsiString).

  2. Поддерживает набор кодировок и преобразований между ними.

  3. Реализует специфичные методы Qt, такие как section, simplified, toHtmlEscaped.

  4. Учитывает особенности индексации UnicodeString (начинается с 1).

 

Модуль позволяет использовать Qt-подобный строковый API в проектах на C++Builder без подключения Qt, что полезно для миграции кода или поддержки кроссплатформенных исходников с Qt-стилем работы со строками.

понедельник, 7 июля 2025 г.

Динамический массив C++ без реаллокаций

ссылка для скачивания

В архиве три файла для C++ (требуется поддержка компилятором C++11): 


1. Qt

2. Visual C++

3. C++Builder

Версия для Visual C++ является базовой. В версии для Qt добавлена поддержка строковых литералов для QString и работа с QList. В версии для C++Builder добавлена поддержка строковых литералов для UnicodeString.  

 

Реализация динамического массива на C++, обеспечивающего стабильность указателей.

Под стабильностью указателей понимается неизменность адресов в памяти, где располагаются объекты, являющиеся элементами массива. Т.е. указатель-переменная, созданный отдельно от массива и указывающий на любой его элемент, сохранит актуальность независимо от того, какие действия производятся с этим массивом.

Немного об идее.

Идея родилась по ходу разработки конкретного кода, где использовались указатели на элементы динамического массива std::vector и, соответственно, потребовалось сохранять валидность этих указателей при изменении размера вектора. А vector так не умеет. Стал смотреть альтернативы - std::list стабилен, но не имеет индексации - поэтому даже не рассматривал его, std::deque работает через чанки, т.е. разрозненные последовательности элементов, но стабильности в нем нет, там тоже бывают реаллокации. Ну, и в C++Builder, в котором проект я разрабатывал, своих нормальных вариантов тоже нет, TList просто недоразумением показался, причем, и старый TList из C++Builder 6, и более новый. Так зародилась идея сделать динамический массив стабильных элементов. Ну, а потом еще выяснилось, что в Qt, на котором я писал программы раньше, начиная с 6 версии фактически ликвидирован класс QList, который превратился в мурзилку для QVector. История такая, что в Qt 5 для всех объектов, кроме простых типов, QList как раз и работал без реаллокации элементов. Они хранились просто в "куче", а для любителей realloc существал QVector. Т.е. всё было логично и удобно. Потом кто-то из разработчиков Qt изучил тысячи строк кода и внезапно обнаружил, что многие программисты используют QList там, где следовало бы применить QVector, и не нашел ничего лучше как просто превратить QList в QVector. Так что для Qt 6, если вдруг потребуется динамический массив без реаллокаций, то тоже можно применять класс nx_array.

Не имея желания изобретать велосипед, я взял за основу стандартные std::list и std::vector. Вся идея очень проста: у класса nx_array два поля данных: data_list[] типа std::list и ptr_vector[] типа std::vector. Элементы массива статично лежат в data_list[], т.е. в "куче", без реаллокаций, а ptr_vector[] хранит указатели на эти элементы. Через эти указатели и реализован доступ по индексу.  

Немного о реаллокациях и стабильности указателей.

Как уже было сказано, указатель-переменная, созданный отдельно от массива и указывающий на любой его элемент, сохранит актуальность независимо от того, какие действия производятся с этим массивом. Однако, это не означает, что элемент массива не может быть удален или перемещен с изменением адреса, что неминуемо приведет к инвалидации указателя. И, само собой, это не означает, что указатель будет как-либо привязан к индексации массива. Например:

nx_array<QString> my_array;                
my_array += "B";                           // добавили элемент my_array[0] со значением "B"
QString* item_ptr = &(my_array[0]);        // item_ptr хранит адрес my_array[0]
// добавляем в массив новый my_array[0]:  
my_array.addItem("A", 0);                  // item_ptr хранит прежний адрес, который теперь указывает на my_array[1]
 

Методы класса.

Методы nx_array условно разделяются на три категории: 

1. Собственные методы класса, необходимые для комфортной работы. Эти методы отличаются своими названиями, записываемыми без символа подчеркивания, где каждое следующее слово пишется с заглавной буквы, например, метод добавления элемента - addItem(). 

2. Реализация некоторых стандартных методов STL, дающая возможность при желании быстро заменить в готовой программе объекты std::vector на nx_array, не исправляя вызовы методов класса. Речь идет о всем знакомым методах push_back(), pop_back() и т.д. 

3. Методы, являющиеся общими для nx_array и STL, они, всё по той же логике совместимости с std::vector, имеют названия из STL: например, функция, возвращающая размер массива, т.е. количество его элементов, называется size(), а функция возвращающая ссылку на элемент массива с проверкой индекса - at(index).

Конструкторы класса

1. Конструктор без параметров и конструктор с параметрами размера и зарезервированного пространства (резервирование относится только к внутреннему массиву ptr_vector, а не к основному хранилищу объектов):

nx_array<int> arr1;               // пустой массив
nx_array<int> arr2(5);            // массив с 5 элементами
nx_array<int> arr3(3, 10);        // 3 элемента, резерв = 10 

2. Конструктор из std::initializer_list:

nx_array<int> arr1{1, 2, 3};                            // массив с тремя элементами
nx_array<int> arr2({1, 2, 3}, 10);                      // резерв = 10
nx_array<QString> arr1{"a", "b", "c"};                  // массив с тремя элементами
nx_array<QString> arr2({"a", "b", "c"}, 10);            // резерв = 10
nx_array<QString> arr3{{"a", "b", "c"}, 10};            // тоже самое
 

3. Конструктор из rvalue-контейнера

nx_array<int> arr1(std::vector<int>{1, 2, 3});    // Перемещение из временного вектора
nx_array<int> arr2(std::list<int>{4, 5, 6});      // Перемещение из временного списка
 

В следующих конструкторах последний bool-параметр означает выбор между копированием (=false) и перемещением (=true):

4. Конструктор из контейнеров:

std::list<int> lst = {1, 2, 3};
nx_array<int> arr1(lst, 10);                        // Копирование из std::list, резерв = 10
nx_array<int> arr2(lst, 10, true);                  // Перемещение, резерв = 10

5. Конструктор из итераторов:

std::vector<int> vec = {1, 2, 3};
nx_array<int> arr1(vec.begin(), vec.end());           // копирование из вектора
nx_array<int> arr2(vec.begin(), vec.end(), 10, true); // перемещение, резерв = 10

6. Конструктор копирования и перемещения:

nx_array<int> original{1, 2, 3};
nx_array<int> copy(original);                       // Копирование
nx_array<int> moved(std::move(original));           // Перемещение
nx_array<int> moved2(original, 0, true);            // Явное перемещение

7. Конструкторы из конкретных контейнеров:

std::array<int, 3> std_arr = {1, 2, 3};
nx_array<int> arr_a(std_arr, 5);                    // Копирование, резерв = 5
std::vector<int> vec = {1, 2, 3};
nx_array<int> arr_v(vec, 10, true);                 // Перемещение, резерв = 10 

8. Конструктор из простого массива:

int raw_arr[] = {1, 2, 3};
nx_array<int> arr1(3, raw_arr);                     // Копирование
nx_array<int> arr2(3, raw_arr, 10, true);           // Перемещение, резерв = 10

// Пример с std::vector: std::vector<int> vec = {4, 5, 6}; nx_array<int> arr3(3, vec.data()); // Копирование из данных вектора


 

Основные методы класса:

bool isPointerValid(const T* ptr) - ищет переданный в функцию указатель в векторе указателей класса. Несмотря на то, что указатели на элементы массива стабильны и в общем и целом можно без опасений создавать переменные-указатели на эти элементы, но указатель всё же потеряет актуальность в случае удаления элемента из массива. По этой причине реализована эта функция поиска элемента по его адресу.

int getItemIndex(const T* ptr) - по сути та же функция, но возвращает не признак того, найден элемент или нет, а его индекс. Если элемент отсутствует, вернет значение -1.

T* getItemPtr(size_type index) - возвращает адрес элемента массива по его индексу.

T& at(size_type index) - возвращает ссылку на элемент массива по его индексу (т.е. тоже самое, что оператор [index]) с проверкой корректности указанного индекса.

size_type size() - возвращает количество элементов массива 

void checkIterator(const const_iterator& it) - функция проверки актуальности итератора

void moveItem(size_type from, size_type to) - перемещает элемент массива на другую позицию

void swapItems(size_type i, size_type j) - меняет два элемента массива местами

void removeItem(size_type index) - удаляет элемент массива по индексу

void removeRange(size_type start, size_type finish = END_POS) - удаляет диапазон элементов массива

void addItem(const T& value, int position = END_POS, bool useExc = false) - добавляет элемент в массив. По умолчанию - в конец массива. Если указан номер элемента, превышающий размер массива, то функция либо добавит элемент в конец (если useExc = false), либо выдаст исключение.

T& emplaceItem(int position = END_POS, Args&&... args) - добавляет элемент с инициализацией

void resize(size_type new_size) - меняет размер массива, т.е. либо удаляет лишние элементы, либо наоборот добавляет пустые элементы в конец массива

void resize(size_type new_size, const T& value) - тоже самое, но со значением по умолчанию для добавляемых элементов

void reverse() - меняет последовательность элементов массива на противоположную

bool sort() - сортирует элементы массива от меньшего к большему. Требует, чтобы типа данных массива поддерживал оператор "<", в противоположном случае функция вернет false

list_type toStdList() - возвращает копию массива в формате std::list

list_type ejectStdList() - возвращает (извлекает) оригинал объекта внутреннего поля std::list класса nx_array через list::splice(), обнуляя исходный массив. Адреса бывших элементов массива сохраняются неизменными, сторонние указатели не теряют актуальности.

list_type toStdList() && - вызывает ejectStdList()

std::vector<T> toStdVector()  - возвращает копию массива в формате std::vector

std::vector<T> toStdVector() && - возвращает std::vector с перемещенными из std::list данными (через std::make_move_iterator), обнуляя исходный массив

std::deque<T> toStdDeque()  - возвращает копию массива в формате std::deque

std::deque<T> toStdDeque() &&  - возвращает std::deque с перемещенными из std::list данными (через std::make_move_iterator), обнуляя исходный массив

QList<T> toQList()  - возвращает копию массива в формате QList

QList<T> toQList() &&  - возвращает QList с перемещенными из std::list данными (через std::make_move_iterator), обнуляя исходный массив

void apply(Function func) - применяет функцию ко всем элементам массива 

T extract(size_type index) - извлекает элемент (через std::move) с удалением из контейнера. Не работает с типами данных, которые нельзя перемещать

T extract(const_iterator pos) - метод, аналогичный предыдущему, но работающий через итератор, а не по индексу. Является аналогом метода extract() в реализации std::list стандарта C++17

 void addArray(const Container& container, int position = END_POS) - присоединение другого массива путем копирования элементов. Поддерживаются массивы типа nx_array , vector, list, deque и QList

void addArray(const Container&& container, int position = END_POS) - присоединение другого массива путем перемещения элементов функцией std::move. Функция аналогичная методу mergeArray(), но требует указания std::move в явном виде при вызове. При вызове для типов данных nx_array или std::list вместо std::move будут вызваться функции spliceArray. 

void addArray(const T* arr, size_type size, int position = END_POS, bool useExc = false) - функция, аналогичная предыдущей, но для добавления копии простого массива. Требует указания его размера.  При useExc = true провоцирует исключения при указании нулевого адреса массива или некорректного параметра position

void spliceArray(list_type& lst, int position = END_POS) - присоединение массива std::list через list.splice, т.е. с сохранением присоединяемых элементов в исходных областях памяти. Указатели, созданные для этих элементов до выполнения функции, сохранят актуальность. Присоединяемый массив обнулится

void spliceArray(nx_array& arr, int position = END_POS) - метод, аналогичный предыдущему, но для nx_array . Поскольку nx_array хранит данные в std::list, то для него также доступно перемещение элементов через list.splice

void mergeArray(Container& container, int position = END_POS) - присоединение другого массива путем перемещения элементов функцией std::move. Поддерживаются массивы типа vector, deque и QList. Не используется для list и nx_array , поскольку для них существует более эффективная функция spliceArray()

Операторы, используемые в nx_array.

Как и в случае с функциями добавления элементов, операторы различают копирование и перемещение объектов через rvalue-ссылки (std::move) или функцией list.splice. Для присвоения копий элементов других классов-контейнеров используется оператор "=", а для добавления к существующим элементам таких копий - операторы "+" и "+=". Для присвоения другого контейнера путем перемещения его элементов используется оператор "<<=", для добавления элементов путем перемещения - "<<".

nx_array<QString> arr1 = {"X", "Y"};

nx_array<QString> arr2 = {"Z"};


arr1 += "A" + QString("B") << arr2; // arr1 = {X, Y, A, B, Z}

                                    // arr2 - пуст 

arr1 += ("A" + QString("B")) << arr2;  // более корректная запись (учитывая, что << имеет более высокий приоритет, чем +)

Оператор [index] работает аналогично стандартному массиву, vector'у и т.п. 

Методы, реализованные для удобной замены std::vector на nx_array.

Методы, возвращающие итераторы: begin(), end(), rbegin(), rend(), cbegin(), cend(), crbegin(),  crend() 

UPD
Добавлено публичное поле isDeleteItemsOnDestroy типа bool. Поле используется для массива, хранящего указатели, применительно к которым следует выполнить команды delete при удалении массива (объекта класса nx_array) из памяти. Поле isDeleteItemsOnDestroy немного схоже с свойством OwnObjects класса TList из Delphi и C++Builder. Однако, в отличие от OwnObjects оно управляет исключительно поведением деструктора ~nx_array(), а не каких-либо других методов класса. Если требуется выполнить команды delete применительно к удаляемым элементам динамического массива nx_array, которые являются указателями на объекты классов, то для этого специально реализованы отдельные методы, имеющие постфикс "_with_delete":


clear_with_delete()

iterator erase_with_delete(const_iterator pos)

removeItem_with_delete(size_type index) 

removeRange_with_delete(size_type start, size_type finish = END_POS) 

replace_with_delete(size_type index, T new_item)


четверг, 16 февраля 2023 г.

Проблема установки WinCC для TIA Portal из-за наличия уже установленной версии

 В продолжение предыдущего поста по поводу установки WinCC. Дошло до установки TIA Portal v15 при наличии уже установленной версии v13. Установщик на v15 общий - и для Step 7, и для WinCC, ну, и при старте выдает вот такое: 

"TIA Portal STEP 7 Professional V15.1 - WinCC Professional V15.1 cannot be operated in parallel with the currently installed version of 'WinCC Professional' or 'WinCC Runtime Professional'. Please remove..."

Тут история такая же, как и с SQL Server из предыдущего поста. Siemens опять развел панику на ровном месте, потому что на самом деле разные версии WinCC Flexible прекрасно уживаются в одной операционной системе. Соответственно, нужно опять подчистить конфигурационный файл установщика, чтобы снять эти ограничения. Файл тот же самый, что и в истории про SQLServer:

 \InstData\Resources\SIA2.ini

В нем находим все упоминания TermMessage9, TermMessage13, TermMessage16, TermMessage17, TermMessage31, TermMessage32, TermMessage36 и удаляем их. Далее установка проходит без эксцессов.

Ну, и если вы хотите быстро находить в установках Siemens файлы, где прописаны подобные ограничения (а это могут быть не только SIA2.ini), то рекомендую использовать бесплатную программу Searcher. Например, установщик требует что-то удалить (remove), так давайте по этому слову и найдём все текстовые файлы *.ini, где содержится слово "remove":

Вот, собственно, и всё.




среда, 15 февраля 2023 г.

Проблема установки WinCC для TIA Portal v11 из-за SQL Server 2005 / 2008

Как известно, старые версии TIA Portal актуальности не теряют (из-за отсутствия совместимости версий для внесения изменений и осуществления Upload). И тут возникла проблема с установкой WinCC Professional v11 из-за уже установленного SQL Server 2008. Установщих TIA Portal v11 устанавливает, помимо всего прочего, SQL Server 2005. Но по какой-то неизвестной причине Siemens решил, что обе версии SQL Server - 2005 и 2008 - не могут существовать в одной операционной системе.

 Чтобы отключить это ограничение, нужно отредактировать файл \InstData\Resources\SIA2.ini. В разделе TERMS нужно закомментировать (поставить ";" в начале строки) или удалить эти две строки:

{Registry};{HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\Tools\Setup\Version};{REGSZ};{NOTEXIST};{0};{TermMessage30};{0} 

{Registry};{HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\Tools\ClientSetup\CurrentVersion\CurrentVersion};{REGSZ};{NOTEXIST};{0};{TermMessage30};{0}

среда, 4 мая 2022 г.

Реализация RS-триггера (Latching Relay) на Siemens LOGO! для сети Ethernet

Сегодняшняя история про Siemens LOGO!, а, как всем известно, любая история про Siemens LOGO! - это всегда небольшой танец с бубном. Вот и в этот раз понадобилось, казалось бы, совершить простейшее действие, а оно оказалось не таким уж и простым...

Общие сведения

 
Но начнём с общей информации. Как вообще работает коммуникация LOGO! с панелями оператора? Смотрите, есть такие способы коннектиться с LOGO!:

1. Через драйвер, который в панелях Siemens так и называется "LOGO!". Этот драйвер есть на новых панелях оператора, присутствующих в WinCC в составе TIA Portal'a: KTP, TP и др. Есть этот драйвер и на WinCC Flexible Smart. Но этого драйвера нет ни в новых WinCC Runtime, ни WinCC Flexible Runtime.

2. Через драйвер, который называется "SIMATIC S7 200". Внезапно драйвер для контроллера S7-200 подошёл к LOGO. Причём, он не просто подошёл, а полностью идентичен по части работы. Так что если для панели оператора не доступен драйвер "LOGO!", но есть драйвер S7-200, то можно использовать его. На той же WinCC Flexible Runtime, например, такой способ является самым удобным. А вот на WinCC Runtime из TIA Portal'a драйвера S7-200 почему-то нет...
Сразу, кстати, проясняю насчет драйвера для S7-300/S7-400: через него работать не будет.

3. Через OPC Server. Лично я использую Multi-Protocol MasterOPC Server от компании Insat. Проблем пока не было. Но этот способ, понятное дело, только для PC.

4. Через Modbus TCP. Тут уже, ясное дело, не только панели оператора, а любые другие устройства можно к LOGO цеплять по Ethernet.
 

Драйверы LOGO! и S7-200

 
Итак, по первым двум способам - через драйверы "LOGO!" и "SIMATIC S7 200". Повторяю, эти способы абсолютно идентичны. Тут всё, в принципе, элементарно: I1, I2, I3, I4, I5, I6, I7, I8, I9, I10... превращаются в тэги с адресами I0.0, I0.1, I0.2, I0.3, I0.4, I0.5, I0.6, I0.7, I1.0, I1.1... соответственно. Тоже самое по битам Q и М. Для всего остального нужно задать таблицу Variable Memory Configuration: Tools -> Parameter VM Mapping... Например, вот так:
 
 
 Соответственно, так мы получили адреса VW0, VW2, VW4, VD6. Далее заводим эти теги в таблицу тегов WinCC / WinCC Flexible, получаем что-то типа того:


Обратите внимание, что у Timer_Period значения должны включать в себя 2 числа. В данном случае числа 1 и 14, например:

Но числовое значение у нас только одно, и в нём 1 и 14 превратятся в 114. Почему не сделано два отдельных числа - это загадка для меня. Назовём это "неудобство номер раз".

 

Драйвер Modicon MODBUS TCP/IP

 

Что касается Modbus TCP/IP, то чтобы к нему подключиться, сначала надо создать соединение в LOGO! SoftComfort:

Tools -> Ethernet Connections...

 


В принципе, и для драйверов S7 из предыдущего раздела следовало бы создавать подключение, но я не замарачивался этим, поскольку просто подключаю панель оператора, а оно и так работает.

Возвращаясь к Modbus. Наши адреса VW0, VW2, VW4... становятся регистрами #1, #2, #3 соответственно. Или в более понятном представлении: 4x400001, 4x400002, 4x400003. Обратите внимание, не 4x4000x, а 4x40000x.

Теперь переходим к "проблеме номер два". Проблема связана со счётчиком. У него число представлено в формате двойного слова. С ним нет проблем при использовании драйверов LOGO! и S7-200, а с драйвером Modbus TCP/IP возникает типичная проблема для Siemens'a, у которого имеется неувязочка по части последовательности передачи младшего и старшего слова данных. А функционала, чтобы поменять их местами, конечно же, нет. Эта тема уже затрагивалась при обсуждении Modbus RTU на S7-1200, и никаких вариантов Siemens не предложил. Какой выход? Если счётчик предназначен для небольших чисел, то можно использовать не весь VD6, а только VW8, что соответствует регистру 4x400005. Как забрать полностью VD6, я не знаю. Может быть, как-то через скрипты в WinCC это можно сделать...

Что касается остальных областей памяти. С ними всё просто: есть зарезервированные адреса.


Т.е. заводим в таблицу тегов, например, 0x8193 - это будет у нас Q1.


Как управлять битами


Как все мы знаем, LOGO! не даёт возможностей использовать в логических цепочках биты Q и M, которым до этого не были присвоены значения через катушки Relay Coil. Например:

Здесь, чтобы использовать I3 не нужно ничего: просто добавили контакт, выбрали I3 - и всё хорошо. Чтобы также добавить контакт M1, сначала нужно сделать присвоение, как на рисунке. Без него создать контакт M1 у нас не получится. Следовательно, раз мы сделали присвоение, то пытаться дистанционно управлять таким битом бесполезно. Ситуация, когда управление битом без его использования в программе LOGO! может пригодится касается только дискретных выходов Q контроллера. Т.е. для тех Q, для которых в программе не созданы катушки присвоения, управление можно производить напрямую с панели оператора. 

Чтобы решить проблему с обычными битами, Siemens ввёл дополнительные компоненты:

Выбирая Network input, вы создаете объект с адресом в области памяти V, по умолчанию - V0.0. И уже им можете свободно управлять с панели оператора. Если используете протокол Modbus, то надо не забывать последовательность битов. Например, биты в регистре 4x400006 (VW10) будут располагаться так:


Соответственно, V10.0 - это 4x400006.8 и т.д. В программе LOGO! можем сделать что-то наподобие такого:

Реализация RS-триггера (Latching Relay)

Вот вроде бы как всё хорошо и понятно. Для каких-то компонентов можно расшарить данные через таблицу Variable Memory Configuration, для других задач есть специальные Network-компоненты, где соответствие выставляется в настройках самих этих компонентов. А теперь возникает как бы логичный вопрос: как сделать так, чтобы битом можно было управлять одновременно и с панели оператора, и в программе контроллера? Если помните, то в упомянутом уже S7-200 были три варианта тех самых катушек для присваивания значений битам:

1. Присваивание (=)

2. Установка бита (S)

3. Сброс бита (R)

Вот так это выглядит в инструкции к S7-200:

Однако, в LOGO! нет компонентов для установки и сброса битов. Причина весьма проста: LOGO! SoftComfort не допускает дублирование катушек (и блоков - для случая с FBD). Условно считается, что вся программа на LOGO! выполняется как реальная электрическая схема, а следовательно, многократных присвоений битов быть не может. И хотя программисты прекрасно понимают, что в конечном итоге компилятор выдаст из всех этих блоков самый обыкновенный машинный код, который будет выполняться последовательно, Siemens пока что не делает на LOGO! того, что четверть века назад почему-то спокойно сделал на S7-200. Выход из данной ситуации известен: использование RS-триггеров (Latching Relay). Способ громоздкий и неудобный (поскольку проверки условий установки и сброса бита могут находится в совершенно разных частях программы), но обходимся тем, что есть. И всё было бы хорошо, если бы мы могли задать биту RS-триггера какое-то соответствие в V-memory. Но, к сожалению, возможности это сделать нам не предоставили.

Итак, какие же у нас варианты? Прежде всего надо придумать простой пример. 

Допустим, есть бит который определяет режим работы (0 - ручной, 1 - автоматический режим). Бит этот устанавливается с панели оператора (HMI). И допустим, есть дискретный вход I0, который принудительно переводит этот бит в 0, т.е. в ручной режим. На языке программирования STL это выглядело бы так (второй аргумент в командах R и S я опускаю, чтоб не мешался):

LD  I0.0
R   V9.0      // это вот наш бит, допустим, который на HMI выглядит
              // в виде переключателя "РУЧН/АВТО"

Т.е. мы бы управляли битом V9.0 дистанционно (в программе этот момент бы никак не фигурировал), а дополнительно сбрасывали бы его по I0.0. Но мы не можем так сделать, т.к. у нас нет катушки для сброса бита компонента Network Input, которым мы управляем с панели оператора. Установка и сброс мы делаем через RS-триггер. Следовательно, на языке STL это было бы так:

// V10.3 - это бит, привязанный к переключателю "РУЧН/АВТО" на HMI
// V9.0  - это RS-триггер, определяющий режим работы
LD  I0.0
R   V10.3     // наш бит надо сбросить при блокирующем сигнале I0.0
ON  V10.3     // если I0.0 = true или V10.3 = false => тогда...
R   V9.0      // "РУЧН"
LDN I0.0
A   V10.3     // если I0.0 = false и V10.3 = true => тогда...
S   V9.0      // "АВТО"

Попробуем что-то подобное изобразить для LOGO!:


И вроде бы всё неплохо начиналось.. Но есть один нюанс: как мы будем сбрасывать бит V10.3 по условию, что замкнулся вход I1? Эта та самая вторая строчка "R V10.3" в моем примере на STL. Если мы этого не сделаем, то I1 хоть и обнулит SF006, но NI4 (V10.3) продолжит оставаться равным 1. Значит, на экране режим работы всё также будет отображаться как "АВТО", хотя по факту будет уже "РУЧН".

Как выйти из этой ситуации? Я предлагаю следующее решение: использовать счётчик. Компонент счётчик (Up/Down Counter) это ведь тоже по сути своей триггер. Только у него входы не R и S, а R и Cnt. Cnt прибавляет +1 к текущему значению счётчика. Например, было 0, стало 1. Мы можем задавать значение, выше которого бит счётчика устанавливается, а ниже - сбрасывается. Т.е. зададим это значение равным 1, и бит счетчика будет сбрасываться при 0 и устанавливаться при больше или равно 1. В чём важное для нашей задачи отличие счётчика от RS-триггера? Как было показано выше, мы можем расшаривать текущее значение счётчика и редактировать его на панели оператора. Если же наш счётчик будет работать в пределах 0..1, т.е. иметь всего два возможных значения, то в этом случае получится, что из всего того двойного слова данных, где это значение хранится, нас будет интересовать один-единственный бит. Самый первый бит. Тот, который отличает текущее значение 0 от значения 1. И вся наша задача легко и просто ушла в одну строку:

Следует не забывать, что у компонента "Счётчик" первый верхний вход - это сброс. Важно не перепутать с компонентом "RS-Триггер", у которого сбросом является второй вход.


Как изображено на самом первом рисунке в этой статье, текущее значение счетчика у нас имеет адрес VD6. Соответственно, интересующий нас бит имеет адрес V9.0 (или 4x400005.16 в случае с Modbus). Меняя этот бит, мы меняем значение счётчика. А сам счетчик настроен на включение при значении больше или равно 1:

Вот мы и получили, по сути дела, то, что и хотели получить в самом начале:

LD  I0.0
R   V9.0

Естественно, что мы можем не только сбрасывать бит триггера программно, но  устанавливать его:


 

В данном примере мы управляем дискретным выходом Q1, привязанным к нашему управляющему биту V9.0 блока C003. Помимо того, что мы можем менять значение V9.0 на панели оператора или в СКАДА, также в коде программы входом I1 этот бит принудительно сбрасывается, а входом I2 принудительно устанавливается. Единственный нюанс: счетчик не должен считать больше 1. Поэтому после I2 стоит бит проверки (M10 == false), что счетчик в данный момент = 0. Эту проверку нельзя выполнить напрямую из-за ограничений среды разработки (контакт C003 не может приходить в блок C003), поэтому задействован промежуточный бит M10, что, в общем-то, не сильно загромоздило нам программу.