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

Описания RDS

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

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

§2.7. Настройка параметров блока

§2.7.3. Расширенные возможности функции обратного вызова

Полный исходный текст на языке C++ для библиотеки (DLL) c моделью блока-генератора из §2.7.2, в который добавлено рисование внешнего вида генерируемой функции в окне настроек (изменения в модели выделены цветом).

      // Рисование в окне настроек
      #include <windows.h>
      #include <math.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;
      }
      //========= Конец главной функции =========

      //=========================================
      //=             Генератор                 =
      //=========================================

      //====== Класс личной области данных ======
      class TTestGenData
      { public:
          int Type;        // Тип (0-sin,1-cos,2-прямоугольные)
          double Period;   // Период
          double Impulse;  // Длительность импульса

          RDS_PDYNVARLINK Time; // Связь с динамической
                                // переменной времени

          int Setup(void);      // Функция настройки
          TTestGenData(void)    // Конструктор класса
            { Type=0; Period=1.0; Impulse=0.5;
              // Подписка на динамическую переменную времени
              Time=rdsSubscribeToDynamicVar(RDS_DVPARENT,
                                            "DynTime",
                                            "D",
                                            TRUE);
            };
          ~TTestGenData(void)	// Деструктор класса
            { // Прекращение подписки
              rdsUnsubscribeFromDynamicVar(Time);
            };
      };

      //==== Прототип функции обратного вызова окна настроек ====
      void RDSCALL TestGenDataCheckFunc2(RDS_HOBJECT win,
                                         RDS_PFORMSERVFUNCDATA data);

      //====== Функция редактирования параметров ======
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      int TTestGenData::Setup(void)
      { RDS_HOBJECT window; // Идентификатор вспомогательного объекта
        BOOL ok;            // Пользователь нажал "OK"
        // Создание окна
        window=rdsFORMCreate(FALSE,-1,-1,"Простой генератор");
        // Добавление полей ввода
        rdsFORMAddEdit(window,0,1,RDS_FORMCTRL_COMBOLIST,
                       "Вид:",210);
        rdsFORMAddEdit(window,0,2,RDS_FORMCTRL_EDIT,
                       "Период:",80);
        rdsFORMAddEdit(window,0,3,RDS_FORMCTRL_EDIT,
                       "Длительность:",80);
        // Установка списка вариантов
        rdsSetObjectStr(window,1,RDS_FORMVAL_LIST,
                        "Синус\nКосинус\nПрямоугольные импульсы");
        // Занесение исходных значений в поля ввода
        rdsSetObjectInt(window,1,RDS_FORMVAL_VALUE,Type);
        rdsSetObjectDouble(window,2,RDS_FORMVAL_VALUE,Period);
        rdsSetObjectDouble(window,3,RDS_FORMVAL_VALUE,Impulse);
        // Включение дополнительной панели слева от полей ввода
        rdsFORMEnableSidePanel(window,1,-1);
        // Добавление области для рисования графика
        rdsFORMAddEdit(window,1,4,RDS_FORMCTRL_PAINTBOX,NULL,100);
        // Автоматическое вычисление высоты этой области
        rdsSetObjectInt(window,4,RDS_FORMVAL_PBHEIGHT,-1);
        // Открытие окна с другой функцией обратного вызова
        ok=rdsFORMShowModalServ(window,TestGenDataCheckFunc2);

        if(ok)
          { // Нажата кнопка OK - запись параметров обратно в блок
            Type=rdsGetObjectInt(window,1,RDS_FORMVAL_VALUE);
            Period=rdsGetObjectDouble(window,2,RDS_FORMVAL_VALUE);
            Impulse=rdsGetObjectDouble(window,3,RDS_FORMVAL_VALUE);
          }
        // Уничтожение окна
        rdsDeleteObject(window);
        // Возвращаемое значение
        return ok?RDS_BFR_MODIFIED:RDS_BFR_DONE;
      }

      //====== Функция обратного вызова для окна настроек ======
      void RDSCALL TestGenDataCheckFunc2(RDS_HOBJECT win,
                                         RDS_PFORMSERVFUNCDATA data)
      { // Считать номер пункта выпадающего списка
        int type=rdsGetObjectInt(win,1,RDS_FORMVAL_VALUE);
        // Вспомогательные переменные для рисования графика
        int y0,y_ampl,x0,x1;
        double pix_period;

        switch(data->Event)
          { // Изменение поля ввода
            case RDS_FORMSERVEVENT_CHANGE:
              // Запретить ввод длительность импульса для sin и cos
              rdsSetObjectInt(win,3,RDS_FORMVAL_ENABLED,type==2);
              // Перерисовать график
              rdsCommandObject(win,RDS_FORM_INVALIDATE);
              break;

            // Рисование
            case RDS_FORMSERVEVENT_DRAW:
              // Заливка фона белым цветом
              rdsXGSetBrushStyle(0,RDS_GFS_SOLID,0xffffff);
              rdsXGFillRect(data->Left,
                            data->Top,
                            data->Left+data->Width,
                            data->Top+data->Height);
              // Координаты рисования
              x0=data->Left+10;             // Начало графика
              x1=data->Left+data->Width-10; // Конец графика
              y0=data->Top+data->Height/2;  // Центр по вертикали
              y_ampl=(data->Height-20)/2;   // Амплитуда
              pix_period=0.5*(x1-x0);       // Период на рисунке
              // Координатные оси
              rdsXGSetPenStyle(0,PS_SOLID,1,0,R2_COPYPEN);
              rdsXGMoveTo(data->Left+5,y0);
              rdsXGLineTo(data->Left+data->Width-5,y0);
              rdsXGMoveTo(x0,data->Top+5);
              rdsXGLineTo(x0,data->Top+data->Height-5);
              // График
              rdsXGSetPenStyle(RDS_GFWIDTH,0,3,0,0);
              if(type==2) // Прямоугольные импульсы
                { double period,impulse,pix_impulse;
                  // Чтение введенных пользователем значений
                  period=rdsGetObjectDouble(win,2,RDS_FORMVAL_VALUE);
                  impulse=rdsGetObjectDouble(win,3,RDS_FORMVAL_VALUE);
                  if(period==0.0) // Нельзя вычислить частоту
                    return;
                  // Длительность импульса на рисунке
                  pix_impulse=impulse*pix_period/period;
                  // Первый период
                  rdsXGMoveTo(x0,y0+y_ampl);
                  rdsXGLineTo(x0,y0-y_ampl);
                  rdsXGLineTo(x0+pix_impulse,y0-y_ampl);
                  rdsXGLineTo(x0+pix_impulse,y0+y_ampl);
                  rdsXGLineTo(x0+pix_period,y0+y_ampl);
                  // Второй период
                  rdsXGLineTo(x0+pix_period,y0-y_ampl);
                  rdsXGLineTo(x0+pix_period+pix_impulse,y0-y_ampl);
                  rdsXGLineTo(x0+pix_period+pix_impulse,y0+y_ampl);
                  rdsXGLineTo(x1,y0+y_ampl);
                }
              else // Синус или косинус
                { double t,y;
                  // Цикл по горизонтали с шагом в 3 точки
                  for(int x=x0;x<=x1;x+=3)
                    { t=2*M_PI*(x-x0)/pix_period;
                      y=y_ampl*((type==0)?sin(t):cos(t));
                      if(x==x0) // Первая точка – установить позицию
                        rdsXGMoveTo(x,y0-y);
                      else // Рисовать линию от предыдущей точки
                        rdsXGLineTo(x,y0-y);
                    } // for(int x=x0...)
                }
              break;
          } // switch
      }
      //=========================================

      //============= Модель блока ==============
      extern "C" __declspec(dllexport)
        int RDSCALL TestGen(int CallMode,
                            RDS_PBLOCKDATA BlockData,
                            LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start  (*((char *)(pStart)))
      #define Ready  (*((char *)(pStart+1)))
      #define y      (*((double *)(pStart+2)))
        // Вспомогательная переменная – указатель на личную область
        // данных блока, приведенный к правильному типу
        TTestGenData *data;

        switch(CallMode)
          { // Инициализация
            case RDS_BFM_INIT:
              BlockData->BlockData=new TTestGenData();
              break;

            // Очистка
            case RDS_BFM_CLEANUP:
              data=(TTestGenData*)(BlockData->BlockData);
              delete data;
              break;

            // Проверка типа переменных
            case RDS_BFM_VARCHECK:
              if(strcmp((char*)ExtParam,"{SSD}")==0)
                return RDS_BFR_DONE;
              return RDS_BFR_BADVARSMSG;

            // Функция настройки
            case RDS_BFM_SETUP:
              data=(TTestGenData*)(BlockData->BlockData);
              return data->Setup();

            // Изменение динамической переменной или запуск расчета
            case RDS_BFM_STARTCALC:
            case RDS_BFM_DYNVARCHANGE:
              data=(TTestGenData*)(BlockData->BlockData);
              if(data->Period==0.0) // Нельзя вычислить частоту
                return 0;
              // Проверка наличия переменной "DynTime"
              if(data->Time!=NULL && data->Time->Data!=NULL)
                { // Динамическая переменная найдена – чтение значения
                  double t=*((double*)data->Time->Data);
                  switch(data->Type)
                    { case 0: // Синус
                        y=sin(2*M_PI*t/data->Period);
                        break;
                      case 1: // Косинус
                        y=cos(2*M_PI*t/data->Period);
                        break;
                      case 2: // Прямоугольные импульсы
                        t=fmod(t,data->Period);
                        y=(t>data->Impulse)?-1.0:1.0;
                        break;
                    }
                  // Взвести Ready для передачи выхода по связям
                  Ready=1;
                }
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef y
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================