Source file: /~heha/ewa/Reluktanzmotor/cdesk-201007.zip/cdesk.cpp

#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
Wrong umlauts? - Assume file is ANSI (CP1252) encoded