49static char *option[N_OPTIONS] = {
56 "Usage: sddscombinelogfiles [<SDDSinputfilelist>] [<SDDSoutputfile>]\n"
57 " [-pipe=[output]] [-overwrite] [-threads=<number>]\n\n"
58 "This program combines data logger output files that are in the one-PV-per-file format.\n"
59 "Only the timestamps present in all input files are retained in the output file.\n\n"
61 " -pipe=[output] Specify the pipe output.\n"
62 " -overwrite Overwrite the output file if it already exists.\n"
63 " -threads=<number> Number of input files to load concurrently. The default is 1.\n\n"
65 " sddscombinelogfiles input1.sdds input2.sdds output.sdds -overwrite\n\n"
66 "Link date: " __DATE__
" " __TIME__
", SVN revision: " SVN_VERSION
"\n";
82static int LoadLogFile(
const char *filename,
LOG_FILE_DATA *logFile);
85int main(
int argc,
char **argv) {
88 KEYED_EQUIVALENT **keyGroup = NULL;
90 char **inputfile = NULL;
92 char *outputfile = NULL;
95 int64_t i, j, m, r, s;
96 unsigned long pipeFlags = 0;
98 double **timeValues = NULL;
99 double **dataValues = NULL;
101 int64_t *rows = NULL;
102 char **dataNames = NULL;
103 char **uniqueDataName = NULL;
104 int uniqueDataNames = 0;
108 double *outputTimeValues = NULL;
109 double **outputDataValues = NULL;
110 int64_t allocated_rows = 0;
116 argc =
scanargs(&s_arg, argc, argv);
119 fprintf(stderr,
"%s", USAGE);
123 for (i_arg = 1; i_arg < argc; i_arg++) {
124 if (s_arg[i_arg].arg_type == OPTION) {
125 switch (
match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
130 if (s_arg[i_arg].n_items != 2 ||
131 sscanf(s_arg[i_arg].list[1],
"%d", &threads) != 1 ||
133 fprintf(stderr,
"Error: Invalid -threads option syntax.\n");
139 s_arg[i_arg].n_items - 1,
141 fprintf(stderr,
"Error: Invalid -pipe option syntax.\n");
144 if (pipeFlags & USE_STDIN) {
145 fprintf(stderr,
"Error: -pipe=in is not supported.\n");
150 fprintf(stderr,
"Error: Unrecognized option.\n%s", USAGE);
154 inputfile =
trealloc(inputfile,
sizeof(*inputfile) * (inputfiles + 1));
155 inputfile[inputfiles++] = s_arg[i_arg].list[0];
159 if (inputfiles > 1) {
160 if (!(pipeFlags & USE_STDOUT)) {
161 outputfile = inputfile[--inputfiles];
162 if (
fexists(outputfile) && !overwrite) {
163 fprintf(stderr,
"Error: Output file '%s' already exists. Use -overwrite to replace it.\n", outputfile);
167 }
else if (inputfiles == 1) {
168 if ((pipeFlags & USE_STDOUT) && outputfile) {
169 fprintf(stderr,
"Error: Too many filenames provided with -pipe=output.\n");
173 fprintf(stderr,
"Error: No input filenames provided.\n%s", USAGE);
177 if (threads > inputfiles)
178 threads = inputfiles;
180 logFile = calloc(inputfiles,
sizeof(*logFile));
182 fprintf(stderr,
"Error: Memory allocation failure.\n");
186#pragma omp parallel for if (threads > 1 && inputfiles > 1) num_threads(threads) schedule(dynamic)
187 for (i = 0; i < inputfiles; i++) {
188 LoadLogFile(inputfile[i], &logFile[i]);
192 for (i = 0; i < inputfiles; i++) {
193 if (!logFile[i].status) {
194 fprintf(stderr,
"%s\n", logFile[i].error);
195 for (j = 0; j < inputfiles; j++)
196 FreeLogFileData(&logFile[j]);
200 pages += logFile[i].pages;
203 timeValues = malloc(
sizeof(*timeValues) * pages);
204 dataValues = malloc(
sizeof(*dataValues) * pages);
205 dataNames = malloc(
sizeof(*dataNames) * pages);
206 rows = malloc(
sizeof(*rows) * pages);
207 if (!timeValues || !dataValues || !dataNames || !rows) {
208 fprintf(stderr,
"Error: Memory allocation failure.\n");
209 for (i = 0; i < inputfiles; i++)
210 FreeLogFileData(&logFile[i]);
216 for (i = 0; i < inputfiles; i++) {
218 for (filePage = 0; filePage < logFile[i].pages; filePage++) {
219 timeValues[page] = logFile[i].page[filePage].timeValues;
220 dataValues[page] = logFile[i].page[filePage].dataValues;
221 dataNames[page] = logFile[i].page[filePage].dataName;
222 rows[page] = logFile[i].page[filePage].rows;
223 logFile[i].page[filePage].timeValues = NULL;
224 logFile[i].page[filePage].dataValues = NULL;
225 logFile[i].page[filePage].dataName = NULL;
228 FreeLogFileData(&logFile[i]);
235 for (page = 0; page < pages; page++) {
237 for (i = 0; i < uniqueDataNames; i++) {
238 if (strcmp(dataNames[page], uniqueDataName[i]) == 0) {
244 uniqueDataName = realloc(uniqueDataName,
sizeof(*uniqueDataName) * (uniqueDataNames + 1));
261 for (i = 0; i < uniqueDataNames; i++) {
268 outputDataValues = malloc(
sizeof(*outputDataValues) * uniqueDataNames);
269 if (uniqueDataNames == 1) {
271 for (page = 0; page < pages; page++) {
272 allocated_rows += rows[page];
275 outputTimeValues = malloc(
sizeof(*outputTimeValues) * allocated_rows);
276 outputDataValues[0] = malloc(
sizeof(*(outputDataValues[0])) * allocated_rows);
279 for (page = 0; page < pages; page++) {
280 for (j = 0; j < rows[page]; j++) {
281 outputTimeValues[i] = timeValues[page][j];
282 outputDataValues[0][i] = dataValues[page][j];
288 flag = malloc(
sizeof(*flag) * pages);
289 for (page = 0; page < pages; page++) {
290 flag[page] = calloc(rows[page],
sizeof(*(flag[page])));
293 array = malloc(
sizeof(*array) * uniqueDataNames);
294 arrayCount = calloc(uniqueDataNames,
sizeof(*arrayCount));
296 for (i = 0; i < uniqueDataNames; i++) {
297 for (page = 0; page < pages; page++) {
298 if (strcmp(dataNames[page], uniqueDataName[i]) == 0) {
300 if (arrayCount[i] == 1) {
301 array[i] = malloc(
sizeof(*(array[i])));
303 array[i] = realloc(array[i],
sizeof(*(array[i])) * arrayCount[i]);
305 array[i][arrayCount[i] - 1] = page;
310 for (i = 0; i < arrayCount[0]; i++) {
312 for (n = 1; n < uniqueDataNames; n++) {
313 for (m = 0; m < arrayCount[n]; m++) {
314 if ((i == m) && (rows[array[0][i]] == rows[array[n][m]]) && (rows[array[0][i]] > 10)) {
315 if ((timeValues[array[0][i]][0] == timeValues[array[n][m]][0]) &&
316 (timeValues[array[0][i]][1] == timeValues[array[n][m]][1]) &&
317 (timeValues[array[0][i]][rows[array[0][i]] - 2] == timeValues[array[n][m]][rows[array[n][m]] - 2]) &&
318 (timeValues[array[0][i]][rows[array[0][i]] - 1] == timeValues[array[n][m]][rows[array[n][m]] - 1])) {
320 for (r = 0; r < rows[array[n][m]]; r++) {
321 if (flag[array[n][m]][r]) {
324 flag[array[0][i]][r] += 1;
325 flag[array[n][m]][r] = 1;
330 for (r = 0; r < rows[array[n][m]]; r++) {
331 if (flag[array[n][m]][r]) {
336 flag[array[0][i]][row] += 1;
337 flag[array[n][m]][r] = 1;
343 for (j = 0; j < keyGroups; j++) {
344 free(keyGroup[j]->equivalent);
350 z = uniqueDataNames - 1;
351 for (n = 0; n < arrayCount[0]; n++) {
352 for (m = 0; m < rows[array[0][n]]; m++) {
353 if (flag[array[0][n]][m] >= z) {
359 outputTimeValues = malloc(
sizeof(*outputTimeValues) * allocated_rows);
360 for (i = 0; i < uniqueDataNames; i++) {
361 outputDataValues[i] = malloc(
sizeof(*(outputDataValues[i])) * allocated_rows);
365 for (i = 0; i < arrayCount[0]; i++) {
366 for (j = 0; j < rows[array[0][i]]; j++) {
367 if (flag[array[0][i]][j] >= z) {
368 outputTimeValues[s] = timeValues[array[0][i]][j];
369 outputDataValues[0][s] = dataValues[array[0][i]][j];
376 fprintf(stderr,
"Error: No matching 'Time' rows found in input files.\n");
382 for (n = 1; n < uniqueDataNames; n++) {
383 for (m = 0; m < arrayCount[n]; m++) {
384 for (r = 0; r < rows[array[n][m]]; r++) {
385 if (flag[array[n][m]][r]) {
388 outputDataValues[n][row] = dataValues[array[n][m]][r];
395 for (i = 0; i < uniqueDataNames; i++) {
399 for (j = 0; j < keyGroups; j++) {
400 if (keyGroup[j]->equivalent)
401 free(keyGroup[j]->equivalent);
406 for (page = 0; page < pages; page++) {
417 for (page = 0; page < pages; page++) {
418 if (timeValues[page])
419 free(timeValues[page]);
420 if (dataValues[page])
421 free(dataValues[page]);
422 free(dataNames[page]);
444 for (i = 0; i < uniqueDataNames; i++) {
445 if (!
SDDS_SetColumnFromDoubles(&SDDS_output, SDDS_SET_BY_NAME, outputDataValues[i], allocated_rows, uniqueDataName[i])) {
451 if (!SDDS_WriteTable(&SDDS_output)) {
462 for (i = 0; i < uniqueDataNames; i++) {
463 free(uniqueDataName[i]);
464 free(outputDataValues[i]);
466 free(outputTimeValues);
467 free(outputDataValues);
468 free(uniqueDataName);
471 if (inputfiles > 0) {
480static int LoadLogFile(
const char *filename,
LOG_FILE_DATA *logFile) {
482 char **columnname = NULL;
483 int32_t columnnames = 0;
489 logFile->page = NULL;
492 logFile->error[0] = 0;
495 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Unable to open '%s'.", filename);
501 if (columnname == NULL) {
502 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Unable to read column names from '%s'.", filename);
506 if (columnnames > 3 || columnnames < 2) {
507 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Unexpected number of columns in '%s'.", filename);
511 if (columnnames == 2) {
512 if (strcmp(
"Time", columnname[0]) == 0) {
514 }
else if (strcmp(
"Time", columnname[1]) == 0) {
517 snprintf(logFile->error,
sizeof(logFile->error),
"Error: 'Time' column is missing in '%s'.", filename);
522 if (columnnames == 3) {
523 if (strcmp(
"CAerrors", columnname[0]) == 0) {
524 if (strcmp(
"Time", columnname[1]) == 0) {
526 }
else if (strcmp(
"Time", columnname[2]) == 0) {
529 snprintf(logFile->error,
sizeof(logFile->error),
"Error: 'Time' column is missing in '%s'.", filename);
532 }
else if (strcmp(
"CAerrors", columnname[1]) == 0) {
533 if (strcmp(
"Time", columnname[0]) == 0) {
535 }
else if (strcmp(
"Time", columnname[2]) == 0) {
538 snprintf(logFile->error,
sizeof(logFile->error),
"Error: 'Time' column is missing in '%s'.", filename);
541 }
else if (strcmp(
"CAerrors", columnname[2]) == 0) {
542 if (strcmp(
"Time", columnname[0]) == 0) {
544 }
else if (strcmp(
"Time", columnname[1]) == 0) {
547 snprintf(logFile->error,
sizeof(logFile->error),
"Error: 'Time' column is missing in '%s'.", filename);
551 snprintf(logFile->error,
sizeof(logFile->error),
"Error: 'CAerrors' column is missing in '%s'.", filename);
556 while ((readCode = SDDS_ReadTable(&SDDS_input)) > 0) {
557 int page = logFile->pages;
559 pageData = realloc(logFile->page,
sizeof(*logFile->page) * (page + 1));
561 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Memory allocation failure.");
564 logFile->page = pageData;
565 logFile->page[page].timeValues = NULL;
566 logFile->page[page].dataValues = NULL;
567 logFile->page[page].dataName = NULL;
568 logFile->page[page].rows = SDDS_RowCount(&SDDS_input);
571 if (!
SDDS_CopyString(&logFile->page[page].dataName, columnname[dataIndex])) {
572 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Memory allocation failure.");
575 if (logFile->page[page].rows > 0) {
577 if (logFile->page[page].timeValues == NULL) {
578 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Unable to read 'Time' data from '%s'.", filename);
583 if (logFile->page[page].dataValues == NULL) {
584 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Unable to read '%s' data from '%s'.", columnname[dataIndex], filename);
590 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Unable to read data from '%s'.", filename);
594 for (j = 0; j < columnnames; j++)
601 snprintf(logFile->error,
sizeof(logFile->error),
"Error: Unable to close '%s'.", filename);
611 for (j = 0; j < columnnames; j++)
617 FreeLogFileData(logFile);
625 for (page = 0; page < logFile->pages; page++) {
626 free(logFile->page[page].timeValues);
627 free(logFile->page[page].dataValues);
628 free(logFile->page[page].dataName);
631 logFile->page = NULL;
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetColumnFromDoubles(SDDS_DATASET *SDDS_dataset, int32_t mode, double *data, int64_t rows,...)
Sets the values for a single data column using double-precision floating-point numbers.
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_DefineSimpleColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *unit, int32_t type)
Defines a simple data column within the SDDS dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
char ** SDDS_GetColumnNames(SDDS_DATASET *SDDS_dataset, int32_t *number)
Retrieves the names of all columns in the SDDS dataset.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
int32_t SDDS_CopyString(char **target, const char *source)
Copies a source string to a target string with memory allocation.
#define SDDS_DOUBLE
Identifier for the double data type.
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
long fexists(const char *filename)
Checks if a file exists.
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
long processPipeOption(char **item, long items, unsigned long *flags)
void free_scanargs(SCANNED_ARG **scanned, int argc)
KEYED_EQUIVALENT ** MakeSortedKeyGroups(long *keyGroups, long keyType, void *data, long points)
Create sorted key groups from data.
long FindMatchingKeyGroup(KEYED_EQUIVALENT **keyGroup, long keyGroups, long keyType, void *searchKeyData, long reuse)
Find a matching key group for a search key.