/* 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
|
|