diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 41410841..9cd525df 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #ifdef _MSC_VER #define snprintf _snprintf @@ -88,6 +90,12 @@ (((micro) & 0x1F) << 1) | \ ((beta) & 0x01)) +#define OSTC4_COMPASS_HEADING_CLEARED_FLAG 0x8000 +#define OSTC4_COMPASS_HEADING_SET_FLAG 0x4000 + +#define OSTC4_SCRUBBER_STATE_ERROR_FLAG 0x4000 +#define OSTC4_SCRUBBER_STATE_WARNING_FLAG 0x2000 + typedef struct hw_ostc_sample_info_t { unsigned int type; unsigned int divisor; @@ -139,6 +147,10 @@ typedef struct hw_ostc_parser_t { unsigned int initial_setpoint; unsigned int initial_cns; hw_ostc_gasmix_t gasmix[NGASMIXES]; + int first_scrubber_time_minutes; + int last_scrubber_time_minutes; + bool scrubber_error_reported; + bool scrubber_warning_reported; } hw_ostc_parser_t; static dc_status_t hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); @@ -438,6 +450,10 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, const parser->gasmix[i].active = 0; parser->gasmix[i].diluent = 0; } + parser->first_scrubber_time_minutes = INT_MAX; + parser->last_scrubber_time_minutes = INT_MAX; + parser->scrubber_error_reported = false; + parser->scrubber_warning_reported = false; parser->serial = serial; *out = (dc_parser_t *) parser; @@ -800,6 +816,22 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned else return DC_STATUS_DATAFORMAT; break; + case 6: + if (parser->first_scrubber_time_minutes == INT_MAX) { + return DC_STATUS_DATAFORMAT; + } + + string->desc = "Remaining scrubber time at start [minutes]"; + snprintf(buf, BUFLEN, "%d", parser->first_scrubber_time_minutes); + break; + case 7: + if (parser->last_scrubber_time_minutes == INT_MAX) { + return DC_STATUS_DATAFORMAT; + } + + string->desc = "Remaining scrubber time at end [minutes]"; + snprintf(buf, BUFLEN, "%d", parser->last_scrubber_time_minutes); + break; default: return DC_STATUS_UNSUPPORTED; } @@ -928,6 +960,7 @@ hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t unsigned int tank = parser->initial != UNDEFINED ? parser->initial - 1 : 0; unsigned int offset = header; + char buf[BUFLEN]; if (version == 0x23 || version == 0x24) offset += 5 + 3 * nconfig; while (offset + 3 <= size) { @@ -1135,30 +1168,83 @@ hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t // Compass heading update if (events & 0x0200) { if (length < 2) { - ERROR (abstract->context, "Buffer overflow detected!"); + ERROR (abstract->context, "Buffer underflow detected!"); return DC_STATUS_DATAFORMAT; } if (callback) { - unsigned int heading = array_uint16_le(data + offset); + unsigned int value = array_uint16_le(data + offset); dc_sample_value_t sample = { .event.type = SAMPLE_EVENT_STRING, .event.flags = SAMPLE_FLAGS_SEVERITY_INFO, }; - if (heading & 0x8000) { - sample.event.name = "Cleared compass heading"; + unsigned int heading = value & 0x1FF; + if (value & OSTC4_COMPASS_HEADING_CLEARED_FLAG) { + snprintf(buf, BUFLEN, "Cleared compass heading"); } else { - if (heading & 0x4000) { + sample.event.value = heading; + + if (value & OSTC4_COMPASS_HEADING_SET_FLAG) { sample.event.type = SAMPLE_EVENT_HEADING; - sample.event.name = "Set compass heading"; + snprintf(buf, BUFLEN, "Set compass heading [degrees]%s", sample.event.value ? "" : ": 0"); } else { - sample.event.name = "Logged compass heading"; + snprintf(buf, BUFLEN, "Logged compass heading [degrees]%s", sample.event.value ? "" : ": 0"); } - sample.event.value = heading & 0x1FF; } + sample.event.name = buf; + + callback(DC_SAMPLE_EVENT, &sample, userdata); + } + + offset += 2; + length -= 2; + } + + // Scrubber state update + if (events & 0x0800) { + if (length < 2) { + ERROR (abstract->context, "Buffer underflow detected!"); + return DC_STATUS_DATAFORMAT; + } + + unsigned int scrubberState = array_uint16_le(data + offset); + int scrubberTimeMinutes = scrubberState & 0x0FFF; // Extract the 12-bit value + if (scrubberState & 0x0800) { // Check if the sign bit is set + scrubberTimeMinutes -= 0x1000; // Perform sign extension + } + if (parser->first_scrubber_time_minutes == INT_MAX) { + parser->first_scrubber_time_minutes = scrubberTimeMinutes; + } + parser->last_scrubber_time_minutes = scrubberTimeMinutes; + + if (callback) { + dc_sample_value_t sample = { + .event.type = SAMPLE_EVENT_STRING, + .event.flags = SAMPLE_FLAGS_SEVERITY_STATE, + .event.value = scrubberTimeMinutes, + }; + + if (scrubberState & OSTC4_SCRUBBER_STATE_ERROR_FLAG) { + if (!parser->scrubber_error_reported) { + sample.event.flags = SAMPLE_FLAGS_SEVERITY_ALARM; + parser->scrubber_error_reported = true; + } + snprintf(buf, BUFLEN, "Scrubber exhausted, time remaining [minutes]%s", sample.event.value ? "" : ": 0"); + } else if (scrubberState & OSTC4_SCRUBBER_STATE_WARNING_FLAG) { + if (!parser->scrubber_warning_reported) { + sample.event.flags = SAMPLE_FLAGS_SEVERITY_WARN; + parser->scrubber_warning_reported = true; + } + snprintf(buf, BUFLEN, "Scrubber warning, time remaining [minutes]%s", sample.event.value ? "" : ": 0"); + } else { + snprintf(buf, BUFLEN, "Scrubber time remaining [minutes]%s", sample.event.value ? "" : ": 0"); + } + + sample.event.name = buf; + callback(DC_SAMPLE_EVENT, &sample, userdata); }