115 if (ImGui::BeginCombo(fmt::format(
"Reference pin##{}",
size_t(
id)).c_str(),
inputPins.at(
_refPinIdx).name.c_str()))
117 for (
size_t i = 0; i <
inputPins.size(); i++)
120 if (ImGui::Selectable(
inputPins.at(i).name.c_str(), is_selected))
125 if (is_selected) { ImGui::SetItemDefaultFocus(); }
133 if (ImGui::Checkbox(fmt::format(
"Output missing as NaN##{}",
size_t(
id)).c_str(), &
_outputMissingAsNaN))
145 if (
ImGui::DragDouble(fmt::format(
"Max time diff to interpolate##{}",
size_t(
id)).c_str(),
159 if (
ImGui::DragDouble(fmt::format(
"Max observation time diff##{}",
size_t(
id)).c_str(),
171 ImGui::SetNextItemOpen(
false, ImGuiCond_FirstUseEver);
172 if (ImGui::CollapsingHeader(fmt::format(
"Pins##{}",
size_t(
id)).c_str()))
174 std::vector<size_t> pinIds;
176 for (
const auto& pin :
inputPins) { pinIds.push_back(
size_t(pin.id)); }
180 std::vector<size_t> inputPinIds;
182 for (
const auto& pin :
inputPins) { inputPinIds.push_back(
size_t(pin.id)); }
183 LOG_DATA(
"{}: old Input pin ids {}",
nameId(), fmt::join(pinIds,
", "));
184 LOG_DATA(
"{}: new Input pin ids {}",
nameId(), fmt::join(inputPinIds,
", "));
187 for (
size_t i = 0; i < pinIds.size(); i++)
189 auto pinId = pinIds.at(i);
190 const auto& inputPinId = size_t(
inputPins.at(i).id);
191 if (pinId != inputPinId)
194 LOG_DATA(
"{}: Pin {} moved index {} -> {}",
nameId(), pinId, i, newPinIdx);
197 for (
auto& term : comb.terms)
199 if (term.pinIndex == i) { term.pinIndex = newPinIdx + 10000; }
207 for (
auto& term : comb.terms)
209 if (term.pinIndex >= 10000) { term.pinIndex -= 10000; }
218 std::vector<size_t> combToDelete;
223 bool keepCombination =
true;
224 if (ImGui::CollapsingHeader(fmt::format(
"{}##id{} c{}", comb.description(
this),
size_t(
id), c).c_str(),
225 _combinations.size() > 1 ? &keepCombination :
nullptr, ImGuiTreeNodeFlags_DefaultOpen))
227 constexpr int COL_PER_TERM = 3;
231 std::vector<size_t> termToDelete;
232 bool addTerm =
false;
233 if (ImGui::BeginTable(fmt::format(
"##Table id{} c{}",
size_t(
id), c).c_str(), COL_PER_TERM *
static_cast<int>(comb.terms.size()),
234 ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX,
237 ImGui::TableNextRow();
239 for (
size_t t = 0; t < comb.terms.size(); t++)
241 auto& term = comb.terms.at(t);
243 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM);
244 ImGui::SetNextItemWidth(COL_SIGN_WIDTH);
245 if (ImGui::InputDouble(fmt::format(
"##factor id{} c{} t{}",
size_t(
id), c, t).c_str(), &term.factor, 0.0, 0.0,
"%.2f"))
250 ImGui::TextUnformatted(
"*");
253 ImGui::SetNextItemWidth(COL_PIN_WIDTH);
254 if (ImGui::BeginCombo(fmt::format(
"##pinIndex id{} c{} t{}",
size_t(
id), c, t).c_str(),
inputPins.at(term.pinIndex).name.c_str()))
256 for (
size_t i = 0; i <
inputPins.size(); i++)
258 const bool is_selected = term.pinIndex == i;
259 if (ImGui::Selectable(
inputPins.at(i).name.c_str(), is_selected))
262 term.dataSelection = size_t(0);
265 if (is_selected) { ImGui::SetItemDefaultFocus(); }
270 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM + 1);
271 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 2.0F);
273 ImGui::GetWindowDrawList()->AddCircleFilled(ImGui::GetCursorScreenPos() + ImVec2(radius, ImGui::GetTextLineHeight() / 2.0F + 2.0F), radius,
274 term.polyReg.empty() ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
275 ImGui::Dummy(ImVec2(radius * 2.0F, ImGui::GetTextLineHeight()));
276 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(term.polyReg.empty() ?
"Signal was not received" :
"Signal was received"); }
278 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM + 2);
279 if (
static_cast<int>(t) * COL_PER_TERM + 2 == COL_PER_TERM *
static_cast<int>(comb.terms.size()) - 1)
281 addTerm |= ImGui::Button(fmt::format(
"+## Add Term id{} c{}",
size_t(
id), c).c_str());
282 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Add Term?"); }
286 ImGui::TextUnformatted(
"+");
290 ImGui::TableNextRow();
291 for (
size_t t = 0; t < comb.terms.size(); t++)
293 auto& term = comb.terms.at(t);
295 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM);
297 if ((std::holds_alternative<size_t>(term.dataSelection) && std::get<size_t>(term.dataSelection) < dataDescriptors.size())
298 || std::holds_alternative<std::string>(term.dataSelection))
300 std::string previewText = std::holds_alternative<size_t>(term.dataSelection)
301 ? dataDescriptors.at(std::get<size_t>(term.dataSelection))
302 : std::get<std::string>(term.dataSelection);
303 float comboWidth = COL_SIGN_WIDTH + COL_PIN_WIDTH + ImGui::CalcTextSize(
"*").x + 2.0F * ImGui::GetStyle().ItemSpacing.x;
304 ImGui::SetNextItemWidth(comboWidth);
305 if (ImGui::BeginCombo(fmt::format(
"##dataIndex id{} c{} t{}",
size_t(
id), c, t).c_str(), previewText.c_str()))
307 for (
size_t i = 0; i < dataDescriptors.size(); i++)
309 const bool is_selected = std::holds_alternative<size_t>(term.dataSelection)
310 ? std::get<size_t>(term.dataSelection) == i
311 : dataDescriptors.at(i) == previewText;
312 if (ImGui::Selectable(dataDescriptors.at(i).c_str(), is_selected))
314 if (dataDescriptors.size() - i <=
_pinData.at(term.pinIndex).dynDataDescriptors.size())
316 term.dataSelection = dataDescriptors.at(i);
320 term.dataSelection = i;
322 for (
size_t t2 = 0; t2 < comb.terms.size(); t2++)
324 if (t2 == t) {
continue; }
325 auto& term2 = comb.terms.at(t2);
327 auto iter = std::ranges::find(dataDescriptors2,
328 std::holds_alternative<size_t>(term.dataSelection)
329 ? dataDescriptors.at(std::get<size_t>(term.dataSelection))
330 : std::get<std::string>(term.dataSelection));
331 if (iter != dataDescriptors2.end())
333 if (std::holds_alternative<size_t>(term.dataSelection))
335 term2.dataSelection =
static_cast<size_t>(std::distance(dataDescriptors2.begin(), iter));
339 term2.dataSelection = term.dataSelection;
346 if (is_selected) { ImGui::SetItemDefaultFocus(); }
350 if (ImGui::IsItemHovered() && ImGui::CalcTextSize(previewText.c_str()).x > comboWidth - 15.0F)
352 ImGui::SetTooltip(
"%s", previewText.c_str());
355 else if (
inputPins.at(term.pinIndex).isPinLinked())
357 ImGui::TextUnformatted(
"Please run the flow");
362 if (comb.terms.size() > 1)
364 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM + 1);
365 if (ImGui::Button(fmt::format(
"X##id{} c{} t{}",
size_t(
id), c, t).c_str()))
368 termToDelete.push_back(t);
370 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Remove Term?"); }
377 for (
const auto& t : termToDelete) { comb.terms.erase(std::next(comb.terms.begin(),
static_cast<std::ptrdiff_t
>(t))); }
381 comb.terms.emplace_back();
385 if (!keepCombination) { combToDelete.push_back(c); }
390 if (ImGui::Button(fmt::format(
"Add Combination##id{}",
size_t(
id)).c_str()))
509 LOG_DATA(
"{}: [{:.3f}s][{} ({})] Received obs for time [{} GPST] ",
nameId(),
510 nodeDataTimeIntoRun,
inputPins.at(pinIdx).name, pinIdx, nodeData->insTime.toYMDHMS(
GPST));
512 if (!
_pinData.at(pinIdx).lastTime.empty())
514 auto dt =
static_cast<double>((nodeData->insTime -
_pinData.at(pinIdx).lastTime).count());
515 if (dt > 1e-6) {
_pinData.at(pinIdx).minTimeStep = std::min(
_pinData.at(pinIdx).minTimeStep, dt); }
517 _pinData.at(pinIdx).lastTime = nodeData->insTime;
521 std::vector<SendRequest> requests;
526 [&](
const Combination::Term& term) { return _pinData.at(term.pinIndex).lastTime.empty()
527 && (inputPins.at(term.pinIndex).queue.empty() || inputPins.at(term.pinIndex).queue.front()->insTime != nodeData->insTime); }))
537 requests.push_back(sr);
539 if (!requests.empty())
541 LOG_DATA(
"{}: Adding new send request with",
nameId(), nodeDataTimeIntoRun, nodeData->insTime.toYMDHMS(
GPST));
542 for ([[maybe_unused]]
const auto& request : requests)
551 auto& dataDescriptors =
_pinData.at(pinIdx).dynDataDescriptors;
552 const std::vector<std::string> nodeDataDescriptors = nodeData->dynamicDataDescriptors();
553 for (
const auto& desc : nodeDataDescriptors)
555 if (std::ranges::find(dataDescriptors, desc) == dataDescriptors.end())
557 dataDescriptors.push_back(desc);
561 auto* sourcePin =
inputPins.at(pinIdx).link.getConnectedPin();
562 if (sourcePin ==
nullptr) {
return; }
567 LOG_DATA(
"{}: Combination: {}",
nameId(), comb.description(
this));
568 for (
size_t t = 0; t < comb.terms.size(); ++t)
570 auto& term = comb.terms.at(t);
571 if (term.pinIndex != pinIdx) {
continue; }
572 if (std::holds_alternative<size_t>(term.dataSelection) && nodeData->staticDescriptorCount() <= std::get<size_t>(term.dataSelection)
573 && std::get<size_t>(term.dataSelection) < dataDescriptors.size())
575 term.dataSelection = dataDescriptors.at(std::get<size_t>(term.dataSelection));
579 if (
auto value = std::holds_alternative<size_t>(term.dataSelection) ? nodeData->getValueAt(std::get<size_t>(term.dataSelection))
580 : nodeData->getDynamicDataAt(std::get<std::string>(term.dataSelection)))
583 term.polyReg.push_back(std::make_pair(nodeDataTimeIntoRun, *value));
589 term.rawData.push_back(nodeData);
590 LOG_DATA(
"{}: Adding NodeData to the end of term.rawData. It now includes:",
nameId());
591 for ([[maybe_unused]]
const auto& data : term.rawData)
598 if (std::ranges::any_of(comb.terms, [&](
const auto& t) {
599 bool termHasNoData = t.rawData.empty();
602 LOG_DATA(
"{}: Skipping, because no data on other term '{}' yet", nameId(), t.description(this, getDataDescriptors(t.pinIndex)));
604 return termHasNoData;
613 std::set<size_t> combsToRemove;
614 for (
auto& sendRequest : sendRequests)
616 if (sendRequest.combIndex != c) {
continue; }
617 const auto& srComb =
_combinations.at(sendRequest.combIndex);
619 for (
const auto& srTerm : srComb.terms)
621 if (combsToRemove.contains(sendRequest.combIndex)) {
continue; }
622 if (srTerm.pinIndex != term.pinIndex || srTerm.dataSelection != term.dataSelection) {
continue; }
624 sendRequest.termIndices.contains(t) ?
"already calculated." :
"still missing");
626 if (sendRequest.termIndices.contains(t)) {
continue; }
628 if (
auto dt =
static_cast<double>((nodeData->insTime - sendRequestTime).count());
631 &&
static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count())
636 sendRequest.result = std::nan(
"");
637 LOG_DATA(
"{}: Setting combination {} to NaN (({} && dt = {} > dt_min = {}) || ({} && dt_interp = {} > {}))",
nameId(),
641 static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count()),
646 combsToRemove.emplace(sendRequest.combIndex);
647 LOG_DATA(
"{}: Removing combination {} (({} && dt = {} > dt_min = {}) || ({} && dt_interp = {} > {}))",
nameId(),
651 static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count()),
657 if (term.polyReg.empty())
659 sendRequest.termNullopt =
true;
664 auto poly = term.polyReg.calcPolynomial();
665 LOG_DATA(
"{}: Updating send request: {} += {:.2f} * {:.3g} (by interpolating to time [{:.3f}s])",
nameId(),
670 sendRequest.termIndices.insert(t);
672 bool exactTimeFound =
false;
673 for (
const auto& rawData : term.rawData)
675 if (rawData->insTime == sendRequestTime)
678 rawData->insTime.toYMDHMS(
GPST),
681 sendRequest.rawData.emplace_back(term.description(
this,
getDataDescriptors(term.pinIndex)),
683 exactTimeFound =
true;
687 if (exactTimeFound) {
continue; }
688 for (
const auto& rawData : term.rawData)
691 rawData->insTime.toYMDHMS(
GPST),
694 sendRequest.rawData.emplace_back(term.description(
this,
getDataDescriptors(term.pinIndex)),
699 for (
const auto& comb : combsToRemove)
707 std::vector<InsTime> emptySendRequests;
708 for (
const auto& [sendRequestTime, sendRequests] : _sendRequests)
710 if (sendRequests.empty())
712 LOG_DATA(
"{}: Discarding send request at [{}]", nameId(), sendRequestTime.toYMDHMS(
GPST));
713 emptySendRequests.push_back(sendRequestTime);
716 for (
const auto& sendRequestTime : emptySendRequests) { _sendRequests.erase(sendRequestTime); }
718 if (!_sendRequests.empty()) {
LOG_DATA(
"{}: Send requests ({})", nameId(), _sendRequests.size()); }
719 for (
const auto& [sendRequestTime, sendRequests] : _sendRequests)
721 LOG_DATA(
"{}: [{:.3f}s] [{}]", nameId(),
math::round(calcTimeIntoRun(sendRequestTime), 8), sendRequestTime.toYMDHMS(
GPST));
722 for (
const auto& sendRequest : sendRequests)
724 const auto& comb = _combinations.at(sendRequest.combIndex);
725 LOG_DATA(
"{}: Combination: {}{}", nameId(), comb.description(
this), sendRequest.termNullopt ?
" (some term war nullopt)" :
"");
726 for (
size_t t = 0; t < comb.terms.size(); ++t)
728 LOG_DATA(
"{}: Term '{}' is {}", nameId(), comb.terms.at(t).description(
this, getDataDescriptors(comb.terms.at(t).pinIndex)),
729 sendRequest.termIndices.contains(t) ?
"added" :
"missing");
734 LOG_DATA(
"{}: [{:.3f}s] Checking wether a send request can be sent ({} requests)", nameId(), nodeDataTimeIntoRun, _sendRequests.size());
735 std::vector<InsTime> requestsToRemove;
736 for (
const auto& [sendRequestTime, sendRequests] : _sendRequests)
738 LOG_DATA(
"{}: [{:.3f}s] [{}]", nameId(),
math::round(calcTimeIntoRun(sendRequestTime), 8), sendRequestTime.toYMDHMS(
GPST));
739 LOG_DATA(
"{}: Combinations (all terms in all combinations must be calculated)", nameId());
740 if (std::ranges::all_of(sendRequests, [&](
const auto& sendRequest) {
741 const auto& comb = _combinations.at(sendRequest.combIndex);
742 LOG_DATA(
"{}: '{}' has {}/{} terms set", nameId(), comb.description(
this), sendRequest.termIndices.size(), comb.terms.size());
743 return comb.terms.size() == sendRequest.termIndices.size();
747 auto dynData = std::make_shared<DynamicData>();
748 dynData->insTime = sendRequestTime;
749 LOG_DATA(
"{}: Sending out dynamic data at [{}] and deleting send request", nameId(), dynData->insTime.toYMDHMS(
GPST));
751 for (
const auto& sendRequest : sendRequests)
753 if (sendRequest.termNullopt) {
continue; }
754 const auto& comb = _combinations.at(sendRequest.combIndex);
756 .description = comb.description(
this),
757 .value = sendRequest.result,
758 .rawData = sendRequest.rawData
760 LOG_DATA(
"{}: {} includes raw data", nameId(), data.description);
761 for ([[maybe_unused]]
const auto& raw : data.rawData)
763 LOG_DATA(
"{}: [{}] from '{}'", nameId(), raw.second->insTime.toYMDHMS(
GPST), raw.first);
765 dynData->data.push_back(data);
768 invokeCallbacks(OUTPUT_PORT_INDEX_DYN_DATA, dynData);
770 requestsToRemove.push_back(sendRequestTime);
778 for (
const auto& insTime : requestsToRemove)
780 _sendRequests.erase(insTime);