diff --git a/docs/inbound_mapping_encoder.md b/docs/inbound_mapping_encoder.md index bac0ef5c..6ae37e03 100644 --- a/docs/inbound_mapping_encoder.md +++ b/docs/inbound_mapping_encoder.md @@ -71,6 +71,7 @@ The modifiers are expected to be numbers. They can be integers, doubles or float | modifier_fast_down | Modifier which will be used when the knob is turned quite fast to the left (relative mode, only) | | value_min | Minimum value | | value_max | Maximum value | +| value_wrap | Enables value wrapping between `value_min` (inclusive) and `value_max` (exclusive). (default is false) | ### Examples @@ -78,5 +79,8 @@ The modifiers are expected to be numbers. They can be integers, doubles or float { ch = 11, cc = 1, mode = "range", type = "enc", dataref = "ixeg/733/altimeter/altimeter_bug_pilot_ind", modifier_up = 0.02, modifier_down = -0.02 } { ch = 11, cc = 4, mode = "relative", type = "enc", dataref = "ixeg/733/altimeter/altimeter_bug_pilot_ind", modifier_up = 0.02, modifier_down = -0.02, modifier_fast_up = 0.20, modifier_fast_down = -0.20 } + +{ ch = 11, cc = 5, type = "enc", dataref = "sim/cockpit/radios/nav1_obs_degm", modifier_up = 1, modifier_down = -1, value_min = 1, value_max = 361, value_wrap = true }, ``` -*The examples above change the altimeter bug for the pilot in the IXEG 737-300.* +*The first two examples above change the altimeter bug for the pilot in the IXEG 737-300. +The third example demonstrates the use of `value_min`, `value_max` and `value_wrap` to archive a wrapping nav course.* diff --git a/src/common/toml_utils.cpp b/src/common/toml_utils.cpp index 96d45818..75f401cd 100644 --- a/src/common/toml_utils.cpp +++ b/src/common/toml_utils.cpp @@ -123,6 +123,37 @@ bool toml_utils::is_array(text_logger& in_log, toml::value& in_data, std::string return false; } +/** + * Read a boolean value + */ +bool toml_utils::read_bool(text_logger& in_log, toml::value& in_data, std::string_view in_name, bool in_fallback) +{ + if (in_name.empty()) { + in_log.error("Internal error (toml_utils::read_bool --> name is empty)"); + return in_fallback; + } + + bool value = in_fallback; + + try { + // read dataref + if (contains(in_log, in_data, in_name)) { + if (in_data[in_name.data()].is_boolean()) { + value = in_data[in_name.data()].as_boolean(); + in_log.debug_param(in_data.location().line(), in_name, std::to_string(value)); + } else { + in_log.error_line(in_data.location().line(), in_data.location().line_str()); + in_log.error(" --> Parameter '" + std::string(in_name) + "' is not a boolean"); + } + } + } catch (toml::type_error& error) { + in_log.error_line(in_data.location().line(), in_data.location().line_str()); + in_log.error(" --> Error reading mapping"); + in_log.error(error.what()); + } + + return value; +} /** * Read the value of a string parameter diff --git a/src/common/toml_utils.h b/src/common/toml_utils.h index 035cddb1..a2f1c5aa 100644 --- a/src/common/toml_utils.h +++ b/src/common/toml_utils.h @@ -40,6 +40,8 @@ class toml_utils { std::string_view in_name); static bool is_array(text_logger& in_log, toml::value& in_data, std::string_view in_name); + static bool read_bool(text_logger& in_log, toml::value& in_data, std::string_view in_name, bool in_fallback = false); + static std::string read_string(text_logger& in_log, toml::value& in_data, std::string_view in_name); static std::set read_str_set_array(text_logger& in_log, diff --git a/src/common/types.h b/src/common/types.h index 23e70e74..38dbf8ee 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -121,6 +121,7 @@ const char* const CFG_KEY_VALUES_PUSH = "values_push"; const char* const CFG_KEY_VALUES_PULL = "values_pull"; const char* const CFG_KEY_VALUE_MIN = "value_min"; const char* const CFG_KEY_VALUE_MAX = "value_max"; +const char* const CFG_KEY_VALUE_WRAP = "value_wrap"; const char* const CFG_KEY_VALUE_ON = "value_on"; const char* const CFG_KEY_VALUE_OFF = "value_off"; const char* const CFG_KEY_VIRTUAL_CHANNEL = "virtual_channel"; diff --git a/src/env/xplane/data_xplane.cpp b/src/env/xplane/data_xplane.cpp index a9d5d9ea..f3b4ac73 100644 --- a/src/env/xplane/data_xplane.cpp +++ b/src/env/xplane/data_xplane.cpp @@ -364,7 +364,7 @@ data_item *data_xplane::retrieve_data(std::string_view in_name) dataref_name = dataref_name.substr(0, dataref_name.find('[')); } - if (m_data_cache.contains(dataref_name)) { + if (!m_data_cache.contains(dataref_name)) { auto item = std::make_unique(); item->dataref = XPLMFindDataRef(dataref_name.c_str()); diff --git a/src/map/map_in/map_in_enc.cpp b/src/map/map_in/map_in_enc.cpp index df504bf3..43e66bca 100644 --- a/src/map/map_in/map_in_enc.cpp +++ b/src/map/map_in/map_in_enc.cpp @@ -106,6 +106,10 @@ void map_in_enc::read_config(text_logger& in_log, toml::value& in_data, toml::va m_value_max = toml_utils::read_float(in_log, in_data, CFG_KEY_VALUE_MAX, false); m_value_max_defined = true; } + + // read value wrap + if (toml_utils::contains(in_log, in_data, CFG_KEY_VALUE_WRAP)) + m_value_wrap = toml_utils::read_bool(in_log, in_data, CFG_KEY_VALUE_WRAP); } else { in_log.debug_line(in_data.location().line(), "Use 'command' mode for encoder mapping"); m_enc_map_type = encoder_map_type::command; @@ -184,6 +188,12 @@ bool map_in_enc::check(text_logger& in_log, const device_settings& in_dev_settin CFG_KEY_VALUE_MAX)); result = false; } + + if (m_value_wrap && (!m_value_min_defined || !m_value_max_defined)) { + in_log.error(source_line()); + in_log.error(" --> Wrapping requires both minimum and maximum values to be defined"); + result = false; + } break; case command: @@ -491,7 +501,11 @@ void map_in_enc::modify_up(midi_message& in_msg, bool in_fast) // change and check the value value = value + modifier; - value = check_value_min_max(value, modifier); + if (m_value_wrap) + // wrap the value in the interval [m_value_min, m_value_max) + value = fmod(value - m_value_min + m_value_max - m_value_min, m_value_max - m_value_min) + m_value_min; + else + value = check_value_min_max(value, modifier); if (env().drf().write(in_msg.log(), m_dataref, value)) { try { @@ -544,7 +558,11 @@ void map_in_enc::modify_down(midi_message& in_msg, bool in_fast) // change and check the value value = value + modifier; - value = check_value_min_max(value, modifier); + if (m_value_wrap) + // wrap the value in the interval [m_value_min, m_value_max) + value = fmod(value - m_value_min + m_value_max - m_value_min, m_value_max - m_value_min) + m_value_min; + else + value = check_value_min_max(value, modifier); if (env().drf().write(in_msg.log(), m_dataref, value)) { try { diff --git a/src/map/map_in/map_in_enc.h b/src/map/map_in/map_in_enc.h index eed8cb14..947454ff 100644 --- a/src/map/map_in/map_in_enc.h +++ b/src/map/map_in/map_in_enc.h @@ -113,6 +113,8 @@ class map_in_enc : public map_in_label { float m_value_min {0.0f}; float m_value_max {0.0f}; + bool m_value_wrap {false}; + std::string m_command_up {}; std::string m_command_down {};