|
| 1 | +/* Time Duration Option |
| 2 | + * |
| 3 | + * From: https://github.com/PokemonAutomation/Arduino-Source |
| 4 | + * |
| 5 | + */ |
| 6 | + |
| 7 | +#include "Common/Cpp/PrettyPrint.h" |
| 8 | +#include "Common/Cpp/ExpressionEvaluator.h" |
| 9 | +#include "Common/Cpp/Json/JsonValue.h" |
| 10 | +#include "Common/Cpp/Containers/Pimpl.tpp" |
| 11 | +#include "Common/Cpp/Concurrency/SpinLock.h" |
| 12 | +#include "Common/Qt/Options/TimeDurationWidget.h" |
| 13 | +#include "TimeDurationOption.h" |
| 14 | + |
| 15 | +//#include <iostream> |
| 16 | +//using std::cout; |
| 17 | +//using std::endl; |
| 18 | + |
| 19 | +namespace PokemonAutomation{ |
| 20 | + |
| 21 | + |
| 22 | +template <typename Type> |
| 23 | +struct TimeDurationCell<Type>::Data{ |
| 24 | + const std::string m_units; |
| 25 | + const Type m_min_value; |
| 26 | + const Type m_max_value; |
| 27 | + const std::string m_default; |
| 28 | + |
| 29 | + mutable SpinLock m_lock; |
| 30 | + std::string m_current; |
| 31 | + Type m_value; |
| 32 | + std::string m_error; |
| 33 | + |
| 34 | + Data( |
| 35 | + std::string units, |
| 36 | + Type min_value, Type max_value, |
| 37 | + std::string default_value |
| 38 | + ) |
| 39 | + : m_units(units) |
| 40 | + , m_min_value(min_value) |
| 41 | + , m_max_value(max_value) |
| 42 | + , m_default(std::move(default_value)) |
| 43 | + , m_current(m_default) |
| 44 | + , m_value(0) |
| 45 | + { |
| 46 | + m_error = process(m_current, m_value); |
| 47 | + } |
| 48 | + |
| 49 | + std::string process(const std::string& text, Type& value) const{ |
| 50 | + if (text.empty()){ |
| 51 | + return "Expression is empty."; |
| 52 | + } |
| 53 | + |
| 54 | + static const std::map<std::string, int64_t> SYMBOLS{ |
| 55 | + {"ms", std::chrono::duration_cast<Type>(std::chrono::milliseconds(1)).count()}, |
| 56 | + {"s", std::chrono::duration_cast<Type>(std::chrono::seconds(1)).count()}, |
| 57 | + {"min", std::chrono::duration_cast<Type>(std::chrono::minutes(1)).count()}, |
| 58 | + {"h", std::chrono::duration_cast<Type>(std::chrono::hours(1)).count()}, |
| 59 | + {"d", std::chrono::duration_cast<Type>(std::chrono::days(1)).count()}, |
| 60 | + {"y", std::chrono::duration_cast<Type>(std::chrono::years(1)).count()}, |
| 61 | + }; |
| 62 | + |
| 63 | + using Rep = typename Type::rep; |
| 64 | + Rep parsed; |
| 65 | + try{ |
| 66 | + parsed = parse_expression(SYMBOLS, text); |
| 67 | + }catch (const ParseException& str){ |
| 68 | + return str.message(); |
| 69 | + } |
| 70 | + // std::cout << "value = " << parsed << " " << m_min_value << " " << m_max_value << std::endl; |
| 71 | + |
| 72 | + if (Type(parsed) < m_min_value){ |
| 73 | + return "Overflow: Number is too small."; |
| 74 | + } |
| 75 | + if (Type(parsed) > m_max_value){ |
| 76 | + return "Overflow: Number is too large."; |
| 77 | + } |
| 78 | + value = (Type)parsed; |
| 79 | + return std::string(); |
| 80 | + } |
| 81 | +}; |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | +template <typename Type> |
| 88 | +TimeDurationCell<Type>::~TimeDurationCell() = default; |
| 89 | +template <typename Type> |
| 90 | +TimeDurationCell<Type>::TimeDurationCell( |
| 91 | + std::string units, |
| 92 | + LockMode lock_while_running, |
| 93 | + Type min_value, Type max_value, |
| 94 | + std::string default_value |
| 95 | +) |
| 96 | + : ConfigOption(lock_while_running) |
| 97 | + , m_data( |
| 98 | + CONSTRUCT_TOKEN, |
| 99 | + std::move(units), |
| 100 | + min_value, max_value, |
| 101 | + std::move(default_value) |
| 102 | + ) |
| 103 | +{} |
| 104 | + |
| 105 | +template <typename Type> |
| 106 | +TimeDurationCell<Type>::TimeDurationCell( |
| 107 | + std::string units, |
| 108 | + LockMode lock_while_running, |
| 109 | + std::string default_value |
| 110 | +) |
| 111 | + : ConfigOption(lock_while_running) |
| 112 | + , m_data( |
| 113 | + CONSTRUCT_TOKEN, |
| 114 | + std::move(units), |
| 115 | + Type(0), std::chrono::milliseconds::max(), |
| 116 | + std::move(default_value) |
| 117 | + ) |
| 118 | +{} |
| 119 | +template <typename Type> |
| 120 | +TimeDurationCell<Type>::TimeDurationCell( |
| 121 | + std::string units, |
| 122 | + LockMode lock_while_running, |
| 123 | + Type min_value, |
| 124 | + std::string default_value |
| 125 | +) |
| 126 | + : ConfigOption(lock_while_running) |
| 127 | + , m_data( |
| 128 | + CONSTRUCT_TOKEN, |
| 129 | + std::move(units), |
| 130 | + min_value, std::chrono::milliseconds::max(), |
| 131 | + std::move(default_value) |
| 132 | + ) |
| 133 | +{} |
| 134 | + |
| 135 | + |
| 136 | +template <typename Type> |
| 137 | +const std::string& TimeDurationCell<Type>::units() const{ |
| 138 | + return m_data->m_units; |
| 139 | +} |
| 140 | +template <typename Type> |
| 141 | +Type TimeDurationCell<Type>::min_value() const{ |
| 142 | + return m_data->m_min_value; |
| 143 | +} |
| 144 | +template <typename Type> |
| 145 | +Type TimeDurationCell<Type>::max_value() const{ |
| 146 | + return m_data->m_max_value; |
| 147 | +} |
| 148 | +template <typename Type> |
| 149 | +const std::string& TimeDurationCell<Type>::default_value() const{ |
| 150 | + return m_data->m_default; |
| 151 | +} |
| 152 | +template <typename Type> |
| 153 | +std::string TimeDurationCell<Type>::current_text() const{ |
| 154 | + const Data& data = *m_data; |
| 155 | + ReadSpinLock lg(data.m_lock); |
| 156 | + return data.m_current; |
| 157 | +} |
| 158 | +template <typename Type> |
| 159 | +TimeDurationCell<Type>::operator Type() const{ |
| 160 | + const Data& data = *m_data; |
| 161 | + ReadSpinLock lg(data.m_lock); |
| 162 | + return data.m_value; |
| 163 | +} |
| 164 | +template <typename Type> |
| 165 | +Type TimeDurationCell<Type>::get() const{ |
| 166 | + const Data& data = *m_data; |
| 167 | + ReadSpinLock lg(data.m_lock); |
| 168 | + return data.m_value; |
| 169 | +} |
| 170 | +template <typename Type> |
| 171 | +std::string TimeDurationCell<Type>::set(std::string text){ |
| 172 | + Data& data = *m_data; |
| 173 | + Type value(0); |
| 174 | + std::string error; |
| 175 | + { |
| 176 | + WriteSpinLock lg(data.m_lock); |
| 177 | + if (data.m_current == text){ |
| 178 | + return std::string(); |
| 179 | + } |
| 180 | + error = data.process(text, value); |
| 181 | + data.m_current = std::move(text); |
| 182 | + data.m_value = value; |
| 183 | + data.m_error.clear(); |
| 184 | + } |
| 185 | + report_value_changed(this); |
| 186 | + return error; |
| 187 | +} |
| 188 | + |
| 189 | +template <typename Type> |
| 190 | +std::string TimeDurationCell<Type>::time_string() const{ |
| 191 | + const Data& data = *m_data; |
| 192 | + ReadSpinLock lg(data.m_lock); |
| 193 | + if (!data.m_error.empty()){ |
| 194 | + return "<font color=\"red\">" + data.m_error + "</font>"; |
| 195 | + } |
| 196 | + return duration_to_string(std::chrono::duration_cast<Milliseconds>(data.m_value)); |
| 197 | +} |
| 198 | +template <typename Type> |
| 199 | +std::string TimeDurationCell<Type>::time_string(const std::string& text) const{ |
| 200 | + const Data& data = *m_data; |
| 201 | + Type value; |
| 202 | + std::string error = data.process(text, value); |
| 203 | + if (error.empty()){ |
| 204 | + return duration_to_string(std::chrono::duration_cast<Milliseconds>(value)); |
| 205 | + }else{ |
| 206 | + return "<font color=\"red\">" + error + "</font>"; |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +template <typename Type> |
| 211 | +void TimeDurationCell<Type>::load_json(const JsonValue& json){ |
| 212 | + Data& data = *m_data; |
| 213 | + const std::string* str = json.to_string(); |
| 214 | + if (str == nullptr){ |
| 215 | + return; |
| 216 | + } |
| 217 | + { |
| 218 | + WriteSpinLock lg(data.m_lock); |
| 219 | + data.m_current = *str; |
| 220 | + data.m_error = data.process(data.m_current, data.m_value); |
| 221 | + } |
| 222 | + report_value_changed(this); |
| 223 | +} |
| 224 | +template <typename Type> |
| 225 | +JsonValue TimeDurationCell<Type>::to_json() const{ |
| 226 | + const Data& data = *m_data; |
| 227 | + ReadSpinLock lg(data.m_lock); |
| 228 | + return data.m_current; |
| 229 | +} |
| 230 | + |
| 231 | +template <typename Type> |
| 232 | +std::string TimeDurationCell<Type>::check_validity() const{ |
| 233 | + const Data& data = *m_data; |
| 234 | + ReadSpinLock lg(data.m_lock); |
| 235 | + return data.m_error; |
| 236 | +} |
| 237 | +template <typename Type> |
| 238 | +void TimeDurationCell<Type>::restore_defaults(){ |
| 239 | + Data& data = *m_data; |
| 240 | + { |
| 241 | + WriteSpinLock lg(data.m_lock); |
| 242 | + data.m_current = data.m_default; |
| 243 | + data.m_error = data.process(data.m_current, data.m_value); |
| 244 | + } |
| 245 | + report_value_changed(this); |
| 246 | +} |
| 247 | + |
| 248 | + |
| 249 | + |
| 250 | + |
| 251 | + |
| 252 | + |
| 253 | + |
| 254 | + |
| 255 | + |
| 256 | +template <typename Type> |
| 257 | +ConfigWidget* TimeDurationCell<Type>::make_QtWidget(QWidget& parent){ |
| 258 | + return new TimeDurationCellWidget<Type>(parent, *this); |
| 259 | +} |
| 260 | + |
| 261 | +template <typename Type> |
| 262 | +ConfigWidget* TimeDurationOption<Type>::make_QtWidget(QWidget& parent){ |
| 263 | + return new TimeDurationOptionWidget<Type>(parent, *this); |
| 264 | +} |
| 265 | + |
| 266 | + |
| 267 | + |
| 268 | + |
| 269 | + |
| 270 | +template class TimeDurationCell<std::chrono::milliseconds>; |
| 271 | +template class TimeDurationOption<std::chrono::milliseconds>; |
| 272 | + |
| 273 | + |
| 274 | + |
| 275 | + |
| 276 | + |
| 277 | + |
| 278 | + |
| 279 | +} |
0 commit comments