/*************************************************************************** * Copyright (C) 2011 by semico * * mail@semico.ru * * * * 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 2 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ /* Функции и структуры для работы с последовательным портом в ОС GNU/Linux*/ /* для pmkcom v0.19 */ static struct termios pots; // сохр. настройки порта termios static struct termios sots; // сохр. настройки stdout/stdin struct termios pts; // настройки порта struct termios sts; // настройки stdout/stdin struct sigaction sact; // обработчик сигналов для корректного выхода struct pollfd ufds[4]; // взаимодействие с poll() struct timeval tv1; // начало посылки команды и текущее время struct timeval tv2; // завершение посылки команды struct timezone tz1; // для корректного вызова gettimeofday /* ----------------------------------------------------------------- ПОРТ */ speed_t symbolic_speed(long speednum) { #ifdef B57600 if (speednum>=57600) return B57600; #endif if (speednum>=38400) return B38400; if (speednum>=19200) return B19200; if (speednum>=9600) return B9600; if (speednum>=4800) return B4800; if (speednum>=2400) return B2400; if (speednum>=1200) return B1200; if (speednum>=600) return B600; return B9600; } void cleanup_termios (int signal) { /* восстановить настройки при выходе */ tcsetattr(pf, TCSANOW,&pots); exit(0); } void die(int exitcode, const char *error, const char *add) { /* вывод сообщения о системной ошибке в stderr */ if (error) fprintf(stderr,"%s: %s\n",error,add); exit(exitcode); } /*-------------------------------------------- PORT OPEN */ int port_open(int ncom) { int re=0; if (ncom<0) ncom=0; if (ncom>255) ncom=255; if (fportname[0]==0) sprintf(portname,"/dev/ttyS%d",com); /* по умолчанию */ else strncpy(portname,fportname,256); /* v0.19 - если наименование порта указано в ком. строке */ /* Открыть порт */ pf=open(portname, O_RDWR); if (pf<0) {re=EX_258; goto end;} /* Порт не открыт */ // Изменить конфигурацию порта tcgetattr(pf,&pts); pots=pts; pts.c_lflag &= ~ICANON; pts.c_lflag &= ~(ECHO | ECHOCTL | ECHONL); pts.c_cflag |= HUPCL; pts.c_cc[VMIN]=1; //1 0 pts.c_cc[VTIME]=0; //0 10 /* Отключаем управляющие символы */ pts.c_cc[VINTR]=_POSIX_VDISABLE; /* ^C=3 */ pts.c_cc[VSUSP]=_POSIX_VDISABLE; /* ^Z=26 */ pts.c_cc[VQUIT]=_POSIX_VDISABLE; /* ^\=28 */ pts.c_oflag &= ~ONLCR; pts.c_iflag &= ~ICRNL; pts.c_iflag &= ~INLCR; // не преобразовывать ПС в ВК pts.c_iflag &= ~IGNCR; // передавать ВК #ifdef IUCLC pts.c_iflag &= ~IUCLC; // Только в Linux (no POSIX) не преобразовывать регистр v0.11 #endif #ifdef IMAXBEL pts.c_iflag &= ~IMAXBEL; // Только в Linux (no POSIX) не передавать символ при переполнении v0.11 #endif // отключение управления потоком pts.c_cflag &= ~CRTSCTS; // Only LINUX ~POSIX pts.c_iflag &= ~(IXON | IXOFF); // XON=11h XOFF=13h #ifdef IXANY pts.c_iflag &= ~IXANY; #endif pts.c_iflag |= IGNBRK; // игнорировать разрывы pts.c_iflag |= IGNPAR; // игнорировать ошибки чётности и кадрирования v0.11 pts.c_iflag &= ~INPCK; // отключить проверку чётности pts.c_iflag &= ~ISTRIP; // 8 бит pts.c_cflag |= CLOCAL; // игнорировать линии управления pts.c_cflag |= CREAD; // разрешение приёма pts.c_cflag &=~CSIZE; // сбросить размер символа pts.c_cflag |= CS8; // установить 8 бит на символ pts.c_cflag &= ~CSTOPB; // один стоп-бит pts.c_cflag &= ~PARENB; // не генерировать проверку на чётность // установка скорости if (speed==0) speed=9600; speed=9600; // отладка cfsetospeed(&pts,symbolic_speed(speed)); cfsetispeed(&pts,symbolic_speed(speed)); // установка обработчика сигналов sact.sa_handler = cleanup_termios; sigaction(SIGHUP, &sact, NULL); sigaction(SIGINT, &sact, NULL); sigaction(SIGPIPE, &sact, NULL); sigaction(SIGTERM, &sact, NULL); // установить изменённые настройки termios tcsetattr(pf, TCSANOW, &pts); end: return(re); } /*------------------------------------------- PORT SPEED ------------*/ void port_speed(long s) { /* установить скорость при уже открытом порте */ cfsetospeed(&pts,symbolic_speed(s)); cfsetispeed(&pts,symbolic_speed(s)); tcsetattr(pf, TCSANOW, &pts); return; } /*------------------------------------------- CLOSE -------------------*/ int port_close(void) { int re=0; // восстановить настройки tcsetattr(pf, TCSANOW, &pots); return(re); } /*--------------------------------------------------------------------*/ int kom_mtest(int n, int k, int z, int r, int len) // передача команд протокола MULTITEST, pf открыт, параметры установлены предварительно { /* Используемые при вызове переменные n - сетевой номер устройства должен быть от 0 до 255 k - соответствует коду типа пакета "K" (см. протокол). Для команд запроса данных - 0x10. z,r - соответствуют кодам группы параметров "Z" и коду параметра "R", см. протокол. Определяют формат и назначение передаваемых данных или запрашиваемой величины. len - длина передаваемых данных D1...DN из буфера dbuf в прибор . Для команд запроса данных - нуль. */ int re=0; /* Код возврата из функции 0 - OK, 256-выход по тайм-ауту, 257-ошибочная контрольная сумма или формат команды, 258 - последовательный порт не открыт или не обнаружен. 259 - ошибка poll (Linux & nix) 1-255 коды ошибок, возвращаемые устройством, см. протокол. */ int i,j,p; // Вспомогательные переменные int l,l1,l2,l3; // Вспомогательные переменные int ks; // контрольная сумма int done=0; // флаг выхода из цикла poll long done1=0; // счетчик числа проходов по циклу do char temp[32]; // буфер для преобразования чисел float* f; // для разбора принятого числа в формате float f_dd=0; // обнулить флаг наличия данных в dd f_str=0; // обнулить флаг наличия строки в str mstr[0]=0; /* Начало формирования команды в буфере kbuf[], формат команды здесь подробно не рассматривается. См. в протоколе */ kbuf[0]=0; // адрес группы, для отдельного устройства равен нулю kbuf[1]=n%256; // адрес (сетевой номер) kbuf[2]=(len+4)%256; // длина пакета, мл. байт kbuf[3]=((len+4)/256)%256; // длина пакета, ст. байт kbuf[4]=k%256; // код типа пакета kbuf[5]=z%256; // код группы параметров kbuf[6]=r%256; // код параметра /* Перенос данных, передаваемых в устройство (если есть) */ if (len!=0) { for (l=0; l=1) {l+=i; if (l>4) { if (lenrbuf==0) { l2=(int)rbuf[2]; l3=(int)rbuf[3]; if (l2<0) l2+=256; if (l3<0) l3+=256; lenrbuf=256*l3+l2+4; if (lenrbuf>maxlbuf) lenrbuf=maxlbuf; } if (l>=lenrbuf) {done=1;} } } else { done=1;} } if ((p<0) && (errno!=EINTR)) {re=259; done=1; goto end; } //die (1, "poll error",""); if (ufds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { re=259; done=1; goto end;; } } while (done==0); /* разбор команды */ /* Проверка количества принятых байт */ if (lenrbuf<=0) {re=256; goto end;} if (re!=0) goto end; /* Проверка контрольной суммы принятого от прибора ответа */ l=0; for (i=0; i=256) l-=256; } j=rbuf[lenrbuf-1]; if (j<0) j+=256; if (j!=l%256) {re=257; goto end;} /* Проверка формата команды - наличие в ответе обязательных полей */ if ((rbuf[0]!=0)||(rbuf[1]!=(unsigned char)n)) {re=257; goto end;} if (((int)(rbuf[5])!=z) || ((int)(rbuf[6])!=r)) {re=257; goto end;} if (rbuf[4]==0x40) {re=(int)rbuf[7]; goto end;} /* принят пакет с сообщением об ошибке */ if (((int)z>=0x10)&&((int)r!=0)&&(lenrbuf==13)) { /* если был запрос числовой величины и длина пакета соответствует типу float, выполнить соответствующее преобразование и перенести принятые данные в глобальную переменную dd */ for (i=7; i7)) { for (i=7; i=32) mstr[i-7]=(char)j; else mstr[i-7]=(char)' '; mstr[i-6]=0; } f_str=1; } end: return(re); }