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

Описания RDS

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

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

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

§2.14.2. Сброс подсистемы в начальное состояние

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

- Ballistics – блок расчета траектории полета снаряда;
- ArtSearch – блок поиска угла возвышения для заданной дальности полета;
- ArtWalk – блок перебора углов для построения графика зависимости дальности полета от угла возвышения;
- StopCalc – блок, останавливающий расчет по сигналу (копия модели из §2.14.1, сюда она включена для того, чтобы все модели из рассматриваемых примеров схем находились в одной библиотеке).

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

      // Глобальная переменная для значения ошибки
      double DoubleErrorValue;

      //========== Главная функция 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
            else
              rdsGetHugeDouble(&DoubleErrorValue);

          }
        return 1;
      }
      //========= Конец главной функции =========

      // Проверка наличия связи у входа блока
      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;
      }
      //===========================================

      // Добавление поля для ввода или индикации вещественного параметра
      BOOL AddWinEditOrDisplayDouble(
        RDS_HOBJECT window, // Идентификатор объекта-окна
        RDS_BHANDLE Block,  // Блок
        int varnum,         // Номер переменной в блоке
        int ctrlnum,        // Идентификатор поля ввода в окне
        const 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;
      }
      //===========================================

      // Функция настройки вещественных параметров, хранящихся в
      // статических переменных блока.
      // Возвращает: 1 – ОК, 0 – отмена.
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      int SetupDoubleVars(
        RDS_BHANDLE Block,    // Настраиваемый блок
        const char *wintitle, // Заголовок окна настройки
        const char **vars)    // Массив строк-описаний полей ввода
      { RDS_HOBJECT window; // Идентификатор вспомогательного объекта
        BOOL ok;            // Пользователь нажал "OK"
        int count=0;
        BOOL *Connections;
        int *VarNums;

        if(vars==NULL) // Ошибка – массив не передан
          return 0;

        // Подсчитываем число полей ввода в массиве vars
        while(vars[count]!=NULL)
          count++;
        if(!count) // Ошибка – массив пуст
          return 0;

        // Отводим вспомогательный массив логических значений, в котором
        // будем запоминать наличие связей, присоединенных к переменным,
        // а также массив целых чисел для номеров переменных, считанных
        // из строк массива vars
        Connections=new BOOL[count];
        VarNums=new int[count];

        // Создаем окно с заголовком wintitle
        window=rdsFORMCreate(FALSE,-1,-1,wintitle);

        // Добавляем поля ввода из массива vars, заполняя массивы
        // Connections и VarNums
        for(int i=0;i<count;i++)
          { char *str;
            // Считываем из строки номер переменной. Указатель на первый
            // после номера символ запишется в str.
            VarNums[i]=strtol(vars[i],&str,0);
            // Если после номера строка кончается, присваиваем str
            // значение NULL, чтобы в качестве заголовка было
            // использовано имя переменной
            if(*str==0)
              str=NULL;
            // Добавляем поле ввода, запоминаем наличие связи
            Connections[i]=AddWinEditOrDisplayDouble(window,Block,
                      VarNums[i],i,str);
          }

        // Открываем окно
        ok=rdsFORMShowModalEx(window,NULL);
        if(ok)
          { // Нажата кнопка OK – запись параметров в переменные
            for(int i=0;i<count;i++)
              if(!Connections[i]) // У переменной нет связи
                { char *str=rdsGetObjectStr(window,i,RDS_FORMVAL_VALUE);
                  // Устанавливаем новое значение по умолчанию
                  rdsSetBlockVarDefValueStr(Block,VarNums[i],str);
                }
          }
        // Удаляем вспомогательные массивы
        delete[] Connections;
        delete[] VarNums;
        // Уничтожаем окно
        rdsDeleteObject(window);
        // Возвращаем 1 или 0
        return ok?1:0;
      }
      //===========================================

      //===========================================
      // Модель блока расчета внешней баллистики
      //===========================================
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      extern "C" __declspec(dllexport)
          int RDSCALL Ballistics(int CallMode,
                                 RDS_PBLOCKDATA BlockData,
                                 LPVOID ExtParam)
      { RDS_PDYNVARLINK Link;
        double t,dt,A,n,F,Fx,Fy,xp,yp;
      // Макроопределения для статических переменных
      #define pStart   ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define D (*((double *)(pStart+2*RDS_VSZ_S)))
      #define m (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
      #define v0 (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D)))
      #define Angle (*((double *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_D)))
      #define l0 (*((double *)(pStart+2*RDS_VSZ_S+4*RDS_VSZ_D)))
      #define h0 (*((double *)(pStart+2*RDS_VSZ_S+5*RDS_VSZ_D)))
      #define ValSet (*((char *)(pStart+2*RDS_VSZ_S+6*RDS_VSZ_D)))
      #define x (*((double *)(pStart+2*RDS_VSZ_S+6*RDS_VSZ_D+RDS_VSZ_L)))
      #define y (*((double *)(pStart+2*RDS_VSZ_S+7*RDS_VSZ_D+RDS_VSZ_L)))
      #define vx (*((double *)(pStart+2*RDS_VSZ_S+8*RDS_VSZ_D+RDS_VSZ_L)))
      #define vy (*((double *)(pStart+2*RDS_VSZ_S+9*RDS_VSZ_D+RDS_VSZ_L)))
      #define v (*((double *)(pStart+2*RDS_VSZ_S+10*RDS_VSZ_D+RDS_VSZ_L)))
      #define Impact (*((char *)(pStart+2*RDS_VSZ_S+11*RDS_VSZ_D+RDS_VSZ_L)))
      #define t0 (*((double *)(pStart+3*RDS_VSZ_S+11*RDS_VSZ_D+RDS_VSZ_L)))
      #define InFlight (*((char *)(pStart+3*RDS_VSZ_S+12*RDS_VSZ_D+RDS_VSZ_L)))
        // Массив описания параметров для универсальной функции настройки
        static const char *setup[]={
          "6Длина ствола, м",
          "7Высота оси, м",
          "2Диаметр снаряда, м",
          "3Масса снаряда, кг",
          "4Начальная скорость, м/с",
          "5Угол вылета, град.",
          NULL};

        switch(CallMode)
          { // Инициализация
            case RDS_BFM_INIT:
              // Подписка на динамическую переменную "DynTime"
              Link=rdsSubscribeToDynamicVar(RDS_DVPARENT,"DynTime",
                        "D",TRUE);
              BlockData->BlockData=Link;
              break;

            // Очистка
            case RDS_BFM_CLEANUP:
              // Прекращение подписки на "DynTime"
              rdsUnsubscribeFromDynamicVar(
                  (RDS_PDYNVARLINK)BlockData->BlockData);
              break;

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

            // Вызов функции настройки
            case RDS_BFM_SETUP:
              return SetupDoubleVars(BlockData->Block,
                         "Внешняя баллистика",setup);

            // Изменение динамической переменной
            case RDS_BFM_DYNVARCHANGE:
              // Получаем доступ к динамической переменной времени
              Link=(RDS_PDYNVARLINK)BlockData->BlockData;
              if(Link==NULL || Link->Data==NULL) // Нет доступа
                break;
              t=*((double*)Link->Data); // В t – текущее время
              if(t==t0) // Время не изменилось - ждем
                break;
              dt=t-t0; // В dt – интервал времени с прошлого шага
              t0=t;    // Запоминаем время в t0 чтобы при следующем
                       // изменени DynTime (t) можно было вычислить dt
              if(!ValSet) // Начальные значения еще не считаны
                { // Начинаем расчет траектории снаряда
                  double alpha=Angle*M_PI/180.0; // Угол в радианах
                  // Занесение в переменные начальных значений
                  vx=v0*cos(alpha);
                  vy=v0*sin(alpha);
                  v=v0;
                  x=l0*cos(alpha);
                  y=h0+l0*sin(alpha);
                  // Сбрасываем сигнал падения
                  Impact=0;
                  // Разрешаем расчет и взводим ValSet
                  InFlight=ValSet=1;
                }
              else if(!InFlight) // Снаряд не в полете – ничего не делаем
                break;
              // Вычисление модуля вектора скорости и силы сопротивления
              // по формуле Забудского
              v=sqrt(vx*vx+vy*vy);
              if(v<240.0)
                { A=0.0140; n=2; }
              else if(v<295.0)
                { A=0.0000583; n=3; }
              else if(v<375.0)
                { A=0.000000000670; n=5; }
              else if(v<419.0)
                { A=0.0000940; n=3; }
              else if(v<550.0)
                { A=0.0394; n=2; }
              else if(v<800.0)
                { A=0.2616; n=1.7; }
              else
                { A=0.713; n=1.55; }
              F=A*M_PI*D*D*pow(v,n)/4.0; // Сила сопротивления
              // Горизонтальная и вертикальная компоненты F
              Fx=F*vx/v;
              Fy=F*vy/v;
              // Запоминаем текущие значения координат
              xp=x; yp=y;
              // Вычисляем новые значения координат по
              // разностным уравнениям
              x+=dt*vx;
              y+=dt*vy;
              // Вычисляем новые значения компонент вектора скорости
              // по разностным уравнениям
              vx-=dt*Fx/m;
              vy-=dt*(9.807+Fy/m);
              if(y<0.0) // Снаряд встретился с поверхностью
                { Impact=1; // Взводим выходной сигнал падения снаряда
                  InFlight=0; // Прекращаем расчет траектории
                  // Вычисляем координату встречи с поверхностью
                  x=xp-yp*(x-xp)/(y-yp);
                  y=vx=vy=0;
                }
              Ready=1; // Для передачи выходов по связям
              break;
          }
        return RDS_BFR_DONE;
      // Отмера макроопределений переменных
      #undef InFlight
      #undef t0
      #undef Impact
      #undef v
      #undef vy
      #undef vx
      #undef y
      #undef x
      #undef ValSet
      #undef h0
      #undef l0
      #undef Angle
      #undef v0
      #undef m
      #undef D
      #undef Ready
      #undef Start
      #undef pStart
      }
      //===========================================

      //===========================================
      // Поиск угла возвышения для заданной дальности
      //===========================================
      // Личная область данных блока
      class TArtSearchData
      { public:
          BOOL SelfReset;  // Блок сам сбросил подсистему
          int Mode;        // Текущее состояние алгоритма:
            #define ASMODE_READY      0 // Готов к поиску
            #define ASMODE_SEARCHING  1 // Идет поиск
            #define ASMODE_FINALRUN   2 // Последний прогон
            #define ASMODE_FINISHED   3 // Поиск завершен
          double AngleStep; // Текущий шаг изменения угла
          double AngleToSet;// Угол, который нужно установить
                            // после сброса
          double OptAngle;  // Наилучший на данный момент угол
          double OptMiss;   // Наименьший на данный момент промах
          BOOL FirstStep;   // Проведенное моделирование – первое с
                            // новым шагом изменения угла

          // Ограничение диапазона и точности установки угла
          double FixAngle(double a,double amin,double amax,double acc)
            { if(a<amin) return amin;
              if(a>amax) return amax;
              return floor((a-amin)/acc)*acc+amin;
            };

          // Вывод сообщения о результатах поиска
          // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
          //   в противном случае необходимо использовать версии функций
          //   с суффиксом "W" и символьные константы с префиксом "L"
          void ShowResults(void)
            { char *str,
                   *angle=rdsDtoA(OptAngle,-1,NULL),
                   *miss=rdsDtoA(OptMiss,0,NULL);
              // Формрование динамической строки с сообщением
              str=rdsDynStrCat("Угол возвышения: ",angle,FALSE);
              rdsAddToDynStr(&str," гр.\nПромах: ",FALSE);
              rdsAddToDynStr(&str,miss,FALSE);
              rdsAddToDynStr(&str," м",FALSE);
              // Вывод текста
              rdsMessageBox(str,"Поиск завершен",MB_OK);
              // Освобождение всех динамических строк
              rdsFree(str);
              rdsFree(angle);
              rdsFree(miss);
            };

          // Конструктор класса
          TArtSearchData(void)
            { SelfReset=FALSE;
              Mode=ASMODE_READY;
            };
      };
      //===========================================

      // Модель блока поиска угла для заданной дальности
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      extern "C" __declspec(dllexport)
          int RDSCALL ArtSearch(int CallMode,
                                RDS_PBLOCKDATA BlockData,
                                LPVOID ExtParam)
      { TArtSearchData *data=(TArtSearchData*)(BlockData->BlockData);
        double delta;
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define MinAngle (*((double *)(pStart+2*RDS_VSZ_S)))
      #define MaxAngle (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
      #define Accuracy (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D)))
      #define Distance (*((double *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_D)))
      #define x (*((double *)(pStart+2*RDS_VSZ_S+4*RDS_VSZ_D)))
      #define Impact (*((char *)(pStart+2*RDS_VSZ_S+5*RDS_VSZ_D)))
      #define Angle (*((double *)(pStart+3*RDS_VSZ_S+5*RDS_VSZ_D)))
        // Массив описания параметров для универсальной функции настройки
        static const char *setup[]={
          "2Минимальный угол, град.",
          "3Максимальный угол, град.",
          "4Точность по углу, град.",
          "5Дальность цели, м",
          NULL};

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

            // Очистка
            case RDS_BFM_CLEANUP:
              delete data;
              break;

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

            // Вызов функции настройки
            case RDS_BFM_SETUP:
              return SetupDoubleVars(BlockData->Block,
                  "Поиск угла",setup);

            // Запуск расчета
            case RDS_BFM_STARTCALC:
              switch(data->Mode)
                { case ASMODE_READY:
                    // Начать поиск угла
                    Angle=MaxAngle; // Начальное значение
                    data->OptMiss=DoubleErrorValue; // Пока не определено
                    data->AngleStep=-(MaxAngle-MinAngle)/5;
                    data->FirstStep=TRUE;
                    data->Mode=ASMODE_SEARCHING;
                    Ready=1; // Для передачи угла по связи
                    break;
                  case ASMODE_FINISHED:
                    // Поиск уже проведен – показать результаты
                    rdsStopCalc();
                    data->ShowResults();
                    break;
                }
              break;

            // Сброс расчета
            case RDS_BFM_RESETCALC:
              if(data->SelfReset)
                { // Блок сам сбросил подсистему
                  data->SelfReset=FALSE; // Очиска признака самосброса
                  // Устанавливаем новый угол
                  Angle=data->AngleToSet;
                  Ready=1; // Для передачи угла по связи
                }
              else // Расчет сброшен пользователем
                data->Mode=ASMODE_READY;
              break;

            // Один такт моделирования
            case RDS_BFM_MODEL:
              if(!Impact) // Снаряд еще не долетел
                break;

              // Снаряд долетел – дальность полета в x
              Impact=0; // Сбрасываем входной сигнал
              if(data->Mode==ASMODE_FINALRUN)
                { // Это был последний (демонстрационный) прогон
                  data->Mode=ASMODE_FINISHED;
                  rdsStopCalc();
                  data->ShowResults();
                  break;
                }
              // Это – очередной прогон в поиске угла
              delta=fabs(x-Distance); // Промах при угле Angle
              if(data->OptMiss==DoubleErrorValue || // Первый прогон
                 delta<data->OptMiss) // При новом угле попали точнее
                { // Запоминаем новый наилучший угол и промах
                  data->OptAngle=Angle;
                  data->OptMiss=delta;
                  // Двигаемся дальше с тем же шагом
                  data->AngleToSet=Angle+data->AngleStep;
                  // Новый расчет будет уже не первым с этим шагом
                  data->FirstStep=FALSE;
                }
              else // Промах увеличился или не изменился
                { if(data->FirstStep)
                    { // Мы только что провели первый расчет с новым
                      // значением шага. Промах увеличился – попробуем
                      // двигаться в обратном направлении с тем же
                      // шагом
                      data->AngleStep=-data->AngleStep;
                      // Новый расчет будет уже не первым с этим шагом
                      data->FirstStep=FALSE;
                    }
                  else
                    { // Это был не первый расчет с данным значением
                      // шага. Мы либо сделали несколько шагов в этом
                      // направлении, либо уже пробовали двигаться
                      // в обратном. Теперь нужно уменьшить шаг вдвое,
                      //  если это возможно.
                      if(fabs(data->AngleStep)<=Accuracy)
                        { // Достигли минимального шага по углу -
                          // выполняем последний расчет
                          data->Mode=ASMODE_FINALRUN;
                          // Устанавливаем найденный угол
                          data->AngleToSet=data->OptAngle;
                          data->SelfReset=TRUE; // Флаг самосброса
                          // Сбрасываем родительскую подсистему
                          rdsResetSystemState(BlockData->Parent);
                          break;
                        }
                       // Минимальный шаг еще не достигнут
                       // Дальше будем двигаться от наилучшего на данный
                       // момент угла с меньшим шагом
                       data->AngleStep=data->AngleStep/2.0;
                       // Не даем шагу стать меньше минимального
                       if(fabs(data->AngleStep)<Accuracy)
                         data->AngleStep=(data->AngleStep<0)?
                             (-Accuracy):Accuracy;
                       // Новый расчет будет первым с этим значением шага
                       data->FirstStep=TRUE;
                    }
                  // Новый угол, который установиться после сброса
                  data->AngleToSet=data->OptAngle+data->AngleStep;
                }
              // Ограничиваем угол и привязываем его к точности
              data->AngleToSet=data->FixAngle(
              data->AngleToSet,MinAngle,MaxAngle,Accuracy);
              // Сбрасываекм родительскую подсистему
              data->SelfReset=TRUE; // Флаг самосброса
              rdsResetSystemState(BlockData->Parent);
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef Angle
      #undef Impact
      #undef x
      #undef Distance
      #undef Accuracy
      #undef MaxAngle
      #undef MinAngle
      #undef Ready
      #undef Start
      #undef pStart
      }
      //===========================================

      //===========================================
      // Блок перебора углов для построения графика
      // зависимости дальности от угла
      //===========================================
      // Личная область данных блока
      class TArtWalkData
      { public:
          BOOL SelfReset;   // Блок сам сбросил подсистему
          double AngleToSet;// Угол, который нужно установить
                            // после сброса
          BOOL FirstStart;  // Начать построение графика с начала

          // Конструктор класса
          TArtWalkData(void)
            { SelfReset=FALSE; FirstStart=TRUE; };
      };
      //===========================================
      // Функция модели блока
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      extern "C" __declspec(dllexport)
          int RDSCALL ArtWalk(int CallMode,
                              RDS_PBLOCKDATA BlockData,
                              LPVOID ExtParam)
      { TArtWalkData *data=(TArtWalkData*)(BlockData->BlockData);
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define MinAngle (*((double *)(pStart+2*RDS_VSZ_S)))
      #define MaxAngle (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
      #define Accuracy (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D)))
      #define Impact (*((char *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_D)))
      #define GraphReady (*((char *)(pStart+3*RDS_VSZ_S+3*RDS_VSZ_D)))
      #define Angle (*((double *)(pStart+4*RDS_VSZ_S+3*RDS_VSZ_D)))
        // Массив описания параметров для универсальной функции настройки
        static const char *setup[]={
          "2Минимальный угол, град.",
          "3Маскимальный угол, град.",
          "4Шаг по углу, град.",
          NULL};

        switch(CallMode)
          { // Инициализация
            case RDS_BFM_INIT:
              BlockData->BlockData=new TArtWalkData();
              break;
            // Очистка
            case RDS_BFM_CLEANUP:
              delete data;
              break;
            // Проверка типов переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSDDDSSD}")?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;
            // Вызов функции настройки
            case RDS_BFM_SETUP:
              return SetupDoubleVars(BlockData->Block,
                  "Перебор углов",setup);

            // Запуск расчета
            case RDS_BFM_STARTCALC:
              if(data->FirstStart)
                { // Самый первый запуск – начало графика
                  Angle=MinAngle; // Начинаем с начала диапазона
                  data->FirstStart=FALSE;
                  Impact=GraphReady=0;
                  Ready=1; // Для передачи по связям
                }
              break;

            // Сброс расчета
            case RDS_BFM_RESETCALC:
              if(data->SelfReset)
                { // Блок сам сбросил подсистему
                  data->SelfReset=FALSE;
                  // Устанавливаем новый угол
                  Angle=data->AngleToSet;
                  Ready=1; // Для передачи по связям
                }
              else // Расчет сброшен кем=то еще
                data->FirstStart=TRUE;
              break;

            // Один такт расчета
            case RDS_BFM_MODEL:
              if(!Impact) // Снаряд не долетел
                { // Сбрасываем сигнал готовности графика
                  GraphReady=0;
                  break;
                }
              // Снаряд долетел – проверяем готовность графика
              if(!GraphReady)
                break;
              // График готов
              Impact=GraphReady=0; // Сбрасываем оба сигнала
              if(Angle>=MaxAngle)
                { // Достигли конца диапазона – прекращаем расчет
                  rdsStopCalc();
                  break;
                }
              // Увеличиваем угол на один шаг
              data->AngleToSet=Angle+Accuracy;
              if(data->AngleToSet>MaxAngle) // Не допускаем выход
                data->AngleToSet=MaxAngle;     // за MaxAngle
              data->SelfReset=TRUE; // Сейчас блок сам сбросит подсистему
              rdsResetSystemState(BlockData->Parent);
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef Angle
      #undef GraphReady
      #undef Impact
      #undef Accuracy
      #undef MaxAngle
      #undef MinAngle
      #undef Ready
      #undef Start
      #undef pStart
      }
      //===========================================

      //===========================================
      // Блок, запускающий и останавливающий расчет
      //===========================================
      extern "C" __declspec(dllexport)
        int RDSCALL StopCalc(int CallMode,
            RDS_PBLOCKDATA BlockData,
            LPVOID ExtParam)
      { switch(CallMode)
          { case RDS_BFM_MODEL:       // Один такт расчета
              rdsStopCalc();
              break;
            case RDS_BFM_MOUSEDOWN: // Нажатие кнопки мыши
            case RDS_BFM_SETUP: // Вызов функции настройки
              if(rdsCalcProcessIsRunning()) // Расчет сейчас запущен
                rdsStopCalc();
              else // Расчет сейчас остановлен
                rdsStartCalc();
              break;
          }
        return RDS_BFR_DONE;
      }
      //=========================================