/* Author: Jozef Simko School year: 3., Bachelor study, 2021/22 Study program: Computer Networks Organization: Technical University of Kosice (TUKE), Faculty of Electrical Engineering and Informatics (FEI), Compiler: Winlibs GCC -- MinGW-W64 x86_64-posix-seh version 11.2.0, built by Brecht Sanders (OS Win) -- gcc version 9.3.0 (OS Linux) compile: WIN -- gcc server.c RS232.c -Wall -Wextra -lwsock32 -o server LINUX -- gcc server.c RS232.c -Wall -Wextra -o server version: 1.4 , 7.6.2022 Usage: Program communicates with both second program (client) using sockets and lasermeter using RS232 COM port communication. It works in active or passive mode according input parameters. */ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #include #include #include #include #endif #include #include #include #include #include "rs232.h" /* Defines lenght of various buffers: - buffer for message from client - buffer for reading lines from config file - buffer for output line which is send to client Should not be less then 30 */ #define MESSAGE_SIZE 32 /* Pre-defined COM port number from RS-232 library documentation table according to used OS Documentation - https://www.teuniz.net/RS-232/ */ #define COM_PORT 16 /* COM port baudrate number from RS-232 library - https://www.teuniz.net/RS-232/ */ #define COM_BAUD 115200 /* Pre-defined COM port mode from RS-232 library - https://www.teuniz.net/RS-232/ first number represents databits, letter defines parity and last number is for stopbits */ #define COM_MODE "8N1" /* Defines lenght of buffer for data from lasermeter It's better to use higher values, it's helpful in case of hardware/RS-232 overload */ #define COM_BUFFER 4096 /* Amount of seconds used to wait before checking and reading data from RS-232 line Waiting time should be approx. 0,1 seconds (from RS-232 library), but the lowest measure (response) time od distance sensor is 0,3 seconds, the greatest value is 4-5 seconds. Value 1 equals 1 second, which is used in loop checking max response time. This value fit perfectly for this software and should not be changed. */ #define WAIT_TIME 1 /* Definition of struct used in main() */ struct var { char *name; int value; }; /*########################################### DEFINED FUNCTIONS #############################################*/ /* Waits exact number of seconds (with dependency on used OS) */ void wait(int sec){ #ifdef _WIN32 Sleep(sec*1000); #else sleep(sec); #endif return; } /* Returns lasermeter device ID from return value of command "dg" */ char getID(int cport_nr){ char dg[] = "dg\r\n"; RS232_cputs(cport_nr, dg); wait(WAIT_TIME); unsigned char buf[MESSAGE_SIZE]; if(RS232_PollComport(cport_nr, buf, MESSAGE_SIZE) <= 0){ return 'x'; } return buf[1]; } /* Loads variables from config.txt file to defined struction Returns pointer on arrays of chars which contain commands from config variables cmd= */ char** setVars(struct var *vars, FILE *cfg){ char line[MESSAGE_SIZE]; memset(line, 0, MESSAGE_SIZE); char **cmds = NULL; cmds = malloc(sizeof(char *)); int cmdID = 0; while(fgets(line, sizeof(line), cfg) != NULL) { if(line[0] == '#' || line[0] == '\n'){ continue; } char *second; char *first = strtok_r(line, "=", &second); if(strcmp(first, "cmd") == 0){ cmds = realloc(cmds, (cmdID+1)*sizeof(char *)); cmds[cmdID] = malloc(sizeof(char)); second[strcspn(second, "\n")] = 0; strcpy(cmds[cmdID], second); cmdID++; continue; } for(int i=0; vars[i].name; i++){ if(strcmp(vars[i].name, first) == 0){ vars[i].value = atoi(second); } } } cmds = realloc(cmds, (cmdID+1)*sizeof(char *)); cmds[cmdID] = NULL; return cmds; } /* Frees allocated memory of pointer to arrays of chars */ void freeCMDS(char **cmds){ int i=0; while(cmds[i] != NULL){ free(cmds[i]); i++; } free(cmds); } /* Returns value of variable defined in struction */ int getVar(struct var *vars, char *var){ for(int i=0; vars[i].name; i++){ if(strcmp(vars[i].name, var) == 0){ return vars[i].value; } } return -1; } /* Changes "raw" command to usable command for lasermeter */ char *makeCMD(char *string, char deviceID){ int n = strlen(string) + 2; char *ptr = (char *)calloc(n, sizeof(char)); strcpy(ptr, string); ptr[n-2] = '\r'; ptr[n-1] = '\n'; if(ptr[1] == 'N'){ ptr[1] = deviceID; } return ptr; } /* Closes socket (with dependency on used OS) */ void cleanUP(int sock){ #ifdef WIN32 closesocket(sock); WSACleanup(); #else close(sock); #endif return; } /* ######################################### */ int main(int argc , char *argv[]){ #ifdef WIN32 WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2 ,2), &wsaData); if (iResult != 0){ printf("WSASturtup() failed\n"); return 0; } #endif int sock; int client_sock; int read_size; struct sockaddr_in server; struct sockaddr_in client; char client_message[MESSAGE_SIZE]; memset(client_message, 0, MESSAGE_SIZE); if (argc < 2 || argc > 3){ fprintf(stderr, "Usage:\n"); fprintf(stderr, "- %s \n", argv[0]); fprintf(stderr, "- %s \n", argv[0]); return 1; } // Decide to use manual or automatic mode int automatic = 0; if(argc == 3){ automatic = 1; } // Create socket if ((sock = socket(PF_INET , SOCK_STREAM , IPPROTO_TCP)) < 0){ printf("socket() failed\n"); cleanUP(sock); return 1; } // Prepare the sockaddr_in structure memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(INADDR_ANY); server.sin_port = htons(atoi(argv[1])); // Bind if (bind(sock,(struct sockaddr *) &server, sizeof(server)) != 0){ printf("bind() failed\n"); cleanUP(sock); return 1; } // Define basic vars struct struct var vars[] = { {"comport", 1}, {"baud", 9600}, {"bits", 8}, {"parity", 0}, {"stopbit", 1}, {"measure rate", 0}, {"maxErrors", 5}, {"samples", -1}, {"units", 1}, {"sNuH", 0}, {NULL, 0} }; FILE *cfg; char **cmds = NULL; // Open config and change variables in struct vars if(automatic == 1){ if ((cfg = fopen(argv[2], "r")) == NULL) { printf("Error! Config file cannot be opened.\n"); cleanUP(sock); return 1; } cmds = setVars(vars, cfg); fclose(cfg); } int cport_nr = COM_PORT; int bdrate = COM_BAUD; unsigned char buf[COM_BUFFER]; char mode[] = COM_MODE; // Load variables from struct vars if(automatic == 1){ cport_nr = getVar(vars, "comport"); bdrate = getVar(vars, "baud"); mode[0] = getVar(vars, "bits") + '0'; if(getVar(vars, "parity") == 2){mode[1] = 'E';} mode[2] = getVar(vars, "stopbit") + '0'; } if(RS232_OpenComport(cport_nr, bdrate, mode, 0)){ printf("Cannot open comport. Server is shutting down.\n"); cleanUP(sock); return 1; } for(;;){ // Listen if(listen(sock, 1) != 0){ printf("listen() failed\n"); cleanUP(sock); return 1; } puts("Waiting for incoming connections..."); // Accept connection from an incoming client #ifdef WIN32 int c = sizeof(server); client_sock = accept(sock, (struct sockaddr *) &client, &c); #else socklen_t c = sizeof(server); client_sock = accept(sock, (struct sockaddr *) &client, &c); #endif if (client_sock < 0){ printf("accept() failed"); cleanUP(sock); return 1; } printf("Connection of client %s accepted\n", inet_ntoa(client.sin_addr)); // Send info about mode to client char init[2] = "0"; if(automatic == 1){ init[0] = '1'; } send(client_sock, init, strlen(init), 0); // Laser sensor start sequence char deviceID = getID(cport_nr); if(deviceID == 'x'){ printf("COM port opened, lasermeter not connected! Check your device!\n"); cleanUP(sock); return 1; } char mode[]="sNc\r\n"; mode[1] = deviceID; RS232_cputs(cport_nr, mode); wait(WAIT_TIME); RS232_PollComport(cport_nr, buf, COM_BUFFER); // Automatic mode if(automatic == 1){ // Load measure rate char *mode; int rate = getVar(vars, "measure rate"); int nDigits = 1; if(rate != 0){ int n = rate; while(n!=0){ n=n/10; nDigits++; } nDigits -= 1; } // User measurement mode if(getVar(vars, "sNuH") == 1){ // Send commands from config (**cmds) int cmd = 0; while(cmds[cmd] != NULL){ char *ptr = makeCMD(cmds[cmd], deviceID); RS232_cputs(cport_nr, ptr); cmd++; free(ptr); } freeCMDS(cmds); mode = malloc((5+nDigits+1) * sizeof(char)); snprintf(mode, 5+nDigits+1, "sNuh+%d", rate); } // Basic measurement mode else{ mode = malloc((4+nDigits+1) * sizeof(char)); snprintf(mode, 4+nDigits+1, "sNh+%d", rate); } mode = makeCMD(mode, deviceID); RS232_cputs(cport_nr, mode); free(mode); wait(WAIT_TIME); // Variables for errors from distance sensor; list of errors, counter of all errors and counter for errors in row char *errors = malloc(sizeof(char)); int errCount = 0; int errInRow = 0; int maxErrors = getVar(vars, "maxErrors"); int samples = getVar(vars, "samples"); // Counter for samples; value is checked if variable 'samples' is greater than -1 (set in config) int measureSamples = 0; // Control variable for error linked with client connection, changes to 1 if error is found later in conditions int e = 0; while(1){ // If amount of errors in row (not interrupted by correct value) reach the limit if(errInRow >= maxErrors){ break; } // If max samples variable is set in config and all samples reach the limit if(samples > -1 && samples == measureSamples){ break; } // If client connection error occured if(e == 1){ break; } memset(buf, 0, COM_BUFFER); RS232_PollComport(cport_nr, buf, COM_BUFFER); time_t rawtime; struct tm *timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); char *rest = (char *) buf; char *first = NULL; printf("%s", buf); /* EXPERIMENTAL CODE (not tested) It happens that sometimes server receives measured value splitted in two halfs. - it looks like g1g+112\r\n61444\r\n Following code block demonstrates design of code which can solve this problem. char halfs[14] = {0}; int halfcount = 0; // has to change loop(X) condition while ((first = strtok_r(rest, "\n", &rest))){ first[strcspn(token, "\r")] = 0; int checkLen = (int)strlen(first); if(checkLen < 12){ if(halfcount == 0){ strcpy(halfs, first); halfcount++; } else{ strcat(halfs, first); halfcount = 0; } } first = halfs; ... ... } */ // Split and handle data // Loop(X) while((first = strtok_r(rest, "\r\n", &rest))){ // Check and handle error char *err = NULL; strtok_r(first, "@", &err); if(strlen(err) > 0){ errCount++; errInRow++; errors = (char *) realloc(errors, (errCount)*6*sizeof(char)+1); strcat(errors, err); strcat(errors, "\r\n"); if(errInRow >= maxErrors){ char warning[]="Too many errors! Please check your device!\n"; printf("%s", warning); printf("%s", errors); send(client_sock, warning, strlen(warning), 0); break; } } // Handle other values else{ /* // EXPERIMENTAL (untested) function // Check if output result doesnt have incorrect lenght (devided to 2 halfs) first[strcspn(token, "\r")] = 0; int checkLen = (int)strlen(first); if(checkLen < 12){ if(halfcount == 0){ strcpy(halfs, first); halfcount++; } else{ strcat(halfs, first); halfcount = 0; } } first = halfs; */ // Change string to integer int res = 0; char *numbers = NULL; strtok_r(first, "+", &numbers); res = atoi(numbers); if(res == 0){ continue; } int units = getVar(vars, "units"); // Change integer to double and apply set units double resd = res * (1/(double)units); char output[MESSAGE_SIZE]; memset(output, 0, MESSAGE_SIZE); // Add time stamp to result snprintf(output, MESSAGE_SIZE, "%02d/%02d/%d - %02d:%02d:%02d: %0.2f\n", timeinfo->tm_mday, timeinfo->tm_mon+1, timeinfo->tm_year+1900, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, resd); errInRow = 0; measureSamples++; // Check availability of client #ifdef WIN32 int iResult = send(client_sock, output, strlen(output), 0); if(iResult == WSAETIMEDOUT){ printf("Client disconnected!\n"); e = 1; break; } #else send(client_sock, output, strlen(output), MSG_NOSIGNAL); if(errno == EPIPE){ printf("Client disconnected!\n"); e = 1; break; } #endif if(samples > -1 && samples == measureSamples){ break; } } } } // Automatic mode end sequence wait(WAIT_TIME); char stop[] = "sNc\r\n"; stop[1] = deviceID; RS232_cputs(cport_nr, stop); memset(buf, 0, COM_BUFFER); if(e == 0){ char end[] = "%"; send(client_sock, end, 1, 0); } wait(WAIT_TIME); printf("End of measuring, shutting down server.\n"); break; } // Manual mode else{ // Receive a message from client while ((read_size = recv(client_sock, client_message, MESSAGE_SIZE, 0)) > 0 ){ memset(buf, 0, COM_BUFFER); char *ptr = makeCMD(client_message, deviceID); RS232_cputs(cport_nr, ptr); int checkCOM = 0; while(RS232_PollComport(cport_nr, buf, COM_BUFFER) <= 0){ wait(WAIT_TIME); if(checkCOM == 5){ char info[] = "No response from COM port, check your device and all cables!\n"; send(client_sock, info, strlen(info), 0); printf("Empty COM-PORT buffer!\n"); cleanUP(sock); return 1; } checkCOM++; } // Send received data back to client send(client_sock, (char*) buf, strlen((char *)buf), 0); memset(client_message, 0, MESSAGE_SIZE); free(ptr); } if(read_size <= 0){ puts("Client disconnected.\n"); } } } cleanUP(sock); return 0; }