113 if (ImGui::BeginCombo(fmt::format(
"Reference pin##{}",
size_t(
id)).c_str(),
inputPins.at(
_refPinIdx).name.c_str()))
115 for (
size_t i = 0; i <
inputPins.size(); i++)
118 if (ImGui::Selectable(
inputPins.at(i).name.c_str(), is_selected))
123 if (is_selected) { ImGui::SetItemDefaultFocus(); }
131 if (ImGui::Checkbox(fmt::format(
"Output missing as NaN##{}",
size_t(
id)).c_str(), &
_outputMissingAsNaN))
143 if (
ImGui::DragDouble(fmt::format(
"Max time diff to interpolate##{}",
size_t(
id)).c_str(),
157 if (
ImGui::DragDouble(fmt::format(
"Max observation time diff##{}",
size_t(
id)).c_str(),
169 ImGui::SetNextItemOpen(
false, ImGuiCond_FirstUseEver);
170 if (ImGui::CollapsingHeader(fmt::format(
"Pins##{}",
size_t(
id)).c_str()))
172 std::vector<size_t> pinIds;
174 for (
const auto& pin :
inputPins) { pinIds.push_back(
size_t(pin.id)); }
178 std::vector<size_t> inputPinIds;
180 for (
const auto& pin :
inputPins) { inputPinIds.push_back(
size_t(pin.id)); }
181 LOG_DATA(
"{}: old Input pin ids {}",
nameId(), fmt::join(pinIds,
", "));
182 LOG_DATA(
"{}: new Input pin ids {}",
nameId(), fmt::join(inputPinIds,
", "));
185 for (
size_t i = 0; i < pinIds.size(); i++)
187 auto pinId = pinIds.at(i);
188 const auto& inputPinId = size_t(
inputPins.at(i).id);
189 if (pinId != inputPinId)
192 LOG_DATA(
"{}: Pin {} moved index {} -> {}",
nameId(), pinId, i, newPinIdx);
195 for (
auto& term : comb.terms)
197 if (term.pinIndex == i) { term.pinIndex = newPinIdx + 10000; }
205 for (
auto& term : comb.terms)
207 if (term.pinIndex >= 10000) { term.pinIndex -= 10000; }
216 std::vector<size_t> combToDelete;
221 bool keepCombination =
true;
222 if (ImGui::CollapsingHeader(fmt::format(
"{}##id{} c{}", comb.description(
this),
size_t(
id), c).c_str(),
223 _combinations.size() > 1 ? &keepCombination :
nullptr, ImGuiTreeNodeFlags_DefaultOpen))
225 constexpr int COL_PER_TERM = 3;
229 std::vector<size_t> termToDelete;
230 bool addTerm =
false;
231 if (ImGui::BeginTable(fmt::format(
"##Table id{} c{}",
size_t(
id), c).c_str(), COL_PER_TERM *
static_cast<int>(comb.terms.size()),
232 ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX,
235 ImGui::TableNextRow();
237 for (
size_t t = 0; t < comb.terms.size(); t++)
239 auto& term = comb.terms.at(t);
241 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM);
242 ImGui::SetNextItemWidth(COL_SIGN_WIDTH);
243 if (ImGui::InputDouble(fmt::format(
"##factor id{} c{} t{}",
size_t(
id), c, t).c_str(), &term.factor, 0.0, 0.0,
"%.2f"))
248 ImGui::TextUnformatted(
"*");
251 ImGui::SetNextItemWidth(COL_PIN_WIDTH);
252 if (ImGui::BeginCombo(fmt::format(
"##pinIndex id{} c{} t{}",
size_t(
id), c, t).c_str(),
inputPins.at(term.pinIndex).name.c_str()))
254 for (
size_t i = 0; i <
inputPins.size(); i++)
256 const bool is_selected = term.pinIndex == i;
257 if (ImGui::Selectable(
inputPins.at(i).name.c_str(), is_selected))
260 term.dataSelection = size_t(0);
263 if (is_selected) { ImGui::SetItemDefaultFocus(); }
268 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM + 1);
269 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 2.0F);
271 ImGui::GetWindowDrawList()->AddCircleFilled(ImGui::GetCursorScreenPos() + ImVec2(radius, ImGui::GetTextLineHeight() / 2.0F + 2.0F), radius,
272 term.polyReg.empty() ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
273 ImGui::Dummy(ImVec2(radius * 2.0F, ImGui::GetTextLineHeight()));
274 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(term.polyReg.empty() ?
"Signal was not received" :
"Signal was received"); }
276 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM + 2);
277 if (
static_cast<int>(t) * COL_PER_TERM + 2 == COL_PER_TERM *
static_cast<int>(comb.terms.size()) - 1)
279 addTerm |= ImGui::Button(fmt::format(
"+## Add Term id{} c{}",
size_t(
id), c).c_str());
280 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Add Term?"); }
284 ImGui::TextUnformatted(
"+");
288 ImGui::TableNextRow();
289 for (
size_t t = 0; t < comb.terms.size(); t++)
291 auto& term = comb.terms.at(t);
293 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM);
295 if ((std::holds_alternative<size_t>(term.dataSelection) && std::get<size_t>(term.dataSelection) < dataDescriptors.size())
296 || std::holds_alternative<std::string>(term.dataSelection))
298 std::string previewText = std::holds_alternative<size_t>(term.dataSelection)
299 ? dataDescriptors.at(std::get<size_t>(term.dataSelection))
300 : std::get<std::string>(term.dataSelection);
301 float comboWidth = COL_SIGN_WIDTH + COL_PIN_WIDTH + ImGui::CalcTextSize(
"*").x + 2.0F * ImGui::GetStyle().ItemSpacing.x;
302 ImGui::SetNextItemWidth(comboWidth);
303 if (ImGui::BeginCombo(fmt::format(
"##dataIndex id{} c{} t{}",
size_t(
id), c, t).c_str(), previewText.c_str()))
305 for (
size_t i = 0; i < dataDescriptors.size(); i++)
307 const bool is_selected = std::holds_alternative<size_t>(term.dataSelection)
308 ? std::get<size_t>(term.dataSelection) == i
309 : dataDescriptors.at(i) == previewText;
310 if (ImGui::Selectable(dataDescriptors.at(i).c_str(), is_selected))
312 if (dataDescriptors.size() - i <=
_pinData.at(term.pinIndex).dynDataDescriptors.size())
314 term.dataSelection = dataDescriptors.at(i);
318 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;
347 if (is_selected) { ImGui::SetItemDefaultFocus(); }
351 if (ImGui::IsItemHovered() && ImGui::CalcTextSize(previewText.c_str()).x > comboWidth - 15.0F)
353 ImGui::SetTooltip(
"%s", previewText.c_str());
356 else if (
inputPins.at(term.pinIndex).isPinLinked())
358 ImGui::TextUnformatted(
"Please run the flow");
363 if (comb.terms.size() > 1)
365 ImGui::TableSetColumnIndex(
static_cast<int>(t) * COL_PER_TERM + 1);
366 if (ImGui::Button(fmt::format(
"X##id{} c{} t{}",
size_t(
id), c, t).c_str()))
369 termToDelete.push_back(t);
371 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(
"Remove Term?"); }
378 for (
const auto& t : termToDelete) { comb.terms.erase(std::next(comb.terms.begin(),
static_cast<std::ptrdiff_t
>(t))); }
382 comb.terms.emplace_back();
386 if (!keepCombination) { combToDelete.push_back(c); }
391 if (ImGui::Button(fmt::format(
"Add Combination##id{}",
size_t(
id)).c_str()))
510 LOG_DATA(
"{}: [{:.3f}s][{} ({})] Received obs for time [{} GPST] ",
nameId(),
511 nodeDataTimeIntoRun,
inputPins.at(pinIdx).name, pinIdx, nodeData->insTime.toYMDHMS(
GPST));
513 if (!
_pinData.at(pinIdx).lastTime.empty())
515 auto dt =
static_cast<double>((nodeData->insTime -
_pinData.at(pinIdx).lastTime).count());
516 if (dt > 1e-6) {
_pinData.at(pinIdx).minTimeStep = std::min(
_pinData.at(pinIdx).minTimeStep, dt); }
518 _pinData.at(pinIdx).lastTime = nodeData->insTime;
522 std::vector<SendRequest> requests;
527 [&](
const Combination::Term& term) { return _pinData.at(term.pinIndex).lastTime.empty()
528 && (inputPins.at(term.pinIndex).queue.empty() || inputPins.at(term.pinIndex).queue.front()->insTime != nodeData->insTime); }))
538 requests.push_back(sr);
540 if (!requests.empty())
542 LOG_DATA(
"{}: Adding new send request with",
nameId(), nodeDataTimeIntoRun, nodeData->insTime.toYMDHMS(
GPST));
543 for ([[maybe_unused]]
const auto& request : requests)
552 auto& dataDescriptors =
_pinData.at(pinIdx).dynDataDescriptors;
553 const std::vector<std::string> nodeDataDescriptors = nodeData->dynamicDataDescriptors();
554 for (
const auto& desc : nodeDataDescriptors)
556 if (std::ranges::find(dataDescriptors, desc) == dataDescriptors.end())
558 dataDescriptors.push_back(desc);
562 auto* sourcePin =
inputPins.at(pinIdx).link.getConnectedPin();
563 if (sourcePin ==
nullptr) {
return; }
568 LOG_DATA(
"{}: Combination: {}",
nameId(), comb.description(
this));
569 for (
size_t t = 0; t < comb.terms.size(); ++t)
571 auto& term = comb.terms.at(t);
572 if (term.pinIndex != pinIdx) {
continue; }
573 if (std::holds_alternative<size_t>(term.dataSelection) && nodeData->staticDescriptorCount() <= std::get<size_t>(term.dataSelection)
574 && std::get<size_t>(term.dataSelection) < dataDescriptors.size())
576 term.dataSelection = dataDescriptors.at(std::get<size_t>(term.dataSelection));
580 if (
auto value = std::holds_alternative<size_t>(term.dataSelection) ? nodeData->getValueAt(std::get<size_t>(term.dataSelection))
581 : nodeData->getDynamicDataAt(std::get<std::string>(term.dataSelection)))
584 term.polyReg.push_back(std::make_pair(nodeDataTimeIntoRun, *value));
590 term.rawData.push_back(nodeData);
591 LOG_DATA(
"{}: Adding NodeData to the end of term.rawData. It now includes:",
nameId());
592 for ([[maybe_unused]]
const auto& data : term.rawData)
599 if (std::ranges::any_of(comb.terms, [&](
const auto& t) {
600 bool termHasNoData = t.rawData.empty();
603 LOG_DATA(
"{}: Skipping, because no data on other term '{}' yet", nameId(), t.description(this, getDataDescriptors(t.pinIndex)));
605 return termHasNoData;
614 std::set<size_t> combsToRemove;
615 for (
auto& sendRequest : sendRequests)
617 if (sendRequest.combIndex != c) {
continue; }
618 const auto& srComb =
_combinations.at(sendRequest.combIndex);
620 for (
const auto& srTerm : srComb.terms)
622 if (combsToRemove.contains(sendRequest.combIndex)) {
continue; }
623 if (srTerm.pinIndex != term.pinIndex || srTerm.dataSelection != term.dataSelection) {
continue; }
625 sendRequest.termIndices.contains(t) ?
"already calculated." :
"still missing");
627 if (sendRequest.termIndices.contains(t)) {
continue; }
629 if (
auto dt =
static_cast<double>((nodeData->insTime - sendRequestTime).count());
632 &&
static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count())
637 sendRequest.result = std::nan(
"");
638 LOG_DATA(
"{}: Setting combination {} to NaN (({} && dt = {} > dt_min = {}) || ({} && dt_interp = {} > {}))",
nameId(),
642 static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count()),
647 combsToRemove.emplace(sendRequest.combIndex);
648 LOG_DATA(
"{}: Removing combination {} (({} && dt = {} > dt_min = {}) || ({} && dt_interp = {} > {}))",
nameId(),
652 static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count()),
658 if (term.polyReg.empty())
660 sendRequest.termNullopt =
true;
665 if (
auto poly = term.polyReg.calcPolynomial())
667 LOG_DATA(
"{}: Updating send request: {} += {:.2f} * {:.3g} (by interpolating to time [{:.3f}s])",
nameId(),
673 sendRequest.termIndices.insert(t);
675 bool exactTimeFound =
false;
676 for (
const auto& rawData : term.rawData)
678 if (rawData->insTime == sendRequestTime)
681 rawData->insTime.toYMDHMS(
GPST),
684 sendRequest.rawData.emplace_back(term.description(
this,
getDataDescriptors(term.pinIndex)),
686 exactTimeFound =
true;
690 if (exactTimeFound) {
continue; }
691 for (
const auto& rawData : term.rawData)
694 rawData->insTime.toYMDHMS(
GPST),
697 sendRequest.rawData.emplace_back(term.description(
this,
getDataDescriptors(term.pinIndex)),
702 for (
const auto& comb : combsToRemove)
710 std::vector<InsTime> emptySendRequests;
711 for (
const auto& [sendRequestTime, sendRequests] : _sendRequests)
713 if (sendRequests.empty())
715 LOG_DATA(
"{}: Discarding send request at [{}]", nameId(), sendRequestTime.toYMDHMS(
GPST));
716 emptySendRequests.push_back(sendRequestTime);
719 for (
const auto& sendRequestTime : emptySendRequests) { _sendRequests.erase(sendRequestTime); }
721 if (!_sendRequests.empty()) {
LOG_DATA(
"{}: Send requests ({})", nameId(), _sendRequests.size()); }
722 for (
const auto& [sendRequestTime, sendRequests] : _sendRequests)
724 LOG_DATA(
"{}: [{:.3f}s] [{}]", nameId(),
math::round(calcTimeIntoRun(sendRequestTime), 8), sendRequestTime.toYMDHMS(
GPST));
725 for (
const auto& sendRequest : sendRequests)
727 const auto& comb = _combinations.at(sendRequest.combIndex);
728 LOG_DATA(
"{}: Combination: {}{}", nameId(), comb.description(
this), sendRequest.termNullopt ?
" (some term war nullopt)" :
"");
729 for (
size_t t = 0; t < comb.terms.size(); ++t)
731 LOG_DATA(
"{}: Term '{}' is {}", nameId(), comb.terms.at(t).description(
this, getDataDescriptors(comb.terms.at(t).pinIndex)),
732 sendRequest.termIndices.contains(t) ?
"added" :
"missing");
737 LOG_DATA(
"{}: [{:.3f}s] Checking wether a send request can be sent ({} requests)", nameId(), nodeDataTimeIntoRun, _sendRequests.size());
738 std::vector<InsTime> requestsToRemove;
739 for (
const auto& [sendRequestTime, sendRequests] : _sendRequests)
741 LOG_DATA(
"{}: [{:.3f}s] [{}]", nameId(),
math::round(calcTimeIntoRun(sendRequestTime), 8), sendRequestTime.toYMDHMS(
GPST));
742 LOG_DATA(
"{}: Combinations (all terms in all combinations must be calculated)", nameId());
743 if (std::ranges::all_of(sendRequests, [&](
const auto& sendRequest) {
744 const auto& comb = _combinations.at(sendRequest.combIndex);
745 LOG_DATA(
"{}: '{}' has {}/{} terms set", nameId(), comb.description(
this), sendRequest.termIndices.size(), comb.terms.size());
746 return comb.terms.size() == sendRequest.termIndices.size();
750 auto dynData = std::make_shared<DynamicData>();
751 dynData->insTime = sendRequestTime;
752 LOG_DATA(
"{}: Sending out dynamic data at [{}] and deleting send request", nameId(), dynData->insTime.toYMDHMS(
GPST));
754 for (
const auto& sendRequest : sendRequests)
756 if (sendRequest.termNullopt) {
continue; }
757 const auto& comb = _combinations.at(sendRequest.combIndex);
759 .description = comb.description(
this),
760 .value = sendRequest.result,
761 .rawData = sendRequest.rawData
763 LOG_DATA(
"{}: {} includes raw data", nameId(), data.description);
764 for ([[maybe_unused]]
const auto& raw : data.rawData)
766 LOG_DATA(
"{}: [{}] from '{}'", nameId(), raw.second->insTime.toYMDHMS(
GPST), raw.first);
768 dynData->data.push_back(data);
771 invokeCallbacks(OUTPUT_PORT_INDEX_DYN_DATA, dynData);
773 requestsToRemove.push_back(sendRequestTime);
781 for (
const auto& insTime : requestsToRemove)
783 _sendRequests.erase(insTime);