Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока-генератора синусоиды, косинусоиды или прямоугольных импульсов, сохраняющего свои параметры в текстовом формате. Изменения относительно примера из §2.7.2 выделены цветом.
// Сохранение параметров в текстовом формате #include <windows.h> #include <math.h> #include <stdio.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); // Функция настройки void SaveText(void); // Сохранение параметров void LoadText(char *text);// Загрузка параметров 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+RDS_VSZ_S))) #define y (*((double *)(pStart+2*RDS_VSZ_S))) // Вспомогательная переменная – указатель на личную область // данных блока, приведенный к правильному типу 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_SAVETXT: data=(TTestGenData*)(BlockData->BlockData); data->SaveText(); break;// Загрузка параметров в текстовом формате case RDS_BFM_LOADTXT: data=(TTestGenData*)(BlockData->BlockData); data->LoadText((char*)ExtParam); break;// Функция настройки 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 } //=========================================// Функция сохранения параметров void TTestGenData::SaveText(void) { char buffer[100]; // Буфер для формирования текста // Формирование текста в буфере при помощи функции sprintf sprintf(buffer, "type %d period %lf impulse %lf", Type,Period,Impulse); // Передача сформированного текста в RDS rdsWriteBlockDataText(buffer,FALSE); } //=========================================/* // Функция сохранения параметров – вариант с условием void TTestGenData::SaveText(void) { char buffer[100]; // Буфер для формирования текста // Формирование текста для Type и Period sprintf(buffer, "type %d period %lf", Type,Period); // Передача сформированного текста в RDS rdsWriteBlockDataText(buffer,FALSE); // Запись Impulse если Type равно 2 if(Type==2) { // Формирование текста sprintf(buffer,"impulse %lf",Impulse); // Передача текста в RDS rdsWriteBlockDataText(buffer,FALSE); } } //========================================= */// Функция загрузки параметров void TTestGenData::LoadText(char *text) { char *word,*ptr,c; // Установка указателя ptr на начало переданного текста ptr=text; // Цикл по словам в тексте for(;;) { // Получить из текста очередное слово word=rdsGetTextWord(ptr,&ptr,&c,TRUE); if(c==0) // Текст закончился – выход из цикла break; if(c=='\n') // Перевод строки – пропускаем и продолжаем continue; if(strcmp(word,"type")==0) // Тип сигнала { // Следующее слово – целое число word=rdsGetTextWord(ptr,&ptr,NULL,FALSE); Type=atoi(word); } else if(strcmp(word,"period")==0) // Период { // Следующее слово - число double word=rdsGetTextWord(ptr,&ptr,NULL,FALSE); Period=atof(word); } else if(strcmp(word,"impulse")==0) // Длительность импульса { // Следующее слово - число double word=rdsGetTextWord(ptr,&ptr,NULL,FALSE); Impulse=atof(word); } else // Неопознанное ключевое слово break; // Ошибка – прекращаем обработку } // Конец цикла for(;;) } //=========================================