#include "comsel.h"
#include <conio.h> // getch
#include <stdio.h> // printf etc.
#include <shlwapi.h> // wnsprinf
#pragma intrinsic(memcpy,memset)
static void clrscr (){
fputs("\033[2J",stdout);
}
static void gotoxy(int x, int y) {
printf("\033[%u;%uH",y+1,x+1);
}
void hidecursor() {
HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO info;
info.dwSize = 100;
info.bVisible = FALSE;
SetConsoleCursorInfo(consoleHandle, &info);
}
struct OneLine{
BYTE wrpos;
char buf[200];
bool push_back(char c) {
if (wrpos>=sizeof buf) return false;
buf[wrpos++]=c;
return true;
}
BYTE space() const {return sizeof buf-wrpos;}
BYTE fill() const {return wrpos;}
char*p() {return buf+wrpos;}
void outLine(unsigned row);
};
struct MicroCom{
HANDLE hCom;
BYTE comno;
OVERLAPPED ovr; // Overlapped-Struktur zum Lesen
OneLine line;
static DWORD lm;
void haveData(DWORD br);
DWORD onEvent();
void readUntilBlock();
void write(char c);
bool openCom(unsigned);
char*onFullLine();
MicroCom();
~MicroCom();
// https://de.wikipedia.org/wiki/Dreierregel_(C%2B%2B)
void operator=(const MicroCom&);
private:
MicroCom(const MicroCom&) /* = delete */;
// MicroCom(const MicroCom&&);
// void operator=(const Microcom&&);
};
typedef fixvec<MicroCom,10,BYTE> MicroComList;
MicroComList microcoms;
DWORD MicroCom::lm=2; // line spacing multiplier 2,1,2,1...
static void textcolor(BYTE c) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),c);
}
bool MicroCom::openCom(unsigned n) {
comno = n;
TCHAR fname[16];
wnsprintf(fname,elemof(fname),TEXT("\\\\.\\COM%u"),n+1);
HANDLE h=CreateFile(fname,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
if (h==INVALID_HANDLE_VALUE) {
textcolor(12);
printf("******** Could not open COM%-2u ********\n",comno+1);
return false;
}
hCom = h;
// memset(&ovr,0,sizeof ovr);
ovr.hEvent = CreateEvent(0,0,0,0);
DCB dcb;
dcb.DCBlength = sizeof dcb;
if (!GetCommState(h,&dcb)){
textcolor(12);
printf("******** Error getting state COM %02d\n",comno+1);
return false;
}
dcb.BaudRate = 38400;
((DWORD*)&dcb)[2]=1; // set fBinary, clear all other bits = Set raw mode
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.EofChar = '\n'; // may terminate input-stream-of-bytes
if (!SetCommState(h,&dcb)){
textcolor(12);
printf("******** Error setting serial port state COM %02d\n",comno+1);
return false;
}
COMMTIMEOUTS timeout={10}; // Intercharacter-Timeout lässt Lücken > 10 ms erkennen
if (!SetCommTimeouts(h, &timeout)) {
textcolor(12);
printf("********** Timeout COM %02d **********\n",comno+1);
return false;
}
return true;
}
MicroCom::MicroCom() {
memset(this,0,sizeof*this);
}
// Raubkopier-Operator, in Ermangelung von C++11
void MicroCom::operator=(const MicroCom&o) {
if (&o==this) return;
memcpy(this,&o,sizeof*this);
memset(const_cast<MicroCom*>(&o),0,sizeof o);
}
MicroCom::~MicroCom() {
if (hCom) {
CancelIo(hCom);
CloseHandle(hCom);
}
if (ovr.hEvent) CloseHandle(ovr.hEvent);
}
void MicroCom::write(char c) {
DWORD bw;
OVERLAPPED ovw;
ZeroMemory(&ovw,sizeof ovw);
if (!(WriteFile(hCom,&c,1,&bw,&ovw)
|| GetLastError()==ERROR_IO_PENDING
&& GetOverlappedResult(hCom,&ovw,&bw,TRUE))
|| bw!=1) { // only one character
textcolor(12);
printf("It is not possible to write at COM %d.\n",comno+1);
}
}
void writeAll(char c) {
for (MicroComList::iterator mi=microcoms.begin(); mi!=microcoms.end(); mi++) mi->write(c);
}
void MicroCom::readUntilBlock() {
DWORD br;
while (ReadFile(hCom,line.p(),line.space(),&br,&ovr)) haveData(br);
if (GetLastError()!=ERROR_IO_PENDING)
printf("Unexpected error %d at ReadFile(COM%u)! Device disappeared?\n",GetLastError(),comno+1);
}
DWORD MicroCom::onEvent() {
DWORD br;
if (!GetOverlappedResult(hCom,&ovr,&br,FALSE)) {
textcolor(12);
printf("Unexpected failure %d of GetOverlappedResult(COM%u)\n",GetLastError(),comno+1);
}
return br;
}
void OneLine::outLine(unsigned row) {
fwrite("\033[s",1,3,stdout); // save cursor and color(s)
fwrite("\033[m",1,3,stdout); // standard text attributes
gotoxy(0,row);
// Good idea: Put COM port number here, in front of text = read input data
//printf("%3u:",comportnumber);
fwrite(buf,1,fill(),stdout); // out string without format processing nor '\n' appending
fwrite("\033[K",1,3,stdout); // clear from cursor to end of line
fwrite("\033[u",1,3,stdout); // restore cursor and color(s)
}
char*MicroCom::onFullLine() {
line.outLine(lm*unsigned(this-microcoms.data())); // calculate line number based on own index in table
line.wrpos=0; // clear target data but leave buffer untouched
return line.p(); // pointer to fresh buffer
}
// Handle incoming data (which is initially on right place),
// and tokenize on '\n'.
// On each '\n', onFullLine() is called,
// and following data is effectively memmove()d to buffer head.
// Multiple '\n' in incoming data is allowed.
// However, this lead to immediate overwrite of screen line
// as onFullLine() is called with almost no time in between.
void MicroCom::haveData(DWORD br) {
char*sp=line.p(); // source pointer
for (DWORD j=0; j<br; j++) {
if (*sp=='\r' || *sp=='\n') {
onFullLine();
++sp; // eat-up character
}else{
line.push_back(*sp++);// add character to buffer *
if (line.space()==0) onFullLine();
}
} // * Initially does nothing than ++line.wrpos because sp points to line.buf[wrpos].
} // After first '\n', line.wrpos is reset, and characters are copied byte-wise.
// Service all asynchronous COM port reads, and remove need for kbhit() polling
// All serial ports must do an asynchronous ReadFile(), and this function returns with same state.
// Return values: 0 = All COM ports handled, no console input,
// microcoms.size() = All COM ports handled, console input available
// other = timeout, missing activity on at least one COM port, no console input
DWORD readAll() {
HANDLE hEvt[11];
DWORD k,m=microcoms.size(),n=m;
for (k=0; k<n; k++) hEvt[k]=microcoms[BYTE(k)].ovr.hEvent; // Event-Array zusammenstellen
hEvt[k]=GetStdHandle(STD_INPUT_HANDLE);
do{
k=WaitForMultipleObjects(n+1,hEvt,FALSE,100);
if (k>=n) return k; // timeout = missing activity on at least one COM port, k==n: console input available
MicroCom&mc = microcoms[BYTE(k)];
mc.haveData(mc.onEvent());
// read bytes in-place. Allow read of multiple bytes in one chunk, for more performance.
mc.readUntilBlock();
hEvt[k]=GetCurrentProcess(); // fill gap with a valid, never signaling handle, so make this loop debuggable
}while(--m);
return 0; // return with no timeout when all serial port reads have serviced once
}
void outhelp() {
puts ("* U- Control press button u *");
puts ("* I- Control press button i *");
puts ("* only PWM press button p *");
puts ("* Remote: on press button R *");
puts ("* Remote: off press button r *");
puts ("* To increase values press button + *");
puts ("* To decrease values press button - *");
puts ("* start or stop all devices press button s *");
puts ("* To clear the screen press button c *");
puts ("* Line spacing multiplier (1 or2) press button m *");
}
int _cdecl main(int argc, char**argv) {
setvbuf(stdout,0,_IONBF,0);
system("title = Fast serial interface, without cursor, created for the Meyer Burger company by Tobias Lucas");
SetConsoleOutputCP(1252);
// SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),ENABLE_MOUSE_INPUT);
HANDLE hOut=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hOut,&csbi);
csbi.dwSize.Y=csbi.srWindow.Bottom-csbi.srWindow.Top+1;
SetConsoleScreenBufferSize(hOut,csbi.dwSize); // remove vertical scroll bar
printf("\033[1;33m");
puts("*******************************************************************************");
puts("* Fast serial interface, without cursor, created for the Meyer Burger company *");
puts("*******************************************************************************");
puts("");
Portlist ports;
if (argc>1) ports.fromCommandLine(GetCommandLine());
else if (ports.dialog()!=IDOK) return 2;
char comlist[100],*cp=comlist;
for (byte k=0; k<ports.size(); k++){
MicroCom mc;
if (mc.openCom(ports[k])) {
cp+=sprintf(cp,"%s%u",k?", ":"",ports[k]+1);
}else{
printf("******** To end press any key *******\n");
_getch();
return 0;
}
mc.readUntilBlock();
microcoms.push_back(mc); // Das ruft den Raubkopier-Operator auf den Plan.
} // Das ruft den Destruktor von mc auf den Plan. Dieser tut nichts.
hidecursor();
clrscr();
printf("\033[%u;%ur",microcoms.size()*MicroCom::lm+2,csbi.dwSize.Y);
gotoxy(0,microcoms.size()*MicroCom::lm+1);
printf("\033[mThe program will use following COM ports: \033[1;44m %s \033[m\n",comlist);
textcolor(14);
puts ("*********************************************************");
puts ("* Program is \033[41m running \033[40m and expects keyboard input: *");
outhelp();
puts ("* To exit the program press button ESC *");
// puts ("* To start press any key *");
puts ("*********************************************************");
// _getch();
writeAll('q'); // stop command to all devices
BYTE dm = 0; //(0...9)
bool on_off = false;
for(;;) if (readAll()==microcoms.size()) {
char cKb = _getch();
switch (cKb) {
case 'c': clrscr(); break; // Clear screen
case 'm': MicroCom::lm ^=3; clrscr(); break; // line spacing multiplier (TODO: Reposition of scroll window)
case 's': on_off=!on_off; writeAll(on_off ? 'O' : 'q'); break; // start/stop command to all devices
case 'Y': // undocumented
case 'y': // undocumented
case 'u': // voltage control
case 'i': // current control
case 'p': // PWM
case 'R': // remote on
case 'r': // remote off
case '+': // increase (what?)
case '-': microcoms[dm].write(cKb); break;
case 'h': textcolor(14); outhelp(); continue;
case 27: puts("\033[m" "Exiting" "\033[r"); return 0; // let destructors do the cleanup
default: if (BYTE(cKb-'0') < microcoms.size()) dm = cKb - '0'; // Device number
else{
textcolor(12);
printf("****** Unknown command %c ******\n",cKb);
continue;
}
}
textcolor(15);
printf("Selected device = %d, ",dm);
if (on_off){ //print on/off
textcolor(12);
puts("all devices started");
}else{
textcolor(10);
puts("all devices stopped");
}
}//for+if
}// main
| Vorgefundene Kodierung: ANSI (CP1252) | 4
|
|
|