From e9e8a5cc1eb1212272288b14035aa4e5a4641cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jozef=20=C5=A0imko?= Date: Fri, 20 May 2022 12:25:09 +0200 Subject: [PATCH] final --- .gitignore | 8 + README.md | 40 +++ autostart.sh | 7 + client.c | 316 ++++++++++++++++++ commands.txt | 13 + config.txt | 26 ++ demo.c | 126 ++++++++ rs232.c | 879 +++++++++++++++++++++++++++++++++++++++++++++++++++ rs232.h | 85 +++++ server.c | 542 +++++++++++++++++++++++++++++++ 10 files changed, 2042 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 autostart.sh create mode 100644 client.c create mode 100644 commands.txt create mode 100644 config.txt create mode 100644 demo.c create mode 100644 rs232.c create mode 100644 rs232.h create mode 100644 server.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9706db3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +server +server.exe +client +client.exe +demo +demo.exe +logy.txt +auto-logy.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..be2a91e --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# TestBP + +## About +This repository contains source codes for client-server network application capable to communicate with distance laser sensor DLS-C 15 via RS-232 interface, which was implemented as a part of bachelor thesis. + +## Author +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) + +## Building +Implemented and tested on: +| Platform | OS | compiler | +|-----------------|---------------------------|-------------------------------------------------| +| Linux | Ubuntu 20.04.1 WLS2 | gcc version 9.3.0 (Ubuntu 9.3.0-10ubuntu2) | +| Windows | Windows 10.0.19043 | gcc version 11.2.0 (MinGW-W64 x86_64-posix-seh) | +| Raspberry Pi | Linux raspberrypi 5.10.17 | gcc version 8.3.0 (Raspbian 8.3.0-6+rpi1) | + +### LINUX + + +### WINDOWS + + +## Launch +### Server +Program *serveru* je po kompilácii možné spustiť v 2 režimoch: +* manuálny režim (./server port) +* automatický režim (./server port config_file) + +### Client +### Demo + +## References +[WinLibs GCC](https://winlibs.com) + diff --git a/autostart.sh b/autostart.sh new file mode 100644 index 0000000..4cdda66 --- /dev/null +++ b/autostart.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +PORT=32500 +server_path="/home/pi/Desktop/TestBP/server" +cfg_path="/home/pi/Desktop/TestBP/config.txt" + +$server_path $PORT $cfg_path diff --git a/client.c b/client.c new file mode 100644 index 0000000..0ff3134 --- /dev/null +++ b/client.c @@ -0,0 +1,316 @@ +/* +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 client.c -Wall -Wextra -lwsock32 -o client + LINUX -- gcc server.c -Wall -Wextra -o server +version: 1.2 , 17.5.2021 +Usage: Program can communicate with second program (server) using sockets. + It works in active or passive mode according to settings of server. +*/ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include + +/* Defines lenght of user input and output send to server +Should not be less than longest user command in commands.txt file */ +#define MESSAGE_SIZE 16 + +/* Defines lenght of buffer for server's messages +It's better to use higher values, it's helpful in case of hardware/network overload */ +#define REPLY_MSG_SIZE 2048 + +/* Defines exact amount of user commands in commands.txt file and works as safety feature +Should not be less than number of lines in commands.txt file */ +#define CMD_BUF_SIZE 13 + +/* Defines lenght of buffer used to read line from commands.txt file +Should not be less than the longest line in file commands.txt */ +#define LINE_SIZE 256 + +/*########################################### + DEFINED FUNCTIONS +#############################################*/ + +/* +Writes input to log file with time stamp +*/ +void write_log(FILE *log, char *mess){ + time_t rawtime; + struct tm * timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + mess[strcspn(mess, "\r")] = 0; + + fprintf (log, "%02d/%02d/%d - %02d:%02d:%02d: %s\n", timeinfo->tm_mday, timeinfo->tm_mon+1, timeinfo->tm_year+1900, + timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, mess); + return; +} + +/* +Closes socket (with dependency on used OS) +*/ +void cleanUP(int sock){ +#ifdef WIN32 + closesocket(sock); + WSACleanup(); +#else + close(sock); +#endif + return; +} + +/* +Frees allocated memory of pointer to array of chars +*/ +void freeCMD(char *commands[], char *ls_commands[], char *description[], int i){ + for(int f=0; f < i; f++){ + free(commands[f]); + free(ls_commands[f]); + free(description[f]); + } + return; +} + +/* ######################################### */ + +int main(int argc , char *argv[]){ + + if (argc != 3){ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "- %s \n", argv[0]); + return 1; + } + + int sock; + struct sockaddr_in server; + char message[MESSAGE_SIZE]; + char server_reply[REPLY_MSG_SIZE]; + +#ifdef WIN32 + WSADATA wsaData; + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0){ + printf("WSASturtup() failed %d\n", iResult); + return 1; + } +#endif + + // Create socket + if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ + printf("socket() failed\n"); + cleanUP(sock); + return 1; + } + + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr(argv[1]); + server.sin_port = htons(atoi(argv[2])); + + // Connect to remote server + if (connect(sock, (struct sockaddr *) &server, sizeof(server)) != 0){ + printf("Cannot connect to server - please check IP, port and status of server before trying again!\n"); + cleanUP(sock); + return 1; + } + + memset(server_reply, 0, REPLY_MSG_SIZE); + + if(recv(sock, server_reply, 2, 0) <= 0){ + printf("Connection interrupted! Check server status!\n"); + cleanUP(sock); + return 1; + } + + // Check server's mode + int automatic = 0; + if(strcmp(server_reply, "0") == 0){ + printf("Connected - manual mode.\n"); + } + else{ + printf("Connected - automatic mode.\n"); + automatic = 1; + } + + char *commands[CMD_BUF_SIZE]; + char *ls_commands[CMD_BUF_SIZE]; + char *description[CMD_BUF_SIZE]; + int i = 0; + + if(automatic == 0){ + + // Load user commands + FILE *fptr; + if ((fptr = fopen("commands.txt", "r")) == NULL) { + printf("Cannot open/create log file!\n"); + cleanUP(sock); + return 1; + } + char line[LINE_SIZE]; + memset(line, 0, LINE_SIZE); + while(fgets(line, sizeof(line), fptr) != NULL) { + char *rest = line; + char *cmd = strtok_r(line, ":", &rest); + char *ls_cmd = strtok_r(rest, ":", &rest); + char *des = strtok_r(rest, ":", &rest); + commands[i] = (char *)calloc(strlen(cmd)+2, sizeof(char)); + strcpy(commands[i], cmd); + + ls_commands[i] = (char *)calloc(strlen(ls_cmd)+2, sizeof(char)); + strcpy(ls_commands[i], ls_cmd); + + description[i] = (char *)calloc(strlen(des)+2, sizeof(char)); + strcpy(description[i], des); + i++; + } + fclose(fptr); + puts("Commands loaded. Type 'show' or 'show info' to see more. Use 'close' for client shutdown."); + } + + FILE *logs; + + // Communication with server + while(1){ + memset(server_reply, 0, REPLY_MSG_SIZE); + + if(automatic == 0){ + logs = fopen("logy.txt", "a"); + + if(logs == NULL){ + printf("Cannot open/create log file!\n"); + cleanUP(sock); + return 1; + } + + // Wait for, read and compare user input + char message_lsm[MESSAGE_SIZE]; + memset(message_lsm, 0, MESSAGE_SIZE); + + printf(">> Enter command: "); + + memset(message, 0, MESSAGE_SIZE); + char* r = fgets(message, MESSAGE_SIZE, stdin); + if (r == NULL){ + printf("Input failed.\n"); + break; + } + + if(strcmp(message, "\n") == 0){ + continue; + } + + message[strcspn(message, "\n")] = 0; + + if(strcmp(message, "close") == 0){ + break; + } + + if(strcmp(message, "show") == 0){ + for(int cmd = 0; cmd < i; cmd++){ + printf("%s\n", commands[cmd]); + } + printf("\n"); + continue; + } + else if(strcmp(message, "show info") == 0){ + for(int cmds = 0; cmds < i; cmds++){ + printf("%s - %s", commands[cmds], description[cmds]); + } + printf("\n"); + continue; + } + else{ + int check = 0; + for(int y=0; y < i; y++){ + if(strcmp(message, commands[y]) == 0){ + strcpy(message_lsm, ls_commands[y]); + write_log(logs, message_lsm); + check = 1; + fclose(logs); + } + } + if(check == 0){ + printf("Invalid input. Try commands 'show' or 'show info' to check all avaible commands.\n"); + continue; + } + } + + // Send data + if(send(sock, message_lsm, strlen(message_lsm), 0) < (ssize_t)strlen(message_lsm)){ + printf("send() failed"); + cleanUP(sock); + freeCMD(commands, ls_commands, description, i); + return 1; + } + + + } + + // Receive a reply from the server + if(recv(sock, server_reply, REPLY_MSG_SIZE, 0) <= 0){ + printf("Server crashed or disconnected!\n"); + cleanUP(sock); + if(automatic == 0){ + freeCMD(commands, ls_commands, description, i); + } + return 0; + } + + // Write received data to logs and to terminal + if(automatic == 0){ + logs = fopen("logy.txt", "a"); + + if(logs == NULL){ + printf("Cannot create/write to log file!\n"); + cleanUP(sock); + return 1; + } + + puts("Server reply: "); + write_log(logs, server_reply); + printf("%s\n", server_reply); + printf("\n"); + fprintf(logs, "\n"); + fclose(logs); + } + else{ + // Char % indicates end of measurement + if(strcmp(server_reply, "%") == 0){ + break; + } + logs = fopen("auto-logy.txt", "a"); + if(logs == NULL){ + printf("Cannot open auto-log file!\n"); + cleanUP(sock); + return 1; + } + + fprintf(logs, "%s", server_reply); + printf("%s", server_reply); + fclose(logs); + } + } + + cleanUP(sock); + return 0; +} diff --git a/commands.txt b/commands.txt new file mode 100644 index 0000000..3096a42 --- /dev/null +++ b/commands.txt @@ -0,0 +1,13 @@ +measure:sNg:triggers simple measurement of distance +buffer measure:sNf+0:triggers continuous measurements of the distance as fast as possible with internal buffering in the device until 'stop' command +read:sNq:reads the latest measurement from continuous measurement (buffer measure) +stop:sNc:stops the current execution and resets the status LEDs +single signal:sNm+0:returns signal strength after single measurement +temperature:sNt:triggers measurement of the temperature inside the sensor +laser on:sNo:switches the laser ON +laser off:sNp:switches the laser OFF +version:sNsv:retrieves the software version +serial:sNsn:setrieves the serial number +type:dt:returns the device type +info:dg:returns the device type, generation and current communication settings +configuration:sNuc:returns characteristic configuration diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..57ec92c --- /dev/null +++ b/config.txt @@ -0,0 +1,26 @@ +################## Config pre server ################### +######################################################## + +# COM port +comport=16 + +# COM port characteristics +baud=115200 +bits=8 +# parita bud N - 0 alebo E - 2 +parity=1 +stopbit=1 + +# Rychlost merania(v ms), pocet ignorovanych errorov a pocet merani +measure rate=0 +maxErrors=5 +#samples=15 + +# Jednotky merania - zaklad v ms, 10 - mm, 100 - cm +units=10 + +# Moznost zapnut user auto-start s nastavenim prikazov ako sNuof, sNuga + +sNuH=0 + +cmd=sNuof+xx diff --git a/demo.c b/demo.c new file mode 100644 index 0000000..89f9f26 --- /dev/null +++ b/demo.c @@ -0,0 +1,126 @@ +/* +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 client.c -Wall -Wextra -lwsock32 -o client + LINUX -- gcc server.c -Wall -Wextra -o server +version: 1.2 , 15.5.2021 +Usage: This demo demonstrates simple way of using Socket API and loading measurements from our server. + Results are compared with MAX and MIN value - be careful on used units. + +*/ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#include +#include + +/* Defines lenght of buffer for server's messages +It's better to use higher values, it's helpful in case of hardware/network overload */ +#define REPLY_MSG_SIZE 2048 + +/* Defines public or local IP address of server in decimal form */ +#define IP_ADDRESS "192.168.0.101" + +/* Defines port used by server */ +#define PORT 32500 + +/* MAX & MIN values are used to compare measurement result +Values should be adjusted to the used units (check server config file) */ +#define MAX_VALUE 1265 +#define MIN_VALUE 1200 + +/* +Closes socket (with dependency on used OS) +*/ +void cleanUP(int sock){ +#ifdef WIN32 + closesocket(sock); + WSACleanup(); +#else + close(sock); +#endif + return; +} + +/* ====================================================== */ + +int main(){ + int sock; + struct sockaddr_in server; + char server_reply[REPLY_MSG_SIZE]; + +#ifdef WIN32 + WSADATA wsaData; + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0){ + printf("WSASturtup() failed %d\n", iResult); + return 1; + } +#endif + + if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ + printf("socket() failed\n"); + cleanUP(sock); + return 1; + } + + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr(IP_ADDRESS); + server.sin_port = htons(PORT); + + if (connect(sock, (struct sockaddr *) &server, sizeof(server)) != 0){ + printf("Cannot connect to server - check IP, port and server status!\n"); + cleanUP(sock); + return 1; + } + + if(recv(sock, server_reply, 2, 0) <= 0){ + printf("Connection interrupted! Check server status!\n"); + cleanUP(sock); + return 1; + } + + while(1){ + memset(server_reply, 0, REPLY_MSG_SIZE); + + if(recv(sock, server_reply, REPLY_MSG_SIZE, 0) <= 0){ + printf("Server crushed or disconnected!\n"); + cleanUP(sock); + return 1; + } + + if(strcmp(server_reply, "%") == 0){ + break; + } + + char *rest = server_reply; + for(int i = 0; i < 3; i++){ + strtok_r(rest, ":", &rest); + } + double result = strtof(rest, NULL); + + if(result > MAX_VALUE){ + printf("Distance out of range - too great (%0.2f)\n", result); + } + if(result < MIN_VALUE){ + printf("Distance out of range - too low (%0.2f)\n", result); + } + } + + cleanUP(sock); + return 0; +} diff --git a/rs232.c b/rs232.c new file mode 100644 index 0000000..3a08ce7 --- /dev/null +++ b/rs232.c @@ -0,0 +1,879 @@ +/* +*************************************************************************** +* +* Author: Teunis van Beelen +* +* Copyright (C) 2005 - 2021 Teunis van Beelen +* +* Email: teuniz@protonmail.com +* +*************************************************************************** +* +* 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. +* +* 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 . +* +*************************************************************************** +*/ + + +/* Last revision: February 9, 2021 */ +/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */ + + +#include "rs232.h" + + +#if defined(__linux__) || defined(__FreeBSD__) /* Linux & FreeBSD */ + +#define RS232_PORTNR 38 + + +int Cport[RS232_PORTNR], + error; + +struct termios new_port_settings, + old_port_settings[RS232_PORTNR]; + +const char *comports[RS232_PORTNR]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev/ttyS4","/dev/ttyS5", + "/dev/ttyS6","/dev/ttyS7","/dev/ttyS8","/dev/ttyS9","/dev/ttyS10","/dev/ttyS11", + "/dev/ttyS12","/dev/ttyS13","/dev/ttyS14","/dev/ttyS15","/dev/ttyUSB0", + "/dev/ttyUSB1","/dev/ttyUSB2","/dev/ttyUSB3","/dev/ttyUSB4","/dev/ttyUSB5", + "/dev/ttyAMA0","/dev/ttyAMA1","/dev/ttyACM0","/dev/ttyACM1", + "/dev/rfcomm0","/dev/rfcomm1","/dev/ircomm0","/dev/ircomm1", + "/dev/cuau0","/dev/cuau1","/dev/cuau2","/dev/cuau3", + "/dev/cuaU0","/dev/cuaU1","/dev/cuaU2","/dev/cuaU3"}; + +int RS232_OpenComport(int comport_number, int baudrate, const char *mode, int flowctrl) +{ + int baudr, + status; + + if((comport_number>=RS232_PORTNR)||(comport_number<0)) + { + printf("illegal comport number\n"); + return(1); + } + + switch(baudrate) + { + case 50 : baudr = B50; + break; + case 75 : baudr = B75; + break; + case 110 : baudr = B110; + break; + case 134 : baudr = B134; + break; + case 150 : baudr = B150; + break; + case 200 : baudr = B200; + break; + case 300 : baudr = B300; + break; + case 600 : baudr = B600; + break; + case 1200 : baudr = B1200; + break; + case 1800 : baudr = B1800; + break; + case 2400 : baudr = B2400; + break; + case 4800 : baudr = B4800; + break; + case 9600 : baudr = B9600; + break; + case 19200 : baudr = B19200; + break; + case 38400 : baudr = B38400; + break; + case 57600 : baudr = B57600; + break; + case 115200 : baudr = B115200; + break; + case 230400 : baudr = B230400; + break; + case 460800 : baudr = B460800; + break; +#if defined(__linux__) + case 500000 : baudr = B500000; + break; + case 576000 : baudr = B576000; + break; + case 921600 : baudr = B921600; + break; + case 1000000 : baudr = B1000000; + break; + case 1152000 : baudr = B1152000; + break; + case 1500000 : baudr = B1500000; + break; + case 2000000 : baudr = B2000000; + break; + case 2500000 : baudr = B2500000; + break; + case 3000000 : baudr = B3000000; + break; + case 3500000 : baudr = B3500000; + break; + case 4000000 : baudr = B4000000; + break; +#endif + default : printf("invalid baudrate\n"); + return(1); + break; + } + + int cbits=CS8, + cpar=0, + ipar=IGNPAR, + bstop=0; + + if(strlen(mode) != 3) + { + printf("invalid mode \"%s\"\n", mode); + return(1); + } + + switch(mode[0]) + { + case '8': cbits = CS8; + break; + case '7': cbits = CS7; + break; + case '6': cbits = CS6; + break; + case '5': cbits = CS5; + break; + default : printf("invalid number of data-bits '%c'\n", mode[0]); + return(1); + break; + } + + switch(mode[1]) + { + case 'N': + case 'n': cpar = 0; + ipar = IGNPAR; + break; + case 'E': + case 'e': cpar = PARENB; + ipar = INPCK; + break; + case 'O': + case 'o': cpar = (PARENB | PARODD); + ipar = INPCK; + break; + default : printf("invalid parity '%c'\n", mode[1]); + return(1); + break; + } + + switch(mode[2]) + { + case '1': bstop = 0; + break; + case '2': bstop = CSTOPB; + break; + default : printf("invalid number of stop bits '%c'\n", mode[2]); + return(1); + break; + } + +/* +http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html + +http://man7.org/linux/man-pages/man3/termios.3.html +*/ + + Cport[comport_number] = open(comports[comport_number], O_RDWR | O_NOCTTY | O_NDELAY); + if(Cport[comport_number]==-1) + { + perror("unable to open comport "); + return(1); + } + + /* lock access so that another process can't also use the port */ + if(flock(Cport[comport_number], LOCK_EX | LOCK_NB) != 0) + { + close(Cport[comport_number]); + perror("Another process has locked the comport."); + return(1); + } + + error = tcgetattr(Cport[comport_number], old_port_settings + comport_number); + if(error==-1) + { + close(Cport[comport_number]); + flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ + perror("unable to read portsettings "); + return(1); + } + memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */ + + new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD; + if(flowctrl) + { + new_port_settings.c_cflag |= CRTSCTS; + } + new_port_settings.c_iflag = ipar; + new_port_settings.c_oflag = 0; + new_port_settings.c_lflag = 0; + new_port_settings.c_cc[VMIN] = 0; /* block untill n bytes are received */ + new_port_settings.c_cc[VTIME] = 0; /* block untill a timer expires (n * 100 mSec.) */ + + cfsetispeed(&new_port_settings, baudr); + cfsetospeed(&new_port_settings, baudr); + + error = tcsetattr(Cport[comport_number], TCSANOW, &new_port_settings); + if(error==-1) + { + tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); + close(Cport[comport_number]); + flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ + perror("unable to adjust portsettings "); + return(1); + } + +/* http://man7.org/linux/man-pages/man4/tty_ioctl.4.html */ + + if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) + { + tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); + flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ + perror("unable to get portstatus"); + return(1); + } + + status |= TIOCM_DTR; /* turn on DTR */ + status |= TIOCM_RTS; /* turn on RTS */ + + if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) + { + tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); + flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ + perror("unable to set portstatus"); + return(1); + } + + return(0); +} + + +int RS232_PollComport(int comport_number, unsigned char *buf, int size) +{ + int n; + + n = read(Cport[comport_number], buf, size); + + if(n < 0) + { + if(errno == EAGAIN) return 0; + } + + return(n); +} + + +int RS232_SendByte(int comport_number, unsigned char byte) +{ + int n = write(Cport[comport_number], &byte, 1); + if(n < 0) + { + if(errno == EAGAIN) + { + return 0; + } + else + { + return 1; + } + } + + return(0); +} + + +int RS232_SendBuf(int comport_number, unsigned char *buf, int size) +{ + int n = write(Cport[comport_number], buf, size); + if(n < 0) + { + if(errno == EAGAIN) + { + return 0; + } + else + { + return -1; + } + } + + return(n); +} + + +void RS232_CloseComport(int comport_number) +{ + int status; + + if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) + { + perror("unable to get portstatus"); + } + + status &= ~TIOCM_DTR; /* turn off DTR */ + status &= ~TIOCM_RTS; /* turn off RTS */ + + if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) + { + perror("unable to set portstatus"); + } + + tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number); + close(Cport[comport_number]); + + flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */ +} + +/* +Constant Description +TIOCM_LE DSR (data set ready/line enable) +TIOCM_DTR DTR (data terminal ready) +TIOCM_RTS RTS (request to send) +TIOCM_ST Secondary TXD (transmit) +TIOCM_SR Secondary RXD (receive) +TIOCM_CTS CTS (clear to send) +TIOCM_CAR DCD (data carrier detect) +TIOCM_CD see TIOCM_CAR +TIOCM_RNG RNG (ring) +TIOCM_RI see TIOCM_RNG +TIOCM_DSR DSR (data set ready) + +http://man7.org/linux/man-pages/man4/tty_ioctl.4.html +*/ + +int RS232_IsDCDEnabled(int comport_number) +{ + int status; + + ioctl(Cport[comport_number], TIOCMGET, &status); + + if(status&TIOCM_CAR) return(1); + else return(0); +} + + +int RS232_IsRINGEnabled(int comport_number) +{ + int status; + + ioctl(Cport[comport_number], TIOCMGET, &status); + + if(status&TIOCM_RNG) return(1); + else return(0); +} + + +int RS232_IsCTSEnabled(int comport_number) +{ + int status; + + ioctl(Cport[comport_number], TIOCMGET, &status); + + if(status&TIOCM_CTS) return(1); + else return(0); +} + + +int RS232_IsDSREnabled(int comport_number) +{ + int status; + + ioctl(Cport[comport_number], TIOCMGET, &status); + + if(status&TIOCM_DSR) return(1); + else return(0); +} + + +void RS232_enableDTR(int comport_number) +{ + int status; + + if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) + { + perror("unable to get portstatus"); + } + + status |= TIOCM_DTR; /* turn on DTR */ + + if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) + { + perror("unable to set portstatus"); + } +} + + +void RS232_disableDTR(int comport_number) +{ + int status; + + if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) + { + perror("unable to get portstatus"); + } + + status &= ~TIOCM_DTR; /* turn off DTR */ + + if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) + { + perror("unable to set portstatus"); + } +} + + +void RS232_enableRTS(int comport_number) +{ + int status; + + if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) + { + perror("unable to get portstatus"); + } + + status |= TIOCM_RTS; /* turn on RTS */ + + if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) + { + perror("unable to set portstatus"); + } +} + + +void RS232_disableRTS(int comport_number) +{ + int status; + + if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1) + { + perror("unable to get portstatus"); + } + + status &= ~TIOCM_RTS; /* turn off RTS */ + + if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1) + { + perror("unable to set portstatus"); + } +} + + +void RS232_flushRX(int comport_number) +{ + tcflush(Cport[comport_number], TCIFLUSH); +} + + +void RS232_flushTX(int comport_number) +{ + tcflush(Cport[comport_number], TCOFLUSH); +} + + +void RS232_flushRXTX(int comport_number) +{ + tcflush(Cport[comport_number], TCIOFLUSH); +} + + +#else /* windows */ + +#define RS232_PORTNR 32 + +HANDLE Cport[RS232_PORTNR]; + + +const char *comports[RS232_PORTNR]={"\\\\.\\COM1", "\\\\.\\COM2", "\\\\.\\COM3", "\\\\.\\COM4", + "\\\\.\\COM5", "\\\\.\\COM6", "\\\\.\\COM7", "\\\\.\\COM8", + "\\\\.\\COM9", "\\\\.\\COM10", "\\\\.\\COM11", "\\\\.\\COM12", + "\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15", "\\\\.\\COM16", + "\\\\.\\COM17", "\\\\.\\COM18", "\\\\.\\COM19", "\\\\.\\COM20", + "\\\\.\\COM21", "\\\\.\\COM22", "\\\\.\\COM23", "\\\\.\\COM24", + "\\\\.\\COM25", "\\\\.\\COM26", "\\\\.\\COM27", "\\\\.\\COM28", + "\\\\.\\COM29", "\\\\.\\COM30", "\\\\.\\COM31", "\\\\.\\COM32"}; + +char mode_str[128]; + + +int RS232_OpenComport(int comport_number, int baudrate, const char *mode, int flowctrl) +{ + if((comport_number>=RS232_PORTNR)||(comport_number<0)) + { + printf("illegal comport number\n"); + return(1); + } + + switch(baudrate) + { + case 110 : strcpy(mode_str, "baud=110"); + break; + case 300 : strcpy(mode_str, "baud=300"); + break; + case 600 : strcpy(mode_str, "baud=600"); + break; + case 1200 : strcpy(mode_str, "baud=1200"); + break; + case 2400 : strcpy(mode_str, "baud=2400"); + break; + case 4800 : strcpy(mode_str, "baud=4800"); + break; + case 9600 : strcpy(mode_str, "baud=9600"); + break; + case 19200 : strcpy(mode_str, "baud=19200"); + break; + case 38400 : strcpy(mode_str, "baud=38400"); + break; + case 57600 : strcpy(mode_str, "baud=57600"); + break; + case 115200 : strcpy(mode_str, "baud=115200"); + break; + case 128000 : strcpy(mode_str, "baud=128000"); + break; + case 256000 : strcpy(mode_str, "baud=256000"); + break; + case 500000 : strcpy(mode_str, "baud=500000"); + break; + case 921600 : strcpy(mode_str, "baud=921600"); + break; + case 1000000 : strcpy(mode_str, "baud=1000000"); + break; + case 1500000 : strcpy(mode_str, "baud=1500000"); + break; + case 2000000 : strcpy(mode_str, "baud=2000000"); + break; + case 3000000 : strcpy(mode_str, "baud=3000000"); + break; + default : printf("invalid baudrate\n"); + return(1); + break; + } + + if(strlen(mode) != 3) + { + printf("invalid mode \"%s\"\n", mode); + return(1); + } + + switch(mode[0]) + { + case '8': strcat(mode_str, " data=8"); + break; + case '7': strcat(mode_str, " data=7"); + break; + case '6': strcat(mode_str, " data=6"); + break; + case '5': strcat(mode_str, " data=5"); + break; + default : printf("invalid number of data-bits '%c'\n", mode[0]); + return(1); + break; + } + + switch(mode[1]) + { + case 'N': + case 'n': strcat(mode_str, " parity=n"); + break; + case 'E': + case 'e': strcat(mode_str, " parity=e"); + break; + case 'O': + case 'o': strcat(mode_str, " parity=o"); + break; + default : printf("invalid parity '%c'\n", mode[1]); + return(1); + break; + } + + switch(mode[2]) + { + case '1': strcat(mode_str, " stop=1"); + break; + case '2': strcat(mode_str, " stop=2"); + break; + default : printf("invalid number of stop bits '%c'\n", mode[2]); + return(1); + break; + } + + if(flowctrl) + { + strcat(mode_str, " xon=off to=off odsr=off dtr=on rts=off"); + } + else + { + strcat(mode_str, " xon=off to=off odsr=off dtr=on rts=on"); + } + +/* +http://msdn.microsoft.com/en-us/library/windows/desktop/aa363145%28v=vs.85%29.aspx + +http://technet.microsoft.com/en-us/library/cc732236.aspx + +https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_dcb +*/ + + Cport[comport_number] = CreateFileA(comports[comport_number], + GENERIC_READ|GENERIC_WRITE, + 0, /* no share */ + NULL, /* no security */ + OPEN_EXISTING, + 0, /* no threads */ + NULL); /* no templates */ + + if(Cport[comport_number]==INVALID_HANDLE_VALUE) + { + printf("unable to open comport\n"); + return(1); + } + + DCB port_settings; + memset(&port_settings, 0, sizeof(port_settings)); /* clear the new struct */ + port_settings.DCBlength = sizeof(port_settings); + + if(!BuildCommDCBA(mode_str, &port_settings)) + { + printf("unable to set comport dcb settings\n"); + CloseHandle(Cport[comport_number]); + return(1); + } + + if(flowctrl) + { + port_settings.fOutxCtsFlow = TRUE; + port_settings.fRtsControl = RTS_CONTROL_HANDSHAKE; + } + + if(!SetCommState(Cport[comport_number], &port_settings)) + { + printf("unable to set comport cfg settings\n"); + CloseHandle(Cport[comport_number]); + return(1); + } + + COMMTIMEOUTS Cptimeouts; + + Cptimeouts.ReadIntervalTimeout = MAXDWORD; + Cptimeouts.ReadTotalTimeoutMultiplier = 0; + Cptimeouts.ReadTotalTimeoutConstant = 0; + Cptimeouts.WriteTotalTimeoutMultiplier = 0; + Cptimeouts.WriteTotalTimeoutConstant = 0; + + if(!SetCommTimeouts(Cport[comport_number], &Cptimeouts)) + { + printf("unable to set comport time-out settings\n"); + CloseHandle(Cport[comport_number]); + return(1); + } + + return(0); +} + + +int RS232_PollComport(int comport_number, unsigned char *buf, int size) +{ + int n; + +/* added the void pointer cast, otherwise gcc will complain about */ +/* "warning: dereferencing type-punned pointer will break strict aliasing rules" */ + + if(!ReadFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL)) + { + return -1; + } + + return(n); +} + + +int RS232_SendByte(int comport_number, unsigned char byte) +{ + int n; + + if(!WriteFile(Cport[comport_number], &byte, 1, (LPDWORD)((void *)&n), NULL)) + { + return(1); + } + + if(n<0) return(1); + + return(0); +} + + +int RS232_SendBuf(int comport_number, unsigned char *buf, int size) +{ + int n; + + if(WriteFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL)) + { + return(n); + } + + return(-1); +} + + +void RS232_CloseComport(int comport_number) +{ + CloseHandle(Cport[comport_number]); +} + +/* +http://msdn.microsoft.com/en-us/library/windows/desktop/aa363258%28v=vs.85%29.aspx +*/ + +int RS232_IsDCDEnabled(int comport_number) +{ + int status; + + GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); + + if(status&MS_RLSD_ON) return(1); + else return(0); +} + + +int RS232_IsRINGEnabled(int comport_number) +{ + int status; + + GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); + + if(status&MS_RING_ON) return(1); + else return(0); +} + + +int RS232_IsCTSEnabled(int comport_number) +{ + int status; + + GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); + + if(status&MS_CTS_ON) return(1); + else return(0); +} + + +int RS232_IsDSREnabled(int comport_number) +{ + int status; + + GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status)); + + if(status&MS_DSR_ON) return(1); + else return(0); +} + + +void RS232_enableDTR(int comport_number) +{ + EscapeCommFunction(Cport[comport_number], SETDTR); +} + + +void RS232_disableDTR(int comport_number) +{ + EscapeCommFunction(Cport[comport_number], CLRDTR); +} + + +void RS232_enableRTS(int comport_number) +{ + EscapeCommFunction(Cport[comport_number], SETRTS); +} + + +void RS232_disableRTS(int comport_number) +{ + EscapeCommFunction(Cport[comport_number], CLRRTS); +} + +/* +https://msdn.microsoft.com/en-us/library/windows/desktop/aa363428%28v=vs.85%29.aspx +*/ + +void RS232_flushRX(int comport_number) +{ + PurgeComm(Cport[comport_number], PURGE_RXCLEAR | PURGE_RXABORT); +} + + +void RS232_flushTX(int comport_number) +{ + PurgeComm(Cport[comport_number], PURGE_TXCLEAR | PURGE_TXABORT); +} + + +void RS232_flushRXTX(int comport_number) +{ + PurgeComm(Cport[comport_number], PURGE_RXCLEAR | PURGE_RXABORT); + PurgeComm(Cport[comport_number], PURGE_TXCLEAR | PURGE_TXABORT); +} + + +#endif + + +void RS232_cputs(int comport_number, const char *text) /* sends a string to serial port */ +{ + while(*text != 0) RS232_SendByte(comport_number, *(text++)); +} + + +/* return index in comports matching to device name or -1 if not found */ +int RS232_GetPortnr(const char *devname) +{ + int i; + + char str[32]; + +#if defined(__linux__) || defined(__FreeBSD__) /* Linux & FreeBSD */ + strcpy(str, "/dev/"); +#else /* windows */ + strcpy(str, "\\\\.\\"); +#endif + strncat(str, devname, 16); + str[31] = 0; + + for(i=0; i. +* +*************************************************************************** +*/ + +/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */ + + +#ifndef rs232_INCLUDED +#define rs232_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + + +#if defined(__linux__) || defined(__FreeBSD__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include + +#endif + +int RS232_OpenComport(int, int, const char *, int); +int RS232_PollComport(int, unsigned char *, int); +int RS232_SendByte(int, unsigned char); +int RS232_SendBuf(int, unsigned char *, int); +void RS232_CloseComport(int); +void RS232_cputs(int, const char *); +int RS232_IsDCDEnabled(int); +int RS232_IsRINGEnabled(int); +int RS232_IsCTSEnabled(int); +int RS232_IsDSREnabled(int); +void RS232_enableDTR(int); +void RS232_disableDTR(int); +void RS232_enableRTS(int); +void RS232_disableRTS(int); +void RS232_flushRX(int); +void RS232_flushTX(int); +void RS232_flushRXTX(int); +int RS232_GetPortnr(const char *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + + diff --git a/server.c b/server.c new file mode 100644 index 0000000..2761e7f --- /dev/null +++ b/server.c @@ -0,0 +1,542 @@ +/* +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.3 , 17.5.2021 +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 + +/* 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(1); + + 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(1); + 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(1); + + char *errors = malloc(sizeof(char)); + int errCount = 0; + int errInRow = 0; + int maxErrors = getVar(vars, "maxErrors"); + int samples = getVar(vars, "samples"); + int measureSamples = 0; + int e = 0; + + while(1){ + if(errInRow >= maxErrors){ + break; + } + + if(samples > -1 && samples == measureSamples){ + break; + } + + 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); + + // Split and handle data + 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{ + int res = 0; + + if((res = atoi(first)) > 0){ + res = atoi(first); + } + else{ + char *numbers = NULL; + strtok_r(first, "+", &numbers); + res = atoi(numbers); + } + + + if(res == 0){ + continue; + } + + int units = getVar(vars, "units"); + + double resd = res * (1/(double)units); + char output[MESSAGE_SIZE]; + memset(output, 0, MESSAGE_SIZE); + + 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(1); + 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(1); + 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(1); + 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; +}