From 1c79271056e12dedc65b6e1e41b38f5ea43bcc3d Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Mon, 12 Oct 2026 19:29:39 +0300 Subject: [PATCH] wip: --- ats0002/ats0002.c | 262 +++++++++++++++++++++++++ ats0002/ats0002.h | 45 ++++- heat_exchander_airflow_pressure_drop | Bin 38200 -> 36968 bytes heat_exchander_airflow_pressure_drop.c | 20 ++ 4 files changed, 326 insertions(+), 1 deletion(-) diff --git a/ats0002/ats0002.c b/ats0002/ats0002.c index b2281b4..98d2423 100644 --- a/ats0002/ats0002.c +++ b/ats0002/ats0002.c @@ -1,4 +1,6 @@ #include "ats0002.h" +#include +#include /** * @brief Normalize the decimal separator in a string from comma to dot. @@ -60,6 +62,31 @@ static void _get_double_in_range(double *result, double min, double max, const c * @param airflow Pointer to the current airflow value * @param data Pointer to air passage data containing anchor points (must not be NULL) */ +/** + * @brief Get the current date as a formatted string (YYYY-MM-DD). + * + * @param buffer Destination buffer (at least 11 bytes) + */ +static void _get_date_string(char *buffer); + +/** + * @brief Save the test report to a minimal PDF file. + * + * @see save_report_to_pdf (public API) + */ +static void _save_report_to_pdf_impl(const char *heat_exchanger_name, + const char *part_number, + const char *side_name, + double airflow, + double airflow_pressure, + const char *unit, + const double *barometric_pressure, + const double *airflow_temperature, + const double *corrected_airflow_pressure, + double pressure_drop_limit, + const char *test_result, + bool recalculated_required); + static void _get_log_interpolated_airflow_pressure_drop_limit(double *limit, const double *airflow, const air_passage_data_t *data); /** @@ -330,4 +357,239 @@ static void _calculate_density_corrected_pressure(double *corrected_airflow_pres double value_6 = *airflow_temperature + 273.2; double value_7 = (0.28443 * value_5) / value_6; *corrected_airflow_pressure = *airflow_pressure * value_7; +} + +void get_yes_no(bool *answer, const char *prompt) +{ + printf("%s (y/n):\n", prompt); + char buf[64]; + for (;;) + { + printf("> "); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + { + *answer = false; + return; + } + buf[strcspn(buf, "\n")] = '\0'; + if (strcmp(buf, "q") == 0 || strcmp(buf, "Q") == 0) + { + *answer = false; + return; + } + if (strcmp(buf, "y") == 0 || strcmp(buf, "Y") == 0 || + strcmp(buf, "yes") == 0 || strcmp(buf, "Yes") == 0 || strcmp(buf, "YES") == 0) + { + *answer = true; + return; + } + if (strcmp(buf, "n") == 0 || strcmp(buf, "N") == 0 || + strcmp(buf, "no") == 0 || strcmp(buf, "No") == 0 || strcmp(buf, "NO") == 0) + { + *answer = false; + return; + } + printf("Please enter 'y' or 'n':\n"); + } +} + +/* ======================== PDF Report Generation ======================== */ + +/** + * @brief Escape special characters in PDF string literals. + * + * PDF strings require backslash-escaping for: \ ( ) + * + * @param[out] dst Destination buffer + * @param[in] dst_size Size of destination buffer + * @param[in] src Source string + */ +static void _pdf_escape_string(char *dst, size_t dst_size, const char *src) +{ + size_t di = 0; + for (size_t si = 0; src[si] != '\0' && di + 2 < dst_size; si++) + { + char c = src[si]; + if (c == 0x5C || c == 0x28 || c == 0x29) + { + dst[di++] = 0x5C; + } + dst[di++] = c; + } + dst[di] = '\0'; +} + +/** + * @brief Get the current date as a formatted string (YYYY-MM-DD). + * + * @param buffer Destination buffer (at least 11 bytes) + */ +static void _get_date_string(char *buffer) +{ + time_t now = time(NULL); + struct tm *t = localtime(&now); + snprintf(buffer, 11, "%04d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday); +} + +/** + * @brief Build a PDF content stream line for a text label. + * + * Appends a BT/ET text block to the stream buffer and advances the y position. + * + * @param stream_buf Content stream buffer + * @param stream_size Size of the content stream buffer + * @param slen Current stream length (updated in-place) + * @param y Current y position (updated in-place: decremented by 18) + * @param font_size Font size in points + * @param fmt printf-style format string for the text + */ +static void _pdf_add_text_line(char *stream_buf, size_t stream_size, int *slen, double *y, + int font_size, const char *fmt, ...) +{ + char raw_line[512]; + va_list args; + va_start(args, fmt); + vsnprintf(raw_line, sizeof(raw_line), fmt, args); + va_end(args); + + char escaped[1024]; + _pdf_escape_string(escaped, sizeof(escaped), raw_line); + + *slen += snprintf(stream_buf + *slen, stream_size - (size_t)*slen, + "BT\n/F1 %d Tf\n50 %.0f Td\n(%s) Tj\nET\n", + font_size, *y, escaped); + *y -= 18; +} + +/** + * Minimal PDF generator. Creates a single-page PDF with text content. + * Uses PDF 1.4 format with Type1 font (Helvetica). No external dependencies. + */ +static void _save_report_to_pdf_impl(const char *heat_exchanger_name, + const char *part_number, + const char *side_name, + double airflow, + double airflow_pressure, + const char *unit, + const double *barometric_pressure, + const double *airflow_temperature, + const double *corrected_airflow_pressure, + double pressure_drop_limit, + const char *test_result, + bool recalculated_required) +{ + char date_str[11]; + _get_date_string(date_str); + + /* Build filename:

_.pdf in project root */ + char filename[256]; + snprintf(filename, sizeof(filename), "%s_%s.pdf", part_number, date_str); + + FILE *f = fopen(filename, "wb"); + if (!f) + { + printf("Error: Cannot create PDF file '%s'\n", filename); + return; + } + + /* --- Build content stream in memory --- */ + char stream_buf[4096]; + int slen = 0; + double y = 750; + + /* Title */ + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 14, "%s", heat_exchanger_name); + /* Separator */ + slen += snprintf(stream_buf + slen, sizeof(stream_buf) - (size_t)slen, + "50 %.0f m 550 %.0f l 0.5 0.5 0.5 RG 1 w S\n", y + 8, y + 8); + y -= 8; + + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "P/N: %s", part_number); + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Side: %s", side_name); + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Airflow: %0.2f LB/MIN", airflow); + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Static inlet pressure: %0.2f %s", airflow_pressure, unit); + + if (recalculated_required) + { + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Barometric pressure: %0.2f MBAR", *barometric_pressure); + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Airflow temperature: %0.2f C", *airflow_temperature); + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Corrected static pressure: %0.2f %s", *corrected_airflow_pressure, unit); + } + + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Max allowed pressure drop: %0.2f %s", pressure_drop_limit, unit); + y -= 6; + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 11, "Test result: %s", test_result); + + /* Footer */ + y -= 6; + _pdf_add_text_line(stream_buf, sizeof(stream_buf), &slen, &y, 8, "Report date: %s", date_str); + + unsigned int stream_len = (unsigned int)slen; + + /* --- Write PDF file --- */ + /* Object layout: + * 1 0 obj — Catalog + * 2 0 obj — Pages + * 3 0 obj — Page + * 4 0 obj — Content stream + * 5 0 obj — Font + */ + fprintf(f, "%%PDF-1.4\n"); + + unsigned int offsets[6] = {0}; + + offsets[1] = (unsigned int)ftell(f); + fprintf(f, "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n"); + + offsets[2] = (unsigned int)ftell(f); + fprintf(f, "2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n"); + + offsets[3] = (unsigned int)ftell(f); + fprintf(f, "3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] " + "/Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>\nendobj\n"); + + offsets[4] = (unsigned int)ftell(f); + fprintf(f, "4 0 obj\n<< /Length %u >>\nstream\n", stream_len); + fwrite(stream_buf, 1, stream_len, f); + fprintf(f, "\nendstream\nendobj\n"); + + offsets[5] = (unsigned int)ftell(f); + fprintf(f, "5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>\nendobj\n"); + + /* Cross-reference table */ + unsigned int xref_offset = (unsigned int)ftell(f); + fprintf(f, "xref\n"); + fprintf(f, "0 6\n"); + fprintf(f, "0000000000 65535 f \n"); + for (int i = 1; i <= 5; i++) + { + fprintf(f, "%010u 00000 n \n", offsets[i]); + } + + fprintf(f, "trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n"); + fprintf(f, "%u\n%%%%EOF\n", xref_offset); + + fclose(f); + printf("Report saved to: %s\n", filename); +} + +void save_report_to_pdf(const char *heat_exchanger_name, + const char *part_number, + const char *side_name, + double airflow, + double airflow_pressure, + const char *unit, + const double *barometric_pressure, + const double *airflow_temperature, + const double *corrected_airflow_pressure, + double pressure_drop_limit, + const char *test_result, + bool recalculated_required) +{ + _save_report_to_pdf_impl(heat_exchanger_name, part_number, side_name, + airflow, airflow_pressure, unit, + barometric_pressure, airflow_temperature, + corrected_airflow_pressure, pressure_drop_limit, + test_result, recalculated_required); } \ No newline at end of file diff --git a/ats0002/ats0002.h b/ats0002/ats0002.h index cde4e90..1d14556 100644 --- a/ats0002/ats0002.h +++ b/ats0002/ats0002.h @@ -204,4 +204,47 @@ void get_airflow_temperature(double *airflow_temperature, const air_passage_data * @param[in] airflow_temperature Pointer to store the current airflow temperature value * @param[in] data Pointer to air passage data containing operational limits (must not be NULL) */ -void get_pressure_drop(double *pressure_drop_limit, const double *airflow, const double *airflow_pressure, double *corrected_airflow_pressure, const double *barometric_pressure, const double *airflow_temperature, const air_passage_data_t *data); \ No newline at end of file +void get_pressure_drop(double *pressure_drop_limit, const double *airflow, const double *airflow_pressure, double *corrected_airflow_pressure, const double *barometric_pressure, const double *airflow_temperature, const air_passage_data_t *data); + +/** + * @brief Save the test report to a PDF file in the project root. + * + * Generates a minimal PDF file containing the test summary with P/N and date. + * The file is saved as "

_YYYY-MM-DD.pdf" in the project root directory. + * + * @param heat_exchanger_name Full heat exchanger description string + * @param part_number Part number string (e.g. "182820-1") + * @param side_name Air passage side description string + * @param airflow Airflow value in LB/MIN + * @param airflow_pressure Static inlet pressure value + * @param unit Pressure unit string ("KPA" or "PSIG") + * @param barometric_pressure Barometric pressure value (may be NULL if not applicable) + * @param airflow_temperature Airflow temperature value (may be NULL if not applicable) + * @param corrected_airflow_pressure Corrected static inlet pressure (may be NULL if not applicable) + * @param pressure_drop_limit Max allowed pressure drop limit + * @param test_result "PASSED" or "FAILED" + * @param recalculated_required Flag indicating if recalculation was performed + */ +void save_report_to_pdf(const char *heat_exchanger_name, + const char *part_number, + const char *side_name, + double airflow, + double airflow_pressure, + const char *unit, + const double *barometric_pressure, + const double *airflow_temperature, + const double *corrected_airflow_pressure, + double pressure_drop_limit, + const char *test_result, + bool recalculated_required); + +/** + * @brief Ask the user a yes/no question. + * + * Prompts the user with a yes/no question. Returns true for "yes", false for "no". + * Handles EXIT command. + * + * @param[out] answer Pointer to store the answer (true=yes, false=no, EXIT=quit) + * @param[in] prompt Question to display + */ +void get_yes_no(bool *answer, const char *prompt); \ No newline at end of file diff --git a/heat_exchander_airflow_pressure_drop b/heat_exchander_airflow_pressure_drop index 05f11718c0915dd4d795969df9bd3e7c19545a70..754ad1ce73e3504a4a55cb801459311442df305e 100755 GIT binary patch literal 36968 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 Q{r$gr(0T83NIUuUKi&&KdjJ3c delta 6799 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, + he->heat_exchanger_type_data[part_number].part_number_description, + hetd->air_passages_data[air_passage_number].air_passage_description, + airflow, + airflow_pressure, + unit, + apd->recalculated_required ? &barometric_pressure : NULL, + apd->recalculated_required ? &airflow_temperature : NULL, + apd->recalculated_required ? &corrected_airflow_pressure : NULL, + pressure_drop_limit, + test_pressure <= pressure_drop_limit ? "PASSED" : "FAILED", + apd->recalculated_required); + } + printf("\n"); uint16_t command = 0; get_user_command(&command); EXIT_CHECK(command);