LINUX.ORG.RU

Com-port странное поведение программы


0

2

Здравствуйте, уважаемые. Исходные данные, имеется некоторое устройство которое с частотой 10Гц посылает на компьютер пакет с данными (16 байт). Устройство подключено к ПК с помощью переходника USB to COM. Если программа, код который приведен ниже, стартует в первый раз после перезагрузки/включения, то она отрабатывает некорректно, то есть получается как будто пакеты приходят не с частотой 10Гц а к примеру 3, или 5 всегда по разному. А если выйти из программы после неудачного старта и снова ее запустить, то все работает как часы.

Теперь вопрос в чем может быть причина такого странного поведения? P/S opensuse 11.3, xfce, устройство подключено на юсб, на этот же комп подключена GPS Garmin LVC18 но на реальный физический ком-порт(может быть причина в прерываниях или еще в чем)

Программа: Функция инициализации ком порта

int opendevice(char* device, int speed)
{
  struct termios new;
  speed_t sspeed;
  int res = 0, fd = 0;
  
  if(( fd = open(device, O_RDWR | O_NDELAY | O_NOCTTY)) < 0){
    perror("Can not open device");    
    return -1;
  }
  
  switch(speed)
  {
    case 2400:
       sspeed = B2400;
       break;
    case 4800:
       sspeed = B4800;
       break;
    case 9600:
       sspeed = B9600;
       break;
    case 19200:
       sspeed = B19200;
       break;
    case 38400:
       sspeed = B38400;
       break;
    case 115200:
       sspeed = B115200;
       break;

  }
  memset (&new, 0, sizeof (new));
  cfmakeraw(&new);
  new.c_cflag &= ~PARENB; // disable parent bit
  new.c_cflag &= ~CSTOPB;// disable parent bit    
  new.c_cflag &= ~CSIZE; // 1 stop bit
  new.c_cflag |= CS8;     // 8bit data
  new.c_cflag |= CREAD;  // Enable Receiver 
  new.c_cflag |= CLOCAL; // Ignore modem control lines. 
  new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  new.c_oflag &= ~OPOST;
  //new.c_cc[VMIN] = 1;    // 1 simbols for read
  //new.c_cc[VTIME] = 1;    // Time of reading of 1 symbol  

  
  res = cfsetispeed(&new, sspeed); //baud rate 4800
  if (res<0){
    perror("Can not set input speed of device");
    fprintf(filelog,"FATAL ERROR: Can not set input speed of device %s. Time %s\n",device,ctime(&ftmcreated));
    return -1;
  };
 
  res = cfsetospeed(&new, sspeed);
  if (res<0){
    perror("Can not set output speed of device");
    fprintf(filelog,"FATAL ERROR: Can not set output speed of device %s. Time %s\n",device,ctime(&ftmcreated));
    return -1;
  };

    
  tcflush(fd, TCIOFLUSH); 
  res = tcsetattr(fd, TCSAFLUSH, &new); 
  if (res<0){
    perror("Can not set attribute to device");
    fprintf(filelog,"FATAL ERROR: Can not set attribute of device %s. Time %s\n",device,ctime(&ftmcreated));
    return -1;
  };
  return fd;
}.

Функция main

int main(int argc, char** argv)
{
  
  unsigned char chs,indx=0,filename[256], devname[256];
  unsigned char data[16];
  int speed = 4800,dmn;
  
  strcpy(devname, "/dev/ttyUSB0");
  
  filelog=fopen("/station/data/logfile.txt","a");    // создаем лог файл
  if(!filelog){
     printf("Cant create file with logs!\n");
     exit(1);
  }
  
  int c, fd;
  if (argc > 1) {    // открываем девайс
    while (1) {
      c = getopt (argc, argv, "d:s:h");
      if (c == -1)
    break;
      switch (c) {
      case '\0':
    fprintf(stderr,"Error: Unknown command ");
    print_help ();
    return -1;
    break;
      case 'h':
    print_help ();
     return 0;
    break;
      case 's':
    speed = atoi(optarg);
    break;
      case 'd':
    strcpy (devname, optarg);
    break;
      default:
    print_help ();
    return 0;
    break;
      }
    }
  }
  
  
  
    ////// Create files ////////////////
  (void) gettimeofday (&tmvl, NULL);    // получаем текущее время и дату в секундах от начала эпохи
  gmt = *gmtime(&tmvl.tv_sec);        // преобразовавыем его в структуру
  sprintf (filename, "%s/%4.4d%2.2d%2.2d%2.2d%2.2d.mvs",
      dir,
      gmt.tm_year + 1900,
      gmt.tm_mon + 1,
      gmt.tm_mday, gmt.tm_hour,gmt.tm_min);    // создаем имя файла с ткущей датой формат ГГГГММДДЧЧММ.mvs
  
  filedata=fopen(filename,"w");
  if(!filedata) fprintf(filelog,"Cant create data file with name %s! Time %s\n",filename,ctime(&ftmcreated));
     
  ftmcreated=tmvl.tv_sec;
  
  
  dmn=daemon(0,1);
  if(dmn<0){
    printf("MVS daemon not started!\n");
    fprintf(filelog,"MVS daemon not started! Time %s\n",devname,ctime(&ftmcreated));
    
    exit(1);
  }
  printf("MVS DAEMON STARTED\n Press ENTER\n");
  if( (fd = opendevice(devname, speed)) < 0) {
     fprintf(stderr,"FATAL ERROR: Can not open device %s\n",devname);
     fprintf(filelog,"FATAL ERROR: Can not open device %s. Time %s\n",devname,ctime(&ftmcreated));
     exit (1);
  }
 
 
  FILE *raw;
  raw=fopen("/station/data/raw.bin","w+b"); //debug
  FILE *dev;
  dev=fopen(devname,"r+b");
  while (1){
      chs=fgetc(dev);
      //fwrite(&chs,sizeof(char),1,raw);
      if((chs == 'C' ) && (indx == 0 )) {data[0]=chs; indx++;}    // sync 'C'
      if((chs == 'A' ) && (indx == 1 )) {data[1]=chs; indx++;}    // sync 'A'
      if((chs == 'D' ) && (indx == 2 )) {data[2]=chs; indx++;}    // sync 'D'
      if((chs == 'R' ) && (indx == 3 )) {data[3]=chs; indx++;}    // sync 'R'
      if((chs == 0x0 ) && (indx == 4 )) {data[4]=chs; indx++;}    // sync '0' channel 0
      if((data[0]=='C')&&(data[1]=='A')&&(data[2]=='D')&&(data[3]=='R')&&(data[4]==0x0)&&(indx>4)){
    data[indx-1] = chs;
    indx++;
    if((data[0]=='C')&&(data[1]=='A')&&(data[2]=='D')&&(data[3]=='R')&&(data[4]==0x0)&&(data[8]==0x1)&&(data[12]==0x2)&&(indx==17)){ // verify channel number
      //printf("INDX %d DATA %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x \n",indx,data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10],data[11],data[12],data[13],data[14],data[15]);
      crtfile(data,indx); - функция обработки полученных данных, в данном случае она не важна
      indx=0;
    }
    else{
      if(indx==17){
        //fprintf(filelog,"Sync corrupt CADR has %s\n",data);
        fwrite(data,sizeof(data),1,raw);
      }
    }
      }
  }
  
fclose (dev);
fclose (raw);
fclose (filedata);
fclose (filelog);
return 0;
}


у меня было что-то похожее, когда udev подключал и регистрировал переходник. сначала тоже всякие глюки, потом нормально. попробуй на обычном rs-232 порте

registrant ★★★★★
()

У меня знакомый в одном из проектов на такое же натолкнулся.
Лечилось установкой таймаута после подключения девайса.
Подробностей не помню.

trex6 ★★★★★
()
Ответ на: комментарий от trex6

Таймаут то это понятно. Только вот устройство при перезагрузке/загрузке никто не отключает физически, оно всегда подлкючено к компьютеру. Если есть какие то идеи куда вставить таймаут, то с удовольствием их выслушаю.

TITO
() автор топика
Ответ на: комментарий от TITO

Там была проблема на старте системы.
ОС опрашивала порты или что-то такое. Если уточню подробности - отпишусь.

trex6 ★★★★★
()

Странно не то, что она иногда глючит, а то, что она иногда работает, как часы. Ибо на таком паровозе никто тебе не обязан доставлять байтики с точностью до миллисекунды.

Даже если бы ты работал в однозадачной системе на нормальном сом-порту и непосредственно из ассемблера дергал бы порты ввода-вывода, даже и тогда гарантии не было бы.

В твоем случае нельзя закладываться на то, что пакеты всегда будут приходить без разрывов и слипания.

ansky ★★★★★
()

Вероятно ты просто получаешь мусор при включении/инициализации (дефолтный бодрейт не совпадает с тем, что нужен тебе, до момента конфигурации вполне могут прочитаться невалидные данные).

Попробуй игнорировать первые ~100байт и потом уже искать границы пакетов.

anonymous
()

Ты про оператор switch & конечные автоматы слышал? Переписал бы этот быдлокод на что-то вменяемое.

ky-san
()

Вообще интересный подход. По такому алгоритму должны проходить блоки типа «CCCCCCCAAAAAAAADDDDDDRRRRR\0».

pathfinder ★★★★
()

Может я скажу глупое капитанство, но вопросу написания строгого парсера стоит уделить достаточно внимания. Если мы читаем что-то из COM-порта, то надо быть готовым к тому, что байтовый поток может содержать любой мусор и из этого мусора надо правильно находить и извлекать пакеты. Совет пропустить первые 100 байт я считаю глупым, никто не застрахован от того, что 101-й байт будет тоже битым.

От парсера требуется две вещи:

1. Пропускать неправильные пакеты.

2. Не терять правильные пакеты.

Второе требование не менее важное, как и первое. Рассмотрим пример. Пускай у нас кадр имеет структуру 'C','A','D','R','\0',[8 байт данных],[контрольная сумма]

Пускай мы получаем пакет вида:

«CADR\0»,«QWERCADR»,'\0' <--- контрольная сумма неправильная

Если выкинуть весь блок считанных байт и начать искать пакет в ещё не прочитанных из порта данных, то можно упустить нормальный пакет, который был на половину считан на предыдущем шаге. Видно, что в примере часть 8 байт данных битого пакета на самом деле содержит стартовую последовательность другого пакета. Такие ситуации надо корректно отрабатывать.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

В моем случае не используется никакой контрольной суммы, к сожалению, так что надо успевать как то ловить все данные которые приходят и потом уже вытаскивать тогда из них. Еще по другому переписал программу, но что то она вообще себя странно ведет,программа стартует и без проблем, то есть триггеры на ошибки не срабатывают (функция open_device), но данные не читаются и не пишутся. Main такая же как в предыдущим случае, но возвращенный дескриптор на ком порт (функция open_device) я передаю функции read_data для чтения.

Хотел бы узнать чем отличается чтение с ком-порта таким образом и вышеприведенным.

void read_data(int fd)
{

    int i,n;
    char  nBytes,indx=0;

    unsigned char chs[16], data[16];
    int timeout = 30000;  // 3seconds
    struct timespec ns;
	
    while((timeout > 0)&&(!stop))
    {
      nBytes = read(fd, chs, 16);
      if(nBytes > 0)
      {
       for(i=0;i<nBytes;i++)
	{

	    if((chs[i] == 'C' ) && (indx == 0 )) {data[0]=chs[i]; indx++;}
	    if((chs[i] == 'A' ) && (indx == 1 )) {data[1]=chs[i]; indx++;}
	    if((chs[i] == 'D' ) && (indx == 2 )) {data[2]=chs[i]; indx++;}
	    if((chs[i] == 'R' ) && (indx == 3 )) {data[3]=chs[i]; indx++;}
	    if((chs[i] == 0x0 ) && (indx == 4 )) {data[4]=chs[i]; indx++;}
	    if((data[0]=='C')&&(data[1]=='A')&&(data[2]=='D')&&(data[3]=='R')&&(indx>4)){ // sync CADR
	      data[indx-1]=chs[i];
	      
	      indx++;
	      	      
	      if((data[0]=='C')&&(data[1]=='A')&&(data[2]=='D')&&(data[3]=='R')&&(data[4]==0x0)&&(data[8]==0x1)&&(data[12]==0x2)&&(indx==17)){ // verify channel number
		
		crtfile(data,indx);
		indx=0;
	      }
	      else{
		if(indx==17){
		  
		  fwrite(data,sizeof(data),1,raw);
		}
	      }
	    }
	}
        timeout = 30000;
        
     } 
     ns.tv_sec = 0;
     ns.tv_nsec = 3000;
     nanosleep(&ns, NULL);
     timeout--;
    }
    if(timeout<1){
      (void) gettimeofday (&tmvl, NULL);	// получаем текущее время и дату в секундах от начала эпохи
      printf("Timeout! %s\n",ctime(&tmvl.tv_sec));
      fprintf(filelog,"Timeout! %s\n",ctime(&tmvl.tv_sec));	//
      fclose(filedata);
      fclose(filelog);
      fclose(raw);
      exit(1);
    }
}

TITO
() автор топика
Ответ на: комментарий от pathfinder

Блоки такого вида не проходят, так как паралельно стоит еще одна система сбора, которая читает с того же девайса, и мои данные и ее получаются абсолютно идентичные. (Почем 2 системы, потому что моя заменит старую, ибо ушел разработчик и поддерживать некому)

TITO
() автор топика
Ответ на: комментарий от pathfinder

Какое громкое слово «парсер»:

int handlePacket(char data[6]) 
{
    printf("data: %s\n", data);
}


// CADR1234<EOF>
// states C -> A -> D -> R -> 1 -> 2 -> 3 -> 4 -> reset;
//        0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> reset;


#define EXPECT(c) do { if( ch == c ) ++state; else state = 0; } while(0)

int handleChar(int ch)
{
    static int state = 0;
    static char data[5];
    static int data_idx;

    switch(state) {
        case 0:
            EXPECT('C');
            break;

        case 1:
            EXPECT('A');
            break;

        case 2:
            EXPECT('D');
            break;

        case 3:
            EXPECT('R');
            break;

        case 4:
        case 5:
        case 6:
            data[data_idx++] = ch;
            ++state;
            break;

        case 7:
            data[data_idx] = ch;
            handlePacket(data);
            state = data_idx = 0;
            break;

        default:
            state = 0;
    }
}

int
main()
{
    const char* d = "CADR222CADR45CADR445CADRCADRCADR444CAADR122312CADR34234CADR2222222CADR444CAD@#$CADR4234CADR133445566CXAD56CA3CADR12313";
    while(*d) {
        handleChar(*d++);
    }
    return 0;
}
anonymous
()
Ответ на: комментарий от anonymous

Какое громкое слово «парсер»:

Неплохо, неплохо, анонимус. Вот тебе более сложная задачка. Идут пакеты в виде:

[0xAA],[N],[DATA],[CHECKSUM]

0xAA - стартовый байт

N - 1 байт, содержащий размер данных

DATA - N байт с данными

CHECKSUM - 1 байт контрольной суммы (пускай простое сложение всех байт N и DATA)

Пример: 0xAA,0x03,0x05,0x06,0x07,0x15

Слабо анонимусу сделать? :)

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

я хоть и не тот анонимус, кому это адрессовано, но всё же...

#include <stdio.h>
#include <stdlib.h>

unsigned char data[] = {
        0xAA, 0x03, 0x05, 0x06, 0x07, 0x15,
        0xAA, 0x02, 0x05, 0x07, 0x0d,   /* wrong cksum! */
        0xAA, 0x04, 0x05, 0x06, 0x07, 0x08, 0x1e,
};

enum { INIT, SIZE, DATA, CKSUM };

struct data {
        unsigned char *data;
        size_t size;
        struct data *next;
} *p, **pp;

void
parse(unsigned char c)
{
        static int state = INIT;
        static int n = 0;
        static unsigned char sum = 0;

        switch (state) {
        case INIT:
                if (c == 0xAA)
                        state = SIZE;
                break;
        case SIZE:
                n = c;
                sum = c;
                *pp = malloc(sizeof(struct data));
                (*pp)->data = malloc(n);
                (*pp)->size = n;
                (*pp)->next = NULL;
                state = DATA;
                break;
        case DATA:
                (*pp)->data[(*pp)->size - n--] = c;
                sum += c;
                if (!n)
                        state = CKSUM;
                break;
        case CKSUM:
                if (c == sum)
                        pp = &(*pp)->next;
                else {
                        free((*pp)->data);
                        free(*pp);
                        *pp = NULL;
                }
                state = INIT;
                break;
        }
}

int
main()
{
        int i;

        p = NULL;
        pp = &p;

        for (i = 0; i < sizeof(data); i++)
                parse(data[i]);

        for (; p; p = p->next) {
                printf("data: ");
                for (i = 0; i < p->size; i++)
                        printf("%.2x", p->data[i]);
                printf("\n");
        }

        return 0;
}
beastie ★★★★★
()
Ответ на: комментарий от beastie

Концепция конечного автомата аккуратно перенесена с примера анонимуса на другой формат пакета. Но в данном случае возможны пропуски правильных кадров при неудачном стечении обстоятельств.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

а всех вариантов ИМО и не отловиш. представь себе следующий пакет данных:

0xAA, 0x03, 0xAA, 0x00, 0x00, 0xAD

валидный пакет в валидном пакете — и как тут решить верно это или ошибка?

beastie ★★★★★
()
Ответ на: комментарий от beastie

а всех вариантов ИМО и не отловиш.

Это верно. Но если говорить о симплексных протоколах, когда постоянно идет передача в одну сторону, то там нередко может возникнуть проблема вечной рассинхронизации. Это довольно распространенная ошибка реализации протокола, когда уже считав часть данных, мы понимаем что не поймали начало и потом выбрасываем весь блок прочитанных данных. В таких случаях лучше сдвинуться на один байт и попробовать снова проанализировать принятые данные на предмет наличия нормального кадра.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

> Концепция конечного автомата аккуратно перенесена с примера анонимуса на другой формат пакета.

Неаккуратно. Посмотри, здесь данные накапливаются во внешних/глобальных объектах, работа со списком заинтегрированна в функцию обработки. Нужно разделять mechanism & policy

anonymous
()
Ответ на: комментарий от anonymous

Неаккуратно. Посмотри, здесь данные накапливаются во внешних/глобальных объектах, работа со списком заинтегрированна в функцию обработки. Нужно разделять mechanism & policy

Ну это все-таки не продакшн код, тут так можно. Мне вот например глаза неприятно режет malloc/free. Но это не важно, главное чтоб основная идея была видна.

pathfinder ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.