2020-10-22 14:02:15 -03:00
|
|
|
/*
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "AP_Generator_IE_FuelCell.h"
|
|
|
|
|
2021-09-24 00:10:47 -03:00
|
|
|
#if HAL_GENERATOR_ENABLED
|
2020-10-22 14:02:15 -03:00
|
|
|
|
2022-02-25 01:06:28 -04:00
|
|
|
#include <AP_SerialManager/AP_SerialManager.h>
|
|
|
|
#include <GCS_MAVLink/GCS.h>
|
|
|
|
|
2020-10-22 14:02:15 -03:00
|
|
|
// Initialize the fuelcell object and prepare it for use
|
|
|
|
void AP_Generator_IE_FuelCell::init()
|
|
|
|
{
|
|
|
|
_uart = AP::serialmanager().find_serial(AP_SerialManager::SerialProtocol_Generator, 0);
|
|
|
|
|
|
|
|
if (_uart == nullptr) {
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "Generator: No serial port found");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_uart->begin(AP::serialmanager().find_baudrate(AP_SerialManager::SerialProtocol_Generator, 0));
|
2021-10-07 10:42:36 -03:00
|
|
|
_health_warn_last_ms = AP_HAL::millis();
|
2020-10-22 14:02:15 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update fuelcell, expected to be called at 20hz
|
|
|
|
void AP_Generator_IE_FuelCell::update()
|
|
|
|
{
|
|
|
|
if (_uart == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t now = AP_HAL::millis();
|
|
|
|
|
|
|
|
// Read any available data
|
|
|
|
uint32_t nbytes = MIN(_uart->available(),30u);
|
|
|
|
while (nbytes-- > 0) {
|
|
|
|
const int16_t c = _uart->read();
|
|
|
|
if (c < 0) {
|
|
|
|
// Nothing to decode
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!decode(c)) {
|
|
|
|
// Sentence not yet valid, don't assign state and output
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have a valid sentence, write the parsed values to unit specific measurements
|
|
|
|
assign_measurements(now);
|
|
|
|
}
|
|
|
|
|
|
|
|
_healthy = (now - _last_time_ms) < HEALTHY_TIMEOUT_MS;
|
|
|
|
|
|
|
|
// Check if we should notify gcs off any change of fuel cell state
|
2021-09-17 16:54:32 -03:00
|
|
|
check_status(now);
|
2020-10-22 14:02:15 -03:00
|
|
|
|
|
|
|
update_frontend();
|
|
|
|
|
|
|
|
log_write();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a single character to the buffer and attempt to decode
|
|
|
|
// Returns true if a complete sentence was successfully decoded
|
|
|
|
bool AP_Generator_IE_FuelCell::decode(char c)
|
|
|
|
{
|
|
|
|
// Start of a string
|
|
|
|
if (c == '<') {
|
|
|
|
_sentence_valid = false;
|
|
|
|
_data_valid = true;
|
|
|
|
_term_number = 0;
|
|
|
|
_term_offset = 0;
|
|
|
|
_in_string = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!_in_string) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// End of a string
|
|
|
|
if (c == '>') {
|
|
|
|
decode_latest_term();
|
|
|
|
_in_string = false;
|
|
|
|
|
|
|
|
return _sentence_valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
// End of a term in the string
|
|
|
|
if (c == ',') {
|
|
|
|
decode_latest_term();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise add the char to the current term
|
|
|
|
_term[_term_offset++] = c;
|
|
|
|
|
|
|
|
// We have overrun the expected sentence
|
|
|
|
if (_term_offset >TERM_BUFFER) {
|
|
|
|
_in_string = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for arming
|
|
|
|
bool AP_Generator_IE_FuelCell::pre_arm_check(char *failmsg, uint8_t failmsg_len) const
|
|
|
|
{
|
|
|
|
// Refuse arming if not healthy
|
|
|
|
if (!healthy()) {
|
2021-01-05 09:04:22 -04:00
|
|
|
strncpy(failmsg, "Not healthy", failmsg_len);
|
2020-10-22 14:02:15 -03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refuse arming if not in running state
|
|
|
|
if (_state != State::RUNNING) {
|
|
|
|
strncpy(failmsg, "Status not running", failmsg_len);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for error codes
|
|
|
|
if (check_for_err_code(failmsg, failmsg_len)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lookup table for running state. State code is the same for all IE units.
|
|
|
|
const AP_Generator_IE_FuelCell::Lookup_State AP_Generator_IE_FuelCell::lookup_state[] = {
|
|
|
|
{ State::STARTING,"Starting"},
|
|
|
|
{ State::READY,"Ready"},
|
|
|
|
{ State::RUNNING,"Running"},
|
|
|
|
{ State::FAULT,"Fault"},
|
|
|
|
{ State::BATTERY_ONLY,"Battery Only"},
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check for any change in error state or status and report to gcs
|
2021-09-17 16:54:32 -03:00
|
|
|
void AP_Generator_IE_FuelCell::check_status(const uint32_t now)
|
2020-10-22 14:02:15 -03:00
|
|
|
{
|
|
|
|
// Check driver health
|
2021-09-17 16:54:32 -03:00
|
|
|
if (!healthy() && (!_health_warn_last_ms || (now - _health_warn_last_ms >= 20000))) {
|
2020-10-22 14:02:15 -03:00
|
|
|
// Don't spam GCS with unhealthy message
|
2021-09-17 16:54:32 -03:00
|
|
|
_health_warn_last_ms = now;
|
2021-01-05 09:04:22 -04:00
|
|
|
gcs().send_text(MAV_SEVERITY_ALERT, "Generator: Not healthy");
|
2020-10-22 14:02:15 -03:00
|
|
|
|
|
|
|
} else if (healthy()) {
|
2021-09-17 16:54:32 -03:00
|
|
|
_health_warn_last_ms = 0;
|
2020-10-22 14:02:15 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If fuel cell state has changed send gcs message
|
|
|
|
if (_state != _last_state) {
|
|
|
|
for (const struct Lookup_State entry : lookup_state) {
|
|
|
|
if (_state == entry.option) {
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "Generator: %s", entry.msg_txt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_last_state = _state;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check error codes
|
|
|
|
char msg_txt[32];
|
|
|
|
if (check_for_err_code_if_changed(msg_txt, sizeof(msg_txt))) {
|
|
|
|
gcs().send_text(MAV_SEVERITY_ALERT, "%s", msg_txt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check error codes and populate message with error code
|
|
|
|
bool AP_Generator_IE_FuelCell::check_for_err_code_if_changed(char* msg_txt, uint8_t msg_len)
|
|
|
|
{
|
|
|
|
// Only check if there has been a change in error code
|
|
|
|
if (_err_code == _last_err_code) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_for_err_code(msg_txt, msg_len)) {
|
|
|
|
_last_err_code = _err_code;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|