Текущий раздел:

Описания RDS

Руководство программиста

Глава 2. Создание моделей блоков

§2.14. Программное управление расчетом

§2.14.4. Отдельный расчет подсистемы

Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, иллюстрирующих отдельный расчет подсистемы. Библиотека содержит три модели:

- SetExclusiveCalc – блок, включающий и выключающий отдельный расчет родительской подсистемы по входным сигналам;
- ChgCalcTickCount – блок, увеличивающий свой выход на единицу каждый такт после изменения своего вещественного входа (нужен для построения графика «выбросов»);
- TestKxC – блок, вычисляющий значение выхода «y» по формуле «y=K×x+C» (копия модели из §2.7.4, сюда она включена для того, чтобы все модели из рассматриваемых примеров схем находились в одной библиотеке).

      // Отдельный расчет подсистемы
      #include <windows.h>
      #include <RdsDef.h>
      // Подготовка описаний сервисных функций
      #define RDS_SERV_FUNC_BODY GetInterfaceFunctions
      #include <RdsFunc.h>

      //========== Главная функция DLL ==========
      int WINAPI DllMain(HINSTANCE /*hinst*/,
                               unsigned long reason,
                               void* /*lpReserved*/)
      { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL
          { // Получение доступа к функциям RDS
            if(!GetInterfaceFunctions())
              RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS
          }
        return 1;
      }
      //========= Конец главной функции =========

      //=========================================
      // Включение/выключение отдельного расчета
      //=========================================
      extern "C" __declspec(dllexport)
        int RDSCALL SetExclusiveCalc(int CallMode,
              RDS_PBLOCKDATA BlockData,
              LPVOID ExtParam)
      { // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define Lock (*((char *)(pStart+2*RDS_VSZ_S)))
      #define Unlock (*((char *)(pStart+3*RDS_VSZ_S)))
      #define Locked (*((char *)(pStart+4*RDS_VSZ_S)))
        switch(CallMode)
          { // Проверка типов переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSSSL}")?
                  RDS_BFR_BADVARSMSG:RDS_BFR_DONE;

            // Такт расчета
            case RDS_BFM_MODEL:
              if(Lock) // Поступил сигнал включения отдельного расчета
                { if(Locked) // Отдельный расчет уже включен
                    { Lock=0; // Сбрасываем сигнал, завершаем модель
                      break;
                    }
                  // Включаем отдельный расчет родительской подсистемы
                  if(rdsSetExclusiveCalc(BlockData->Parent,TRUE))
                    { // Включить удалось
                      Lock=0;   // Сбрасываем сигнал
                      Locked=1; // Запоминаем факт включения
                    }
                }
              if(Unlock) // Поступил сигнал выключения
                { if(Locked) // Отдельный расчет был включен - выключаем
                    rdsSetExclusiveCalc(BlockData->Parent,FALSE);
                  Locked=Lock=Unlock=0; // Сбрасываем сигналы и состояние
                }
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef Locked
      #undef Unlock
      #undef Lock
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================

      //=========================================
      // Счетчик тактов
      //=========================================
      extern "C" __declspec(dllexport)
          int RDSCALL ChgCalcTickCount(int CallMode,
                              RDS_PBLOCKDATA BlockData,
                              LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart  ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define Count (*((RDSINT32 *)(pStart+2*RDS_VSZ_S)))
      #define Stop (*((RDSINT32 *)(pStart+2*RDS_VSZ_S+RDS_VSZ_I)))
      #define Enabled (*((char *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_I)))
      #define x (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_I+RDS_VSZ_L)))
      #define xold (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_I+RDS_VSZ_L+RDS_VSZ_D)))
        switch(CallMode)
          { // Проверка типов переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSIILDD}")?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;

            // Запуск расчета
            case RDS_BFM_STARTCALC:
              Start=1; // Принудительный запуск модели в следующем такте
              break;

            // Один такт моделирования
            case RDS_BFM_MODEL:
              if(!Enabled) // Работа блока не разрешена
                { xold=x; // Запоминаем значение входа
                  break;
                }
              // Работа блока разрешена
              if(xold==x && Count==0)
                break; // Вход не изменился или счет не идет
              // Изменился вход (x!=xold) или уже считаем (Count!=0)
              Count++; // Увеличиваем число тактов
              if(Count>Stop) // Пора остановить расчет
                rdsStopCalc();
              Start=1; // Принудительный перезапуск модели
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef xold
      #undef x
      #undef Enabled
      #undef Stop
      #undef Count
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================

      //=========================================================================
      // Блок y=Kx+C из прошлых примеров (нужен только как пример алгебраического
      // блока, в его модель после §2.7.4 не внесено никаких изменений)
      //=========================================================================
      // Проверка наличия связи у входа блока
      BOOL CheckBlockInputConnection(
        RDS_BHANDLE Block,            // Идентификатор блока
        int num,                      // Номер входа
        RDS_PVARDESCRIPTION pVarDescr)// Указатель на структуру
                                      // описания переменной
      { RDS_CHANDLE c; // Идентификатор связи
        RDS_POINTDESCRIPTION PtDescr; // Структура описания точки связи

        // Заполнение служебных полей структур их размерами
        PtDescr.servSize=sizeof(PtDescr);
        pVarDescr->servSize=sizeof(RDS_VARDESCRIPTION);

        // Получение описания переменной блока по номеру
        if(rdsGetBlockVar(Block,num,pVarDescr)==NULL)
          return FALSE; // Нет такой переменной

        // Перебор всех связей, подключенных к этому блоку
        c=NULL;
        for(;;)
          { // Найти связь, следующую за c, и заполнить структуру
            // описания точки соединения PtDescr
            c=rdsGetBlockLink(Block,c,TRUE,FALSE,&PtDescr);
            if(c==NULL) // Больше нет связей
              break;
            // Найдена очередная связь – сравнение имени заданной
            // переменной с именем переменной точки этой связи
            if(strcmp(PtDescr.VarName,pVarDescr->Name)==0)
              return TRUE; // Имена совпали – есть связь, соединенная
                           // с переменной блока
          }
        // Все подключенные связи перебраны, а связь, подключенная
        // к заданной переменной так и не была найдена
        return FALSE;
      }
      //=========================================

      // Добавление поля для ввода или индикации вещественного параметра
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      BOOL AddWinEditOrDisplayDouble(
        RDS_HOBJECT window, // Идентификатор объекта-окна
        RDS_BHANDLE Block,  // Блок
        int varnum,         // Номер переменной в блоке
        int ctrlnum,        // Идентификатор поля ввода в окне
        char *title)        // Заголовок поля или NULL
      { // Структура описания переменной блока
        RDS_VARDESCRIPTION VarDescr;
        // Проверка наличия связи у переменной varnum в блоке Block
        // и заполнение структуры VarDescr описанием переменной
        BOOL conn=CheckBlockInputConnection(Block,varnum,&VarDescr);

        if(conn) // К переменной подключена связь
          { // Вспомогательные переменные
            char *caption; // Заголовок поля
            double *cur; // Указатель на данные переменной
            // Заголовок поля формируется из имени переменной и
            // текста “подключена связь"
            caption=rdsDynStrCat(title?title:VarDescr.Name,
                                 " (подключена связь)",
                                 FALSE);
            // Добавление поля для индикации текущего значения
            rdsFORMAddEdit(window,0,ctrlnum,RDS_FORMCTRL_DISPLAY,
                           caption,80);
            // Освобождение динамически сформированной строки заголовка поля
            rdsFree(caption);
            // Получение указателя на данные переменной
            cur=(double*)rdsGetBlockVarBase(Block,varnum,NULL);
            // Проверка – переменная должна существовать и иметь тип double
            if(cur!=NULL && VarDescr.Type=='D')
              // Занесение текущего значения переменной в поле
              rdsSetObjectDouble(window,ctrlnum,RDS_FORMVAL_VALUE,
                                 *cur);
          }
        else // К переменной не подключена связь
          { // Вспомогательная переменная для значения по умолчанию
            char *defval;
            // Получение строки со значением переменной по умолчанию
            // (необходимо потом освободить при помощи rdsFree)
            defval=rdsGetBlockVarDefValueStr(Block,varnum,NULL);
            // Добавление поля для ввода параметра
            rdsFORMAddEdit(window,0,ctrlnum,RDS_FORMCTRL_EDIT,
                           title?title:VarDescr.Name,80);
            // Занесение в поле ввода значения перменной по умолчанию
            rdsSetObjectStr(window,ctrlnum,RDS_FORMVAL_VALUE,defval);
            // Освобождение динамически сформированной строки
            rdsFree(defval);
          }
        // Возврат: TRUE – есть связь, FALSE – нет связи
        return conn;
      }
      //=========================================

      // Функция настройки K и C для модели блока y=Kx+C
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      BOOL TestKxCSetup(
        RDS_BHANDLE Block, // Идентификатор блока
        int numK,          // Номер переменной K в блоке
        int numC)          // Номер переменной C в блоке
      { RDS_HOBJECT window; // Идентификатор вспомогательного объекта
        BOOL ok;            // Пользователь нажал "OK"
        BOOL K_conn,C_conn; // Флаги наличия связей у K и C

        // Создание окна
        window=rdsFORMCreate(FALSE,-1,-1,"Kx+C");

        // Добавление полей для ввода или индикации K и C
        // (в зависимости от наличия связей)
        K_conn=AddWinEditOrDisplayDouble(window,Block,numK,1,NULL);
        C_conn=AddWinEditOrDisplayDouble(window,Block,numC,2,NULL);

        // Открытие окна
        ok=rdsFORMShowModalEx(window,NULL);
        if(ok)
          { // Нажата кнопка OK – запись параметров в блок
            if(!K_conn) // У K нет связи
              { // Получение строки из поля ввода
                char *str=rdsGetObjectStr(window,1,RDS_FORMVAL_VALUE);
                // Установка значения переменной K по умолчанию
                rdsSetBlockVarDefValueStr(Block,numK,str);
              }
            if(!C_conn) // У С нет связи
              { // Получение строки из поля ввода
                char *str=rdsGetObjectStr(window,2,RDS_FORMVAL_VALUE);
                // Установка значения переменной C по умолчанию
                rdsSetBlockVarDefValueStr(Block,numC,str);
              }
          }
        // Уничтожение окна
        rdsDeleteObject(window);
        // Возвращаемое значение – истина, если нажата "OK"
        return ok;
      }
      //=========================================

      // Функция модели блока
      extern "C" __declspec(dllexport)
        int RDSCALL TestKxC(int CallMode,
                            RDS_PBLOCKDATA BlockData,
                            LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define x (*((double *)(pStart+2*RDS_VSZ_S)))
      #define K (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
      #define C (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D)))
      #define y (*((double *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_D)))
        switch(CallMode)
          { // Проверка типа переменных
            case RDS_BFM_VARCHECK:
              if(strcmp((char*)ExtParam,"{SSDDDD}")==0)
                return RDS_BFR_DONE;
              return RDS_BFR_BADVARSMSG;

            // Запуск расчета
            case RDS_BFM_STARTCALC:
              // Если это запуск с начала, взвести Start
              if(((RDS_PSTARTSTOPDATA)ExtParam)->FirstStart)
                Start=1; // Модель запустится в первом же такте
              break;

            // Такт расчета
            case RDS_BFM_MODEL:
              y=K*x+C;
              break;

            // Функция настройки
            case RDS_BFM_SETUP:
              if(TestKxCSetup(BlockData->Block,3,4))
                { // Нажата “OK"
                  Start=1; // Запустить модель в следующем такте
                  return RDS_BFR_MODIFIED;
                }
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef y
      #undef C
      #undef K
      #undef x
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================