Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока-генератора синусоиды, косинусоиды или прямоугольных импульсов, сохраняющего свои параметры в текстовом формате при помощи вспомогательного объекта RDS. Изменения относительно примера из §2.8.3 выделены цветом.
// Сохранение параметров в текстовом формате с использованием вспомогательного объекта #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); // Функция настройки 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 } //=========================================// Глобальный массив ключевых слов // Индексы 0 1 2 const char *TestGen_Keywords[]={"type","period","impulse",NULL}; // define-константы для индексов #define TESTGEN_KW_TYPE 0 #define TESTGEN_KW_PERIOD 1 #define TESTGEN_KW_IMPULSE 2 //========================================= // Функция сохранения параметров void TTestGenData::SaveText(void) { // Запись "type" и целого значения rdsWriteWordValueText(TestGen_Keywords[TESTGEN_KW_TYPE],Type); // Запись "period" и вещественного значения rdsWriteWordDoubleText(TestGen_Keywords[TESTGEN_KW_PERIOD], Period); // Запись "impulse" и вещественного значения if(Type==2) rdsWriteWordDoubleText(TestGen_Keywords[TESTGEN_KW_IMPULSE], Impulse); } //========================================= // Функция загрузки параметров void TTestGenData::LoadText(char *text) { RDS_HOBJECT Parser; // Вспомогательный объект BOOL work=TRUE; // Флаг цикла // Создание объекта для разбора текста Parser=rdsSTRCreateTextReader(TRUE); // Передача объекту массива ключевых слов rdsSTRAddKeywordsArray(Parser,TestGen_Keywords,-1,0); // Передача объекту разбираемого текста rdsSetObjectStr(Parser,RDS_HSTR_SETTEXT,0,text); // Цикл до тех пор, пока в тексте не кончатся слова while(work) { int id; // Уникальный идентификатор слова // Извлечь из текста и опознать слово id=rdsSTRGetWord(Parser,NULL,NULL,NULL,TRUE); // Действия в зависимости от слова switch(id) { // Нет слова – конец текста case RDS_HSTR_DEFENDOFTEXT: work=FALSE; // Выйти из цикла break; // Перевод строки – пропускаем case RDS_HSTR_DEFENDOFLINE: break; // Слово "type" case TESTGEN_KW_TYPE: // Извлекаем следующее слово и переводим в целое Type=rdsGetObjectInt(Parser,RDS_HSTR_READINT,TRUE); break; // Слово "period" case TESTGEN_KW_PERIOD: // Извлекаем следующее слово и переводим в double Period=rdsGetObjectDouble(Parser,RDS_HSTR_READDOUBLE,TRUE); break; // Слово "impulse" case TESTGEN_KW_IMPULSE: // Извлекаем следующее слово и переводим в double Impulse=rdsGetObjectDouble(Parser,RDS_HSTR_READDOUBLE,TRUE); break; default: // Слово не опознано – ошибка work=FALSE; // Выйти из цикла } // Конец switch(...) } // Конец while(work) // Удаление вспомогательного объекта rdsDeleteObject(Parser); } //=========================================