#include "cdesk.h"
#include "simulate.h"
#include "plotxy.h"
#include "staticx.h"
#include "editx.h"
#include <tchar.h>
#include <shlwapi.h>
#include <commctrl.h>
//#include <uxtheme.h>
//#include <vsstyle.h>
HINSTANCE ghInstance;
HWND ghMainWnd,ghDlgWnd;
TCHAR sDecimal[2];
TCHAR sTime[2];
bool iMeasure;
CONFIG config;
static Simulate sim;
void EnableDlgItem(HWND Wnd, UINT id, bool ena) {
EnableWindow(GetDlgItem(Wnd,id),ena);
}
int FillCombo(HWND Wnd,UINT wnd_id,UINT str_id,int setsel) {
HWND hCombo=GetDlgItem(Wnd,wnd_id);
ComboBox_ResetContent(hCombo);
TCHAR s[256];
s[LoadString(ghInstance,str_id,s,elemof(s)-1)+1]=0; // laden und doppelnull-terminieren
for(const TCHAR*sp=s;*sp;sp+=lstrlen(sp)+1) {
ComboBox_AddString(hCombo,sp);
}
if (setsel>=0) return ComboBox_SetCurSel(hCombo,setsel);
else return ComboBox_GetCurSel(hCombo);
}
bool CONFIG::load() {
HKEY key;
bool ret=false;
if (!RegOpenKeyEx(HKEY_CURRENT_USER,TEXT("Software\\h#s"),0,KEY_READ,&key)) {
TCHAR s[MAX_PATH];
GetModuleFileName(ghInstance,s,elemof(s));
TCHAR*n=PathFindFileName(s);
TCHAR*e=PathFindExtension(n); if (e!=n) *e=0;
HKEY key2;
if (!RegOpenKeyEx(key,n,0,KEY_READ,&key2)) {
DWORD siz=sizeof config;
if (!RegQueryValueEx(key2,TEXT("config"),0,0,(BYTE*)&config,&siz)) {
ret=true;
}
siz=0;
if (!RegQueryValueEx(key2,TEXT("generate"),0,0,0,&siz)) {
unsigned el=siz/sizeof(Simulate::GENPARAM);
sim.genparams.resize(el);
sim.genstates.resize(el);
siz=el*sizeof(Simulate::GENPARAM);
RegQueryValueEx(key2,TEXT("generate"),0,0,(BYTE*)&sim.genparams[0],&siz);
for (unsigned i=0; i<el; i++) {
sim.genstates[i].amp=0;
sim.genstates[i].pha=sim.genparams[i].pha;
}
}
RegCloseKey(key2);
}
RegCloseKey(key);
}
return ret;
}
// Speichert unter HKCU\Software\h#s\<exename>,
// sodass der Anwender unter verschiedenen EXE-Dateinamen verschiedene Konfigurationen ablegen kann.
// Einerseits kann das durch mehrfache EXE-Dateikopien geschehen,
// unter NTFS eleganter mit symbolischen oder Hardlinks, wie unter Linux üblich
void CONFIG::save() {
HKEY key;
if (!RegCreateKeyEx(HKEY_CURRENT_USER,TEXT("Software\\h#s"),0,0,0,KEY_WRITE,0,&key,0)) {
TCHAR s[MAX_PATH];
int l=LoadString(ghInstance,108,s,elemof(s)); // "haftmann#software"
RegSetValueEx(key,0,0,REG_SZ,(BYTE*)s,(l+1)*sizeof(TCHAR)); // Beschreibung für "h#s"
GetModuleFileName(ghInstance,s,elemof(s));
TCHAR*n=PathFindFileName(s);
TCHAR*e=PathFindExtension(n); if (e!=n) *e=0;
HKEY key2;
if (!RegCreateKeyEx(key,n,0,0,0,KEY_WRITE,0,&key2,0)) {
l=GetWindowText(ghMainWnd,s,elemof(s)); // "Steuertafel"
RegSetValueEx(key2,0,0,REG_SZ,(BYTE*)s,(l+1)*sizeof(TCHAR)); // Beschreibung für <exename>
RegSetValueEx(key2,TEXT("config"),0,REG_BINARY,(BYTE*)&config,sizeof config);
RegSetValueEx(key2,TEXT("generate"),0,REG_BINARY,(BYTE*)&sim.genparams[0],DWORD(sim.genparams.size()*sizeof(Simulate::GENPARAM)));
RegCloseKey(key2);
}
RegCloseKey(key);
}
}
static Float testdata[]={0,3,2,7,3.5,2};
struct XDATA:public DATA{
// XDATA() {
// size=elemof(testdata);
// precis=1;
// range.a=0;
// range.e=elemof(testdata)-1;
// }
// virtual int getCaps() const {return 0;}
virtual int size() const {return elemof(testdata);}
virtual Float precis() const {return 1;}
// virtual Float dt() const {return 1;}
// virtual __int64 t0() const {FILETIME ft; GetSystemTimeAsFileTime(&ft); return *(__int64*)&ft;}
virtual void getRange(Range<Float>&r) const {r.a=0; r.e=size()-1;}
virtual Float get(int i) const {return (Float)i;}
// virtual void insert(int i, Float)
// virtual ACTION change(size_t,Float*,ACTION) {return ACTION(query|ins|del);}
virtual const TCHAR*name() const {return TEXT("Zeit");}
virtual const TCHAR*unit() const {return TEXT("s");}
};
struct YDATA:public DATA{
// YDATA() {
// size=elemof(testdata);
// precis=0.1F;
// range.a=0;
// range.e=7;
// }
virtual int getCaps(int) const {return can_set;}
virtual int size() const {return elemof(testdata);}
virtual Float precis() const {return 0.1f;}
virtual Float get(int i) const {return testdata[i];}
// virtual ACTION change(size_t i,Float*v,ACTION a) {
virtual void set(int i,Float v) {testdata[i]=v;} // TODO: Genauigkeit einfließen lassen!
virtual const TCHAR*name() const {return TEXT("Strom");}
virtual const TCHAR*unit() const {return TEXT("A");}
};
static XDATA xdata;
static YDATA ydata;
static TRACEATTR myplot={
&xdata,&ydata,0,0,0,0,TEXT("Erster"),RGB(230,40,30),RGB(90,140,39),0,0,2,PS_DASH,-1,TRACEATTR::EDITABLE,{TEXT("Arial"),-18,0,0}};
static struct Showwave{
WAVEFORMATEX*wf;
WAVEHDR*wh;
struct XDATA:public DATA{
Showwave*p;
virtual int size() const {return p->wh->dwBufferLength/p->wf->nBlockAlign;}
virtual Float get(int i) const {return Float(i)*10/size()/*/p->wf->nSamplesPerSec*/;}
virtual void getRange(Range<Float>&r) const {r.a=0; r.e=10/*(Float)size()/p->wf->nSamplesPerSec*/;}
XDATA(Showwave*_p):p(_p) {}
}xdata;
struct YDATAL:public DATA{
Showwave*p;
virtual int size() const {return p->wh->dwBufferLength/p->wf->nBlockAlign;}
virtual Float get(int i) const {return ((short*)p->wh->lpData)[i*2]/327.68F;}
virtual void getRange(Range<Float>&r) const {r.a=-10; r.e=10;}
YDATAL(Showwave*_p):p(_p) {}
}ydataL;
struct YDATAR:public DATA{
Showwave*p;
virtual int size() const {return p->wh->dwBufferLength/p->wf->nBlockAlign;}
virtual Float get(int i) const {return ((short*)p->wh->lpData)[i*2+1]*30518E-9F;}
virtual void getRange(Range<Float>&r) const {r.a=-10; r.e=10;}
YDATAR(Showwave*_p):p(_p) {}
}ydataR;
Showwave():xdata(this),ydataL(this),ydataR(this) {}
}showwave;
static TRACEATTR wavplotL={
&showwave.xdata,&showwave.ydataL,0,0,0,0,TEXT("links"),RGB(0,0,255),0,0,0,1,PS_SOLID,-1,0,{TEXT("Arial"),-18,0,0}};
static TRACEATTR wavplotR={
&showwave.xdata,&showwave.ydataR,0,0,0,0,TEXT("rechts"),RGB(255,0,0),0,0,0,1,PS_SOLID,-1,0,{TEXT("Arial"),-18,0,0}};
static struct Size:public SIZE{
operator POINT() const {return *(POINT*)this;}
operator bool() const {return cx||cy;}
Size&operator=(const SIZE&sz) {return operator=(*(Size*)&sz);}
Size() {};
Size(LPARAM lParam) {cx=GET_X_LPARAM(lParam); cy=GET_Y_LPARAM(lParam);}
Size&Window(HWND w) {Rect rc(w); return operator=(SIZE(rc));}
Size&Client(HWND w) {Rect rc(w,true); return operator=(SIZE(rc));}
}MinTrackSize,PrevClientSize;
struct NUMSETUP{
float offsets[3]; // in A
float winkel[2]; // in °
float strom; // in A
float drehzahl[3]; // in U/min, ?, ?
DWORD switches; // 6 Bits benutzt
}numsetup={
{-0.83F,-0.16F,-0.40F},
{ 0.00F,28.00F},
12,
{100,0.4F,0.2F},
0
};
struct NUMOUT{
float x[5];
}numout={
{176.660F,1082.708F,12,0,28}
};
void SetDlgItemFloat(HWND Wnd, int id, float v, int nk) {
TCHAR buf[16];
_sntprintf(buf,elemof(buf),TEXT("%.*f"),nk,v);
TCHAR*p=_tcschr(buf,'.');
if (p) *p=::sDecimal[0];
SetDlgItemText(Wnd,id,buf);
}
static const Simulate::GENPARAM gp[]={
{
TEXT("CH1"),
1, // links
Simulate::GENPARAM::sine,
{100,10000}, // 100 Hz, “10000
},{
TEXT("CH2"),
2, // rechts
Simulate::GENPARAM::sine,
{100,10000,0,1./3}, // dito 120°
},{
TEXT("CH3"),
0, // keine Ausgabe
Simulate::GENPARAM::sine,
{100,10000,0,2./3}, // dito 240°
}
};
static struct Simdata{
// static void cb(char*buf,DWORD len,void*cbd) {Simdata*sd=(Simdata*)cbd; sd->callback(buf,len);}
// void callback(char*buf,DWORD len) {
// TODO:
// Triggerpunkte suchen
// Ins Diagramm überführen
// }
HWAVEOUT hwo;
WAVEFORMATEX wf;
unsigned buflen;
WAVEHDR wh[4];
bool run;
void init() {
for (unsigned i=0; i<elemof(gp); i++) {
sim.genparams.push_back(gp[i]);
Simulate::GENSTATE gs={0,gp[i].pha}; // Startphase rüberkopieren
sim.genstates.push_back(gs);
}
}
void start() {
wf.nSamplesPerSec=200000;
wf.wBitsPerSample=16;
wf.nChannels=2;
wf.wFormatTag=WAVE_FORMAT_PCM;
wf.nBlockAlign=wf.nChannels*((wf.wBitsPerSample+7)>>3);
wf.nAvgBytesPerSec=wf.nSamplesPerSec*wf.nBlockAlign;
wf.cbSize=0;
buflen=sim.blocklen=20000;
sim.dt=1/double(wf.nSamplesPerSec);
if (!waveOutOpen(&hwo,WAVE_MAPPER,&wf,LONG_PTR(waveOutProc),(LONG_PTR)this,CALLBACK_FUNCTION)) {
run=true;
for (int i=0; i<elemof(wh); i++) {
WAVEHDR&h=wh[i];
h.lpData=new char[h.dwBufferLength=buflen*wf.nBlockAlign];
h.dwFlags=0;
waveOutPrepareHeader(hwo,&h,sizeof h);
sim.generate(h.lpData);
waveOutWrite(hwo,&h,sizeof h);
}
}
}
void stop() {
run=false;
waveOutReset(hwo);
for (int i=0; i<elemof(wh); i++) {
WAVEHDR&h=wh[i];
waveOutUnprepareHeader(hwo,&h,sizeof h);
delete[] h.lpData;
}
waveOutClose(hwo);
}
static void CALLBACK waveOutProc(HWAVEOUT hwo,UINT msg,Simdata*self,WAVEHDR&h,void*) {
if (msg==WOM_DONE && self->run) { // Nichts 'reinstecken nach waveOutReset()
sim.generate(h.lpData);
HWND hPlot=GetDlgItem(ghMainWnd,16);
PostMessage(hPlot,XY_UPDATETRACE,wavplotL.itrace,0);
if (self->run) waveOutWrite(hwo,&h,sizeof h); // naja, eigentlich müsste hier eine Critical Section her
}
}
}simdata;
INT_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
ghMainWnd=Wnd;
HWND hPlot=GetDlgItem(Wnd,16);
SendMessage(hPlot,XY_ADDTRACE,0,(LPARAM)&myplot);
simdata.init(); // 3 Vorgaben einstellen
showwave.wf=&simdata.wf;
showwave.wh=simdata.wh;
SendMessage(hPlot,XY_ADDTRACE,0,(LPARAM)&wavplotL);
SendMessage(hPlot,XY_ADDTRACE,0,(LPARAM)&wavplotR);
WINDOWPLACEMENT wp;
wp.length=sizeof wp;
GetWindowPlacement(Wnd,&wp);
Rect&rc=*(Rect*)&wp.rcNormalPosition;
MinTrackSize=SIZE(rc);
if (config.load()) { // ggf. simdata-Vorgaben überschreiben
config.rcMain=rc;
PrevClientSize.Client(Wnd);
SetWindowPlacement(Wnd,&wp);
}
for (int i=0; i<9; i++) SetDlgItemFloat(Wnd,17+i,numsetup.offsets[i],2); // TODO: EditX::setDouble
for (int j=0; j<5; j++) SetDlgItemFloat(Wnd,40+j,numout.x[j],1);
// sim=new Simulate(&ip,simdata.cb,&simdata);
// sim->start();
simdata.start();
}return TRUE;
case WM_DEVICECHANGE: {
MessageBeep(0);
}break;
case WM_SIZE: if (wParam!=SIZE_MINIMIZED) {
Size ClientSize(lParam);
if (PrevClientSize.cx||PrevClientSize.cy) {
POINT delta={ClientSize.cx-PrevClientSize.cx,ClientSize.cy-PrevClientSize.cy};
HDWP wpi=BeginDeferWindowPos(64);
for (HWND w=GetFirstChild(Wnd);w;w=GetNextSibling(w)) {
Rect rc;
GetWindowRect(w,&rc);
rc.right-=rc.left;
rc.bottom-=rc.top;
ScreenToClient(Wnd,(POINT*)&rc);
UINT flags=GetWindowID(w)==16?SWP_NOZORDER|SWP_NOMOVE:SWP_NOZORDER|SWP_NOSIZE;
wpi=DeferWindowPos(wpi,w,0,
rc.left+delta.x,
rc.top+delta.y,
rc.right+delta.x,
rc.bottom+delta.y,
flags);
}
EndDeferWindowPos(wpi);
}
PrevClientSize=ClientSize;
}break;
case WM_GETMINMAXINFO: {
MINMAXINFO*mmi=(MINMAXINFO*)lParam;
mmi->ptMinTrackSize=MinTrackSize;
}break;
case WM_CTLCOLORBTN: {
switch (GetDlgCtrlID((HWND)lParam)) {
case 35:
case 36: { // Autocheckbox theta_on, theta_off
DefWindowProc(Wnd,Msg,wParam,lParam);
}return TRUE;
}
}break;
/*
case WM_NOTIFY: switch (((NMHDR*)lParam)->code) {
case NM_CUSTOMDRAW: {
NMCUSTOMDRAW*cd=(NMCUSTOMDRAW*)lParam;
switch (cd->hdr.idFrom) {
case 35:
case 36: switch (cd->dwDrawStage) { // Autocheckbox theta_on, theta_off
case CDDS_PREPAINT: {
SIZE sz;
#if 0
HTHEME hTheme=OpenThemeData(cd->hdr.hwndFrom,L"BUTTON");
int stateID=CBS_UNCHECKEDNORMAL;
GetThemePartSize(hTheme,cd->hdc,BP_CHECKBOX,stateID,0,TS_TRUE,&sz);
CloseThemeData(hTheme);
#else
sz.cx=GetSystemMetrics(SM_CXMENUCHECK)+GetSystemMetrics(SM_CXEDGE)-1;
#endif
TCHAR s[32];
GetWindowText(cd->hdr.hwndFrom,s,elemof(s));
Rect rc(*(Rect*)&cd->rc); // Copy-Konstruktor
rc.left+=sz.cx;
hehaDrawText(cd->hdc,s,-1,&rc,DT_SUPERSUB|DT_SINGLELINE|DT_VCENTER);
bool focus=GetFocus()==cd->hdr.hwndFrom;
if (focus) {
hehaDrawText(cd->hdc,s,-1,&rc,DT_SUPERSUB|DT_SINGLELINE|DT_VCENTER|DT_CALCRECT);
//rc.left-=1; rc.top+=2; rc.right+=1; rc.bottom+=1; // Korrektur damit's gleich aussieht(!)
DrawFocusRect(cd->hdc,&rc);
}
SetWindowLongPtr(Wnd,DWLP_MSGRESULT,CDRF_SKIPDEFAULT);
}return TRUE;
}break;
}
}break;
}break;
*/
case WM_COMMAND: switch (wParam) {
// Menükommandos
case 101: {
DialogBox(ghInstance,MAKEINTRESOURCE(101),Wnd,SettingsDlgProc);
}break;
case 102: {
TCHAR fname[MAX_PATH];
TCHAR tem[256];
TCHAR buf[1024];
LoadString(ghInstance,LOWORD(wParam),tem,sizeof(tem));
GetModuleFileName(0,fname,sizeof(fname));
HANDLE f=CreateFile(fname,0,0,0,OPEN_EXISTING,0,0);
wsprintf(buf,tem,fname,GetFileSize(f,0));
CloseHandle(f);
MessageBox(Wnd,buf,TEXT("Meldung"),MB_OK);
}break;
case 112: DialogBoxParam(ghInstance,MAKEINTRESOURCE(wParam),Wnd,Simulate::SimulDlgProc,(LPARAM)&sim); break;
case 113: DialogBox(ghInstance,MAKEINTRESOURCE(wParam),Wnd,DrawTextTestDlg); break;
// Dialogelemente
case 35:
case 36: {
EnableDlgItem(Wnd,UINT(wParam)+20-35,!Button_GetCheck((HWND)lParam));
}break;
case 2: {
simdata.stop(); /*delete sim;*/
WINDOWPLACEMENT wp;
wp.length=sizeof wp;
GetWindowPlacement(Wnd,&wp);
config.rcMain=wp.rcNormalPosition;
config.save();
EndDialog(Wnd,0);
}break;
}break;
}
return FALSE;
}
// Statische Konstruktoren aufrufbar machen
#pragma data_seg(".CRT$AAA")
int ctor_start=0; // Eine Null vor allen Konstruktorzeigern (eigentlich würde die Segmentadresse reichen, aber die ist in C/C++ nicht zu bekommen)
// Konstruktorzeiger landen in ".CRT$xxx" (xxx beliebig), wobei der Linker die Segmente alphabetisch sortiert
// und wegen Segmentausrichtung (bspw. 8 Byte) Nullen einfügt.
#pragma data_seg(".CRT$ZZZ")
int ctor_end=0; // Eine Null nach allen Konstruktorzeigern
#pragma data_seg() // Standardverhalten reaktivieren
// _initterm(a,e) ruft alle Zeiger zwischen <a> und <e> auf, die nicht Null sind.
// Die Konstruktorfunktionen legen ggf. Destruktorkode für atexit() ab,
// daher gibt es kein Pendant für statische Destruktoren.
extern "C" void _cdecl _initterm(void*a,void*e);
extern "C" void _cdecl _fltused() {};
void WinMainCRTStartup() {
_initterm(&ctor_start,&ctor_end); // Alle statischen Konstruktorzeiger zwischen ctor_start und ctor_end aufrufen
WNDCLASSEX wc;
wc.cbSize=sizeof wc;
GetClassInfoEx(0,WC_DIALOG,&wc);
// wc.lpfnWndProc=DefDlgProc;
wc.lpszClassName=MAKEINTATOM(100);
RegisterClassEx(&wc);
StaticX::init();
EditX::init();
PlotXY::init();
ExitProcess(UINT(DialogBox(0,MAKEINTRESOURCE(100),0,MainDlgProc)));
}
// VS2019-64-Bit-Compiler bei /NODEFAULTLIB glücklich machen
void _cdecl operator delete(void*a,size_t) {free(a);}
//_declspec(dllimport) float std::numeric_limits<float>::infinity() {return 0;}
Detected encoding: ANSI (CP1252) | 4
|
|