Навигация:
<< >> Оглавление Указатель
Текст С++

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

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

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

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

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

  // Проверка наличия связи у входа блока
   CheckBlockInputConnection(
     Block,             // Идентификатор блока
    int num,                       // Номер входа
     pVarDescr) // Указатель на структуру
                                   // описания переменной
  {  c; // Идентификатор связи
     PtDescr; // Структура описания точки связи

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

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

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

  // Добавление поля для ввода или индикации вещественного параметра
   (
     window, // Идентификатор объекта-окна
     Block,  // Блок
    int varnum,         // Номер переменной в блоке
    int ctrlnum,        // Идентификатор поля ввода в окне
    const char *title)  // Заголовок поля или NULL
  { // Структура описания переменной блока
     VarDescr;
    // Проверка наличия связи у переменной varnum в блоке Block
    // и заполнение структуры VarDescr описанием переменной
     conn=CheckBlockInputConnection(Block,varnum,&VarDescr);

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


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

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

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

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

    // Создаем окно с заголовком wintitle
    window=(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]=(window,Block,
                  VarNums[i],i,str);
      }

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

  //===========================================
  // Модель блока расчета внешней баллистики
  //===========================================
  // 
  // 
  // 
  extern "C" __declspec(dllexport)
      int  Ballistics(int CallMode,
                              BlockData,
                              ExtParam)
  {  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 :
          // Подписка на динамическую переменную "DynTime"
          Link=(,"DynTime",
                    "D",TRUE);
          BlockData->BlockData=Link;
          break;

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

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

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

        // Изменение динамической переменной
        case :
          // Получаем доступ к динамической переменной времени
          Link=()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 ;
  // Отмера макроопределений переменных
  #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:
       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;   // Наименьший на данный момент промах
       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;
        };

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

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

  // Модель блока поиска угла для заданной дальности
  // 
  // 
  // 
  extern "C" __declspec(dllexport)
      int  ArtSearch(int CallMode,
                             BlockData,
                             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 :
          BlockData->BlockData=new TArtSearchData();
          break;

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

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

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

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

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

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

          // Снаряд долетел – дальность полета в x
          Impact=0; // Сбрасываем входной сигнал
          if(data->Mode==ASMODE_FINALRUN)
            { // Это был последний (демонстрационный) прогон
              data->Mode=ASMODE_FINISHED;
              ();
              data->ShowResults();
              break;
            }
          // Это – очередной прогон в поиске угла
          delta=fabs(x-Distance); // Промах при угле Angle
          if(data->OptMiss== || // Первый прогон
             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; // Флаг самосброса
                      // Сбрасываем родительскую подсистему
                      (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; // Флаг самосброса
          (BlockData->Parent);
          break;
      }
    return ;
  // Отмена макроопределений
  #undef Angle
  #undef Impact
  #undef x
  #undef Distance
  #undef Accuracy
  #undef MaxAngle
  #undef MinAngle
  #undef Ready
  #undef Start
  #undef pStart
  }
  //===========================================

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

      // Конструктор класса
      TArtWalkData(void)
        { SelfReset=FALSE; FirstStart=TRUE; };
  };
  //===========================================
  // Функция модели блока
  // 
  // 
  // 
  extern "C" __declspec(dllexport)
      int  ArtWalk(int CallMode,
                           BlockData,
                           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 :
          BlockData->BlockData=new TArtWalkData();
          break;
        // Очистка
        case :
          delete data;
          break;
        // Проверка типов переменных
        case :
          return strcmp((char*)ExtParam,"{SSDDDSSD}")?
            :;
        // Вызов функции настройки
        case :
          return (BlockData->Block,
              "Перебор углов",setup);

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

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

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

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


<< >> Оглавление Указатель