Quelltext /~heha/mb-iwp/RX10/RX10.zip/src/RX10.cpp

/* Ansteuerung des Rotationsachsenkalibrators RX10
 * via serielle Schnittstelle
 * Siehe http://www.tu-chemnitz.de/~ygu/mb-iwp/RX10
 * h#s 10/15
 */

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <setupapi.h>
#include <devguid.h>
#include <stdio.h>
#include <tchar.h>

#pragma intrinsic(memcpy,strlen)

static struct TConfig{
 POINTS WinPos;		// Fensterposition des Dialogs
 BYTE SerialNo;		// Nummer der seriellen Schnittstelle, 0 = COM1
 bool Grad;		// Winkel in Grad (sonst in Schritten 0..71)
}Config;

static HINSTANCE hInstance;
static HWND ghMainWnd;
static HWND ghStatus;

#define elemof(x) (sizeof(x)/sizeof((x)[0]))
#define T(x) TEXT(x)
#define nobreak

/******************************
 * Hardwarenahe Kommunikation *
 ******************************/

static SP_CLASSIMAGELIST_DATA ild;
static HANDLE hCom;

static HANDLE Open(int i) {
 TCHAR s[12];
 _sntprintf(s,elemof(s),T("\\\\.\\COM%u"),i+1);
 HANDLE h=CreateFile(s,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 if (h==INVALID_HANDLE_VALUE) h=0;
 return h;
}

static void Enum(HWND hCombo) {
 COMBOBOXEXITEM cbei;
 TCHAR s[12];
 cbei.mask=CBEIF_TEXT|CBEIF_LPARAM|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE|CBEIF_OVERLAY;
 cbei.pszText=s;
 cbei.iItem=0;
 SetupDiGetClassImageIndex(&ild,(LPGUID)&GUID_DEVCLASS_PORTS,&cbei.iImage);
 cbei.iSelectedImage=cbei.iImage;
 ComboBox_ResetContent(hCombo);
 SetWindowText(hCombo,NULL);

 HDEVINFO devs=SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,NULL,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
 if (devs!=INVALID_HANDLE_VALUE) {
  SP_DEVINFO_DATA devInfo;
  devInfo.cbSize=sizeof devInfo;
  for (UINT i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
   HKEY hKey;
   TCHAR s[16];
   DWORD slen=sizeof s;
   *s=0;
   if ((hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ))
     ==INVALID_HANDLE_VALUE) continue;
   RegQueryValueEx(hKey,T("PortName"),NULL,NULL,(LPBYTE)s,&slen);
   RegCloseKey(hKey);
   if (*s=='C') {	// Fehlschläge und LPTx ausfiltern
    int i=_ttoi(s+3)-1;	// Nullbasierte COM-Nummer
    cbei.pszText=s;
    cbei.lParam=i;
    cbei.iOverlay=0;
    if (i==Config.SerialNo) {
     if (!hCom) cbei.iOverlay=2;	//durchkreuzt!
    }else{
     HANDLE h=Open(i);
     if (h) CloseHandle(h);
     else cbei.iOverlay=2;
    }
    SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
    if (i==Config.SerialNo) ComboBox_SetCurSel(hCombo,cbei.iItem);
    cbei.iItem++;
   }
  }
  SetupDiDestroyDeviceInfoList(devs);
 }
}

static char recvbuf[82];
static int recvidx;

static void Status(UINT id) {
 TCHAR s[82];
 LoadString(hInstance,id,s,elemof(s));
 SetWindowText(ghStatus,s);
}

static void Send(char*str) {
 if (!hCom) return;
#ifdef DEBUG
 MessageBeep(0);
#else
 DWORD dw,len=strlen(str);
 if (!WriteFile(hCom,str,len,&dw,NULL)
 || dw!=len
 || !WriteFile(hCom,"\r\n",2,&dw,NULL)
 || dw!=2) Status(3);
#endif
}

static void EnableWindows(HWND w, int n, BOOL enab) {
 do{
  EnableWindow(w,enab);
  w=GetNextSibling(w);
 }while(--n);
}

static void CALLBACK Poll(HWND,UINT,UINT_PTR,DWORD) {
 if (!hCom) return;
 DWORD space=sizeof recvbuf-recvidx;
 if (!ReadFile(hCom,recvbuf+recvidx,space,&space,NULL)) {Status(4); return;}
 if (!space) return;	// nichts gelesen
 recvidx+=space;
 char*e=(char*)memchr(recvbuf,'\n',recvidx);
 if (recvidx==sizeof recvbuf) e=recvbuf+sizeof recvbuf-1;	// volle Zeile
 if (e) {
  *e=0;
  if (e!=recvbuf && e[-1]=='\r') *--e=0;
  if (e!=recvbuf) SetWindowTextA(ghStatus,recvbuf);		// nur wenn nicht leer: ausgeben!
  if (strstr(recvbuf,"Position")) {
   e=(char*)memchr(recvbuf,':',recvidx);
   if (e) {
    UINT pos=atoi(e+1);
    SendDlgItemMessage(ghMainWnd,20,TBM_SETPOS,TRUE,pos);
    if (Config.Grad) pos*=5;
    SetDlgItemInt(ghMainWnd,21,pos,FALSE);
    KillTimer(ghMainWnd,2);		// kein Kommando auslösen
    EnableWindows(GetDlgItem(ghMainWnd,20),7,TRUE);
   }
  }
  recvidx=0;
 }
}

static void Open() {
 hCom=Open(Config.SerialNo);
 if (!hCom) {
  Status(1);
  return;
 }
 Status(2);
 DCB dcb;
 dcb.DCBlength=sizeof dcb;
 BuildCommDCB(T("9600,n,8,1"),&dcb);
 SetCommState(hCom,&dcb);
 COMMTIMEOUTS to={MAXDWORD,0,0,1,10};
 SetCommTimeouts(hCom,&to);
 recvidx=0;
#ifdef DEBUG
 EnableWindows(GetDlgItem(ghMainWnd,20),7,TRUE);
#else
 Send("c");	// aktuelle Position abfragen und von Poll() einsetzen lassen
 SetTimer(ghMainWnd,1,100,Poll);
#endif
}

static void Close() {
 if (hCom) CloseHandle(hCom);
 hCom=0;
 KillTimer(ghMainWnd,1);
 EnableWindows(GetDlgItem(ghMainWnd,20),7,FALSE);
}

static void Send() {
 char s[32];
 BOOL ok;
 UINT v=GetDlgItemInt(ghMainWnd,21,&ok,FALSE);
 if (ok) {
  if (Config.Grad) v/=5;
  SendDlgItemMessage(ghMainWnd,20,TBM_SETPOS,TRUE,v);	// Schieber aktualisieren (generiert kein WM_HSCROLL)
  sprintf(s,"a%02d",v);
 }else{
  GetDlgItemTextA(ghMainWnd,21,s,elemof(s));
 }
 Send(s);
}

/************************
 * Konfigurationsdialog *
 ************************/

// Lädt Konfigurationsdaten aus Registrierung
static void LoadConfig() {
 HKEY key;
 if (!RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\RX10"),
   0,KEY_QUERY_VALUE,&key)) {
  DWORD size=sizeof(Config);
  RegQueryValueEx(key,T("Config"),NULL,NULL,(LPBYTE)&Config,&size);
  RegCloseKey(key);
 }
 WINDOWPLACEMENT wp;
 wp.length=sizeof wp;
 GetWindowPlacement(ghMainWnd,&wp);
 OffsetRect(&wp.rcNormalPosition,Config.WinPos.x-wp.rcNormalPosition.left,Config.WinPos.y-wp.rcNormalPosition.top);
 SetWindowPlacement(ghMainWnd,&wp);
}

// Speichert Konfigurationsdaten in Registrierung
static void SaveConfig() {
 WINDOWPLACEMENT wp;
 wp.length=sizeof(wp);
 GetWindowPlacement(ghMainWnd,&wp);
 Config.WinPos.x=(short)wp.rcNormalPosition.left;
 Config.WinPos.y=(short)wp.rcNormalPosition.top;
 HKEY key;
 if (RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\RX10"),
   0,NULL,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,NULL,&key,NULL)) return;
 TCHAR s[128];
 RegSetValue(key,NULL,REG_SZ,s,GetWindowText(ghMainWnd,s,elemof(s))*sizeof(TCHAR));
 RegSetValueEx(key,T("Config"),0,REG_BINARY,(LPBYTE)&Config,sizeof(Config));
 RegCloseKey(key);
}

static INT_PTR CALLBACK SetupDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   ild.cbSize=sizeof(ild);
   SetupDiGetClassImageList(&ild);
   SendDlgItemMessage(Wnd,110,CBEM_SETIMAGELIST,0,(LPARAM)ild.ImageList);
   SendMessage(Wnd,WM_TIMER,2,0);
  }return TRUE;

  case WM_COMMAND: switch (LOBYTE(wParam)) {
   case IDOK: {
    HWND hCombo=GetDlgItem(Wnd,110);
    int i=ComboBox_GetCurSel(hCombo);
    if (i<0) {SetFocus(hCombo); break;}
    Config.SerialNo=(BYTE)ComboBox_GetItemData(hCombo,i);
   }nobreak;
   case IDCANCEL: {
    EndDialog(Wnd,wParam);
   }break;
  }break;

  case WM_TIMER: {
   KillTimer(Wnd,wParam);
   Enum(GetDlgItem(Wnd,110));
  }break;

  case WM_DEVICECHANGE: {
   SetTimer(Wnd,1,2000,NULL);
  }break;

  case WM_DESTROY: {
   SetupDiDestroyClassImageList(&ild);
  }break;
 }
 return FALSE;
}

/***************
 * Haupfenster *
 ***************/

static INT_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
 switch (Msg) {
  case WM_INITDIALOG: {
   ghMainWnd=Wnd;
   ghStatus=GetDlgItem(Wnd,10);
   HICON ico=LoadIcon(hInstance,MAKEINTRESOURCE(100));
   SetClassLongPtr(Wnd,GCLP_HICON,(LONG_PTR)ico);
// Mit SetWindowPos() _vor_ SetClassLongPtr kommt kein (kleines) Icon!
// Bei SetWindowPlacement ist es egal ob davor oder danach.
   SetClassLongPtr(Wnd,GCLP_HICONSM,(LONG_PTR)ico);
   LoadConfig();
   CheckRadioButton(Wnd,23,24,23+Config.Grad);
   STARTUPINFO si;
   si.cb=sizeof(si);
   GetStartupInfo(&si);
   ShowWindow(Wnd,si.wShowWindow);
   SendDlgItemMessage(Wnd,20,TBM_SETRANGE,FALSE,MAKELONG(0,71));	// Slider-Bereich
   SendDlgItemMessage(Wnd,22,UDM_SETRANGE32,0,Config.Grad?355:71);	// UpDown-Bereich
   Open();
  }return TRUE;
  
  case WM_SIZE: {
   SendMessage(ghStatus,Msg,wParam,lParam);
  }break;
  
  case WM_TIMER: switch (wParam) {
   case 2: KillTimer(Wnd,wParam); Send(); break;
  }break;
  
  case WM_HSCROLL: if (LOBYTE(wParam)!=SB_ENDSCROLL) {
   UINT pos=SendMessage((HWND)lParam,TBM_GETPOS,0,0);
   if (Config.Grad) pos*=5;	// größere Schritte
   SetDlgItemInt(Wnd,21,pos,FALSE);
   KillTimer(Wnd,2);
  }else SetTimer(Wnd,2,500,NULL);
  break;

  case WM_NOTIFY: {
   NMHDR*h=(NMHDR*)lParam;
   if (h->idFrom==22 && h->code==UDN_DELTAPOS) {
    NMUPDOWN*ud=(NMUPDOWN*)h;
    if (Config.Grad) ud->iDelta*=5;
    int v=ud->iPos+ud->iDelta;
    if (Config.Grad) v/=5;
    SendDlgItemMessage(Wnd,20,TBM_SETPOS,TRUE,v);
   }
  }break;

  case WM_COMMAND: switch (wParam) {
   case MAKELONG(21,EN_UPDATE): {
    if (hCom) SetTimer(Wnd,2,1000,NULL);
   }break;
   case 23: if (Config.Grad) {
    BOOL ok;
    UINT v=GetDlgItemInt(Wnd,21,&ok,FALSE);
    if (ok) {
     SetDlgItemInt(Wnd,21,v/5,FALSE);
     KillTimer(Wnd,2);		// kein Kommando auslösen
    }
    SendDlgItemMessage(Wnd,22,UDM_SETRANGE32,0,71);
    Config.Grad=false;
   }break;
   case 24: if (!Config.Grad) {
    BOOL ok;
    UINT v=GetDlgItemInt(Wnd,21,&ok,FALSE);
    if (ok) {
     SetDlgItemInt(Wnd,21,v*5,FALSE);
     KillTimer(Wnd,2);		// kein Kommando auslösen
    }
    SendDlgItemMessage(Wnd,22,UDM_SETRANGE32,0,355);
    Config.Grad=true;
   }break;
   case 1: Send(); break;	// Kommando je nach Editfenster
   case 26: Send("h"); break;	// Referenzfahrt
   case 11: if (DialogBox(hInstance,MAKEINTRESOURCE(101),Wnd,SetupDlgProc)==IDOK) {
    Close();
    Open();
   }break;
   case 2: {
    Close();
    SaveConfig();
    EndDialog(Wnd,wParam);
   }break;
  }break;

  case WM_ENDSESSION: if (wParam) SaveConfig(); break;
 }
 return FALSE;
}

INT_PTR CALLBACK WinMainCRTStartup() {
 hInstance=GetModuleHandle(NULL);
 InitCommonControls();
 ExitProcess(DialogBox(hInstance,MAKEINTRESOURCE(100),0,MainDlgProc));
}
Vorgefundene Kodierung: ANSI (CP1252)4
Umlaute falsch? - Datei sei ANSI-kodiert (CP1252)