Source file: /~heha/messtech/sensokom.zip/source/SENSOKOM.C

#include <windows.h>
#include <shellapi.h>	//DragQueryFile, DragFinish
#include <commdlg.h>	//GetOpenFileName, GetSaveFileName
#ifdef _WIN32
# define SetViewportExt(a,b,c) SetViewportExtEx(a,b,c,NULL)
# define SetWindowExt(a,b,c) SetWindowExtEx(a,b,c,NULL)
#else
# include <time.h>	//time, asctime, localtime
# include <stdarg.h>	//va_list
#endif
#include <stdio.h>	//fprintf...
#include <math.h>	//fabs, sqrt, atof
#include <string.h>	//memmove
#pragma hdrstop

#include "driver.h"
#include "dialog.h"	//Verschiedene Dialoge
#include "a_input.h"	//Dialog: Konfiguration des analogen Eingangs
#include "d_output.h"	//Dialog: Konfiguration der digitalen Ausgänge
#include "sensokom.rh"	//Definition aller Ressourcen (CM_xxxx-Konstanten)
#include "toolstat.h"	//Buttonleiste und Statuszeile
#include <commctrl.h>	//bei WIN32 System-Header zuerst finden lassen!

/*******************************
 ** Datei-Ein/Ausgabe (*.SES) **
 *******************************/

typedef struct{
 char x[32];	// Datum und Uhrzeit in ASCII - wer hat sich das ausgedacht??
 float y;
 int i,io,o;
}WERT, near*NPWERT;

// Zu speichernde/anzuzeigende Daten:
char MessartStr[64],EinheitStr[8];
NPWERT Werte;
int Messnumber, Messmax;
BOOL dirty;

static BOOL ReAllocWerte(int n) {
 register NPWERT w=Werte;
 if (n) {
  register UINT bytes=n*sizeof(WERT);
  w=w?(NPWERT)LocalReAlloc(w,bytes,LMEM_MOVEABLE|LMEM_ZEROINIT):
      (NPWERT)LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,bytes);
  if (!w) {		// kann eigentlich nur beim Vergrößern passieren
   MBox(MainWnd,0xA21,0);
   return FALSE;	// nichts ändern, Einlesen abbrechen lassen
  }
 }else{
  if (w) w=(NPWERT)LocalFree(w);
 }
 Werte=w;
 Messmax=n;
 if (Messnumber>Messmax) Messnumber=Messmax;
 return TRUE;
}

#ifdef __BORLANDC__
# pragma argsused
#endif
BOOL FileOpen(LPCSTR fname,UINT unused) {
// lädt die Datei in die globale Datenstruktur
 FILE *f;
 char buf[80];
#if defined(__SMALL__) || defined(__MEDIUM__)
 lstrcpy(buf,fname);
 f=fopen(buf,"rt");
#else
 f=fopen(fname,"rt");
#endif
 if (!f) return FALSE;
 ReAllocWerte(0);		// alles freigeben
 fgets(MessartStr,sizeof(MessartStr),f);
 MessartStr[lstrlen(MessartStr)-1]=0;	// \n abhacken
 fgets(EinheitStr,sizeof(EinheitStr),f);
 EinheitStr[lstrlen(EinheitStr)-1]=0;
 while (fgets(buf,sizeof(buf)-1,f)) {
  WERT wert;
  if (sscanf(buf,"%31[^\t]%f%d%d%d",wert.x,&wert.y,&wert.i,&wert.io,&wert.o)
    ==5) {
   if (Messnumber==Messmax && !ReAllocWerte(Messmax+50)) break;
   Werte[Messnumber]=wert;
   Messnumber++;
  }
 }
 fclose(f);
 ReAllocWerte(Messnumber);	// verkleinern
 return TRUE;
}

#ifdef __BORLANDC__
# pragma argsused
#endif
BOOL FileSave(LPCSTR fname,UINT unused) {
 FILE *f;
 int i;
#if defined(__SMALL__) || defined(__MEDIUM__)
 char buf[80];
 lstrcpy(buf,fname);
 f=fopen(buf,"wt");
#else
 f=fopen(fname,"wt");
#endif
 if (!f) return FALSE;
 fprintf(f,"%s\n",MessartStr);
 fprintf(f,"%s\n",EinheitStr);
 for (i=0; i<Messnumber; i++) {
  fprintf(f,"%s\t%f\t%i\t%i\t%i\n",
    Werte[i].x,
    Werte[i].y,
    Werte[i].i,
    Werte[i].io,
    Werte[i].o);
 }
 if (fclose(f)) return FALSE;
 return TRUE;
}

/******************************************
 ** Paint-Routinen zur Selbstdarstellung **
 ******************************************/

float Y_Max,Y_Min,Y_MB;	// Minimum, Maximum, Differenz
float Y_am, Y_ls, Y_es;	// Mittelwert,lin. Abweichung, Standardabweichung
BYTE KennlinienModus=TRUE, MinMaxModus=TRUE;
BYTE Nullinie=TRUE; BYTE Unendlich=TRUE;
BYTE IsRaster,IsMessung;
RECT RA={65,10,365,310};	// Analog-Display-Rechteck
int y3=320, y4=340, y5=360, y6=380;

int Digital(int x) {
 return x?-15:-1;	// bestimmt die Höhe der Digitalsignale
}
static void MaleKennlinie(HDC dc, int X_Current, int X_max) {
// Zeichnet die ganze Kennlinie
// X_Current=Anzahl der Messpunkte, X_max=Nummer Messpunkt rechts
 int i;
 if (X_max<2) return;		//Notbremse
 X_max--;
 if (Y_MB!=0) {
  HPEN pen=CreatePen(PS_SOLID,1,0xFF0000L);	//blau
  HPEN open=(HPEN)SelectObject(dc,pen);
  if (!KennlinienModus) {       // Messpunktmodus: senkrechte Linien
   for (i=0; i<X_Current; i++) {
    int x=iitrafo(i,0,X_max,RA.left,RA.right);
    int y=fitrafo(Werte[i].y,Y_Min,Y_Max,RA.bottom,RA.top);
    int y0=Limit(fitrafo(0,Y_Min,Y_Max,RA.bottom,RA.top),RA.top,RA.bottom);
    Line(dc,x,y,x,y0);
   }
  }else{			// Kennlinienmodus
   NPPOINT pt=(NPPOINT)LocalAlloc(LMEM_FIXED,X_Current*sizeof(POINT));
   if (pt) {
    NPPOINT pt_it=pt;
    for (i=0; i<X_Current; i++) {
     pt_it->x=iitrafo(i,0,X_max,RA.left,RA.right);
     pt_it->y=fitrafo(Werte[i].y,Y_Min,Y_Max,RA.bottom,RA.top);
     pt_it++;
    }
    SaveDC(dc);
    IntersectClipRect(dc,RA.left,RA.top,RA.right+1,RA.bottom+1);
    Polyline(dc,pt,X_Current);
    RestoreDC(dc,-1);
    LocalFree(pt);
   }
  }
  SelectObject(dc,open);
  DeleteObject(pen);
 }//(Y_MB!=0)
//------------- Zeichnen des I-, IO- und O-Verlaufs ------------------------
 if (X_Current) {
  HPEN pen,open;
  UINT bytes=X_Current*(2*sizeof(POINT));
  NPPOINT i_pt =(NPPOINT)LocalAlloc(LMEM_FIXED,bytes);
  NPPOINT io_pt=(NPPOINT)LocalAlloc(LMEM_FIXED,bytes);
  NPPOINT o_pt =(NPPOINT)LocalAlloc(LMEM_FIXED,bytes);
  if (i_pt && io_pt && o_pt) {		// kein Chaos stiften, wenn =0
   NPPOINT i_it =i_pt;
   NPPOINT io_it=io_pt;
   NPPOINT o_it =o_pt;
   int i_pr=0, io_pr=0, o_pr=0;		// Initialisierung nur für Warnings
   X_Current--;
   for (i=0; i<=X_Current; i++) {
    int x=RA.left+MulDiv(RA.right-RA.left,i,X_max);

    int y=Werte[i].i;
    if (i==X_Current || i && y!=i_pr) {	// bei Ende oder Flanke
     i_it->x=x;  i_it->y=y4+Digital(i_pr);  i_it++;
    }
    if (!i || y!=i_pr) {			// bei Anfang oder Flanke
     i_it->x=x;  i_it->y=y4+Digital(y);     i_it++;
    }
    i_pr=y;				// vorherigen Wert merken

    y=Werte[i].io;
    if (i==X_Current || i && y!=io_pr) {	// bei Ende oder Flanke
     io_it->x=x; io_it->y=y5+Digital(io_pr);io_it++;
    }
    if (!i || y!=io_pr) {			// bei Anfang oder Flanke
     io_it->x=x; io_it->y=y5+Digital(y);    io_it++;
    }
    io_pr=y;				// vorherigen Wert merken

    y=Werte[i].o;
    if (i==X_Current || i && y!=o_pr) {	// bei Ende oder Flanke
     o_it->x=x;  o_it->y=y6+Digital(o_pr);  o_it++;
    }
    if (!i || y!=o_pr) {			// bei Anfang oder Flanke
     o_it->x=x;  o_it->y=y6+Digital(y);     o_it++;
    }
    o_pr=y;				// vorherigen Wert merken
   }
   pen=CreatePen(PS_SOLID,1,0x00FF00L);	//grün
   open=(HPEN)SelectObject(dc,pen);
   Polyline(dc,i_pt,i_it-i_pt);		// C++ dividiert die Differenz selbst!
   Polyline(dc,io_pt,io_it-io_pt);
   Polyline(dc,o_pt,o_it-o_pt);
   SelectObject(dc,open);
   DeleteObject(pen);
  }
  LocalFree(i_pt);
  LocalFree(io_pt);
  LocalFree(o_pt);
 }
}

static void _cdecl MyDrawText(HDC dc, int x, int y, int w, int h, UINT f,
  char *format, ...) {
 RECT R;
 char s[256];
 SetRect(&R,x,y,x+w,y+h);
 if (f&DT_RIGHT) {
  SetTextAlign(dc,TA_RIGHT);
  x+=w;
 }
// DrawText(dc,s,,&R,f);
 ExtTextOut(dc,x,y,ETO_OPAQUE,&R,s,
   vsprintf(s,format,(va_list)(&format+1)),NULL);
 if (f&DT_RIGHT) SetTextAlign(dc,0);
}

void ExpandMinMax(void) {
 int i;
 for (i=0; i<Messnumber; i++) {
  if (Werte[i].y<Y_Min) Y_Min=Werte[i].y;
  if (Werte[i].y>Y_Max) Y_Max=Werte[i].y;
 }
 Y_MB=Y_Max-Y_Min;
}

void CalcMinMax(void) {
//--- Ermittlung von Y_Min,Y_Max der Messwerte ---
 if (MinMaxModus) {
  Y_Min=1E38F; Y_Max=-1E38F;
  if (Nullinie) Y_Min=Y_Max=0;	// Nullinie erzwingen
  ExpandMinMax();
 }
 Y_MB=Y_Max-Y_Min;
}

void CalcKennwerte(void) {
//--- Streuungen usw. berechnen ---
 int i;
 CalcMinMax();
 Y_am=Y_ls=Y_es=0;
 if (!Messnumber) return;
 for (i=0; i<Messnumber; i++) Y_am+=Werte[i].y;
 Y_am/=Messnumber;
 for (i=0; i<Messnumber; i++) {
  float y=Werte[i].y-Y_am;
  Y_ls+=(float)fabs(y);	// Fehler addieren
  Y_es+=y*y;		// Fehlerquadrate addieren
 }
 Y_ls/=Messnumber;
 Y_es=Messnumber>1?(float)sqrt(Y_es/(Messnumber-1)):0;
}	// Standardabweichung

void BuildKennlinie(HDC dci) {
// Veränderliche Größen (neu) zeichnen, auch dci=0 möglich
 HDC dc=dci;
 HFONT font,ofont;
 if (!dci) {
  RECT R;
  HRGN rgn;
  dc=GetDC(MainWnd);
  CalcClientRect(&R);	// Nicht in die Toolbar / Statuszeile malen!
  rgn=CreateRectRgn(R.left,R.top,R.right,R.bottom);
  SelectClipRgn(dc,rgn);
  DeleteObject(rgn);
  SetViewportOrgEx(dc,R.left,R.top,NULL);
  font=CreateFont(-12,0,0,0,0,0,0,0,0,0,0,0,0,T("Arial"));
  ofont=SelectObject(dc,font);
 }
// Kennlinie wegen neuer Minima und Maxima neu skaliert zeichnen
 if (dci || MinMaxModus) {
  MyDrawText(dc,0,RA.top,RA.left-8,20,DT_RIGHT,"%.3f %s",Y_Max, EinheitStr);
  MyDrawText(dc,0,RA.bottom-20,RA.left-8,20,DT_RIGHT,"%.3f %s",Y_Min, EinheitStr);
  Rectangle(dc,RA.left-1,RA.top-1,RA.right+1,RA.bottom+1);
  if (IsRaster) {		// waagerechte Hilfslinien
   HPEN pen=CreatePen(PS_DOT,0,0);
   HPEN open=(HPEN)SelectObject(dc,pen);
   int i;
   for (i=1;i<=9;i++) {
    Line(dc,RA.left,RA.bottom-i*30,RA.right,RA.bottom-i*30);
   }
   SelectObject(dc,open);
   DeleteObject(pen);
  }
//------------------------- Zeichnen der Abszisse (y=0) ---------------------
  if (Y_Min<0 && Y_Max>0) {	// Null-Linie (Y_MB ist <>0)
   HPEN pen=CreatePen(PS_DASH,0,0);
   HPEN open=(HPEN)SelectObject(dc,pen);
   int y0=fitrafo(0,Y_Min,Y_Max,RA.bottom,RA.top);
   Line(dc, RA.left,y0,RA.right,y0);
   SelectObject(dc,open);
   DeleteObject(pen);
  }
 }
//-------------------------- Zeichnen der Kennlinie ------------------------
 MaleKennlinie(dc,Messnumber,Messmax);
 MyDrawText(dc,RA.right+8, 30,180,20,0,"%s",MessartStr);
 if (Messnumber) {
  MyDrawText(dc,RA.right+8, 80,180,20,0,"%s",Werte[0].x); // Uhrzeit
  MyDrawText(dc,RA.right+8,160,130,20,0,"%.3f %s",Y_am,EinheitStr);
  MyDrawText(dc,RA.right+8,220,130,20,0,"%.3f %s",Y_ls,EinheitStr);
  MyDrawText(dc,RA.right+8,280,130,20,0,"%.3f %s",Y_es,EinheitStr);
 }
 if (!dci) {
  SelectObject(dc,ofont);
  DeleteObject(font);
  ReleaseDC(MainWnd,dc);
 }
}

void OnPaint (HDC dc) {
// Paint-Routine für Fenster, Drucker und Druckvorschau.
 HFONT font,ofont;
 font=CreateFont(-12,0,0,0,0,0,0,0,0,0,0,0,0,T("Arial"));
 ofont=SelectObject(dc,font);
 MyDrawText(dc,RA.right+8, 10,130, 20,0,"Messart:");
 MyDrawText(dc,RA.right+8, 60,130, 20,0,"Startzeit:");
// MyDrawText(dc,370, 70,80, 20,"Messwert:");
// MyDrawText(dc,370,100,80, 20,"Eingang:");
// MyDrawText(dc,370,120,80, 20,"E/A:");
// MyDrawText(dc,370,140,80, 20,"Ausgang:");
 MyDrawText(dc,RA.right+8,140,130,40,0,"arithm. Mittelwert:");
 MyDrawText(dc,RA.right+8,200,130,40,0,"lineare Streuung:");
 MyDrawText(dc,RA.right+8,260,130,40,0,"empirische Streuung:");
 MyDrawText(dc,0,320,RA.left-8,20,DT_RIGHT,"Eingang:");
 MyDrawText(dc,0,340,RA.left-8,20,DT_RIGHT,"E/A:");
 MyDrawText(dc,0,360,RA.left-8,20,DT_RIGHT,"Ausgang:");
 Rectangle(dc,RA.left-1,y3,RA.right+1,y6+1);
 BuildKennlinie(dc);
 SelectObject(dc,ofont);
 DeleteObject(font);
}

/****************************************
 ** Laden und Speichern (SENSOKOM.INI) **
 ****************************************/

BYTE CurrentAdresse=0;
char DdeServer[32];		// leer wenn lokal
BYTE HaveToolbar=TRUE;
BYTE HaveStatus=TRUE;
INTERVALL intervall={10,2};	// Vorgabe-Intervall beim Messung starten

void EscapeIfHaveBlanks(char*s) {
 if (lstrchr(s,' ')) {
  int l=lstrlen(s);
  memmove(s+1,s,l);
  s[0]=s[++l]='"';
  s[++l]=0;
 }
}

static void RegisterSES(void) {
// Endung .SES auf dieses Programm festnageln
 TCHAR s[MAX_PATH];
// RegSetValue(HKEY_CLASSES_ROOT,T(".SES"),REG_SZ,"SENSOKOM-Daten",15);
 GetModuleFileName(hInstance,s,elemof(s));
 EscapeIfHaveBlanks(s);
 lstrcat(s,T(" \"%1\""));
 RegSetValue(HKEY_CLASSES_ROOT,T(".SES\\shell\\open\\command"),REG_SZ,
   s,(lstrlen(s)+1)*sizeof(TCHAR));
}

static void MyQueryByte(LPCTSTR key, BYTE *val) {
 *val=(BYTE)GetPrivateProfileInt(T("SK"),key,*val,StdProfile);
}
static void MyQueryInt(LPCTSTR key, int *val) {
 *val=(int)GetPrivateProfileInt(T("SK"),key,*val,StdProfile);
}
static void LoadSetup(void) {
 MyQueryByte(T("Adresse"),&CurrentAdresse);
 GetPrivateProfileString(T("SK"),T("DdeServer"),T(""),DdeServer,sizeof(DdeServer),
   StdProfile);
 MyQueryByte(T("KennlinienModus"),&KennlinienModus);
 MyQueryByte(T("MinMaxModus"),&MinMaxModus);
 MyQueryByte(T("Nullinie"),&Nullinie);
 MyQueryByte(T("Raster"),&IsRaster);
 MyQueryByte(T("Unendlich"),&Unendlich);
 MyQueryByte(T("Werkzeugleiste"),&HaveToolbar);
 MyQueryByte(T("Statuszeile"),&HaveStatus);
 MyQueryInt(T("Intervall.Anzahl")  ,&intervall.count);
 MyQueryInt(T("Intervall.Sekunden"),&intervall.elaps);
}
static void LoadDlgPos(LPCTSTR key, UINT idCommand, PPOINT pt) {
// lädt die Position modusloser Dialoge und stellt sie ggf. wieder her
 TCHAR buf[32];
 BOOL state;
 GetPrivateProfileString(T("SK"),key,T(""),buf,elemof(buf),StdProfile);
 if (sscanf(buf,"%d,%d,%d",&state,&pt->x,&pt->y)==3 && state) 
   PostMessage(MainWnd,WM_COMMAND,idCommand,1);	// lParam = Kennung
}
static void LoadWinPos(void) {
 WINDOWPLACEMENT wp;
 TCHAR buf[32];
 InitStruct(&wp,sizeof(wp));
 GetWindowPlacement(MainWnd,&wp);
 GetPrivateProfileString(T("SK"),T("WinPos"),T(""),buf,elemof(buf),StdProfile);
 sscanf(buf,"%d,%d,%d,%d",
   &wp.rcNormalPosition.left,
   &wp.rcNormalPosition.top,
   &wp.rcNormalPosition.right,
   &wp.rcNormalPosition.bottom);
 MoveRectIntoFullScreen(&wp.rcNormalPosition);
 SetWindowPlacement(MainWnd,&wp);
 LoadDlgPos(T("DisplayDlg"),CM_ANZEIGEN_DISPLAY,&DisplayDlgPos);
 LoadDlgPos(T("TabelleDlg"),CM_ANZEIGEN_TABELLE,&TabelleDlgPos);
 LoadDlgPos(T("SchaltplanDlg"),CM_ANZEIGEN_SCHALTUNG,&SchaltplanDlgPos);
}
static void MySetInt(LPCTSTR key, int val) {
 TCHAR buf[8];
 wsprintf(buf,T("%i"),val);
 WritePrivateProfileString(T("SK"),key,buf,StdProfile);
}
static void SaveSetup(void) {
 MySetInt(T("Adresse"),CurrentAdresse);
 WritePrivateProfileString(T("SK"),T("DdeServer"),DdeServer,StdProfile);
 MySetInt(T("KennlinienModus"),KennlinienModus);
 MySetInt(T("MinMaxModus"),MinMaxModus);
 MySetInt(T("Nullinie"),Nullinie);
 MySetInt(T("Raster"),IsRaster);
 MySetInt(T("Unendlich"),Unendlich);
 MySetInt(T("Werkzeugleiste"),Toolbar?1:0);
 MySetInt(T("Statuszeile"),Status?1:0);
 MySetInt(T("Intervall.Anzahl")  ,intervall.count);
 MySetInt(T("Intervall.Sekunden"),intervall.elaps);
}
static void SaveDlgPos(LPCTSTR key, HWND Wnd, PPOINT pt) {
// Dialog-Position und ~Anzeigezustand speichern
 TCHAR buf[32];
 wsprintf(buf,"%d,%d,%d",Wnd?1:0,pt->x,pt->y);
 WritePrivateProfileString(T("SK"),key,buf,StdProfile);
}
static void SaveWinPos(void) {
 WINDOWPLACEMENT wp;
 TCHAR buf[32];
 InitStruct(&wp,sizeof(wp));
 GetWindowPlacement(MainWnd,&wp);
 wvsprintf(buf,"%d,%d,%d,%d",(va_list)&wp.rcNormalPosition);
 WritePrivateProfileString(T("SK"),T("WinPos"),buf,StdProfile);
 SaveDlgPos(T("DisplayDlg"),DisplayDlg,&DisplayDlgPos);
 SaveDlgPos(T("TabelleDlg"),TabelleDlg,&TabelleDlgPos);
 SaveDlgPos(T("SchaltplanDlg"),SchaltplanDlg,&SchaltplanDlgPos);
}

/***************************************
 ** Helferlein von globalen Variablen **
 ***************************************/

static BOOL ModulOK(void) {
 if (!ISM_Info(CurrentAdresse,NULL,0)) {
  MessageBox(0,"Modul nicht betriebsbereit!","Fehler", MB_ICONSTOP|MB_TASKMODAL);
  return FALSE;
 }
 return TRUE;
}

static void MyCreateDialogParam(HWND *Wnd,UINT id, DLGPROC dlgproc,LPARAM lParam) {
 if (*Wnd) SetActiveWindow(*Wnd);
 else *Wnd=CreateDialogParam(gHInstance,MAKEINTRESOURCE(id),MainWnd,
   dlgproc,lParam);
}

static void _cdecl SetStatusText(int n, const char *format, ...) {
 char s[64];
 vsprintf(s,format,(va_list)(&format+1));
 SendMessage(Status,SB_SETTEXT,n,(LPARAM)(LPSTR)s);
}

static void ShowAdresse(void) {
 SetStatusText(0,
   CurrentAdresse?"aktuelle Adresse: %d":"keine Adresse gewählt!",
   CurrentAdresse);
}

static void Invalidate(void) {
 RECT R;
 CalcClientRect(&R);
 InvalidateRect(MainWnd,&R,TRUE);
}

TCHAR current[MAX_PATH];	// Momentaner Dateiname

static void SetWindowTitle(void) {
 TCHAR buf[64];
 if (IsMessung) {
  if (Messnumber) {
   wsprintf(buf,"%d Prozent vom Messvorgang",MulDiv(100,Messnumber,Messmax));
  }else lstrcpy(buf,T("Messung gestartet"));
 }else{
  lstrcpy(buf,current[0]?GetFileNamePtr(current):"unbenannt");
  lstrcat(buf,dirty?" *":" -");
  lstrcat(buf," SENSOKOM");
 }
 SetWindowText(MainWnd,buf);
}

static void MyCheckMenuItem(UINT id, BOOL state) {
 CheckMenuItem(MainMenu,id,state?MF_CHECKED:MF_UNCHECKED);
 if (Toolbar) SendMessage(Toolbar,TB_CHECKBUTTON,id,MAKELONG(state,0));
}

static void MenuCheck(void) {
 MyCheckMenuItem(CM_NULLINIE,Nullinie);
 MyCheckMenuItem(CM_RASTER,IsRaster);
 MyCheckMenuItem(CM_MESSUNG_UNENDLICH,Unendlich);
}

static void MyEnableMenuItem(UINT id, BOOL state) {
 EnableMenuItem(MainMenu,id,state?MF_ENABLED:MF_GRAYED);
 if (Toolbar) SendMessage(Toolbar,TB_ENABLEBUTTON,id,MAKELONG(state,0));
}

static void MenuEnable(void) {
// schaltet in Menü und Toolbar, wenn es etwas zu sehen gibt
 int i;
 static UINT IDs[]={CM_FILENEW,CM_FILESAVE,CM_FILESAVEAS,
   CM_PRINTPREVIEW,CM_PRINT,
   CM_BEARBEITEN_KENNLINIE,CM_BEARBEITEN_MESSPUNKTE,
   CM_BEARBEITEN_DEFBEREICH,CM_BEARBEITEN_MINMAX};
 for (i=0; i<elemof(IDs); i++) MyEnableMenuItem(IDs[i],Messnumber);
}

static void MenuEnableStartStop(void) {
 MyEnableMenuItem(CM_MESSUNG_STARTEN,!IsMessung);
 MyEnableMenuItem(CM_MESSUNG_STOPPEN, IsMessung);
}

void SetStatusParts(void) {
 static int widths[]={130,320,420,450,475,-1};
 if (!Status) return;
 SendMessage(Status,SB_SETPARTS,elemof(widths),(LPARAM)(LPINT)widths);
 ShowAdresse();
}

BOOL DirtyOK(void) {	// Liefert TRUE wenn bereit zur Aktenvernichtung
 if (dirty) switch (MBox(MainWnd,0xA20,MB_YESNOCANCEL)) {
  case IDYES: SendMessage(MainWnd,WM_COMMAND,CM_FILESAVE,0); break;
  case IDNO: dirty=FALSE; break;	// Akte vernichten
 }
 return !dirty;		// wenn <dirty> FALSE war oder jetzt ist
}

/***********************
 ** Messung ausführen **
 ***********************/

HCURSOR Cursor;
BOOL block_wm_mousemove;

void MessungStarten(void) {
 if (DialogBoxParam(hInstance,MAKEINTRESOURCE(8000),MainWnd,IntervallDlgProc,
   (LPARAM)&intervall)!=IDOK) return;

 if (!MinMaxModus) {
  Y_Min=-10; Y_Max=10;
  if (PairInput(MainWnd,MAKEINTRESOURCE(0xB00), //"Eingabe des erwarteten Messbereiches"
    NULL,NULL,&Y_Min,&Y_Max,TRUE,3)!=IDOK) return;
 }
 LoadString(hInstance,Get_Messart(CurrentAdresse)+2560,MessartStr,elemof(MessartStr));
 Get_Einheit(CurrentAdresse,EinheitStr);
 current[0]=0;
 dirty=TRUE;
 ReAllocWerte(intervall.count);
 Messnumber=0;
 IsMessung=TRUE;
 MenuEnableStartStop();
 SetWindowTitle();
 Cursor=LoadCursor(gHInstance,MAKEINTRESOURCE(SENSOCURSOR_2));
 SetCursor(Cursor);
 ShowCursor(TRUE);
 Invalidate();
 SetTimer(MainWnd,2,intervall.elaps*1000,NULL);
}

void MessungStoppen(void) {
 if (IsMessung) {
  MenuEnable();
  IsMessung=FALSE;
  MenuEnableStartStop();
  SetWindowTitle();
  KillTimer(MainWnd,2);
  ShowCursor(FALSE);
  Cursor=LoadCursor(gHInstance,MAKEINTRESOURCE(SENSOCURSOR_1));
  SetCursor(Cursor);
  ReAllocWerte(Messnumber);	// stutzen
  Invalidate();
 }
}

void MessungMachen (void) {
 char AntwortString[32];
 if (Messnumber<Messmax) {	// eigentlich unsinnige Notbremse
  block_wm_mousemove=TRUE;
  if (ISM_Abfrage(CurrentAdresse,AntwortString,sizeof(AntwortString))) {
   WERT wert;
#ifdef _WIN32
   int i=GetDateFormatA(LOCALE_USER_DEFAULT,0,NULL,NULL,wert.x,sizeof(wert.x));
   if (!wert.x[i-1]) i--;	// diese Fkt. scheint das Null-Byte mitzuzählen!
   wert.x[i]=' '; i++;
   GetTimeFormatA(LOCALE_USER_DEFAULT,TIME_FORCE24HOURFORMAT,
     NULL,NULL,wert.x+i,sizeof(wert.x)-i);
#else
   time_t t;
   time(&t);
   lstrcpy(wert.x,asctime(localtime(&t)));
   wert.x[24]='\0';	// \n entfernen!
#endif
   wert.y= (float)atof(Get_Messwert(AntwortString));
   wert.i= GetInp(AntwortString);
   wert.io=Get_IO(AntwortString);
   wert.o= GetOut(AntwortString);
   Werte[Messnumber++]=wert;
   SetWindowTitle();
   CalcKennwerte();
   BuildKennlinie(0);
  }
  block_wm_mousemove=FALSE;
 }
 if (Messnumber==2) MenuEnable();	// zwischendurch Schaltmöglichkeit
 if (Messnumber==Messmax) {
  MessageBeep(0);	// Gong(?) für Ende der Messung
  if (Unendlich) {
   if (ReAllocWerte(Messmax+10)) {
    Invalidate();	// Kurven (auch digital) verschieben sich
   }else MessungStoppen();
  }else MessungStoppen();	// hoffentlich optimiert der Compiler!
 }
}

/****************************************************
 ** Hauptfenster-Prozedur (lange switch-Anweisung) **
 ****************************************************/

#define WM_OPENFILE (WM_USER+101)	// wParam=FilterIndex, lParam=FileName

UINT WM_ComDlgHelp;

LRESULT CALLBACK FrameWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 LRESULT ret=HandleToolStat(Wnd,Msg,wParam,lParam);
 switch (Msg) {
  case WM_CREATE: {
   BOOL InQuote;
   TCHAR s[MAX_PATH], *dst;
   LPTSTR src;
   Cursor=LoadCursor(gHInstance,MAKEINTRESOURCE(SENSOCURSOR_1));
   RegisterSES();
   WM_ComDlgHelp=RegisterWindowMessage(HELPMSGSTRING);
   LoadSetup();
   Com_Init(*DdeServer?(LPCSTR)DdeServer:(LPCSTR)NULL,2,9600);	/* DDE starten */
   LoadWinPos();
   if (HaveToolbar) ToggleToolbar(CM_TOOLBAR);
   if (HaveStatus) ToggleStatus(CM_STATUS);
   SetStatusParts();
   SetMenuBitmaps();
   MenuCheck();
   src=((LPCREATESTRUCT)lParam)->lpCreateParams;	// Kommandozeile
   InQuote=FALSE;
   dst=s;
   for (;;src++) {
    switch (*src) {
     case '"': InQuote=!InQuote; break;
     case  0 : InQuote=FALSE; nobreak;
     case ' ': if (!InQuote) {
      *dst=0;			// terminieren
      if (s!=dst) SendMessage(Wnd,WM_OPENFILE,0,(LPARAM)(LPTSTR)s);
      dst=s;			// Wenn mindestens ein Zeichen drin
      break;
     }nobreak; 
     default: *dst++=*src;	// alle anderen Zeichen rausfummeln
    }
    if (!*src) break;
   }

  }break;

  case WM_OPENFILE: {
   if (!DirtyOK()) break;
   MessungStoppen();
   if (FileOpen((LPTSTR)lParam,wParam)) {
    lstrcpyn(current,(LPTSTR)lParam,sizeof(current));
    SetWindowTitle();
    MenuEnable();
    if (!MinMaxModus) ExpandMinMax();
    CalcKennwerte();
    Invalidate();
   }else MBox(Wnd,0xA1F,0,lParam);
  }break;

  case WM_DROPFILES: {
   TCHAR s[260];
   DragQueryFile((HDROP)wParam,0,s,elemof(s));
   DragFinish((HDROP)wParam);
   SendMessage(Wnd,WM_OPENFILE,0,(LPARAM)(LPTSTR)s);
  }break;

  case WM_PAINT: {
   PAINTSTRUCT PS;
   RECT R;
   CalcClientRect(&R);
   BeginPaint(Wnd,&PS);
   SetViewportOrgEx(PS.hdc,R.left,R.top,NULL);
   OnPaint(PS.hdc);
   EndPaint(Wnd,&PS);
  }return 0;

  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 98:
   case 99: return ret;
   case CM_FILENEW: {
    if (!DirtyOK()) break;
    MessungStoppen();
    ReAllocWerte(0);	// Wertetabelle vernichten
    current[0]=0;	// Dateiname vernichten
    MessartStr[0]=EinheitStr[0]=0;
    MenuEnable();	// Menü umstellen
    SetWindowTitle();	// Fenstertitel auf "unbenannt"
    Invalidate();	// Client neu zeichnen
   }break;
   case CM_FILEOPEN: {
    OPENFILENAME ofn;
    TCHAR s[MAX_PATH];
    InitStruct(&ofn,sizeof(ofn));
    ofn.hwndOwner=Wnd;
    ofn.lpstrFilter=T("Diagrammdateien\0*.ses\0");
    ofn.lpstrFile=s;
    ofn.nMaxFile=elemof(s);
    s[0]=0;
    ofn.Flags=OFN_LONGNAMES|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_SHOWHELP;
    if (GetOpenFileName(&ofn))
      SendMessage(Wnd,WM_OPENFILE,(WPARAM)ofn.nFilterIndex,(LPARAM)(LPSTR)s);
   }break;
   case CM_FILESAVE: if (current[0]) {
filesave:
    MessungStoppen();
    if (FileSave(current,0)) {
     dirty=FALSE;
     SetWindowTitle();
    }else MBox(Wnd,0xA1F,0,(LPTSTR)current);
    break;
   }nobreak;
   case CM_FILESAVEAS: {
    OPENFILENAME ofn;
    InitStruct(&ofn,sizeof(ofn));
    ofn.hwndOwner=Wnd;
    ofn.lpstrFilter=T("Diagrammdateien\0*.ses\0");
    ofn.lpstrFile=current;
    ofn.nMaxFile=elemof(current);           
    ofn.Flags=OFN_LONGNAMES|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_SHOWHELP;
    if (GetSaveFileName(&ofn)) goto filesave;
   }break;

   case CM_PRINTPREVIEW:
   case CM_PRINT:
   case CM_PRINTERSETUP: {
    static HGLOBAL ghDevMode;
    static HGLOBAL ghDevNames;
    PRINTDLG pd;
    InitStruct(&pd,sizeof(pd));
    pd.hwndOwner=Wnd;
    pd.hDevMode=ghDevMode;
    pd.hDevNames=ghDevNames;
    pd.Flags=PD_ALLPAGES|PD_USEDEVMODECOPIES|PD_NOPAGENUMS|PD_NOSELECTION
      |PD_RETURNIC|PD_SHOWHELP;
    switch (LOWORD(wParam)) {
     case CM_PRINTPREVIEW: pd.Flags|=PD_RETURNDEFAULT; break;
     case CM_PRINT: pd.Flags|=PD_RETURNDC; break;
     case CM_PRINTERSETUP: pd.Flags=PD_PRINTSETUP; nobreak;
    }
    if (LOWORD(wParam)==CM_PRINTPREVIEW && ghDevNames || PrintDlg(&pd)) {
     POINT sheet;
     ghDevMode=pd.hDevMode;
     ghDevNames=pd.hDevNames;
     if (LOWORD(wParam)==CM_PRINTERSETUP) break;
     if (!pd.hDC) {
      LPDEVNAMES dn=GlobalLock(ghDevNames);
      if (dn) pd.hDC=CreateIC(
	(LPTSTR)dn+dn->wDriverOffset,
	(LPTSTR)dn+dn->wDeviceOffset,
	(LPTSTR)dn+dn->wOutputOffset,
	NULL);
      GlobalUnlock(ghDevNames);
     }
     sheet.x=GetDeviceCaps(pd.hDC,HORZRES);
     sheet.y=GetDeviceCaps(pd.hDC,VERTRES);
     if (LOWORD(wParam)==CM_PRINT) {
      DOCINFO di;
      InitStruct(&di,sizeof(di));
      di.lpszDocName=current;
      if (pd.Flags&PD_PRINTTOFILE) di.lpszOutput="C:/TEMP/AUSDRUCK.PS";
      StartDoc(pd.hDC,&di);
      StartPage(pd.hDC);
      SetMapMode(pd.hDC,MM_ISOTROPIC);
      SetWindowExt(pd.hDC,550,550);
      SetViewportExt(pd.hDC,sheet.x,sheet.y);
      OnPaint(pd.hDC);
      EndPage(pd.hDC);
      EndDoc(pd.hDC);
     }else MBox(Wnd,0xA1E,0,sheet.x,sheet.y);	// Druckvorschau fehlt (X,Y)
     DeleteDC(pd.hDC);
    }
   }break;

   case CM_EXIT: {
    SendMessage(Wnd,WM_CLOSE,0,0);
   }break;
   case CM_HELPINDEX: {
    WinHelp(MainWnd,HelpFileName,HELP_INDEX,0);
   }break;
   case CM_HELPABOUT: {
    DialogBox(hInstance,MAKEINTRESOURCE(CM_HELPABOUT),Wnd,AboutDlgProc);
   }break;

   case CM_KONFIGURATION_ADRESSENSELEKTION: {
    int a=DialogBox(hInstance,MAKEINTRESOURCE(LOWORD(wParam)),
      Wnd,SearchDlgProc);
    if (a && Status) {
     CurrentAdresse=(BYTE)a;
     ShowAdresse();
    }
   }break;
   case CM_KONFIGURATION_A_INPUT: {
    if (!ModulOK()) return 0;
    DialogBoxParam(hInstance,MAKEINTRESOURCE(LOWORD(wParam)),
      Wnd,ParamDlgProc,CurrentAdresse);
   }break;
   case CM_KONFIGURATION_D_OUTPUT: {
    if (!ModulOK()) return 0;
    DialogBoxParam(hInstance,MAKEINTRESOURCE(LOWORD(wParam)),
      Wnd,OutputDlgProc,CurrentAdresse);
   }break;
   case CM_KONFIGURATION_TRANSMITTER: {
    if (!ModulOK()) return 0;
    if (DialogBoxParam(hInstance,MAKEINTRESOURCE(LOWORD(wParam)),
      Wnd,TransmitterDlgProc,CurrentAdresse)!=IDOK) return 0;
    MBox(Wnd,0xA1C,MB_OK);
   }break;
   case CM_KONFIGURATION_SCHNITTSTELLE: {
    DialogBoxParam(hInstance,MAKEINTRESOURCE(LOWORD(wParam)),
      Wnd,SchnittstelleDlgProc,(LPARAM)DdeServer);
   }break;
   case CM_ANZEIGEN_DISPLAY: {
    MyCreateDialogParam(&DisplayDlg,LOWORD(wParam),DisplayDlgProc,0);
    SetTimer(Wnd,1,1000,NULL);
   }break;
   case CM_ANZEIGEN_TABELLE: {
    MyCreateDialogParam(&TabelleDlg,LOWORD(wParam),TabelleDlgProc,CurrentAdresse);
    SetTimer(Wnd,1,1000,NULL);
   }break;
   case CM_ANZEIGEN_SCHALTUNG: {
    MyCreateDialogParam(&SchaltplanDlg,LOWORD(wParam),SchaltplanDlgProc,
      Get_Messart(CurrentAdresse));
   }break;

   case CM_BEARBEITEN_MESSPUNKTE: {
    KennlinienModus=FALSE;
    Invalidate();
   }break;
   case CM_BEARBEITEN_KENNLINIE: {
    KennlinienModus=TRUE;
    Invalidate();
   }break;
   case CM_BEARBEITEN_DEFBEREICH: {
    if (PairInput(Wnd,MAKEINTRESOURCE(0xB01),
	//"Eingabe des darzustellenden Messbereiches",
      NULL, NULL, &Y_Min, &Y_Max, TRUE, 3) ==IDOK) {
     MinMaxModus=FALSE;
     Invalidate();
    }
   }break;
   case CM_BEARBEITEN_MINMAX: {
    MinMaxModus=TRUE;
    CalcMinMax();
    Invalidate();
   }break;
   case CM_NULLINIE: {
    Nullinie=(BYTE)!Nullinie;
    MenuCheck();
    Invalidate();
   }break;
   case CM_RASTER: {
    IsRaster=(BYTE)!IsRaster;
    MenuCheck();
    Invalidate();
   }break;
   case CM_BEARBEITEN_COPY: {
    SendMessage(Wnd,WM_COPY,0,0);	// zu Standard-Windows-Nachricht umsetzen
   }break;
   case CM_TOOLBAR: {
    ToggleToolbar(CM_TOOLBAR);
    MenuEnable();		// Toolbar-Enables mitführen
    MenuEnableStartStop();
    MenuCheck();
   }break;
   case CM_STATUS: {
    ToggleStatus(CM_STATUS);
    SetStatusParts();
   }break;
   case CM_MESSUNG_STARTEN: {
    if (!ModulOK()) break;
    if (!DirtyOK()) break;
    MessungStarten();
   }break;
   case CM_MESSUNG_UNENDLICH: {
    Unendlich=(BYTE)!Unendlich;
    MenuCheck();
   }break;
   case CM_MESSUNG_STOPPEN: {
    MessungStoppen();
   }break;
  }break;

  case WM_KEYDOWN: switch (wParam) {
   case VK_ESCAPE: MessungStoppen(); break;
  }break;

#ifdef _WIN32
  case WM_NOTIFY: {
  }return ret;
#endif

  case WM_COPY: {
   HDC hMeta;
   RECT R;
#ifdef _WIN32
   SetRect(&R,0,0,12000,10240);
   hMeta=CreateEnhMetaFile(0,NULL,&R,T("Sensokom\0Kurve\0"));
   SetWindowExtEx(hMeta,550,400,NULL);
   OnPaint(hMeta);
   OpenClipboard(Wnd);
   EmptyClipboard();
   SetClipboardData(CF_ENHMETAFILE,CloseEnhMetaFile(hMeta));
#else
   HGLOBAL hMFP=GlobalAlloc(GMEM_SHARE,sizeof(METAFILEPICT));
   LPMETAFILEPICT pMFP=(LPMETAFILEPICT)GlobalLock(hMFP);
   SetRect(&R,0,0,550,400);
   hMeta=CreateMetaFile(NULL);
   SetWindowExt(hMeta,R.right,R.bottom);
   OnPaint(hMeta);
   pMFP->mm=MM_ANISOTROPIC;
   pMFP->xExt=R.right;
   pMFP->yExt=R.bottom;
   pMFP->hMF=CloseMetaFile(hMeta);
   GlobalUnlock(hMFP);
   OpenClipboard(Wnd);
   EmptyClipboard();
   SetClipboardData(CF_METAFILEPICT,hMFP);
#endif
   CloseClipboard();
  }break;

  case WM_TIMER: switch (wParam) {
   case 1: {
    char recv[32];
    block_wm_mousemove=TRUE;	// verhindert den unerklärlichen Effekt,
// dass bei aufgeklapptem Menü WM_MOUSEMOVE-Nachrichten vorbei kommen,
// mit dem stark störenden Effekt, dass der Mauspfeil zum Kreuz wird
    if (ISM_Abfrage(CurrentAdresse,recv,sizeof(recv))) {
     if (DisplayDlg) SendMessage(DisplayDlg,WM_USER,0,(LPARAM)(LPSTR)recv);
     if (TabelleDlg) SendMessage(TabelleDlg,WM_USER,0,(LPARAM)(LPSTR)recv);
     if (!DisplayDlg && !TabelleDlg) KillTimer(Wnd,1);
    }
    block_wm_mousemove=FALSE;
   }break;
   case 2: {
    MessungMachen();
   }break;
  }break;

  case WM_MOUSEMOVE: if (!block_wm_mousemove) {
   SetCursor(Cursor);
   if (Messnumber>=2
   && RA.left<=(int)LOWORD(lParam) && (int)LOWORD(lParam)<=RA.right
   && RA.top<=(int)HIWORD(lParam) && (int)HIWORD(lParam)<=y6 ) {
  // Berechnung des X-Wertes aus den grafischen Koordinaten
    int xw=MulDiv((int)LOWORD(lParam)-RA.left,Messmax-1,RA.right-RA.left);
  // Anzeigen der Werte
    SetStatusText(1,"%s",Werte[xw].x);
    SetStatusText(2,"%.3f %s",Werte[xw].y,EinheitStr);
    SetStatusText(3,"%d",Werte[xw].i);
    SetStatusText(4,"%d",Werte[xw].io);
    SetStatusText(5,"%d",Werte[xw].o);
   }
  }break;

  case WM_QUERYENDSESSION:
  case WM_CLOSE: {
   if (!DirtyOK()) return FALSE; // kein DefWindowProc, kein Windows beenden
   Cursor=LoadCursor(0,IDC_WAIT); SetCursor(Cursor);
   Com_Init(NULL,0,0);		/* abmelden und DDE beenden */
   SaveWinPos();
   SaveSetup();
  }break;

  case WM_DESTROY: {
   WinHelp(Wnd,HelpFileName,HELP_QUIT,0);
   DeleteMenuBitmaps();
   PostQuitMessage(0);
  }break;

  default: if (Msg==WM_ComDlgHelp){
   WinHelp(Wnd,HelpFileName,HELP_CONTEXT,100);
  }
 }
 return DefWindowProc(Wnd,Msg,wParam,lParam);
}

/************************
 ** Mini-Hauptprogramm **
 ************************/

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,LPSTR cmdLine,int cmdShow) {
 static WNDCLASS wc={CS_HREDRAW|CS_VREDRAW,FrameWndProc,0,0,0,0,0,
   (HBRUSH)(COLOR_WINDOW+1),MAKEINTRESOURCE(SDI_MENU),T("SK")};
 MSG Msg;
 wc.hInstance=hInstance=gHInstance=hInst;
 StdProfile=T("SENSOKOM.INI");
 HelpFileName=T("SENSOKOM.HLP");
 if (!hPrevInst) {
  wc.hInstance=hInst;
  wc.hIcon=LoadIcon(gHInstance,MAKEINTRESOURCE(IDI_SENSOKOMAPPLICATION));
  RegisterClass(&wc);
 }
 AccTable=LoadAccelerators(gHInstance,MAKEINTRESOURCE(SDI_MENU));

 CreateWindowEx(WS_EX_ACCEPTFILES|WS_EX_OVERLAPPEDWINDOW,T("SK"),T("SENSOKOM - Steuerprogramm"),
   WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
   0,0,gHInstance,cmdLine);
 ShowWindow(MainWnd,cmdShow);

 while (GetMessage(&Msg,0,0,0)) {
  if (AccTable && TranslateAccelerator(MainWnd,AccTable,&Msg)) continue;
  TranslateMessage(&Msg);
  DispatchMessage(&Msg);
 }
 return Msg.wParam;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded