/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2017 Melin Software HB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ // SportIdent.cpp: implementation of the SportIdent class. // ////////////////////////////////////////////////////////////////////// // Check implementation r56. #include "stdafx.h" #include "meos.h" #include "SportIdent.h" #include #include #include #include "localizer.h" #include "meos_util.h" #include "oPunch.h" #include #include ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// //#define DEBUG_SI SI_StationData::SI_StationData() { stationNumber=0; stationMode=0; extended=false; handShake=false; autoSend=false; radioChannel = 0; } SI_StationInfo::SI_StationInfo() { ThreadHandle=0; hComm=0; memset(&TimeOuts, 0, sizeof(TimeOuts)); tcpPort=0; localZeroTime=0; } SportIdent::SportIdent(HWND hWnd, DWORD Id) { ClassId=Id; hWndNotify=hWnd; //hComm=0; //ThreadHandle=0; n_SI_Info=0; ZeroTime=0; InitializeCriticalSection(&SyncObj); tcpPortOpen=0; serverSocket=0; } SportIdent::~SportIdent() { closeCom(0); DeleteCriticalSection(&SyncObj); } // CRC algoritm used by SPORTIdent GmbH // implemented for MeOS in C++ WORD SportIdent::calcCRC(BYTE *data, DWORD count) { // Return 0 for no or one data byte if (count<2) return 0; size_t index=0; WORD crc = (WORD(data[index])<<8) + WORD(data[index+1]); index +=2; // Return crc for two data bytes if (count==2) return crc; WORD value; for (size_t k = count>>1; k>0; k--) { if (k>1) { value = (WORD(data[index])<<8) + WORD(data[index+1]); index +=2; } else // If the number of bytes is odd, complete with 0. value = (count&1) ? data[index]<<8 : 0; for (int j = 0; j<16; j++) { if (crc & 0x8000) { crc <<= 1; if (value & 0x8000) crc++; crc ^= 0x8005; } else { crc <<= 1; if (value & 0x8000) crc++; } value <<= 1; } } return crc; } void SportIdent::setCRC(BYTE *bf) { DWORD len=bf[1]; WORD crc=calcCRC(bf, len+2); bf[len+2]=HIBYTE(crc); bf[len+3]=LOBYTE(crc); } bool SportIdent::checkCRC(BYTE *bf) { DWORD len=bf[1]; WORD crc=calcCRC(bf, len+2); return bf[len+2]==HIBYTE(crc) && bf[len+3]==LOBYTE(crc); } bool SportIdent::readSystemData(SI_StationInfo *si, int retry) { BYTE c[16]; BYTE buff[256]; c[0]=STX; c[1]=0x83; c[2]=0x02; c[3]=0x70; //Request address 0x70 c[4]=0x06; //And 6 bytes //Address 0x74 = protocoll settings //Address 0x71 = Programming ctrl/readout/finish etc. setCRC(c+1); c[7]=ETX; DWORD written=0; WriteFile(si->hComm, c, 8, &written, NULL); Sleep(100); memset((void *)buff, 0, 30); DWORD offset = 0; readBytes_delay(buff, sizeof(buff), 15, si->hComm); if (buff[0] == 0xFF && buff[1] == STX) offset++; if (1){ if (checkCRC(LPBYTE(buff+1 + offset))){ si->data.resize(1); SI_StationData &da = si->data[0]; da.stationNumber=511 & MAKEWORD(buff[4 + offset], buff[3 + offset]); BYTE PR=buff[6+4 + offset]; BYTE MO=buff[6+1 + offset]; da.extended = (PR&0x1)!=0; da.handShake = (PR&0x4)!=0; da.autoSend = (PR&0x2)!=0; da.stationMode = MO & 0xf; } else if (retry>0) return readSystemData(si, retry-1); else return false; } else if (retry>0) return readSystemData(si, retry-1); else return false; return true; } bool SportIdent::readSystemDataV2(SI_StationInfo &si) { BYTE c[16]; BYTE buff[4096]; int maxbytes = 256; while (readByte(c[0], si.hComm) == 1 && maxbytes> 0) { maxbytes--; } // 02 83 01 00 80 bf 17 03 c[0]=STX; c[1]=0x83; c[2]=0x02; c[3]=0; c[4]=0x80; c[5]=0xbf; c[6]=0x17; setCRC(c+1); c[7]=ETX; DWORD written=0; WriteFile(si.hComm, c, 8, &written, NULL); Sleep(100); memset((void *)buff, 0, sizeof(buff) ); // DWORD offset = 0; int consumed = 0; int read = readBytes_delay(buff, sizeof(buff), -1, si.hComm); const int requested = 0x80; while ( (read - consumed) >= requested) { while (consumed < read && buff[consumed] != STX) consumed++; BYTE *db = buff + consumed; if ((read - consumed) >= requested && db[0] == STX) { si.data.push_back(SI_StationData()); int used = analyzeStation(db, si.data.back()); if (used == 0) { si.data.pop_back(); // Not valid break; } else consumed += used; // Test duplicate units: si.data.push_back(si.data.back()); } else break; } return si.data.size() > 0; } int SportIdent::analyzeStation(BYTE *db, SI_StationData &si) { DWORD size = 0; DWORD addr = 0x70; if (checkCRC(LPBYTE(db+1))) { size = DWORD(db[2]) + 6; bool dongle = db[0x11] == 0x6f && db[0x12] == 0x21; if (dongle) { BYTE PR=db[69]; BYTE MO=db[6+1+addr]; si.extended=(PR&0x1)!=0; si.handShake = false; si.autoSend=db[68] == 1; si.stationMode=MO & 0xf; si.radioChannel = db[58] & 0x1; si.stationNumber = 0; } else { si.stationNumber=511 & MAKEWORD(db[4], db[3]); BYTE PR=db[6+4+addr]; BYTE MO=db[6+1+addr]; si.extended=(PR&0x1)!=0; si.handShake=(PR&0x4)!=0; si.autoSend=(PR&0x2)!=0; si.stationMode=MO & 0xf; si.radioChannel = 0; } } return size; } string decode(BYTE *bf, int read) { string st; for(int k=0;k<=read;k++){ if (bf[k]==STX) st+="STX "; else if (bf[k]==ETX) st+="ETX "; else if (bf[k]==ACK) st+="ACK "; else if (bf[k]==DLE) st+="DLE "; else{ char d[10]; sprintf_s(d, "%02X ", bf[k]); st+=d; } } return st; } bool SportIdent::openComListen(const wchar_t *com, DWORD BaudRate) { closeCom(com); SI_StationInfo *si = findStation(com); if (!si) { SI_Info[n_SI_Info].ComPort=com; SI_Info[n_SI_Info].ThreadHandle=0; si=&SI_Info[n_SI_Info]; n_SI_Info++; } si->data.clear(); wstring comfile=wstring(L"//./")+com; si->hComm = CreateFile( comfile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); if (si->hComm == INVALID_HANDLE_VALUE) { si->hComm=0; return false; // error opening port; abort } //Store comport //ComPort=com; GetCommTimeouts(si->hComm, &si->TimeOuts); COMMTIMEOUTS MyTimeOuts=si->TimeOuts; MyTimeOuts.ReadIntervalTimeout=50; MyTimeOuts.ReadTotalTimeoutMultiplier = 10; MyTimeOuts.ReadTotalTimeoutConstant = 300; MyTimeOuts.WriteTotalTimeoutMultiplier = 10; MyTimeOuts.WriteTotalTimeoutConstant = 300; SetCommTimeouts(si->hComm, &MyTimeOuts); DCB dcb; memset(&dcb, 0, sizeof(dcb)); dcb.DCBlength=sizeof(dcb); dcb.BaudRate=BaudRate; dcb.fBinary=TRUE; dcb.fDtrControl=DTR_CONTROL_DISABLE; dcb.fRtsControl=RTS_CONTROL_DISABLE; dcb.Parity=NOPARITY; dcb.StopBits=ONESTOPBIT; dcb.ByteSize =8; SetCommState(si->hComm, &dcb); return true; } bool SportIdent::tcpAddPort(int port, DWORD zeroTime) { closeCom(L"TCP"); SI_StationInfo *si = findStation(L"TCP"); if (!si) { SI_Info[n_SI_Info].ComPort=L"TCP"; SI_Info[n_SI_Info].ThreadHandle=0; si=&SI_Info[n_SI_Info]; n_SI_Info++; } si->tcpPort=port; si->localZeroTime=zeroTime; si->hComm=0; return true; } bool SportIdent::openCom(const wchar_t *com) { closeCom(com); SI_StationInfo *si = findStation(com); if (!si) { SI_Info[n_SI_Info].ComPort=com; SI_Info[n_SI_Info].ThreadHandle=0; si=&SI_Info[n_SI_Info]; n_SI_Info++; } si->data.clear(); wstring comfile=wstring(L"//./")+com; si->hComm = CreateFile( comfile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0,//FILE_FLAG_OVERLAPPED, 0); if (si->hComm == INVALID_HANDLE_VALUE) { si->hComm=0; return false; // error opening port; abort } GetCommTimeouts(si->hComm, &si->TimeOuts); COMMTIMEOUTS MyTimeOuts=si->TimeOuts; MyTimeOuts.ReadIntervalTimeout=50; MyTimeOuts.ReadTotalTimeoutMultiplier = 10; MyTimeOuts.ReadTotalTimeoutConstant = 300; MyTimeOuts.WriteTotalTimeoutMultiplier = 10; MyTimeOuts.WriteTotalTimeoutConstant = 300; SetCommTimeouts(si->hComm, &MyTimeOuts); DCB dcb; memset(&dcb, 0, sizeof(dcb)); dcb.DCBlength=sizeof(dcb); dcb.BaudRate=CBR_38400; dcb.fBinary=TRUE; dcb.fDtrControl=DTR_CONTROL_DISABLE; dcb.fRtsControl=RTS_CONTROL_DISABLE; dcb.Parity=NOPARITY; dcb.StopBits=ONESTOPBIT; dcb.ByteSize =8; SetCommState(si->hComm, &dcb); BYTE c[128]; c[0]=WAKEUP; c[1]=STX; c[2]=STX; c[3]=0xF0; c[4]=0x01; c[5]=0x4D; setCRC(c+3); c[8]=ETX; DWORD written; WriteFile(si->hComm, c, 9, &written, NULL); Sleep(700); //c[6]= DWORD read; BYTE buff[128]; memset(buff, 0, sizeof(buff)); read = readBytes(buff, 1, si->hComm); if (read == 1 && buff[0] == 0xFF){ Sleep(100); read = readBytes(buff, 1, si->hComm); } if (read==1 && buff[0]==STX) { ReadFile(si->hComm, buff, 8, &read, NULL); if (!readSystemDataV2(*si)) readSystemData(si, 1); } else { dcb.BaudRate=CBR_4800; SetCommState(si->hComm, &dcb); WriteFile(si->hComm, c, 9, &written, NULL); Sleep(600); //c[6]= DWORD read; BYTE buff[128]; read=readByte(*buff, si->hComm); if (read==1 && buff[0]==STX) { ReadFile(si->hComm, buff, 8, &read, NULL); readSystemData(si); } else { BYTE cold[8]; cold[0]=STX; cold[1]=0x70; cold[2]=0x4D; cold[3]=ETX; WriteFile(si->hComm, cold, 4, &written, NULL); Sleep(500); read=readByte(*buff, si->hComm); if (read!=1 || buff[0]!=STX) { closeCom(si->ComPort.c_str()); return false; } read=readBytesDLE_delay(buff, sizeof(buff), 4, si->hComm); if (read==4) { si->data.resize(1); if (buff[1]>30) si->data[0].stationNumber = buff[1]; else{ si->data[0].stationNumber = buff[2]; } if (buff[3]!=ETX) readByte(buff[4], si->hComm); } } } return true; } SI_StationInfo *SportIdent::findStation(const wstring &com) { for(int i=0;iComPort==L"TCP") { if (tcpPortOpen) { EnterCriticalSection(&SyncObj); shutdown(SOCKET(serverSocket), SD_BOTH); LeaveCriticalSection(&SyncObj); Sleep(300); EnterCriticalSection(&SyncObj); closesocket(SOCKET(serverSocket)); tcpPortOpen = 0; serverSocket = 0; LeaveCriticalSection(&SyncObj); } } else if (si && si->hComm) { if (si->ThreadHandle) { EnterCriticalSection(&SyncObj); TerminateThread(si->ThreadHandle, 0); LeaveCriticalSection(&SyncObj); CloseHandle(si->ThreadHandle); si->ThreadHandle=0; } SetCommTimeouts(si->hComm, &si->TimeOuts); //Restore CloseHandle(si->hComm); si->hComm=0; } } } int SportIdent::readByte_delay(BYTE &byte, HANDLE hComm) { byte=0; return readBytes_delay(&byte, 1, 1, hComm); } int SportIdent::readByte(BYTE &byte, HANDLE hComm) { byte=0; if (!hComm) return -1; DWORD dwRead; if (ReadFile(hComm, &byte, 1, &dwRead, NULL)) { #ifdef DEBUG_SI2 char t[64]; sprintf_s(t, 64, "read=%02X\n", (int)byte); OutputDebugString(t); #endif if (dwRead) return 1; else return 0; } else return -1; } int SportIdent::readBytes_delay(BYTE *byte, DWORD buffSize, DWORD len, HANDLE hComm) { int read=0; int d; bool autoLen = false; if (len == -1) { len = min(buffSize, 10); autoLen = true; } int toread=len; for(d=0;d<7 && read(buffSize - read, len); if (maxToRead <= 0) return read; int r = readBytes(byte+read, maxToRead, hComm); if (r==-1) { if (read > 0) return read; return -1; } read+=r; if (read == 1 && byte[0] == NAK) return read; if (autoLen && r == len) { int rloop; Sleep(100); while (read < int(buffSize) && (rloop = readBytes(byte+read, min(16, buffSize-read), hComm)) > 0) { read += rloop; } } if (read < toread) { len = toread - read; Sleep(100); } } #ifdef DEBUG_SI2 char t[64]; sprintf_s(t, 64, "retry=%d\n", d); OutputDebugString(t); for (int k = 0; k < read; k++) { char t[64]; sprintf_s(t, 64, "mreadd=%02X\n", (int)byte[k]); OutputDebugString(t); } #endif return read; } int SportIdent::readBytes(BYTE *byte, DWORD len, HANDLE hComm) { if (!hComm) return -1; DWORD dwRead; if (ReadFile(hComm, byte, len, &dwRead, NULL)) { #ifdef DEBUG_SI2 for (int k = 0; k < dwRead; k++) { char t[64]; sprintf_s(t, 64, "mread=%02X\n", (int)byte[k]); OutputDebugString(t); } #endif return dwRead; } else return -1; } int SportIdent::readBytesDLE_delay(BYTE *byte, DWORD buffSize, DWORD len, HANDLE hComm) { int read=0; int toread=len; int d; for(d=0;d<15 && read 0) { DWORD ip=0; DWORD op=0; for (ip=0;ip 10) { if (GetTickCount() > timeout) { break; } else iter = 0; } Sleep(0); } r=recv(client, temp, 15, 0); if (r==15) { //BYTE c[15]={0, 0x64,0,0x48, 0xa4, 0x07, 00, 0x05, 00, 00, 00, 0x8d, 0x16, 0x06, 0x00}; //memcpy(temp, c, 15); op.Type=temp[0]; op.CodeNo=*(WORD*)(&temp[1]); op.SICardNo=*(DWORD *)(&temp[3]); op.CodeDay=*(DWORD *)(&temp[7]); op.CodeTime=*(DWORD *)(&temp[11]); if (op.Type == 64 && op.CodeNo>1 && op.CodeNo<=192 && op.CodeTime == 0) { // Recieved card int nPunch = op.CodeNo; vector punches(nPunch); r = recv(client, (char*)&punches[0], 8 * nPunch, 0); if (r == 8 * nPunch) { SICard card; card.CheckPunch.Code = -1; card.StartPunch.Code = -1; card.FinishPunch.Code = -1; card.CardNumber = op.SICardNo; for (int k = 0; k < nPunch; k++) { punches[k].Time /= 10; if (punches[k].Code == oPunch::PunchStart) card.StartPunch = punches[k]; else if (punches[k].Code == oPunch::PunchFinish) card.FinishPunch = punches[k]; else if (punches[k].Code == oPunch::PunchCheck) card.CheckPunch = punches[k]; else card.Punch[card.nPunch++] = punches[k]; } addCard(card); } } else addPunch(op.CodeTime/10, op.CodeNo, op.SICardNo, 0); } else r=-1; } //close the client socket closesocket(client); } else { int err=WSAGetLastError(); if (err==WSAESHUTDOWN || err==WSAECONNABORTED) { tcpPortOpen=0; serverSocket=0; closesocket(server); //MessageBox(0, "TCP CLOSE", 0, MB_OK); return 0; } } } //MessageBox(0, "TCP CLOSE BYE", 0, MB_OK); //closesocket() closes the socket and releases the socket descriptor closesocket(server); WSACleanup(); return 1; } bool SportIdent::MonitorSI(SI_StationInfo &si) { HANDLE hComm=si.hComm; if (!hComm) return false; DWORD dwCommEvent; DWORD dwRead; BYTE chRead; if (!SetCommMask(hComm, EV_RXCHAR)) { // Error setting communications event mask. //Sleep(1000); return false; } else { for ( ; ; ) { if (WaitCommEvent(hComm, &dwCommEvent, NULL)) { if (dwCommEvent & EV_RXCHAR) do{ if ( (dwRead=readByte(chRead, hComm))!=-1) { // A byte has been read; process it. if (chRead==STX && readByte(chRead, hComm)) { switch(chRead) { case 0xD3:{//Control Punch, extended mode. BYTE bf[32]; bf[0]=chRead; readBytes(bf+1, 17, hComm); if (checkCRC(LPBYTE(bf))) { WORD Station=MAKEWORD(bf[3], bf[2]); DWORD ShortCard=MAKEWORD(bf[7], bf[6]); DWORD Series=bf[5]; DWORD Card=MAKELONG(MAKEWORD(bf[7], bf[6]), MAKEWORD(bf[5], bf[4])); if (Series<=4 && Series>=1) Card=ShortCard+100000*Series; DWORD Time=0; if (bf[8]&0x1) Time=3600*12; Time+=MAKEWORD(bf[10], bf[9]); #ifdef DEBUG_SI char str[128]; sprintf_s(str, "EXTENDED: Card = %d, Station = %d, StationMode = %d", Card, Station, si.StationMode); MessageBox(NULL, str, NULL, MB_OK); #endif addPunch(Time, Station & 511, Card & 0x00FFFFFF, si.stationMode()); } break; } case 0x53:{ //Control Punch, old mode BYTE bf[32]; bf[0]=chRead; //Sleep(200); readBytesDLE(bf+1, 30, hComm); BYTE Station=bf[2]; BYTE Series=bf[3]; WORD Card=MAKEWORD(bf[5], bf[4]); DWORD DCard; if (Series==1) DCard=Card; else if (Series<=4) DCard=Card+100000*Series; else DCard=MAKELONG(Card, Series); //if (Series!=1) // Card+=100000*Series; DWORD Time=MAKEWORD(bf[8], bf[7]); BYTE p=bf[1]; if (p&0x1) Time+=3600*12; #ifdef DEBUG_SI char str[128]; sprintf_s(str, "OLD: Card = %d, Station = %d, StationMode = %d", DCard, Station, si.StationMode); MessageBox(NULL, str, NULL, MB_OK); #endif addPunch(Time, Station, DCard, si.stationMode()); break; } case 0xE7:{//SI5/6 removed BYTE bf[32]; readBytes(bf+1, 10, hComm); break; } case 0xE6:{ BYTE bf[32]; bf[0]=0xE6; readBytes(bf+1, 10, hComm); //ReadByte(chRead); //ETX! if (checkCRC(LPBYTE(bf))) getSI6DataExt(hComm); break; } case 0xE5:{ BYTE bf[32]; bf[0]=0xE5; readBytes(bf+1, 10, hComm); if (checkCRC(LPBYTE(bf))) getSI5DataExt(hComm); break; } case 0x46: readByte(chRead, hComm); //0x49?! readByte(chRead, hComm); //ETX! getSI5Data(hComm); break; case 0x66:{ //STX, 66h, CSI, TI, TP, CN3, CN2, CN1, CN0, ETX BYTE bf[32]; //SICard5Detect si5; //si5.code=0xE5; bf[0]=0xE6; readBytesDLE(bf+1, 8, hComm); getSI6Data(hComm); break; } case 0xB1:{ BYTE bf[200]; bf[0]=chRead; readBytes(bf+1, 200, hComm); //GetSI5DataExt(hComm); MessageBox(NULL, L"Programmera stationen utan AUTOSEND!", NULL, MB_OK); } break; case 0xE1:{ BYTE bf[200]; bf[0]=chRead; readBytes(bf+1, 200, hComm); MessageBox(NULL, L"Programmera stationen utan AUTOSEND!", NULL, MB_OK); } break; case 0xE8:{ BYTE bf[32]; bf[0]=0xE8; readBytes(bf+1, 10, hComm); //ReadByte(chRead); //ETX! if (checkCRC(LPBYTE(bf))) getSI9DataExt(hComm); break; } // MessageBox(NULL, "SI-card not supported", NULL, MB_OK); // break; default: BYTE bf[128]; bf[0]=chRead; int rb=readBytes(bf+1, 120, hComm); string st; for(int k=0;k<=rb;k++){ if (bf[k]==STX) st+="STX "; else if (bf[k]==ETX) st+="ETX "; else if (bf[k]==ACK) st+="ACK "; else if (bf[k]==DLE) st+="DLE "; else{ char d[10]; sprintf_s(d, "%02X (%d) ", bf[k], bf[k]); st+=d; } } //MessageBox(NULL, st.c_str(), "Unknown SI response", MB_OK); } } } else { // An error occurred in the ReadFile call. return false; } } while (dwRead); else { // MessageBox(hWndNotify, "EXIT 1", NULL, MB_OK); // Error in WaitCommEvent // return false; } } else { for(int i=0;i2) compact = true; } } if (compact) { BYTE b2[128*7]; // 192 punches, sicard6 star for (int k = 0; k<128*8; k++) { if (k<128) b2[k] = b[k]; else if (k>=256 && k<128*6) b2[k+128] = b[k]; else if (k>=128*6) b2[k-128*5] = b[k]; } memcpy(b, b2, sizeof(b2)); } c[0]=ACK; WriteFile(hComm, c, 1, &written, NULL); OutputDebugString(L"-ACK-"); SICard card; getCard6Data(b, card); addCard(card); } void SportIdent::getSI5Data(HANDLE hComm) { BYTE c[128]; c[0]=STX; c[1]=0x31; c[2]=ETX; DWORD written=0; WriteFile(hComm, c, 3, &written, NULL); if (written==3) { Sleep(900); BYTE bf[256]; memset(bf, 0, 256); int read=readBytesDLE(bf, 3, hComm); if (read==0) { Sleep(1000); read=readBytesDLE(bf, 3, hComm); } if (bf[0]==STX && bf[1]==0x31) { BYTE sum=bf[2]; //Start calc checksum read=readBytesDLE(bf, 128, hComm); BYTE cs, etx; readBytesDLE(&cs, 1, hComm); readByte(etx, hComm); for(int i=0;i<128;i++) sum+=bf[i]; if (sum==cs) { c[0]=ACK; WriteFile(hComm, c, 1, &written, NULL); //BYTE *Card5Data=bf+5; SICard card; getCard5Data(bf, card); addCard(card); } } } } bool SportIdent::getCard5Data(BYTE *data, SICard &card) { /* ofstream fout("si.txt"); for(int m=0;m<128;m++) { if (m%16==0) fout << endl; char bf[16]; sprintf(bf, "%02x ", (DWORD)data[m]); fout << bf; } fout << endl; return 0; */ memset(&card, 0, sizeof(card)); DWORD number=MAKEWORD(data[5], data[4]); if (data[6]==1) card.CardNumber=number; else card.CardNumber=100000*data[6]+number; data+=16; analyseSI5Time(data+3, card.StartPunch.Time, card.StartPunch.Code); analyseSI5Time(data+5, card.FinishPunch.Time, card.FinishPunch.Code); analyseSI5Time(data+9, card.CheckPunch.Time, card.CheckPunch.Code); // card.StartPunch=MAKEWORD(data[4], data[3]); // card.FinishPunch=MAKEWORD(data[6], data[5]); card.nPunch=data[7]-1; data+=16; for (DWORD k=0;k> 2) & 0x0F; // BYTE month = ((dt1 & 0x03) << 2) + (dt0 >> 6); // BYTE day = (dt0 >> 1) & 0x1F; control=cn; time=MAKEWORD(ptl, pth)+3600*12*(dt0&0x1); } else { control=-1; time=0; } } bool SportIdent::getCard6Data(BYTE *data, SICard &card) { /*ofstream fout("si.txt"); for(int m=0;m<128*3;m++) { if (m%16==0) fout << endl; char bf[16]; sprintf_s(bf, 16, "%02x ", (DWORD)data[m]); fout << bf; } fout << endl;*/ //return 0; memset(&card, 0, sizeof(card)); WORD hi=MAKEWORD(data[11], data[10]); WORD lo=MAKEWORD(data[13], data[12]); card.CardNumber=MAKELONG(lo, hi); data+=16; // DWORD control; // DWORD time; analysePunch(data+8, card.StartPunch.Time, card.StartPunch.Code); analysePunch(data+4, card.FinishPunch.Time, card.FinishPunch.Code); analysePunch(data+12, card.CheckPunch.Time, card.CheckPunch.Code); card.nPunch=min(int(data[2]), 192); int i; memcpy(card.LastName, data+32, 20); for(i=19;i>0 && card.LastName[i]==0x20;i--) card.LastName[i]=0; memcpy(card.FirstName, data+32+20, 20); for(i=19;i>0 && card.FirstName[i]==0x20;i--) card.FirstName[i]=0; data+=128-16; for(unsigned k=0;k>6)&0x3); time=MAKEWORD(ptl, pth)+3600*12*(ptd&0x1); return true; } else { control=-1; time=0; return false; } } void SportIdent::analyseSI5Time(BYTE *data, DWORD &time, DWORD &control) { if (*LPWORD(data)!=0xEEEE) { time=MAKEWORD(data[1], data[0]); if (ZeroTime<12*3600) { //Förmiddag if ( time < ZeroTime ) time+=12*3600; //->Eftermiddag } else { //Eftermiddag if ( time >= ZeroTime%(12*3600) ) { //Eftermiddag time+=12*3600; } // else Efter midnatt OK. } control=0; } else { control=-1; time=0; } } void SportIdent::EnumrateSerialPorts(list &ports) { //Make sure we clear out any elements which may already be in the array ports.clear(); //Determine what OS we are running on OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); BOOL bGetVer = GetVersionEx(&osvi); //On NT use the QueryDosDevice API if (bGetVer && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)) { //Use QueryDosDevice to look for all devices of the form COMx. This is a better //solution as it means that no ports have to be opened at all. TCHAR szDevices[65535]; DWORD dwChars = QueryDosDevice(NULL, szDevices, 65535); if (dwChars) { int i=0; for (;;) { //Get the current device name TCHAR* pszCurrentDevice = &szDevices[i]; //If it looks like "COMX" then //add it to the array which will be returned int nLen = _tcslen(pszCurrentDevice); if (nLen > 3 && _tcsnicmp(pszCurrentDevice, _T("COM"), 3) == 0) { //Work out the port number int nPort = _ttoi(&pszCurrentDevice[3]); ports.push_front(nPort); } // Go to next NULL character while(szDevices[i] != _T('\0')) i++; // Bump pointer to the next string i++; // The list is double-NULL terminated, so if the character is // now NULL, we're at the end if (szDevices[i] == _T('\0')) break; } } //else // TRACE(_T("Failed in call to QueryDosDevice, GetLastError:%d\n"), GetLastError()); } else { //On 95/98 open up each port to determine their existence //Up to 255 COM ports are supported so we iterate through all of them seeing //if we can open them or if we fail to open them, get an access denied or general error error. //Both of these cases indicate that there is a COM port at that number. for (UINT i=1; i<256; i++) { //Form the Raw device name wchar_t sPort[256]; swprintf_s(sPort, 256, L"\\\\.\\COM%d", i); //Try to open the port BOOL bSuccess = FALSE; HANDLE hPort = ::CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); if (hPort == INVALID_HANDLE_VALUE) { DWORD dwError = GetLastError(); //Check to see if the error was because some other app had the port open or a general failure if (dwError == ERROR_ACCESS_DENIED || dwError == ERROR_GEN_FAILURE) bSuccess = TRUE; } else { //The port was opened successfully bSuccess = TRUE; //Don't forget to close the port, since we are going to do nothing with it anyway CloseHandle(hPort); } //Add the port number to the array which will be returned if (bSuccess) ports.push_front(i); } } } void SportIdent::addCard(const SICard &sic) { EnterCriticalSection(&SyncObj); try { ReadCards.push_front(sic); } catch(...) { LeaveCriticalSection(&SyncObj); throw; } LeaveCriticalSection(&SyncObj); PostMessage(hWndNotify, WM_USER, ClassId, 0); } void SportIdent::addPunch(DWORD Time, int Station, int Card, int Mode) { SICard sic; memset(&sic, 0, sizeof(sic)); sic.CardNumber=Card; sic.StartPunch.Code = -1; sic.CheckPunch.Code = -1; sic.FinishPunch.Code = -1; if (Mode==0 || Mode == 11){ // 11 is dongle if (Station>30){ sic.Punch[0].Code=Station; sic.Punch[0].Time=Time; sic.nPunch=1; } else if (Station == oPunch::PunchStart) { sic.StartPunch.Time = Time; sic.StartPunch.Code = oPunch::PunchStart; } else if (Station == oPunch::PunchCheck) { sic.CheckPunch.Time = Time; sic.CheckPunch.Code = oPunch::PunchCheck; } else{ sic.FinishPunch.Time=Time; sic.FinishPunch.Code = oPunch::PunchFinish; } } else{ if (Mode==0x02 || Mode == 50){ sic.Punch[0].Code=Station; sic.Punch[0].Time=Time; sic.nPunch=1; } else if (Mode == 3) { sic.StartPunch.Time=Time; sic.StartPunch.Code = oPunch::PunchStart; } else if (Mode == 10) { sic.CheckPunch.Time=Time; sic.CheckPunch.Code = oPunch::PunchCheck; } else{ sic.FinishPunch.Time=Time; sic.FinishPunch.Code = oPunch::PunchFinish; } } sic.PunchOnly=true; addCard(sic); } bool SportIdent::getCard(SICard &sic) { bool ret=false; EnterCriticalSection(&SyncObj); if (!ReadCards.empty()) { sic=ReadCards.front(); ReadCards.pop_front(); ret=true; } LeaveCriticalSection(&SyncObj); return ret; } void start_si_thread(void *ptr) { SportIdent *si=(SportIdent *)ptr; if (!si->Current_SI_Info) return; EnterCriticalSection(&si->SyncObj); SI_StationInfo si_info=*si->Current_SI_Info; //Copy data. si->Current_SI_Info=0; LeaveCriticalSection(&si->SyncObj); if (si_info.ComPort==L"TCP") { si->MonitorTCPSI(si_info.tcpPort, si_info.localZeroTime); } else { if (!si_info.hComm) MessageBox(NULL, L"ERROR", 0, MB_OK); si->MonitorSI(si_info); } } void SportIdent::startMonitorThread(const wchar_t *com) { SI_StationInfo *si = findStation(com); if (si && (si->hComm || si->ComPort==L"TCP")) { if (si->ComPort==L"TCP") tcpPortOpen=0; Current_SI_Info=si; si->ThreadHandle=(HANDLE)_beginthread(start_si_thread, 0, this); while((volatile void *)Current_SI_Info) Sleep(0); if (si->ComPort==L"TCP") { DWORD ec=0; while( (GetExitCodeThread(si->ThreadHandle, &ec)!=0 && ec==STILL_ACTIVE && tcpPortOpen==0)) Sleep(0); } } else MessageBox(NULL, L"ERROR", 0, MB_OK); } void checkport_si_thread(void *ptr) { int *port=(int *)ptr; wchar_t bf[16]; swprintf_s(bf, 16, L"COM%d", *port); SportIdent si(NULL, *port); if (!si.openCom(bf)) *port=0; //No SI found here else { bool valid = true; SI_StationInfo *sii = si.findStation(bf); if (sii) { if (sii->data.empty() || sii->data[0].stationNumber>=1024 || sii->data[0].stationMode>15 || !(sii->data[0].autoSend || sii->data[0].handShake)) valid = false; } if (valid) *port = -(*port); else *port = 0; } si.closeCom(0); //_endthread(); } bool SportIdent::autoDetect(list &ComPorts) { list Ports; EnumrateSerialPorts(Ports); int array[128]; //memset(array, 0, 128*sizeof(int)); int i=0; while(!Ports.empty() && i<128) { array[i]=Ports.front(); Ports.pop_front(); (HANDLE)_beginthread(checkport_si_thread, 0, &array[i]); i++; //Sleep(0); } int maxel=1; while(maxel>0) { Sleep(300); maxel=0; for(int k=0;kComPort==L"TCP") return tcpPortOpen && serverSocket; else return si!=0 && si->hComm && si->ThreadHandle; } void SportIdent::getInfoString(const wstring &com, vector &infov) { infov.clear(); SI_StationInfo *si = findStation(com); if (com==L"TCP") { if (!si || !tcpPortOpen || !serverSocket) { infov.push_back(L"TCP: "+lang.tl(L"ej aktiv.")); return; } wchar_t bf[128]; swprintf_s(bf, lang.tl(L"TCP: Port %d, Nolltid: %s").c_str(), tcpPortOpen, L"00:00:00");//WCS infov.push_back(bf); return; } if (!(si!=0 && si->hComm && si->ThreadHandle)) { infov.push_back(com+L": "+lang.tl(L"ej aktiv.")); return; } for (size_t k = 0; k < si->data.size(); k++) { wstring info = si->ComPort; if (si->data.size() > 1) info += makeDash(L"-") + itow(k+1); const SI_StationData &da = si->data[k]; if (da.extended) info+=lang.tl(L": Utökat protokoll. "); else info+=lang.tl(L": Äldre protokoll. "); switch(da.stationMode){ case 2: case 50: info+=lang.tl(L"Kontrol"); break; case 4: info+=lang.tl(L"Mål"); break; case 3: info+=lang.tl(L"Start"); break; case 5: info+=lang.tl(L"Läs brickor"); break; case 7: info+=lang.tl(L"Töm"); break; case 10: info+=lang.tl(L"Check"); break; case 11: info+=lang.tl(L"SRR Dongle ") + (da.radioChannel == 0? lang.tl(L"red channel.") : lang.tl(L"blue channel.")); break; default: info+=lang.tl(L"Okänd funktion"); } if (da.stationNumber) { wchar_t bf[16]; swprintf_s(bf, L" (%d).", da.stationNumber); info+=bf; } info += lang.tl(L" Kommunikation: "); if (da.autoSend) info+=lang.tl(L"skicka stämplar."); else if (da.handShake) info+=lang.tl(L"handskakning."); else info+=lang.tl(L"[VARNING] ingen/okänd."); infov.push_back(info); } } vector SICard::codeLogData(int row) const { vector log; log.push_back(itos(row)); if (readOutTime[0] == 0) log.push_back(getLocalTime()); else log.push_back(readOutTime); log.push_back(itos(CardNumber)); log.push_back(""); log.push_back(""); wstring fn(FirstName), ln(LastName), cn(Club); string fnn(fn.begin(), fn.end()); string lnn(ln.begin(), ln.end()); string cnn(cn.begin(), cn.end()); log.push_back(fnn); log.push_back(lnn); log.push_back(cnn); log.push_back(""); log.push_back(""); log.push_back(""); log.push_back(""); //email log.push_back(""); log.push_back(""); log.push_back(""); log.push_back(""); //zip log.push_back(""); //CLR_NO log.push_back(""); log.push_back(""); //We set monday on every punch, since this is our way of //saying that we have 24-h clock. if (signed(CheckPunch.Code) >= 0) { log.push_back(itos(CheckPunch.Code)); log.push_back("MO"); log.push_back(formatTimeN(CheckPunch.Time)); } else { log.push_back(""); //CHCK_NO log.push_back(""); log.push_back(""); } if (signed(StartPunch.Code) >= 0) { log.push_back(itos(StartPunch.Code)); log.push_back("MO"); log.push_back(formatTimeN(StartPunch.Time)); } else { log.push_back(""); //START_NO log.push_back(""); log.push_back(""); } if (signed(FinishPunch.Code) >= 0) { log.push_back(itos(FinishPunch.Code)); log.push_back("MO"); log.push_back(formatTimeN(FinishPunch.Time)); } else { log.push_back(""); //FINISH_NO log.push_back(""); log.push_back(""); } log.push_back(itos(nPunch)); for (int k=0;k<192;k++) { if (k0) { log.push_back("MO"); log.push_back(formatTimeN(Punch[k].Time)); } else { log.push_back(""); log.push_back(""); } } else { log.push_back(""); log.push_back(""); log.push_back(""); } } return log; } vector SICard::logHeader() { vector log; log.push_back("No."); log.push_back("read at"); log.push_back("SI-Card"); log.push_back("St no"); log.push_back("cat"); log.push_back("First name"); log.push_back("name"); log.push_back("club"); log.push_back("country"); log.push_back("sex"); log.push_back("year-op"); log.push_back("EMail"); log.push_back("mobile"); log.push_back("city"); log.push_back("street"); log.push_back("zip"); log.push_back("CLR_CN"); log.push_back("CLR_DOW"); log.push_back("clear time"); log.push_back("CHK_CN"); log.push_back("CHK_DOW"); log.push_back("check time"); log.push_back("ST_CN"); log.push_back("ST_DOW"); log.push_back("start time"); log.push_back("FI_CN"); log.push_back("FO_DOW"); log.push_back("Finish time"); log.push_back("No. of punches"); for (int k=0;k<192;k++) { string pf = itos(k+1); log.push_back(pf+".CN"); log.push_back(pf+".DOW"); log.push_back(pf+".Time"); } return log; } unsigned SICard::calculateHash() const { unsigned h = nPunch * 100000 + FinishPunch.Time; for (unsigned i = 0; i < nPunch; i++) { h = h * 31 + Punch[i].Code; h = h * 31 + Punch[i].Time; } h += StartPunch.Time; return h; } string SICard::serializePunches() const { string ser; if (CheckPunch.Code != -1) ser += "C-" + itos(CheckPunch.Time); if (StartPunch.Code != -1) { if (!ser.empty()) ser += ";"; ser += "S-" + itos(StartPunch.Time); } for (DWORD i = 0; i < nPunch; i++) { if (!ser.empty()) ser += ";"; ser += itos(Punch[i].Code) + "-" + itos(Punch[i].Time); } if (FinishPunch.Code != -1) { if (!ser.empty()) ser += ";"; ser += "F-" + itos(FinishPunch.Time); } return ser; } void SICard::deserializePunches(const string &arg) { FinishPunch.Code = -1; StartPunch.Code = -1; CheckPunch.Code = -1; vector out; split(arg, ";", out); nPunch = 0; for (size_t k = 0; k< out.size(); k++) { vector mark; split(out[k], "-", mark); if (mark.size() != 2) throw std::exception("Invalid string"); DWORD *tp = 0; if (mark[0] == "F") { FinishPunch.Code = 1; tp = &FinishPunch.Time; } else if (mark[0] == "S") { StartPunch.Code = 1; tp = &StartPunch.Time; } else if (mark[0] == "C") { CheckPunch.Code = 1; tp = &CheckPunch.Time; } else { Punch[nPunch].Code = atoi(mark[0].c_str()); tp = &Punch[nPunch++].Time; } *tp = atoi(mark[1].c_str()); } if (out.size() == 1) PunchOnly = true; }