-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParamReader.h
More file actions
552 lines (476 loc) · 16.4 KB
/
ParamReader.h
File metadata and controls
552 lines (476 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
#ifndef PARAMREADER_H
#define PARAMREADER_H
/*
* ParamReader.h
*
* This header defines the ParamReader class, which is responsible for parsing
* parameter files that map physical detector channels to logical element IDs.
*
* In particle physics experiments, detectors often have many physical channels
* (ADC, TDC, Scaler) that need to be mapped to logical positions in the detector
* geometry (e.g., crystal positions in a calorimeter). This class handles that
* mapping.
*
* Key design points:
* - Single-hit system: Each physical channel maps to exactly one logical element
* - Parameter files have "Element:" lines with channel information
* - Supports M-suffix notation (e.g., 100M0, 100M1) for multi-hit capability,
* though the system is designed for single-hit operation
*/
#include <cstdio> /* Standard C file operations */
#include <cstdlib> /* Memory allocation, exit functions */
#include <cstring> /* String manipulation */
#include <cctype> /* Character classification */
#include <limits> /* Numeric limits */
#include <algorithm> /* For std::max */
#include "UtHash.h" /* Hash table implementation */
/*
* Record structure: Stores the mapping for a single logical detector element
*
* Each logical element (e.g., a crystal in a calorimeter) has:
* - id: The logical ID (0, 1, 2, ...)
* - adc: Physical ADC channel number
* - tdc: Physical TDC channel number
* - scaler: Physical scaler channel number
* - adc_suffix, tdc_suffix, scaler_suffix: M-suffix values (0, 1, 2, ...)
* These indicate multiple hits on the same physical channel
*/
typedef struct {
int id;
int adc;
int tdc;
int scaler;
int adc_suffix;
int tdc_suffix;
int scaler_suffix;
} Record;
/*
* Hash entry structures: Used to create hash tables for fast lookups
*
* These structures allow us to quickly find the Record corresponding to
* a physical channel number. For example, given ADC channel 100, we can
* quickly find which logical element it belongs to.
*/
typedef struct {
int adc; /* Physical ADC channel number */
Record *rec; /* Pointer to the corresponding Record */
UT_hash_handle hh; /* Required by uthash library */
} ADCEntry;
typedef struct {
int tdc; /* Physical TDC channel number */
Record *rec; /* Pointer to the corresponding Record */
UT_hash_handle hh; /* Required by uthash library */
} TDCEntry;
typedef struct {
int scaler; /* Physical scaler channel number */
Record *rec; /* Pointer to the corresponding Record */
UT_hash_handle hh; /* Required by uthash library */
} ScalerEntry;
/*
* ParamReader class: Handles the parsing and mapping of parameter files
*
* This class:
* 1. Reads parameter files with "Element:" lines
* 2. Parses channel numbers and M-suffixes
* 3. Creates hash tables for fast physical->logical lookups
* 4. Provides methods for both physical->logical and logical->physical mapping
*
* Important note: This implementation is designed for single-hit operation,
* meaning each physical channel maps to exactly one logical element.
* If multiple "Element:" lines use the same physical channel, the last one wins.
*/
class ParamReader {
private:
/* Hash tables for fast lookups (physical channel -> Record) */
ADCEntry *_adc_hash;
TDCEntry *_tdc_hash;
ScalerEntry *_scaler_hash;
/* Array of all Records, indexed by logical ID */
Record **records;
int rec_cnt; /* Number of Records currently stored */
int rec_cap; /* Current capacity of the records array */
const char *filename; /* Name of the parameter file being processed */
/* Tracking the maximum M-suffix encountered for each channel type */
int max_adc_suffix;
int max_tdc_suffix;
int max_scaler_suffix;
/* Maximum hits per channel (from config file) */
int adc_maxhits;
int tdc_maxhits;
int scaler_maxhits;
/* Positions of ADC, TDC, Scaler values in parameter file lines */
int adc_pos;
int tdc_pos;
int scaler_pos;
bool _emptyFile; /* Flag indicating if no parameter file is used */
/* Helper function: Parse integer prefix from a token */
static int parseIntPrefix(const char *tok) {
/*
* This function extracts the numeric part from tokens like "100M0"
*
* Parameters:
* tok - Input token (e.g., "100M0")
*
* Returns:
* The integer prefix (e.g., 100) or -1 if parsing fails
*/
if (!tok) return -1;
char *endptr = nullptr;
long v = strtol(tok, &endptr, 10);
/* Check if we actually parsed a number */
if (endptr == tok) return -1;
/* Handle overflow/underflow */
if (v > std::numeric_limits<int>::max())
return std::numeric_limits<int>::max();
if (v < std::numeric_limits<int>::min())
return std::numeric_limits<int>::min();
return (int)v;
}
/* Helper function: Parse M-suffix from a token */
static int parseSuffix(const char *tok) {
/*
* This function extracts the M-suffix from tokens like "100M0"
*
* Parameters:
* tok - Input token (e.g., "100M0")
*
* Returns:
* The M-suffix value (e.g., 0) or -1 if no suffix is present
*/
if (!tok) return -1;
/* Find 'M' or 'm' in the token */
const char *m = strchr(tok, 'M');
if (!m) m = strchr(tok, 'm');
if (!m) return -1;
/* Parse the number after 'M' */
return parseIntPrefix(m + 1);
}
/* Helper function: Check if line starts with "Element:" */
static bool lineStartsWithElement(const char* line) {
/*
* This function checks if a line begins with "Element:" (case-insensitive)
*
* Parameters:
* line - Input line to check
*
* Returns:
* true if the line starts with "Element:", false otherwise
*/
if (!line) return false;
/* Skip leading whitespace */
while (*line && isspace((unsigned char)*line)) ++line;
/* Compare with "element:" (case-insensitive) */
const char* keyword = "element:";
for (int i = 0; keyword[i]; ++i) {
if (tolower((unsigned char)line[i]) != keyword[i]) return false;
}
return true;
}
/* Helper function: Ensure records array has enough capacity */
void ensureCapacity() {
/*
* This function ensures the records array can hold at least rec_cnt elements
*
* If the current capacity is insufficient, it doubles the capacity (or sets to 128 if empty)
*/
if (rec_cnt < rec_cap) return;
/* Calculate new capacity (double current or start at 128) */
int new_cap = rec_cap ? rec_cap * 2 : 128;
/* Allocate new array */
Record **new_arr = (Record**)malloc(new_cap * sizeof(Record*));
if (!new_arr) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
/* Copy existing records if any */
if (records && rec_cnt > 0)
memcpy(new_arr, records, rec_cnt * sizeof(Record*));
/* Clean up old array and update pointers */
free(records);
records = new_arr;
rec_cap = new_cap;
}
/*
* Helper function: Insert a Record into the hash tables
*
* For single-hit operation, if a physical channel already exists in the hash,
* we simply update its Record pointer (the last definition wins)
*/
bool insertIntoHashes(Record *r) {
/* Process ADC channel */
if (adc_pos > 0) {
ADCEntry *e = nullptr;
HASH_FIND_INT(_adc_hash, &r->adc, e);
if (e) {
/* Update existing entry (single-hit: last definition wins) */
e->rec = r;
}
else {
/* Create new entry */
e = (ADCEntry*)malloc(sizeof(ADCEntry));
if (!e) return false;
e->adc = r->adc;
e->rec = r;
HASH_ADD_INT(_adc_hash, adc, e);
}
}
/* Process TDC channel (same pattern as ADC) */
if (tdc_pos > 0) {
TDCEntry *e = nullptr;
HASH_FIND_INT(_tdc_hash, &r->tdc, e);
if (e) e->rec = r;
else {
e = (TDCEntry*)malloc(sizeof(TDCEntry));
if (!e) return false;
e->tdc = r->tdc;
e->rec = r;
HASH_ADD_INT(_tdc_hash, tdc, e);
}
}
/* Process Scaler channel (same pattern) */
if (scaler_pos > 0) {
ScalerEntry *e = nullptr;
HASH_FIND_INT(_scaler_hash, &r->scaler, e);
if (e) e->rec = r;
else {
e = (ScalerEntry*)malloc(sizeof(ScalerEntry));
if (!e) return false;
e->scaler = r->scaler;
e->rec = r;
HASH_ADD_INT(_scaler_hash, scaler, e);
}
}
return true;
}
/* Helper function: Free all hash tables */
void freeHashes() {
/*
* This function frees all memory associated with the hash tables
*
* It iterates through each hash table and frees all entries
*/
ADCEntry *ae, *tmp_ae;
HASH_ITER(hh, _adc_hash, ae, tmp_ae) { HASH_DEL(_adc_hash, ae); free(ae); }
_adc_hash = nullptr;
TDCEntry *te, *tmp_te;
HASH_ITER(hh, _tdc_hash, te, tmp_te) { HASH_DEL(_tdc_hash, te); free(te); }
_tdc_hash = nullptr;
ScalerEntry *se, *tmp_se;
HASH_ITER(hh, _scaler_hash, se, tmp_se) { HASH_DEL(_scaler_hash, se); free(se); }
_scaler_hash = nullptr;
}
/* Helper function: Clean up all resources */
void cleanup() {
/*
* This function frees all memory allocated by the ParamReader
*
* It's called during initialization, destruction, and error handling
* to ensure no memory leaks
*/
freeHashes();
/* Free all Records */
for (int i = 0; i < rec_cnt; ++i) free(records[i]);
/* Free the records array */
free(records);
/* Reset state */
records = nullptr;
rec_cnt = rec_cap = 0;
}
public:
/* Constructor: Initialize with default values */
ParamReader()
: _adc_hash(nullptr), _tdc_hash(nullptr), _scaler_hash(nullptr),
records(nullptr), rec_cnt(0), rec_cap(0), filename(nullptr),
max_adc_suffix(-1), max_tdc_suffix(-1), max_scaler_suffix(-1),
adc_maxhits(0), tdc_maxhits(0), scaler_maxhits(0),
adc_pos(0), tdc_pos(0), scaler_pos(0),
_emptyFile(true)
{
/* All initialization happens in the initializer list */
}
/*
* Initialize the ParamReader with configuration parameters
*
* Parameters:
* fname - Parameter file name (nullptr or empty for no file)
* adc_pos_, adc_maxhits_ - ADC position in file and max hits
* tdc_pos_, tdc_maxhits_ - TDC position in file and max hits
* scaler_pos_, scaler_maxhits_ - Scaler position and max hits
*/
void init(const char *fname,
int adc_pos_=0, int adc_maxhits_=0,
int tdc_pos_=0, int tdc_maxhits_=0,
int scaler_pos_=0, int scaler_maxhits_=0)
{
/* Clean up any previous state */
cleanup();
/* Set configuration parameters */
filename = fname;
max_adc_suffix = max_tdc_suffix = max_scaler_suffix = -1;
adc_maxhits = adc_maxhits_;
tdc_maxhits = tdc_maxhits_;
scaler_maxhits = scaler_maxhits_;
adc_pos = adc_pos_;
tdc_pos = tdc_pos_;
scaler_pos = scaler_pos_;
/* Mark as empty if no filename provided */
_emptyFile = (!fname || fname[0] == '\0');
}
/* Destructor: Clean up all resources */
~ParamReader() {
cleanup();
}
/*
* Read and parse the parameter file
*
* Returns:
* true if successful, false if there was an error
*/
bool readFile() {
/* Start with a clean state */
cleanup();
/* Handle empty file case */
if (!filename || filename[0] == '\0') {
_emptyFile = true;
return true;
}
/* Open the parameter file */
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("Error: cannot open \"%s\"\n", filename);
_emptyFile = false;
return false;
}
_emptyFile = false;
char line[1024];
int elementLine = 0; /* Counter for logical IDs */
/* Process each line in the file */
while (fgets(line, sizeof(line), fp)) {
/* Skip non-Element lines */
if (!lineStartsWithElement(line)) continue;
/* Skip the "Element:" token */
char *tok = strtok(line, " \t\r\n");
if (!tok) continue;
/* Parse tokens after "Element:" */
char *tokens[25];
int tokenCount = 0;
while ((tok = strtok(nullptr, " \t\r\n")) && tokenCount < 25)
tokens[tokenCount++] = tok;
/* Initialize channel values */
int adc_val = -1, tdc_val = -1, scaler_val = -1;
int adc_suf = -1, tdc_suf = -1, scaler_suf = -1;
/* Parse ADC channel if position is specified */
if (adc_pos > 0 && tokenCount >= adc_pos) {
adc_val = parseIntPrefix(tokens[adc_pos - 1]);
adc_suf = parseSuffix (tokens[adc_pos - 1]);
}
/* Parse TDC channel if position is specified */
if (tdc_pos > 0 && tokenCount >= tdc_pos) {
tdc_val = parseIntPrefix(tokens[tdc_pos - 1]);
tdc_suf = parseSuffix (tokens[tdc_pos - 1]);
}
/* Parse Scaler channel if position is specified */
if (scaler_pos > 0 && tokenCount >= scaler_pos) {
scaler_val = parseIntPrefix(tokens[scaler_pos - 1]);
scaler_suf = parseSuffix (tokens[scaler_pos - 1]);
}
/* Create a new Record for this element */
Record *rec = (Record*)malloc(sizeof(Record));
if (!rec) {
fclose(fp);
cleanup();
return false;
}
/* Fill in the Record */
rec->id = elementLine++;
rec->adc = adc_val;
rec->tdc = tdc_val;
rec->scaler = scaler_val;
rec->adc_suffix = adc_suf;
rec->tdc_suffix = tdc_suf;
rec->scaler_suffix = scaler_suf;
/* Ensure we have space in the records array */
ensureCapacity();
/* Add the Record to the array */
records[rec_cnt++] = rec;
/* Update maximum M-suffix values */
if (adc_suf > max_adc_suffix) max_adc_suffix = adc_suf;
if (tdc_suf > max_tdc_suffix) max_tdc_suffix = tdc_suf;
if (scaler_suf> max_scaler_suffix)max_scaler_suffix = scaler_suf;
/* Insert the Record into the hash tables */
if (!insertIntoHashes(rec)) {
fclose(fp);
cleanup();
return false;
}
}
/* Clean up and return success */
fclose(fp);
return true;
}
/*
* Physical -> Logical mapping functions
*
* These functions take a physical channel number and return the corresponding
* logical element ID, or -1 if not found.
*
* Note: These use the hash tables for fast lookups.
*/
int findADC(int adc) const {
if (_emptyFile) return -2;
ADCEntry *e = nullptr;
HASH_FIND_INT(_adc_hash, &adc, e);
return e ? e->rec->id : -1;
}
int findTDC(int tdc) const {
if (_emptyFile) return -2;
TDCEntry *e = nullptr;
HASH_FIND_INT(_tdc_hash, &tdc, e);
return e ? e->rec->id : -1;
}
int findScaler(int scaler) const {
if (_emptyFile) return -2;
ScalerEntry *e = nullptr;
HASH_FIND_INT(_scaler_hash, &scaler, e);
return e ? e->rec->id : -1;
}
int getADC_id(int ch) const {
if (ch < 0 || ch >= rec_cnt) return -1;
return records[ch]->adc;
}
int getTDC_id(int ch) const {
if (ch < 0 || ch >= rec_cnt) return -1;
return records[ch]->tdc;
}
int getScaler_id(int ch) const {
if (ch < 0 || ch >= rec_cnt) return -1;
return records[ch]->scaler;
}
int getADC_ch(int id) const { return findADC(id); }
int getTDC_ch(int id) const { return findTDC(id); }
int getScaler_ch(int id) const { return findScaler(id); }
int getNumberOfElements() const { return rec_cnt; }
/*
* Get the maximum number of hits per channel
*
* This considers both the M-suffix values in the parameter file
* and the maxhits values from the config file.
*
* For single-hit systems, this should typically be 1.
*/
int getADCMaxHits() const {
int suffix_hits = (max_adc_suffix >= 0) ? max_adc_suffix + 1 : 0;
return (suffix_hits > adc_maxhits) ? suffix_hits : adc_maxhits;
}
int getTDCMaxHits() const {
int suffix_hits = (max_tdc_suffix >= 0) ? max_tdc_suffix + 1 : 0;
return (suffix_hits > tdc_maxhits) ? suffix_hits : tdc_maxhits;
}
int getSCALERMaxHits() const {
int suffix_hits = (max_scaler_suffix >= 0) ? max_scaler_suffix + 1 : 0;
return (suffix_hits > scaler_maxhits) ? suffix_hits : scaler_maxhits;
}
/* Get the parameter file name */
const char* getFileName() const { return filename; }
};
#endif