Belle II Software  release-08-01-10
DesSerCOPPER.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <daq/rawdata/modules/copper.h>
10 #include <daq/rawdata/DesSerCOPPER.h>
11 
12 #include <fcntl.h>
13 #include <sys/ioctl.h>
14 
15 using namespace std;
16 using namespace Belle2;
17 
18 
19 
20 //----------------------------------------------------------------
21 // Implementation
22 //----------------------------------------------------------------
23 
24 DesSerCOPPER::DesSerCOPPER(string host_recv, int port_recv, const string& host_send, int port_send, int shmflag,
25  const std::string& nodename, int nodeid, int finesse_bitflag)
26 {
27  m_finesse_bit_flag = finesse_bitflag;
28 
29  for (int i = 0 ; i < m_num_connections; i++) {
30  // m_hostname_from.push_back( "localhost");
31  m_hostname_from.push_back(host_recv);
32  // m_port_from.push_back(30000);
33  m_port_from.push_back(port_recv) ;
34  m_socket_recv.push_back(-1);
35  }
36 
37  // m_port_to = 31001;
38  m_port_to = port_send;
39  // m_hostname_local = "localhost";
40  m_hostname_local = host_send;
41  m_nodename = nodename;
42 
43  m_shmflag = shmflag;
44 
45  m_nodeid = nodeid;
46 
47  // B2INFO("DeSerializerPrePC: Constructor done.");
48  printf("[DEBUG] DeSerializerPrePC: Constructor done.\n"); fflush(stdout);
49 }
50 
51 
52 
53 DesSerCOPPER::~DesSerCOPPER()
54 {
55 }
56 
57 void DesSerCOPPER::DataAcquisition()
58 {
59  // For data check
60  // B2INFO("initializing...");
61  printf("[DEBUG] initializing...\n"); fflush(stdout);
62  initialize(false);
63 
64 
65  // B2INFO("Done.");
66  printf("[DEBUG] Done.\n"); fflush(stdout);
67 
68  unsigned int m_prev_ftsweve32 = 0xffffffff;
69 
70  if (m_start_flag == 0) {
71  //
72  // Connect to eb0: This should be here because we want Serializer first to accept connection from eb1tx
73  //
74  initializeCOPPER();
75  // Connect();
76 
77  if (m_status.isAvailable()) {
78  // B2INFO("DeSerializerPrePC: Waiting for Start...\n");
79  printf("[DEBUG] DeSerializerPrePC: Waiting for Start...\n"); fflush(stdout);
80  m_status.reportRunning();
81  }
82  m_start_time = getTimeSec();
83  n_basf2evt = 0;
84  }
85 
86  //
87  // Main loop
88  //
89  while (1) {
90  //
91  // Stand-by loop
92  //
93 #ifdef NONSTOP
94  if (m_run_pause > 0 || m_run_error > 0) {
95  waitResume();
96  }
97 #endif
98 
99  clearNumUsedBuf();
100  // RawDataBlock raw_datablk[ NUM_EVT_PER_BASF2LOOP_PC ];
101  RawDataBlockFormat raw_datablk[ NUM_EVT_PER_BASF2LOOP_PC ];
102 
103 
104  //
105  // Recv loop
106  //
107  for (int j = 0; j < NUM_EVT_PER_BASF2LOOP_PC; j++) {
108  //
109  // Receive data from COPPER
110  //
111  //int delete_flag_from =
112  //0; // Delete flag for temp_rawdatablk.It can be set to 1 by setRecvdBuffer if the buffer size is larger than that of pre-allocated buffer.
113  //int delete_flag_to =
114  //0; // Delete flag for raw_datablk[i]. It can be set to 1 by getNewBuffer if the buffer size is larger than that of pre-allocated buffer.
115  RawDataBlockFormat temp_rawdatablk;
116  try {
117 
118  int delete_flag = 0, m_size_word = 0;
119 
120  if (m_start_flag == 0) {
121  printf("[DEBUG] DeSerializerCOPPER: Reading the 1st event from COPPER FIFO... run %.8x eve %.8x\n", m_prev_exprunsubrun_no,
122  m_prev_ftsweve32); fflush(stdout);
123  }
124  int* temp_buf;
125 
126  temp_buf = readOneEventFromCOPPERFIFO(j, &delete_flag, &m_size_word);
127  m_status.copyEventHeader(temp_buf);
128  if (m_start_flag == 0) {
129  printf("[DEBUG] DeSerializerCOPPER: Done. the size of the 1st event is %d words\n", m_size_word); fflush(stdout);
130  }
131  const int num_nodes = 1;
132  const int num_events = 1;
133  PreRawCOPPERFormat_v2 temp_rawcopper;
134  temp_rawcopper.SetBuffer(temp_buf, m_size_word, 0, num_events, num_nodes);
135  // Fill header and trailer
136  try {
137  m_prev_ftsweve32 = temp_rawcopper.FillTopBlockRawHeader(m_nodeid, m_prev_ftsweve32, m_prev_exprunsubrun_no, &m_exprunsubrun_no);
138  m_prev_exprunsubrun_no = m_exprunsubrun_no;
139  // fillNewRawCOPPERHeader( &temp_rawcopper );
140  } catch (string err_str) {
141  print_err.PrintError(m_shmflag, &m_status, err_str);
142  exit(1);
143  }
144 
145 // if (m_dump_fname.size() > 0) {
146 // dumpData((char*)temp_buf, m_size_word * sizeof(int));
147 // }
148  m_recvd_totbytes += m_size_word * sizeof(int);
149 
150  // temp_rawdblk = raw_dblkarray.appendNew();
151  // temp_rawdatablk->SetBuffer(temp_buf, m_size_word, delete_flag, num_events, num_nodes);
152  raw_datablk[ j ].SetBuffer(temp_buf, m_size_word, delete_flag, num_events, num_nodes);
153 
154  } catch (string err_str) {
155  printf("Error was detected\n"); fflush(stdout);
156  break;
157  }
158  }
159 
160 #ifdef NONSTOP
161  // Goto Stand-by loop when run is paused or stopped by error
162  if (m_run_pause != 0 || m_run_error != 0) continue;
163 #endif
164 
165 
167  // From Serializer.cc
169  if (m_start_flag == 0) {
170  m_start_time = getTimeSec();
171  n_basf2evt = 0;
172  }
173 
174  //
175  // Send data
176  //
177  for (int j = 0; j < NUM_EVT_PER_BASF2LOOP_PC; j++) {
178  if (m_start_flag == 0) {
179  // B2INFO("SerializerPC: Sending the 1st packet...");
180  printf("[DEBUG] SerializerPC: Sending the 1st packet...\n"); fflush(stdout);
181  }
182  try {
183  m_sent_totbytes += sendByWriteV(&(raw_datablk[ j ]));
184  } catch (string err_str) {
185 #ifdef NONSTOP
186  break;
187 #endif
188  print_err.PrintError((char*)err_str.c_str(), __FILE__, __PRETTY_FUNCTION__, __LINE__);
189  exit(1);
190  }
191  if (m_start_flag == 0) {
192  // B2INFO("Done. ");
193  printf("[DEBUG] Done.\n"); fflush(stdout);
194  m_start_flag = 1;
195  }
196  }
197 
198 #ifdef NONSTOP
199  // Goto Stand-by loop when run is paused or stopped by error
200  if (m_run_pause != 0 || m_run_error != 0) continue;
201 #endif
202 
203  //
204  // Monitor
205  //
206  if (max_nevt >= 0 || max_seconds >= 0.) {
207 #ifdef AIUEO
208  if (n_basf2evt % 10000 == 0) {
209 // if ((n_basf2evt * NUM_EVT_PER_BASF2LOOP_PC >= max_nevt && max_nevt > 0)
210 // || (getTimeSec() - m_start_time > max_seconds && max_seconds > 0.)) {
211  printf("[DEBUG] RunStop was detected. ( Setting: Max event # %d MaxTime %lf ) Processed Event %d Elapsed Time %lf[s]\n",
212  max_nevt, max_seconds, n_basf2evt * NUM_EVT_PER_BASF2LOOP_PC, getTimeSec() - m_start_time);
213  }
214 #endif
215  }
216 
217  if ((n_basf2evt * NUM_EVT_PER_BASF2LOOP_PC) % 100000 == 0) {
218  double interval = cur_time - m_prev_time;
219  double total_time = cur_time - m_start_time;
220  printf("[DEBUG] Event %12d Rate %6.2lf[kHz] Recvd %6.2lf[MB/s] sent %6.2lf[MB/s] RunTime %8.2lf[s] interval %8.4lf[s]\n",
221  n_basf2evt * NUM_EVT_PER_BASF2LOOP_PC,
222  (n_basf2evt - m_prev_nevt)*NUM_EVT_PER_BASF2LOOP_PC / interval / 1.e3,
223  (m_recvd_totbytes - m_recvd_prev_totbytes) / interval / 1.e6,
224  (m_sent_totbytes - m_sent_prev_totbytes) / interval / 1.e6,
225  total_time,
226  interval);
227  fflush(stdout);
228 
229  m_prev_time = cur_time;
230  m_recvd_prev_totbytes = m_recvd_totbytes;
231  m_sent_prev_totbytes = m_sent_totbytes;
232  m_prev_nevt = n_basf2evt;
233  cur_time = getTimeSec();
234  }
235 
236  n_basf2evt++;
237 
238  if (m_status.isAvailable()) {
239  m_status.setOutputNBytes(m_sent_totbytes);
240  m_status.setOutputCount(n_basf2evt * NUM_EVT_PER_BASF2LOOP_PC);
241  }
242 
243  }
244 
245  return;
246 }
247 
249 // From Serializer.cc
251 
252 #ifdef NONSTOP
253 void DesSerCOPPER::waitResume()
254 {
255  if (m_run_pause == 0) {
256  while (true) {
257  if (checkRunPause()) break;
258 #ifdef NONSTOP_DEBUG
259  printf("\033[31m");
260  printf("###########(DesSerCOPPER) Waiting for Runstop() ###############\n");
261  fflush(stdout);
262  printf("\033[0m");
263 #endif
264  sleep(1);
265  }
266  }
267 
268  // close COPPER FIFO
269  if (m_cpr_fd != -1) close(m_cpr_fd);
270  m_cpr_fd = -1;
271 
272  while (true) {
273 #ifdef NONSTOP_DEBUG
274  printf("\033[31m");
275  printf("###########(Ser) Waiting for Resume ###############\n");
276  fflush(stdout);
277  printf("\033[0m");
278 #endif
279  if (checkRunRecovery()) {
280  m_run_pause = 0;
281  m_run_error = 0;
282 
283  break;
284  }
285  sleep(1);
286  }
287 
288  printf("Done!\n"); fflush(stdout);
289 
290  if (CheckConnection(m_socket_send) < 0) {
291  printf("Trying Accept1\n"); fflush(stdout);
292  Accept(false);
293  printf("Trying Accept2\n"); fflush(stdout);
294  }
295 
296  resumeRun();
297  return;
298 }
299 
300 
301 
302 void DesSerCOPPER::resumeRun()
303 {
304  initializeCOPPER();
305 #ifdef NONSTOP_DEBUG
306  printf("\033[34m");
307  printf("###########(Ser) the 1st event sicne the resume ###############\n");
308  fflush(stdout);
309  printf("\033[0m");
310 #endif
311  m_run_error = 0;
312  m_run_pause = 0;
313  return;
314 }
315 
316 
317 
318 #endif
319 
320 
321 
322 int* DesSerCOPPER::readOneEventFromCOPPERFIFO(const int entry, int* delete_flag, int* m_size_word)
323 {
324 
325  // prepare buffer
326  *m_size_word = 0;
327  int* temp_buf = m_bufary[ entry ];
328  temp_buf[0] = BUF_SIZE_WORD ;
329  *delete_flag = 0;
330 
331  //
332  // Read data from HSLB
333  //
334 #ifndef DUMMY
335  int recvd_byte = (m_pre_rawcpr.tmp_header.RAWHEADER_NWORDS) * sizeof(int);
336  // Firstly, read data with an allocated buffer.
337  while (1) {
338  int read_size = 0;
339  if ((read_size = read(m_cpr_fd, (char*)m_bufary[entry] + recvd_byte, sizeof(int) * BUF_SIZE_WORD - recvd_byte)) < 0) {
340  if (errno == EINTR) {
341  continue;
342  } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
343 
344  if (recvd_byte > (int)((m_pre_rawcpr.tmp_header.RAWHEADER_NWORDS) * sizeof(int))) {
345  char err_buf[500];
346  sprintf(err_buf, "[FATAL] EAGAIN return in the middle of an event( COPPER driver should't do this.). Exting...");
347  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
348  exit(-1);
349  }
350 
351 #ifdef NONSTOP
352  // Check run-pause request from SLC
353  string err_str;
354  callCheckRunPause(err_str);
355 #endif
356  continue;
357 
358  } else {
359  char err_buf[500];
360  sprintf(err_buf, "[FATAL] Failed to read data from COPPER. Exiting...");
361  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
362  exit(-1);
363  }
364  } else {
365  recvd_byte += read_size;
366  if (recvd_byte - (m_pre_rawcpr.tmp_header.RAWHEADER_NWORDS) * sizeof(int) > (int)(sizeof(int) *
367  (m_pre_rawcpr.POS_DATA_LENGTH + 1)))break;
368  }
369  }
370 
371  //
372  // Calcurate data size
373  //
374  *m_size_word = m_bufary[ entry ][ m_pre_rawcpr.POS_DATA_LENGTH + (m_pre_rawcpr.tmp_header.RAWHEADER_NWORDS) ]
375  + m_pre_rawcpr.SIZE_COPPER_DRIVER_HEADER + m_pre_rawcpr.SIZE_COPPER_DRIVER_TRAILER
376  + m_pre_rawcpr.tmp_header.RAWHEADER_NWORDS +
377  m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS; // 9 words are COPPER haeder and trailer size.
378 
379  //
380  // If there are data remaining to be read, continue reading
381  //
382  if ((int)((*m_size_word - m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS) * sizeof(int)) > recvd_byte) {
383 
384  // If event size is larger than BUF_SIZE_WORD, allocate a new buffer
385  if (*m_size_word > BUF_SIZE_WORD) {
386  *delete_flag = 1;
387  temp_buf = new int[ *m_size_word ];
388  memcpy(temp_buf, m_bufary[ entry ], recvd_byte);
389  recvd_byte += readFD(m_cpr_fd, (char*)temp_buf + recvd_byte,
390  (*m_size_word - m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS) * sizeof(int) - recvd_byte, *delete_flag);
391  } else {
392 
393  recvd_byte += readFD(m_cpr_fd, (char*)(m_bufary[ entry ]) + recvd_byte,
394  (*m_size_word - m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS) * sizeof(int) - recvd_byte, *delete_flag);
395  }
396 
397  if ((int)((*m_size_word - m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS) * sizeof(int)) != recvd_byte) {
398  char err_buf[500];
399 
400  sprintf(err_buf, "[FATAL] CORRUPTED DATA: Read less bytes(%d) than expected(%lu:%d). Exiting...\n",
401  recvd_byte,
402  *m_size_word * sizeof(int) - m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS * sizeof(int),
403  m_bufary[ entry ][ m_pre_rawcpr.POS_DATA_LENGTH ]);
404  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
405  exit(-1);
406  }
407  } else if ((int)((*m_size_word - m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS) * sizeof(int)) < recvd_byte) {
408  char err_buf[500];
409  sprintf(err_buf, "[FATAL] CORRUPTED DATA: Read more than data size. Exiting...: %d %lu %lu %d %d\n",
410  recvd_byte, *m_size_word * sizeof(int), m_pre_rawcpr.tmp_trailer.RAWTRAILER_NWORDS * sizeof(int),
411  m_bufary[ entry ][ m_pre_rawcpr.POS_DATA_LENGTH ], m_pre_rawcpr.POS_DATA_LENGTH);
412  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
413  exit(-1);
414  }
415 #else
416  //
417  // Make dummy data
418  //
419  *m_size_word = 256 + entry;
420  m_bufary[entry][0] = *m_size_word;
421 #endif
422 
423  //
424  // Fill Data length
425  //
426  temp_buf[ 0 ] = *m_size_word;
427 
428 
429 #ifdef TIME_MONITOR
430  if (n_basf2evt >= 50000 && n_basf2evt < 50500) {
431  cur_time = getTimeSec();
432  time_array2[ n_basf2evt - 50000 ] = cur_time - m_start_time;
433  }
434 #endif
435 
436 #ifdef CHECK_SUM
437  unsigned int checksum = 0;
438  for (int i = 0; i < m_bufary[entry][0]; i++) {
439  if (i != 2) checksum += m_bufary[entry][i];
440  }
441  m_bufary[entry][2] = checksum;
442 #endif
443  return temp_buf;
444 }
445 
446 
447 
448 void DesSerCOPPER::initializeCOPPER()
449 {
450 
451 #ifndef DUMMY
452  m_use_slot = 0; /* bit mask */
453  int slot_shift;
454 
455  if ((m_finesse_bit_flag & 0x1) == 1) {
456  slot_shift = 0; // a:0, b:1, c:2, d:3
457  m_use_slot |= 1 << slot_shift; //
458  }
459 
460  if (((m_finesse_bit_flag >> 1) & 0x1) == 1) {
461  slot_shift = 1; // a:0, b:1, c:2, d:3
462  m_use_slot |= 1 << slot_shift; //
463  }
464 
465  if (((m_finesse_bit_flag >> 2) & 0x1) == 1) {
466  slot_shift = 2; // a:0, b:1, c:2, d:3
467  m_use_slot |= 1 << slot_shift; //
468  }
469 
470  if (((m_finesse_bit_flag >> 3) & 0x1) == 1) {
471  slot_shift = 3; // a:0, b:1, c:2, d:3
472  m_use_slot |= 1 << slot_shift; //
473  }
474  //
475  // Present slots to use
476  //
477  if (! m_use_slot) {
478  char err_buf[100] = "[FATAL] Slot is not specified. Exiting...";
479  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
480  exit(1);
481  } else {
482  int slot;
483  printf("[DEBUG] ");
484  for (slot = 0; slot < 4; slot++) {
485  if (m_use_slot & (1 << slot)) printf(" %c", 'A' + slot);
486  }
487  printf("\n");
488  }
489 
490 
491 #endif
492 
493 #ifndef YAMADA_DUMMY
494  // B2INFO("Opening COPPER..."); fflush(stderr);
495  printf("[DEBUG] Opening COPPER...\n"); fflush(stdout);
496  openCOPPER();
497  // B2INFO("Done.\n"); fflush(stderr);
498  printf("[DEBUG] Done.\n"); fflush(stdout);
499 #endif
500 }
501 
502 
503 void DesSerCOPPER::openCOPPER()
504 {
505 
506  if (m_cpr_fd != -1) {
507  close(m_cpr_fd);
508  m_cpr_fd = -1;
509  }
510  //
511  // Open a finesse device
512  //
513  if ((m_cpr_fd = open("/dev/copper/copper", O_RDONLY)) == -1) {
514  char err_buf[500];
515  sprintf(err_buf, "[FATAL] Failed to open /dev/copper/copper. Exiting... ");
516  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
517  exit(1);
518  }
519 
520  int set_regval = 15; // How many events to be stored in COPPER FIFO before request for DMA
521  // int set_regval=1;
522  ioctl(m_cpr_fd, CPRIOSET_LEF_WA_FF, &set_regval);
523  ioctl(m_cpr_fd, CPRIOSET_LEF_WB_FF, &set_regval);
524  ioctl(m_cpr_fd, CPRIOSET_LEF_WC_FF, &set_regval);
525  ioctl(m_cpr_fd, CPRIOSET_LEF_WD_FF, &set_regval);
526  ioctl(m_cpr_fd, CPRIOSET_FINESSE_STA, &m_use_slot, sizeof(m_use_slot));
527 
528  int v = 511 - 32;
529 
530  ioctl(m_cpr_fd, CPRIOSET_LEF_WA_AF, &v, sizeof(v));
531  ioctl(m_cpr_fd, CPRIOSET_LEF_WB_AF, &v, sizeof(v));
532  ioctl(m_cpr_fd, CPRIOSET_LEF_WC_AF, &v, sizeof(v));
533  ioctl(m_cpr_fd, CPRIOSET_LEF_WD_AF, &v, sizeof(v));
534 
535 
536  // B2INFO("DeSerializerCOPPER: openCOPPER() done.");
537  printf("[DEBUG] DeSerializerCOPPER: openCOPPER() done.\n"); fflush(stdout);
538 
539 }
540 
541 
542 
543 int DesSerCOPPER::readFD(int fd, char* buf, int data_size_byte, int /*delete_flag*/)
544 {
545 
546  int n = 0;
547  while (1) {
548  int read_size = 0;
549  if ((read_size = read(fd, (char*)buf + n, data_size_byte - n)) < 0) {
550  if (errno == EINTR) {
551  continue;
552  } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
553  if (n > 0) {
554  char err_buf[500];
555  sprintf(err_buf, "[FATAL] Return due to EAGAIN in the middle of an event( COPPER driver would't do this.). Exting...");
556  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
557  exit(-1);
558  }
559 #ifdef NONSTOP
560  // Check run-pause request from SLC
561  string err_str;
562  try {
563  callCheckRunPause(err_str);
564  } catch (string err_str) {
565  if (delete_flag) {
566  // Delete allocated buffer
567  // B2WARNING("Delete buffer before going to Run-pause state");
568  printf("[WARNING] Delete buffer before going to Run-pause state\n"); fflush(stdout);
569  delete buf;
570  }
571  throw (err_str);
572  }
573 #endif
574  continue;
575  } else {
576 #ifdef NONSTOP
577  m_run_error = 1;
578  // B2ERROR(err_buf);
579  printf("[ERROR] Failed to read data from COPPER. %s %s %d",
580  __FILE__, __PRETTY_FUNCTION__, __LINE__);
581  string err_str = "RUN_ERROR";
582  throw (err_str);
583 #endif
584  char err_buf[500];
585  sprintf(err_buf, "[FATAL] Failed to read data from COPPER. %s %s %d",
586  __FILE__, __PRETTY_FUNCTION__, __LINE__);
587  print_err.PrintError(m_shmflag, &m_status, err_buf, __FILE__, __PRETTY_FUNCTION__, __LINE__);
588  exit(-1);
589  }
590  } else {
591  n += read_size;
592  if (n == data_size_byte)break;
593  }
594  }
595  return n;
596 }
The Raw COPPER class ver.1 ( the latest version since May, 2014 ) This class stores data received by ...
unsigned int FillTopBlockRawHeader(unsigned int m_node_id, unsigned int prev_eve32, unsigned int prev_exprunsubrun_no, unsigned int *cur_exprunsubrun_no) OVERRIDE_CPP17
should be called by DeSerializerCOPPER.cc and fill contents in RawHeader
The RawDataBlockFormat class Format information for rawdata handling.
virtual void SetBuffer(int *bufin, int nwords, int delete_flag, int num_events, int num_nodes)
set buffer ( delete_flag : m_buffer is freeed( = 0 )/ not freeed( = 1 ) in Destructer )
Abstract base class for different kinds of events.