Скрипты на Сicode и CiVBA в помощь разработчикам

Обсуждение SCADA-систем: Citect SCADA, Vijeo Citect, CitectSCADA, CitectFacilities, PowerLogic SCADA, PowerSCADA, MX4SCADA, Desigo Insight.
Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

Сicode: Функция определения текущей языковой раскладки

#11

Сообщение alex » 27 окт 2016, 14:48

/*
        (с) 2016 http://www.proasutp.com

        GetKeyboardLayout - функция возвращает идентификатор текущего языка ввода.

        Вход:
                Нет.
        Выход:
               1033 - английский язык;
               1049 - русский язык;
                 -1 - ошибка выполнения функции.
*/


INT FUNCTION GetKeyboardLayout()

    INT fGetKeyboardLayout = 0;
    STRING sResult = "";
    INT iResult = 0;

    INT CONST_BAD_RESULT = -1;      //код ошибки выполнения функции
    INT CONST_ENGLISH_LANG = 1033;  //идентификатор английского языка
    INT CONST_RUSSIAN_LANG = 1049;  //идентификатор русского языка

    fGetKeyboardLayout = DLLOpen ("user32.dll", "GetKeyboardLayout", "JJ");
   
    IF (fGetKeyboardLayout = CONST_BAD_RESULT) THEN
        RETURN CONST_BAD_RESULT;
    END

    sResult = DLLCall (fGetKeyboardLayout,"");
    DLLClose (fGetKeyboardLayout);
   
    iResult = StrToInt (sResult) BITAND 0x0000FFFF;
    IF ((iResult <> CONST_ENGLISH_LANG) AND (iResult <> CONST_RUSSIAN_LANG)) THEN
        iResult = CONST_BAD_RESULT;
    END
   
    RETURN iResult;
   
END



Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

Сicode: Работа с файловыми функциями на примере подсчета количества строк текстового файла

#12

Сообщение alex » 24 янв 2017, 22:34

Пример использования...

//
// (c) 2017 www.proasutp.com
//
// Тестовая функция для демонстрации работы функции GetCountLinesInTextFile
//

FUNCTION TestGetCountLinesInTextFile ()
   
    STRING FILE_NAME = "d:\www.proasutp.com\TestFile.txt";
    INT ERROR_CODE = -1;
    INT OK_BUTTON_MODE = 0;
   
    INT nCount = 0;

    nCount = GetCountLinesInTextFile (FILE_NAME);
   
    IF nCount <> ERROR_CODE THEN
        Message ("Количество строк в файле", "В файле <" + FILE_NAME + "> " + IntToStr(nCount) + " строк.", OK_BUTTON_MODE);
    ELSE
        Message ("Количество строк в файле", "Ошибка доступа к файлу <" + FILE_NAME + ">.", OK_BUTTON_MODE);   
    END
   
END

и собственно сама функция...

/*
        (c) 2017 www.proasutp.com

        GetCountLinesInTextFile - функция подсчитывает количество строк в текстовом файле

        Вход:
                sFileName - полный путь к текстовому файлу.
        Выход:
                Возвращает количество строк в текстовом файле (0...N), либо -1 - ошибка выполнения функции.
        Примечание:
                Размер текстового файла не более 1Мб.
               

*/


INT FUNCTION GetCountLinesInTextFile (STRING sFileName = "")

    INT USER_ERROR_CHECKING_MODE = 1;           // Режим обработки ошибок пользователем
    INT SYSTEM_ERROR_CHECKING_MODE = 0;         // Режим обработки ошибок системой
   
    INT OVERFLOW_ERROR = 275;                   // Код ошибки переполнения, ошибка возникает при размере строки более 255 символов
   
    STRING READ_ONLY_FILE_ACCESS_MODE = "r";    // Режим доступа к файлу только на чтение
   
    INT ERROR_CODE = -1;                        // Обобщенный код ошибки
    INT NO_ERROR = 0;                           // Код отсутствия ошибки

    INT hFile = 0;                              // Хендл открытого файла
    INT nCount = 0;                             // Счетчик строк
    INT bIsError = 0;                           // Код ошибки выполнения функции
    STRING sLine = "";                          // Содержимое прочитанной строки

    // Проверка на отсутствие имени файла
   
    IF sFileName = "" THEN
        RETURN ERROR_CODE;
    END

    // Включаем режим самостоятельной обработки ошибок выполнения функций...

    ErrSet(USER_ERROR_CHECKING_MODE);
   
    // Открываем переданный файла в режим только для чтения...
   
    hFile = FileOpen (sFileName, READ_ONLY_FILE_ACCESS_MODE);
   
    // Контроль успешности открытия файла...
   
    IF IsError() <> NO_ERROR THEN
        RETURN ERROR_CODE;
    END

    // В цикле, последовательно читаем строку за строкой и считаем их...

    nCount = 0;

    WHILE NOT FileEOF(hFile) DO
   
        sLine = FileReadLn(hFile);
       
        // Контроль успеха выполнения функции чтения строк...
       
        bIsError = IsError();
        IF (bIsError <> OVERFLOW_ERROR) AND (bIsError <> NO_ERROR) THEN
            RETURN ERROR_CODE;
        END
       
        // Если получен код переполнения, значит строка больше 255 символом,
        // поэтому в цикле читаем остальные части длИнной строки порциям по 255 символов до конца
       
        WHILE bIsError = OVERFLOW_ERROR DO
       
            sLine = FileReadLn(hFile);
           
            bIsError = IsError();
            IF (bIsError <> OVERFLOW_ERROR) AND (bIsError <> NO_ERROR) THEN
                RETURN ERROR_CODE;
            END
           
        END
       
        nCount = nCount + 1;

    END
   
    // Закрываем файл...
   
    FileClose (hFile);
   
    // Контроль успеха выполнения функции закрытия файла...
   
    bIsError = IsError();
    IF bIsError <> NO_ERROR THEN
        RETURN ERROR_CODE;
    END
   
    // Возвращаем количество строк в файле.
   
    RETURN nCount;
   
END



Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

Cicode: Программная кастомизация стандартной формы выбора принтера

#13

Сообщение alex » 29 янв 2017, 12:26

Иногда встают задачи по кастомизации, изменению стандартных вещей в Citect или операционной системе, такую кастомизацию можно сделать различными путями, например, внесением изменений в стандартные файлы программного обеспечения, то такой путь не совсем правильный, куда более элегантное решение - это программно, динамически и гибко подстраивать систему под свои задачи, как пример, на форуме возникла тема PagePrint как отключить кнопку Сеть(network), собственно по мотивам этой темы и был реализован данный подход.

/*
        (c) 2017 http://www.proasutp.com

        HideNetworkButtonInSelectPrinterForm - функция скрывает кнопку "Сеть..." на стандартной форме/диалоге выбора принтера.

        Вход:
                Нет

        Выход:
                Успех (TRUE)/Неуспех (FALSE) выполнения функции.
*/


INT FUNCTION HideNetworkButtonInSelectPrinterForm ()

    // Константы

    STRING PRINT_DIALOG_RU_CAPTION = "Настройка печати";            // заголовок формы выбора принтера в русскоязычной ОС
    STRING PRINT_DIALOG_EN_CAPTION = "Print Setup";                 // заголовок формы выбора принтера в англоязычной ОС
    STRING PRINT_DIALOG_CLASS_NAME = "#32770";                      // имя класса формы выбора принтера
       
    STRING PRINT_DIALOG_NETWORK_BUTTON_RU_CAPTION = "С&еть...";     // заголовок кнопки выбора сетевого принтера в русскоязычной ОС
    STRING PRINT_DIALOG_NETWORK_BUTTON_EN_CAPTION = "Net&work...";  // заголовок кнопки выбора сетевого принтера в англоязычной ОС
    STRING PRINT_DIALOG_NETWORK_BUTTON_CLASS_NAME = "Button";       // имя класса кнопки выбора сетевого принтера
    STRING PRINT_DIALOG_NETWORK_BUTTON_ID = "1037"; //"0x40D"       // идентификатор кнопки выбора сетевого принтера на форме выбора принтера
   
    INT HIDE_DIALOG_ITEM = 0;                                       // скрыть элемент управления на форме
    INT SHOW_DIALOG_ITEM = 1;                                       // показать элемент управления на форме
   
    INT RU_LAND_ID = 0x419;                                         // идентификатор русского языка интерфейса пользователя ОС
    INT EN_LANG_ID = 0x409;                                         // идентификатор английского языка интерфейса пользователя ОС
   
    INT OK_RESULT = 1;                                              // успех (TRUE) выполнения функции
    INT BAD_RESULT = 0;                                             // неуспех (FALSE) выполнения функции
   
    // Локальные переменные
   
    INT hGetSystemDefaultUILang = 0;                                // хендл функции получения языка по умолчанию интерфейса пользователя ОС                              
    INT hGetDlgItem = 0;                                            // хендл функции получения хендла элемента управления формы
    INT hShowWindow = 0;                                            // хендл функции управления видимостью элемента управления формы
    INT hFindWindow = 0;                                            // хендл функции поиска окна
   
    INT bIsOk = 0;                                                  // возвращаемый функцией результат работы
    INT hSelectPrintDlgWnd = 0;                                     // хендл найденной формы выбора принтера
    INT hNetworkButton = 0;                                         // хендл кнопки выбора сетевого принтера
   
    INT nLangID = 0;                                                // язык интерфейса пользователя ОС по умолчанию
    STRING sArgs = "";                                              // список аргументов для вызываемой DLL функции
   
    // 1. Открываем ссылки на необходимые функции для дальнейшего их использования...
   
    hGetSystemDefaultUILang = DLLOpen ("KERNEL32.DLL", "GetSystemDefaultUILanguage", "J");
    hFindWindow = DLLOpen ("USER32.DLL", "FindWindowA", "JCC"); // выбираем ANSI версию функции
    hGetDlgItem = DLLOpen ("USER32.DLL", "GetDlgItem", "JJJ");
    hShowWindow = DLLOpen ("USER32.DLL", "ShowWindow", "AJJ");
   
    // 2. Получаем текущий язык операционной системы...
   
    nLangID = DLLCall (hGetSystemDefaultUILang, "");
   
    // 3. Выделяем младшее слово, отвечающее за язык интерфейса пользователя операционной системы...
   
    nLangID = nLangID BITAND 0xFFFF;
   
    // 4. Формируем соответствующим образом строку аргументов для поиска формы выбора принтера...
    //    (поддерживается работа только на ОС на русском и английском языке)
   
    SELECT CASE nLangID
    CASE RU_LAND_ID
        sArgs = "^"" + PRINT_DIALOG_CLASS_NAME + "^"," + "^"" + PRINT_DIALOG_RU_CAPTION + "^"";
    CASE EN_LANG_ID
        sArgs = "^"" + PRINT_DIALOG_CLASS_NAME + "^"," + "^"" + PRINT_DIALOG_EN_CAPTION + "^"";
    CASE ELSE
        sArgs = "";
    END SELECT
   
    IF sArgs <> "" THEN
   
        // 5. Ищем форму выбора принтера...
   
        hSelectPrintDlgWnd = DLLCall (hFindWindow, sArgs);
       
        IF hSelectPrintDlgWnd <> 0 THEN
       
            // 6. Если форма найдена, получаем хендл кнопки выбора сетевого принтера
           
            sArgs = IntToStr (hSelectPrintDlgWnd) + "," + PRINT_DIALOG_NETWORK_BUTTON_ID;
            hNetworkButton = DLLCall (hGetDlgItem, sArgs);
           
            IF hNetworkButton <> 0 THEN
           
                // 7. Если хендл кнопки получен, делаем кнопку невидимой...
           
                sArgs = IntToStr (hNetworkButton) + "," + IntToStr (HIDE_DIALOG_ITEM);
                DLLCall (hShowWindow, sArgs);
               
                bIsOk = OK_RESULT;     
           
            ELSE
                bIsOk = BAD_RESULT;    
            END
           
        ELSE
            bIsOk = BAD_RESULT;
        END
   
    ELSE
        bIsOk = BAD_RESULT;
    END
   
    // 8. Закрываем все ссылки на функции...
   
    DLLClose (hGetSystemDefaultUILang);
    DLLClose (hShowWindow);
    DLLClose (hGetDlgItem);
    DLLClose (hFindWindow);
   
    // 9. Возвращаем сформированный результат работы функции.
   
    RETURN bIsOk;

END

Результаты работы функции HideNetworkButtonInSelectPrinterForm
HideNetworkButtonInSelectPrinterForm_2.png

HideNetworkButtonInSelectPrinterForm _1.png

p.s. Разработка производилась под Windows 7 SP1 Professional x64 в Citect SCADA 2016. В другом окружении работа функции не гарантируется, но ожидается :)
У вас нет необходимых прав для просмотра вложений в этом сообщении.



Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

Cicode: Программная кастомизация стандартной формы диалога печати стандарнтого ActiveX компонента Database Exchange

#14

Сообщение alex » 12 фев 2017, 23:27

Продолжение предыдущего поста по теме программной кастомизации стандартных элементов Citect, но уже в отношении диалога печати ActiveX-компонента Database Exchange, который используется для организации обмена данными с базами данных, например, может быть использован для управления рецептами. В приведенном ниже скрипте делаются невидимыми два элемента формы диалога печати, это кнопка "Найти принтер" и чекбокс "Печать в файл".

p.s. Скрипт отличается от скрипта в предыдущем посте тем, что форма диалога печати более сложно организована, в связи с чем скрипт немного усложнился.

Код: [Выделить всё] [Развернуть/Свернуть] [Загрузить] (HideFndPrntBtnAndPrntToFileChboxInPrntDlgFrm.ci)
/*
        (c) 2017 http://www.proasutp.com

        HideFndPrntBtnAndPrntToFileChboxInPrntDlgFrm - функция скрывает кнопку "Найти принтер..." и чекбок "Печать в файл"
                                                       на стандартной форме диалога печати ActiveX-компонента Database Exchange.

        Вход:
                Нет

        Выход:
                Успех (TRUE)/Неуспех (FALSE) выполнения функции.
*/


INT FUNCTION HideFndPrntBtnAndPrntToFileChboxInPrntDlgFrm ()

    // Константы

    STRING PRINT_DIALOG_RU_CAPTION = "Печать";                  // заголовок формы диалога печати в русскоязычной ОС          
    STRING PRINT_DIALOG_EN_CAPTION = "Print";                   // заголовок формы диалога печати в англоязычной ОС
    STRING PRINT_DIALOG_CLASS_NAME = "#32770";                  // имя класса формы диалога печати

    STRING CHILD_WIN_OF_PRINT_DIALOG_RU_CAPTION = "Общие";      // заголовок дочернего окна формы диалога печати русскоязычной ОС            
    STRING CHILD_WIN_OF_PRINT_DIALOG_EN_CAPTION = "General";    // заголовок дочернего окна формы диалога печати англоязычной ОС          
    STRING CHILD_WIN_OF_PRINT_DIALOG_CLASS_NAME = "#32770";     // имя класса дочернего окна формы диалога печати
               
    STRING PRINT_DIALOG_FIND_PRINTER_BUTTON_ID = "1003";        // идентификатор кнопки поиска принтера
    STRING PRINT_DIALOG_PRINT_TO_FILE_CHECKBOX_ID = "1002";     // идентификатор чекбокса печати в файла
     
    INT HIDE_DIALOG_ITEM = 0;                                   // скрыть элемент управления на форме
    INT SHOW_DIALOG_ITEM = 1;                                   // показать элемент управления на форме
   
    INT RU_LAND_ID = 0x419;                                     // идентификатор русского языка интерфейса пользователя ОС
    INT EN_LANG_ID = 0x409;                                     // идентификатор английского языка интерфейса пользователя ОС
   
    INT OK_RESULT = 1;                                          // успех (TRUE) выполнения функции
    INT BAD_RESULT = 0;                                         // неуспех (FALSE) выполнения функции
   
    // Локальные переменные
   
    INT hGetSystemDefaultUILang = 0;                            // хендл функции получения языка по умолчанию интерфейса пользователя ОС
    INT hFindWindowEx = 0;                                      // хендл функции расширенного поиска окна
    INT hGetDlgItem = 0;                                        // хендл функции получения хендла элемента управления формы
    INT hShowWindow = 0;                                        // хендл функции управления видимостью элемента управления формы

    INT bIsOk = 0;                                              // возвращаемый функцией результат работы
    INT hPrintDlgWnd = 0;                                       // хендл найденной формы диалога печати
    INT hChildPrintDlgWnd = 0;                                  // хендл найденного дочернего окна формы диалога печати
    INT hDlgItem = 0;                                           // хендл элемента формы диалога печати, который необходимо скрыть
   
    INT nLangID = 0;                                            // язык интерфейса пользователя ОС по умолчанию
    STRING sArgs = "";                                          // список аргументов для вызываемой DLL функции
   
    // 1. Открываем ссылки на необходимые функции для дальнейшего их использования...
   
    hGetSystemDefaultUILang = DLLOpen ("KERNEL32.DLL", "GetSystemDefaultUILanguage", "J");
    hFindWindowEx = DLLOpen ("USER32.DLL", "FindWindowExA", "JJJCC");
    hGetDlgItem = DLLOpen ("USER32.DLL", "GetDlgItem", "JJJ");
    hShowWindow = DLLOpen ("USER32.DLL", "ShowWindow", "AJJ");
   
    // 2. Получаем текущий язык операционной системы...
   
    nLangID = DLLCall (hGetSystemDefaultUILang, "");
   
    // 3. Выделяем младшее слово, отвечающее за язык интерфейса пользователя операционной системы...
   
    nLangID = nLangID BITAND 0xFFFF;
   
    // 4. Формируем соответствующим образом строку аргументов для поиска формы выбора принтера...
    //    (поддерживается работа только на ОС на русском и английском языке)
   
    SELECT CASE nLangID
    CASE RU_LAND_ID
        sArgs = "0, 0, ^"" + PRINT_DIALOG_CLASS_NAME + "^"," + "^"" + PRINT_DIALOG_RU_CAPTION + "^"";
    CASE EN_LANG_ID
        sArgs = "0, 0, ^"" + PRINT_DIALOG_CLASS_NAME + "^"," + "^"" + PRINT_DIALOG_EN_CAPTION + "^"";
    CASE ELSE
        sArgs = "";
    END SELECT
   
    IF sArgs <> "" THEN
   
        // 5. Ищем форму диалога печати...
   
        hPrintDlgWnd = DLLCall (hFindWindowEx, sArgs);
         
        IF hPrintDlgWnd <> 0 THEN
       
            // 6. Если форма найдена, ищем дочернее окно, на котором находятся элементы, которые необходимо скрыть...

            SELECT CASE nLangID
            CASE RU_LAND_ID
                sArgs = IntToStr(hPrintDlgWnd) + ", 0,^"" + CHILD_WIN_OF_PRINT_DIALOG_CLASS_NAME + "^"," + "^"" + CHILD_WIN_OF_PRINT_DIALOG_RU_CAPTION + "^"";
            CASE EN_LANG_ID
                sArgs = IntToStr(hPrintDlgWnd) + ", 0, ^"" + CHILD_WIN_OF_PRINT_DIALOG_CLASS_NAME + "^"," + "^"" + CHILD_WIN_OF_PRINT_DIALOG_EN_CAPTION + "^"";
            CASE ELSE
                sArgs = "";
            END SELECT
           
            hChildPrintDlgWnd = DLLCall (hFindWindowEx, sArgs);

            IF hChildPrintDlgWnd <> 0 THEN
           
                // 7. Если дочернее окно найдено, получаем хендл кнопки поиска принтера...
           
                sArgs = IntToStr (hChildPrintDlgWnd) + "," + PRINT_DIALOG_FIND_PRINTER_BUTTON_ID;
                hDlgItem  = DLLCall (hGetDlgItem, sArgs);
           
                IF hDlgItem <> 0 THEN
               
                    // 8. Если хендл кнопки получен, делаем кнопку невидимой...
                               
                    sArgs = IntToStr (hDlgItem) + ", " + IntToStr(HIDE_DIALOG_ITEM);
                    DLLCall (hShowWindow, sArgs);
           
                    // 9. Далее получаем хендл чекбокса печати в файл...
                   
                    sArgs = IntToStr (hChildPrintDlgWnd) + "," + PRINT_DIALOG_PRINT_TO_FILE_CHECKBOX_ID;
                    hDlgItem  = DLLCall (hGetDlgItem, sArgs);
               
                    IF hDlgItem  <> 0 THEN
                   
                        // 10. Если хендл чекбокса печати в файл получен, делаем чекбокс невидимым...
                                   
                        sArgs = IntToStr (hDlgItem ) + ", " + IntToStr(HIDE_DIALOG_ITEM);
                        DLLCall (hShowWindow, sArgs);
                       
                        bIsOk = OK_RESULT;
                       
                    ELSE
                        bIsOk = BAD_RESULT;    
                    END
                           
                ELSE
                    bIsOk = BAD_RESULT;    
                END
               
            ELSE
                bIsOk = BAD_RESULT;
            END
           
        ELSE
            bIsOk = BAD_RESULT;
        END
   
    ELSE
        bIsOk = BAD_RESULT;
    END
   
    // 11. Закрываем все ссылки на функции...
   
    DLLClose (hShowWindow);
    DLLClose (hGetDlgItem);
    DLLClose (hFindWindowEx);
    DLLClose (hGetSystemDefaultUILang);
   
    // 12. Возвращаем сформированный результат работы функции.
   
    RETURN bIsOk;

END

Результаты работы функции HideFndPrntBtnAndPrntToFileChboxInPrntDlgFrm
HideFndPrntBtnAndPrntToFileChboxInPrntDlgFrm_1.png

HideFndPrntBtnAndPrntToFileChboxInPrntDlgFrm_2.png
У вас нет необходимых прав для просмотра вложений в этом сообщении.



Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

Cicode: Искользование ASCII драйвера для работы с навигационными устройствами по протоколу NMEA 0183

#15

Сообщение alex » 26 апр 2017, 11:38

Задача интеграции Citect SCADA с навигационными устройствами, работающими в основном по протоколу
NMEA 0183
Чтобы увидеть ссылку зарегистрируйтесь или войдите под своим логином.
, является задачей достаточно специфической не только для Citect SCADA, но и SCADA-систем других производителей. Если для других SCADA для этого надо находить зачастую внешние решения, начиная с использования стороннего OPC-сервера и заканчивая разработкой драйвера на C, C++ или .NET, то в Citect SCADA это все можно реализовать штатными средствами. Пример реализации протокола NMEA0183 поверх ASCII-драйвера, см. ниже...

Скрытая информация. Вы не состоите в группах, которым доступна эта информация. Как получить доступ к информации?


p.s. Код публикуется как есть, без всякой ответственности за его корректную работу и применение. Если кто воспользуется, отпишитесь о корректности работы.



Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

С#: Интерфейсный класс на Microsoft Visual C# для доступа к Citect SCADA через CTAPI

#16

Сообщение alex » 28 апр 2017, 10:45

Для тех, кто использует для решения частных задач в рамках создания систем диспетчеризации Microsoft Visual C#, ниже приведен интерфейсный класс к CTAPI Citect SCADA.

Скрытая информация. Вы не состоите в группах, которым доступна эта информация. Как получить доступ к информации?



Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

Re: Cicode: Шифрование и дешифрование строк с помощью простого XOR метода

#17

Сообщение alex » 03 июл 2018, 20:56

//
// Шифрование и дешифрования строк с помощью простого XOR метода.
// Просто передайте на вход строку для шифрования и зашифрованную строку
// для ее дешифрования.
//
// Аргументы: sData - Строка для шифрования/дешифрования.
//            sKey  - Ключ шифрования/дешифрования.
//
// Примечания: 1. Вы можете использовать ключ шифрования любой длины, чем длинее ключи тем шифрование сильнее.
//             2. Выходная строка получается той же длины что и входная.
//

STRING FUNCTION EncryptDecrypt(STRING sData, STRING sKey = "r|_9g(c~rmt<v+V<b7@Dxaw`9Ej\*Ox/")

    INT nChar = 0;
    INT iInChar = 0;
    INT iOutChar = 0;
    INT iKeyChar = 0;
    INT iDataLength = 0;
    INT iKeyLength = 0;
    STRING sOutput = 0;

    iDataLength = StrLength(sData);
    iKeyLength = StrLength(sKey);

    FOR nChar = 0 TO iDataLength - 1 DO
       
        // Использование StrGet/SetChar работает быстрее, но поддерживает только 128 символов
       
        iInChar = StrToChar( StrMid (sData, nChar, 1));
        iKeyChar = StrToChar( StrMid (sKey, (nChar + iDataLength) MOD iKeyLength, 1));
        iOutChar = iInChar BITXOR iKeyChar;

        // Не шифровать/дешифровывать если результат нулевой
       
        IF iOutChar = 0 THEN
            iOutChar = iInChar;
        END

        sOutput = sOutput + CharToStr (iOutChar);
       
    END

    RETURN sOutput;
   
END
 



Аватара пользователя
alex
Администратор
Сообщений в теме: 18
Сообщения: 1745
Зарегистрирован: 05 апр 2010, 21:58
Откуда: Москва
Благодарил (а): 47 раз
Поблагодарили: 102 раза
Контактная информация:

Сicode: Экспорт исторических данных тегов трендов Citect SCADA в Wonderware Historian

#18

Сообщение alex » 11 июл 2018, 22:31

//
// CitectTrendToWonderwareHistorian.ci - Экспорт исторических данных тегов трендов Citect SCADA в Wonderware Historian
//
// v1.00b       23.02.2017  Эрик Блек   Релиз
// v1.01b       23.02.2017  Эрик Блек   Изменен журнал сообщений
// v1.02b       23.02.2017  Эрик Блек   Добавлена документация
//
// Функция разработана для экспорта исторических данных из файлов графиков тегов трендов Citect SCADA
// Функция экспортирует исторические данные в формате FastLoad в CSV-файл, который Wonderware Historian
// может автоматически импортировать
//
// Vijeo Historian Connector (CitectHistorian Connector) v2.1 разработан для передачи онлайн графиков и алармов
// в Wonderware Historian. Однако, функция бекфиллинга коннектора реализована для восстановления исторических данных
// тегов трендов Citect SCADA за короткие периоды времени в отсутствии связи (по умолчанию 1 день). При установке
// нового Wonderware Historian может потребоваться восстановить исторические данные тегов трендов Citect SCADA
// за более длительный промежуток времени, который может исчисляться месяцами или даже годами.
//
// Ниже приведенный код протестирован с Vijeo Citect 7.50 SP1 и Citect SCADA 8.00 и Wonderware Historian 2014 R2 SP1 Patch 1
//
// Чтобы осуществить экспорт, сначала необходимо установить и сконфигурировать Wonderware Historian, затем установить
// Vijeo Historian Connector, и только теперь можно запускать ёданный код для бекфиллинга исторических данных.
// Код автоматически экспортирует данные начиная с указанной даты до (по умолчанию) текущей. Ситуация считается нормальной,
// если временной диапазон экспортируемых данных может отличаться или перекрываться тем интервалом времени, за который
// имеются исторические данные в каждом теге тренда. Экспорт исторических данных производится в соответствии
// с настройками каждого тега тренда, более того, экспортируются только изменения значения.
//
// Код может быть запущен из клиента Citect SCADA, чтобы снизить нагрузку на процесс сервера трендов. Можно также
// вызвать функцию TrendExportSleep() перед или во время экспорта для замедления выполнения экспорта и в то же время
// снижения нагрузки на процессор и жесткий диск.
//
// Инструкции:
//
// 1. Установите и сконфигурируйте Wonderware Historian
//
// 2. Отметьте теги трендов Citect SCADA, которые должны быть хисторизированы в Wonderware Historian путем установки
//    параметра Historize в значение TRUE в окне/строке конфигурирования тега тренда
//
// 3. Установите и сконфигурируйте Vijeo Historian Connector, обычно это делается на одном из серверов Citect SCADA
//    Vijeo Historian Connector сделает доступными теги трендов в Wonderware Historian
//
// 4. Откройте системную консоль управления Wonderware Historian, перейти к Configuration Editor\System Configuration\Parameters
//    и установите AllowOriginals = 1, тем самым позволив данным быть импортированными как 'оригинальные' значения
//    (иначе формат CSV-файла должен быть изменен, чтобы не экспортировать данные как 'оригинальные' значения)
//
// 5. Щелкните правой кнопкой мыши на Parameters и выберите Commit Pending Changes
//
// 6. Запустите TrendsToWwHist() c выбранными по своему желанию аргументами, все данные тегов трендов
//    с Historize = TRUE будут экспортированы. Важно, что на стороне Wonderware Historian должны существовать теги
//    с такими же именами, иначе экспортированные теги не будут импортированы. Когда Wonderware Historian успешно
//    импортирует данные из файла экспорта по пути C:\Historian\Data\DataImport\FastLoad, файл будет удален. Если возникнуть
//    проблемы с импортом файла, файл будет перемещен в C:\Historian\Data\Support и соответствующие сообщения будут
//    помещены в журнал Archestra, который можно посмотреть через системную консоль управления Wonderware
 
STRING mcCRLF = "^0x0D^0x0A";                       // коды возврата каретки и перевод строки
STRING mcSep = ",";                                 // разделитель полей в CSV-файле
STRING mcExportTempPath = "[Data]\TrendExportTemp";
INT mcMaxSamples = 4000;                            // Максимальное количество значений в одном запросе

INT mcSampleStateGood = 0;
INT mcSampleStateGated = 1;
INT mcSampleStateNA = 2;

INT mcOpcQualGood = 192;
INT mcOpcQualBad = 0;

STRING msFileBuffer = "";
REAL mrTrendSamples[4000];
INT mnTagDecimals = 0;
INT mnTagWidth = 0;
INT mhCreatePath = -1;
REAL mrLastSample = 0;
INT mnLastSampleState = 0;
STRING  msLastTag = "";
INT mnSleepMS = -1;
INT mnStartTime = 0;
INT mnTotalSamples = 0;

// Функция экспорта исторических значений тегов трендов в Wonderware Historian FastLoad CSV-файлы.
// Файлы создаются в директории [Data]\TrendExportTemp и затем перемещаются в директорию [Data]\WWImport.
//
// Аргументы:
//
//  sStartDate      - Начальная дата экспорта данных.
//                    Используется локальный формат времени Windows, например, "20.02.2017"
//  sEndDate        - Конечная дата экспорта данных.
//                    Используется локальный формат времени Windows, например, "20.02.2017"(по умолчанию, текущая дата)
//  sWWImportPath   - Путь к директории FastLoad на сервере Wonderware Historian
//  sTagFilter      - Опциональный фильтр для экспорта только определенных тегов. Смотрите описание функции TrnBrowseOpen()
//
// Если оставить путь sWWImportPath по умолчанию, то созданные файлы необходимо будет перенести вручную
// из директории Citect [Data]\WWImport в директорию FastLoad на сервере Wonderware Historian
// (обычно это C:\Historian\Data\DataImport\FastLoad)
//
// Wonderware Historian будет импортировать только теги, которые уже были созданы в нем.
//
// Сообщение о состоянии и ошибки отображаются в окне Kernel Citect SCADA,
// также сообщения об ошибках сохраняются в журнале syslog
//
// Пример использования:
//
//  // Экспорт исторических данных тега тренда EntrySpeed
//  TrendsToWwHist("01.01.2015", "23.02.2017", "H:\Data\DataImport\FastLoad", "TAG=EntrySpeed");

FUNCTION TrendsToWwHist (STRING sStartDate, STRING sEndDate = Date(2), STRING sWWImportPath = "[Data]\WWImport", STRING sTagFilter = "")
   
    INT cLocalTimeDate = 10;
    INT hTrends = 0;
    STRING  sTag = "";
    STRING  sCluster = "";
    REAL rPeriodSecs = 0.0;
    INT bHistorize = 0;
    INT nErr = 0;
    INT nExportErr = 0;
    INT nTags = 0;
    INT nFails = 0;
    INT nTag = 0;  
    INT nEndTime = 0;
    INT nStartTime = 0;
    INT nSamples = 0;
   
    ErrSetLevel(1);
   
    nEndTime = StrToDate(sEndDate);
    nStartTime = StrToDate(sStartDate);
   
    mnStartTime = TimeCurrent ();
    mnTotalSamples = 0;
   
    IF nStartTime = 0 THEN
   
        ErrLog("TrendsToWwHist () Неправильная начальная дата");
       
        RETURN;
       
    END

    IF nEndTime = 0 THEN
   
        ErrLog("TrendsToWwHist () Неправильная конечная дата");
       
        RETURN;
       
    END
   
    IF StrLength(sTagFilter) > 0 THEN
   
        sTagFilter = sTagFilter + ";";
       
    END
   
    sTagFilter = sTagFilter + "HISTORIAN=1;"
    hTrends = TrnBrowseOpen (sTagFilter, "CLUSTER,TAG,SAMPLEPER");
   
    IF hTrends = -1 THEN
   
        ErrLog ("TrendsToWwHist () Ошибка просмотра тегов трендов");
       
        RETURN;
       
    END
   
    nTags = TrnBrowseNumRecords (hTrends);
    nErr = TrnBrowseFirst (hTrends);
   
    ErrLog ("TrendsToWwHist () Начало экспорта " + nTags:# + " тегов трендов с "
             + TimeToStr(nStartTime, cLocalTimeDate) + " по " + TimeToStr(nEndTime, cLocalTimeDate));
             
    ErrLog ("TrendsToWwHist () Фильтр: '" + sTagFilter + "' экспорт в:" + PathToStr(sWWImportPath));
   
    WHILE nErr = 0 DO
   
        nTag = nTag + 1;
       
        sTag = TrnBrowseGetField (hTrends, "TAG");
        sCluster = TrnBrowseGetField (hTrends, "CLUSTER");
        sTag = sCluster + "." + sTag;
       
        rPeriodSecs = TrnBrowseGetField (hTrends, "SAMPLEPER");
       
        nSamples = (nEndTime - nStartTime) / rPeriodSecs;
       
        TraceMsg ("TrendsToWwHist () Экспорт " + nTag:# + "/" + nTags:# + " " + sTag);
       
        nExportErr = TrnExportWwHist (sTag, nEndTime, rPeriodSecs, nSamples, sWWImportPath);
       
        IF nExportErr <> 0 THEN
       
            nFails = nFails + 1;
           
        END

        nErr = TrnBrowseNext (hTrends);
       
    END
   
    ErrLog ("TrendsToWwHist () Успешно экспортировано " + (nTags - nFails):# + "/" + nTags:#
            + " тегов трендов. " + mnTotalSamples:# + " значений за " + (TimeCurrent() - mnStartTime):# + " секунд");
           
END

// Функция устанавливает паузу (в миллисекундах) между обработками блоков размером 4000 значений
// Установка значения -1 означает запретить паузы, -1 - это значение по умолчанию
// Отсутствие пауз обеспечивает максимальную скорость экспорта
// Функция может быть вызвана как перед вызовом TrendsToWwHist(), так и во время ее выполнения
// Функция позволяет регулировать уровень загрузки процессора

FUNCTION TrendExportSleep (INT nMS)

    mnSleepMS = nMS;

END


// Функция экспортирует исторические данные одного тега тренда в Wonderware FastLoad CSV-файл
//
// Аргументы:
//
//  sTag            - Имя тега тренда (может быть в формате <ИмяКластера>.<ИмяТегаТренда>)
//  nEndTime        - Время в формате Citect SCADA для указания конечного времения экспортируемых данных
//  rPeriodSecs     - Количество секунд (или долей секунды) между значениями выборки
//  nSamples        - Количество значений выборки для экспорта
//  sWWImportPath   - Полный путь, где будут формироваться файлы с экспортированными значениями
//  nMode           - Номер режима TrnGetTable() (режимы 1 и 2 всегда включены)
//
//  Выход:             - 0 - функция выполнилась успешно, иначе код ошибки Citect SCADA
//

INT FUNCTION TrnExportWwHist (STRING sTag, INT nEndTime, REAL rPeriodSecs, INT nSamples, STRING sWWImportPath, INT nMode = 0)
                 
    INT cTrnShowGated = 1
    INT cTrnReverseOrder = 2;
    INT cErrCannotOpenFile = 262;
    STRING cCriticalSection = "TrnExportWwHist";
    INT cLocalTimeDate = 10;
   
    INT nSample = 0;               
    INT nSampleState = 0;
    REAL rSampleTime = 0.0;
    REAL rBlockEndTime = 0.0;
    INT nBlockSize=1;
    INT nLastUpdateTime = 0;
    INT nError = 0;
    STRING sSampleMS = "";
    INT nSamplesRemaining = 0;
    INT nTrnGetTableMode = 0;
    STRING sFileName = "";
    STRING sExportFile = "";
    STRING sFastLoadFile = "";
    INT nErr = 0;
    INT hCSV = 0;
   
    nLastUpdateTime = TimeCurrent ();
   
    ErrSetLevel (1);
   
    EnterCriticalSectionFix (cCriticalSection);

    sFilename = StrReplace (sTag, ".", "_");
    sFilename = StrReplace (sFileName, "\", "_") + ".CSV";
    sExportFile = PathAdd (PathToStr (mcExportTempPath), sFileName);
   
    // Применение дополнительных режимов выполнения функции
   
    nTrnGetTableMode = nMode BITOR cTrnShowGated BITOR cTrnReverseOrder;

    GetTrendFormat (sTag);
   
    hCSV = CreateCSV (sExportFile);

    IF hCSV = -1 THEN
   
        LeaveCriticalSection (cCriticalSection);
        ErrLog ("TrnExportWwHist () Не удалось создать '" + sExportFile + "'. Err:" + IsError():#);
        RETURN cErrCannotOpenFile;
   
    END

    rBlockEndTime = nEndTime - (nSamples * rPeriodSecs);
    nSamplesRemaining = nSamples;
       
    WHILE (rBlockEndTime <= nEndTime) AND (nSamplesRemaining > 0) DO

        nBlockSize = Min (nSamplesRemaining, mcMaxSamples);
        rBlockEndTime = rBlockEndTime + (nBlockSize * rPeriodSecs);
               
        IF TimeCurrent () - nLastUpdateTime >= 5 THEN
       
            TraceMsg ("TrnExportWwHist () Экспорт " + sTag + " " + TimeToStr(rBlockEndTime, cLocalTimeDate));
            nLastUpdateTime = TimeCurrent ();
       
        END
               
        ReadTrendsBlock (sTag, rBlockEndTime, rPeriodSecs, nBlockSize, nTrnGetTableMode);
       
        FOR nSample = 0 TO nBlockSize - 1 DO
           
            mnTotalSamples = mnTotalSamples + 1;
            nSampleState = TrnIsValidValue (mrTrendSamples[nSample]);
            rSampleTime = rBlockEndTime - ((nBlockSize - nSample - 1) * rPeriodSecs);

            // Записывать текущее значение только если оно отличается от предыдущего
           
            IF SampleMatchesLast (sTag, mrTrendSamples[nSample], nSampleState) = FALSE THEN
           
                WriteCSVLine (hCSV, sTag, rSampleTime, mrTrendSamples[nSample], nSampleState);
                mrLastSample = mrTrendSamples[nSample];
                mnLastSampleState = nSampleState;
                msLastTag = sTag;
           
            END
        END

        nSamplesRemaining = nSamplesRemaining - nBlockSize;

        IF mnSleepMS <> -1 THEN
       
            SleepMS (mnSleepMS);
       
        END
       
    END

    FileCloseBuf (hCSV);
   
    // Перемещение CSV-файла в директорию FastLoad Wonderware Historian
   
    sFastLoadFile = PathToStr (PathAdd(sWWImportPath, sFileName));
    nErr = FileMove (sExportFile, sFastLoadFile);

    IF nErr <> 0 THEN
   
        ErrLog ("TrnExportWwHist () Ошибка перемещения файла для '" + sFileName + "' Err:" + nErr:#);
        ErrLog ("  '" + sExportFile + "' to '" + sFastLoadFile);
   
    ELSE
   
        TraceMsg ("TrnExportWwHist () Завершено " + sTag);
   
    END

    LeaveCriticalSection (cCriticalSection);
   
    RETURN nErr;
   
END

// Функция возвращает TRUE если переданное значение имеет то же самое значение что предыдущее
// Функция возвращает TRUE если предыдущее и текущее состояние были NA или Gated.
// Это позволяет исключить одинаковые значения в файле экспорта

PRIVATE INT FUNCTION SampleMatchesLast (STRING sTag, REAL rSample, INT nSampleState)

    IF sTag = msLastTag THEN
   
        IF rSample = mrLastSample THEN
       
            RETURN TRUE;
           
        ELSE
       
            SELECT CASE nSampleState
           
            CASE mcSampleStateNA, mcSampleStateGated
           
                RETURN nSampleState = mnLastSampleState;
               
            END SELECT
           
        END
       
    END
   
END

// Функция создает FastLoad CSV-файл и заголовок в нём

PRIVATE INT FUNCTION CreateCSV (STRING sFile)

    STRING cUserName = "CitectHistorian";   // Имя не имеет значение, можно использовать любое
    STRING cUTC = "0";
    STRING cTimeZone = "unused";
    STRING cFastLoadByTagname = "10";
    STRING cRecreateBlocks = "1";
    STRING cWriteMode = "w";
    STRING cSep = ",";
   
    STRING sLine = "";
    INT hCSV = 0;
   
    CreatePath (sFile);
    hCSV = FileOpen (sFile, cWriteMode);
   
    IF hCSV <> -1 THEN
   
        FileWriteLnBuf (hCSV, "ASCII");
        FileWriteLnBuf (hCSV, mcSep);
   
        sLine = cUserName + mcSep
              + cUTC + mcSep
              + cTimeZone + mcSep
              + cFastLoadByTagname + mcSep
              + cRecreateBlocks;
   
        FileWriteLnBuf (hCSV, sLine);
       
    END
   
    RETURN hCSV;
   
END

// Запись значения в CSV-файл

PRIVATE FUNCTION WriteCSVLine (INT hCSV, STRING sTag, REAL rDateTime, REAL rSample, INT nSampleState)

    STRING cOperationOrigVal = "0";
    STRING cInEngUnits = "0";
    STRING cTimeFormat = "yyyy.MM.dd" + mcSep + "HH:mm:ss.FFF"; //2009.06.15,13:45:30.090
    INT cUTC = TRUE;
   
    STRING  sDateTime = TimeFormat (rDateTime, cTimeFormat, cUTC);

    STRING  sLine = sTag + mcSep
                  + cOperationOrigVal + mcSep
                  + sDateTime + mcSep
                  + cInEngUnits + mcSep
                  + SampleVal (rSample, nSampleState) + mcSep
                  + SampleStateToOPC (nSampleState);
   
    FileWriteLnBuf (hCSV, sLine);  

END

// Функция определяет формат значений исторических значений переменного тега
// (длина значение, количество десятичных знаков) основываясь на информации
// связанного с ним переменного тега и сохраняет эти значения в переменных
// модуля. Если переменный тег не найден, то принимается формат по умолчанию
// т.е. #.######

PRIVATE FUNCTION GetTrendFormat (STRING sTag)

    INT cFmtWidth = 6;
    INT cFmtDecimals = 7;
    INT cTagName = 0;
   
    // Читаем формат значения тега тренда из переменного тега, если их имена совпадаю
   
    IF TagInfo (sTag, cTagName) <> "" THEN 

        mnTagDecimals = TagInfo (sTag, cFmtDecimals);
        mnTagWidth = TagInfo (sTag, cFmtWidth);
       
    // Используем формат по умолчанию если переменный тег с таким именем не найден
       
    ELSE   
   
        mnTagDecimals = 6;
        mnTagWidth = 8;
   
    END

END
       
// Функция читает указанный блок исторических значений тега тренда в массив
// rEndTime - это значение даты и времени в формате Citect SCADA,
//            где целая часть - секунды, а вещественная - миллисекунды

PRIVATE FUNCTION ReadTrendsBlock (STRING sTag, REAL rEndTime, REAL rPeriodSecs, INT nSamples, INT iMode)

    INT nEndTime = 0;
    INT nMS = 0;
    INT nSamplesRead = 0;
    STRING sError = "";
    INT nError = 0;

    INT nSample = 0;

    nEndTime = Fix (rEndTime);
    nMS = (rEndTime - nEndTime) * 1000;

    nSamplesRead = TrnGetTable (sTag, nEndTime, rPeriodSecs, nSamples, mrTrendSamples,  iMode, nMS);
   
    //IsError возвращает код ошибки 302, если какие-либо значения в блоке имели статус недоступных (N/A)
   
    nError = IsError ();

       
    IF nSamplesRead <> nSamples THEN
   
        sError = "ReadTrendsBlock () Прочитано " + nSamplesRead:# + "/" + nSamples:# + " для '"
                 + sTag + "' за " + TimeFormat (nEndTime, "G") + " Ошибка:" + nError:#;
        ErrLog (sError);
   
    END

END


// Функция-обвертка для функции FileWriteLn()
// Функция использует буфер FileWriteBuf, для снижения количества обращений к жесткому диску

PRIVATE FUNCTION FileWriteLnBuf (INT hFile, STRING sText)

    FileWriteBuf (hFile, sText + mcCRLF);
   
END

// Функция-обвертка вокруг функции FileWrite()
// Функция задействует буферизацию для снижения количества обращений к жесткому диску

PRIVATE FUNCTION FileWriteBuf (INT hFile, STRING sText)

    INT cMaxChars = 254;
    INT nBufferLen = 0;
    INT nError = 0;
    STRING sRemainingText = "";

    nBufferLen = StrLength (msFileBuffer);
   
    // Копируем все что помещается в буфер
   
    msFileBuffer = StrLeft (msFileBuffer + sText, cMaxChars);                      

    // Удаляем текст который был скопирован в буфер

    sRemainingText = StrTrimLeft (sText, StrLength (msFileBuffer) - nBufferLen);   

    // Сбрасываем буфер на жесткий диск
   
    IF StrLength (msFileBuffer) = cMaxChars THEN
   
        IsError (); //Сбрасываем код последней ошибки
        FileWrite (hFile, msFileBuffer);
        nError = IsError ();
       
        IF nError <> 0 THEN
       
            ErrLog ("FileWriteBuf () Ошибка записи буфера в файл. Ошибка:" + nError:#);
            RETURN;
           
        END
       
        msFileBuffer = sRemainingText;
       
    END
   
END

// Функция-обвертка вокруг функции FileClose(),
// также эта функция перед закрытием сбрасывает буфер в файл

PRIVATE FUNCTION FileCloseBuf(INT hFile)

    INT nError = 0;
   
    IsError (); //Сбрасываем код последней ошибки
    FileWrite (hFile, msFileBuffer);
    nError = IsError ();
   
    IF nError <> 0 THEN
   
        ErrLog ("FileCloseBuf () Ошибка записи буфера в файл. Ошибка:" + nError:#);
   
    END

    msFileBuffer = "";
   
    FileClose (hFile);
   
END

// Функция возвращает OPC качество для указанного значения

PRIVATE STRING FUNCTION SampleStateToOPC (INT nSampleState)

    SELECT CASE nSampleState
   
        CASE mcSampleStateGood

            RETURN mcOPCQualGood;

        CASE mcSampleStateNA, mcSampleStateGated

            RETURN mcOPCQualBad;

    END SELECT
   
END

// Функция конвертирует значение тега тренда в форматированную строку
// Недоступные (N/A) и подстановочные (Gated) значения возвращаются как "NULL"

PRIVATE STRING FUNCTION SampleVal (REAL rValue, INT nSampleState)
   
    IF nSampleState = mcSampleStateGood THEN
   
        IF mnTagDecimals <> 0 THEN
       
            RETURN StrTrim (RealToStr (rValue, mnTagWidth, mnTagDecimals));
           
        ELSE
       
            RETURN rValue:#.######;
           
        END
       
    ELSE
   
        RETURN "NULL";
       
    END
   
END

// Функция заменяет функцию EnterCriticalSection. Смотри встроенную помощь
// описания функции EnterCriticalSection.
// Функция возвращает 0 при успехе или код ошибки Cicode SCADA
// (возвращаемый фукнцией SemWait). Данная функция - исправление ошибки
// описанной в статье Q3402 базы знаний

PRIVATE INT FUNCTION EnterCriticalSectionFix (STRING sName)

    INT hSem = 0;
    INT iErr = 0;

    hSem = SemOpen (sName, 2);
    iErr = SemWait (hSem, -1);

    WHILE iErr = 297 DO // Повторять, если собственник семафора не существует
   
          iErr = SemWait (hSem, -1);
         
    END

    RETURN iErr;
   
END

// Функция возвращает целую часть переданного вещественного значения. Использование типа
// REAL позволяет применять функцию к значениям типа INT, которые вне диапазона возмонжных
// значений типа INT

PRIVATE REAL FUNCTION Fix (REAL rValue)

    RETURN rValue - (rValue MOD 1);

END


// Функция конвертирует представления даты и времени представленное в формате Citect SCADA
// в форматированную строку (смотрите статью Q6123 базы знаний). Функция похожа на функцию
// TimeToStr(), но позволяет задействовать пользовательские форматы, такие как TimestampFormat()
//
//  Аргументы:
//
//  rTime   - Значение даты и времени в формате Citect SCADA (по умолчанию - это текущая дата и время)
//            с миллисекундами в вещественной части
//  sFormat - Пользовательский формат представления даты и времени. (!)Чуствительно к регистру.
//            Смотрите описание функции TimestampFormat()
//  bUTC    - TRUE - возвращает время в формате UTC, FALSE - возвращает локальное время (значение по умолчанию)

PRIVATE STRING FUNCTION TimeFormat (REAL rTime, STRING sFormat, INT bUTC = FALSE)

    INT nTime = Fix (rTime);
    INT nMS = (rTime - nTime) * 1000;

    RETURN TimestampFormat (TimeIntToTimestamp (nTime, nMS, TRUE), sFormat, bUTC);

END

// Функция возвращает переданную строку с удаленными в начале nCharsToTrim символами
// или пустую строку "" для некорректных значений аргументов

PRIVATE STRING FUNCTION StrTrimLeft (STRING sText, INT nCharsToTrim)

    INT nTextLen = StrLength (sText);
   
    IF (nCharsToTrim >= 0) AND nCharsToTrim < nTextLen THEN
   
        RETURN StrMid (sText, nCharsToTrim, StrLength(sText) - nCharsToTrim);
       
    ELSE
   
        RETURN "";
       
    END
   
END

// Функция возвращает строку, в которой подстрока sFind была заменена на подстроку sReplace
// заданное количество раз.
//
// Аргументы:
//
//  sText       - Строка, в которой необходимо произвести замену одной подстроки на другую
//  sFind       - Подстрока, которую надо найти и заменить на другую
//  sReplace    - Подстрока, на которую будет производиться замена
//  iStart      - Опционально. Начальная позиция в строке, начиная с которой будет осуществляться поиск
//                Если значение не указано, то значение по умолчанию значение равно 0 - это означает поиск
//                с начала строки
//  iCount      - Опционально. Количество замен, которые надо выполнить. Если значение не указано,
//                то по умолчанию значение равно -1, что означает сделать все возможные замены.

PRIVATE STRING FUNCTION StrReplace (STRING sText, STRING sFind, STRING sReplace, INT iStart = 0, INT iCount = -1)

    STRING sResult = "";
    STRING sResultLeft = "";
    STRING sResultRight = "";
    INT iFindLen = 0;
    INT iReplaceLen = 0;
    INT iResultLen = 0;
    INT iSearchPos = 0;
    INT iReplaceCount = 0;
    INT bEndReplace = 0;   
   
    sResult = sText;
    iFindLen = StrLength (sFind);
    iReplaceLen = StrLength (sReplace);
   
    IF iCount = 0 THEN
   
        bEndReplace = 1;
   
    END
   
    IF iStart > 0 THEN
   
        iSearchPos = iStart;
   
    END
   
    WHILE NOT bEndReplace DO
   
        iSearchPos = StrSearch (iSearchPos, sResult, sFind);
       
        IF iSearchPos > -1 THEN
           
            iResultLen = StrLength (sResult);
            sResultLeft = StrLeft (sResult, iSearchPos);
            sResultRight = StrRight (sResult, iResultLen - iFindLen - iSearchPos);
            sResult = sResultLeft + sReplace + sResultRight;
           
            iReplaceCount = iReplaceCount + 1;
           
            IF (iReplaceCount >= iCount) AND (iCount <> -1) THEN
           
                bEndReplace = 1;
           
            END
           
            iSearchPos = iSearchPos + iReplaceLen;
           
        ELSE
       
            bEndReplace = 1;
       
        END
   
    END
   
    RETURN sResult;

END  

// Функция создает все необходимые директории по указанным пути
// Если путь включает имя файла, имя файла игнорируется
// Может использоваться замещение пути
//
// Функция возвращает TRUE при успешном выполнении

PRIVATE INT FUNCTION CreatePath (STRING sPath)

    sPath = PathToStr (sPath);
   
    IF mhCreatePath = -1 THEN
   
        mhCreatePath = DLLOpen ("dbghelp.dll", "MakeSureDirectoryPathExists", "AC");
       
    END
   
    RETURN DLLCallEx (mhCreatePath, sPath);
   
END

// Функция объединяет два пути или путь и имя файла, вставляя обратный слеш
// между ними где это необходимо

PRIVATE STRING FUNCTION PathAdd (STRING sPath1, STRING sPath2)

    IF StrLength (sPath1) > 0 AND StrLength(sPath2) > 0 THEN
   
        IF (StrRight(sPath1, 1) <> "\") AND (StrLeft(sPath2, 1) <> "\") THEN
       
            sPath1 = sPath1 + "\";
           
        END    
       
    END
   
    RETURN sPath1 + sPath2;
   
END

// Функция-обвертка вокруг функции FileRename. Функция используется при перемещении файлов

PRIVATE INT FUNCTION FileMove (STRING sSource, STRING sDest)

    CreatePath (sDest);
   
    RETURN FileRename (sSource, sDest);
   
END
 




Если эта тема может быть полезна другим, поделитесь ссылкой:

Вернуться в «Citect SCADA, Vijeo Citect, CitectSCADA, CitectFacilities, PowerLogic SCADA, PowerSCADA, MX4SCADA, Desigo Insight»