607 lines
16 KiB
C
607 lines
16 KiB
C
/*
|
|
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 <winsock2.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#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 <Server Port>\n", argv[0]);
|
|
fprintf(stderr, "- %s <Server Port> <Config file>\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;
|
|
}
|