#include "surec_yonetici_panel.hpp" #include #include // GetOpenFileNameW — Dosya Gönder diyaloğu #include #include #include #include "imgui.h" #include "fiko/cekirdek/cekirdek.hpp" // Cekirdek + olay struct'ları (SurecKatildi…) #include "fiko/ui/pencere_cerceve.hpp" #include "orkestra_kule.hpp" // g_orkestraKule — node şeridi (Fiko Orchestrator yüzü) namespace fiko { namespace { constexpr size_t OLAY_KAPASITE = 2048; // forward olay akışı ring kapasitesi std::string kucult(const std::string& s) { std::string r = s; for (char& c : r) c = (char)std::tolower((unsigned char)c); return r; } // Saniye → "s", "mm:ss" veya "sa:dd:ss". std::string uptimeMetin(uint64_t sn) { char buf[32]; if (sn < 60) std::snprintf(buf, sizeof(buf), "%llus", (unsigned long long)sn); else if (sn < 3600) std::snprintf(buf, sizeof(buf), "%llu:%02llu", (unsigned long long)(sn / 60), (unsigned long long)(sn % 60)); else std::snprintf(buf, sizeof(buf), "%llu:%02llu:%02llu", (unsigned long long)(sn / 3600), (unsigned long long)((sn % 3600) / 60), (unsigned long long)(sn % 60)); return buf; } const char* durumMetin(SurecDurum d) { switch (d) { case SurecDurum::Aktif: return "Aktif"; case SurecDurum::Mesgul: return "Meşgul"; case SurecDurum::Yanitsiz: return "Yanıtsız"; } return "?"; } ImVec4 durumRenk(SurecDurum d) { switch (d) { case SurecDurum::Aktif: return ImVec4(0.36f, 0.82f, 0.44f, 1); // yeşil case SurecDurum::Mesgul: return ImVec4(0.95f, 0.76f, 0.28f, 1); // sarı case SurecDurum::Yanitsiz: return ImVec4(0.62f, 0.62f, 0.66f, 1); // gri } return ImVec4(1, 1, 1, 1); } ImVec4 nodeDurumRenk(orkestra::NodeDurum d) { using orkestra::NodeDurum; switch (d) { case NodeDurum::Online: case NodeDurum::Idle: return ImVec4(0.36f, 0.82f, 0.44f, 1); // yeşil case NodeDurum::Busy: return ImVec4(0.95f, 0.76f, 0.28f, 1); // sarı case NodeDurum::Error: return ImVec4(0.92f, 0.32f, 0.32f, 1); // kırmızı case NodeDurum::Yanitsiz: return ImVec4(0.95f, 0.55f, 0.20f, 1); // turuncu case NodeDurum::Offline: return ImVec4(0.62f, 0.62f, 0.66f, 1); // gri } return ImVec4(1, 1, 1, 1); } // PID'e ait görünür üst-seviye pencereyi bul (Odakla için). HWND pencereBul(uint32_t pid) { struct Ara { uint32_t pid; HWND bulunan; } ara{pid, nullptr}; EnumWindows( [](HWND h, LPARAM lp) -> BOOL { auto* a = (Ara*)lp; DWORD p = 0; GetWindowThreadProcessId(h, &p); if (p == a->pid && IsWindowVisible(h) && GetWindow(h, GW_OWNER) == nullptr) { a->bulunan = h; return FALSE; // dur } return TRUE; }, (LPARAM)&ara); return ara.bulunan; } } // namespace void FikoSurecYoneticiPanel::degistir() { acik_ = !acik_; if (!acik_) kopru_.olayAboneligiYayinla(false); // kapanış → forward aboneliğini iptal et } void FikoSurecYoneticiPanel::olayEkle(const ForwardOlay& fo) { if (olaylar_.size() >= OLAY_KAPASITE) olaylar_.erase(olaylar_.begin(), olaylar_.begin() + 256); // en eski batch'i at olaylar_.push_back(fo); } void FikoSurecYoneticiPanel::listeyiYenile() { const double simdi = ImGui::GetTime(); if (sonYenile_ >= 0.0 && simdi - sonYenile_ < 0.5) return; // 500 ms throttle sonYenile_ = simdi; liste_ = kopru_.surecler(); // Diff → SurecKatildi/Ayrildi/DurumDegisti (yalnız dashboard açıkken izlenir). if (!diffKuruldu_) { // ilk yenilemede mevcutları seed'le (spam yok) for (const SurecBilgi& s : liste_) oncekiDurum_.push_back({s.pid, (uint32_t)s.durum}); diffKuruldu_ = true; return; } OlayGunlugu& olaylar = cek_.olaylar(); // Katılan / durum değişen. for (const SurecBilgi& s : liste_) { auto it = std::find_if(oncekiDurum_.begin(), oncekiDurum_.end(), [&](auto& e) { return e.first == s.pid; }); if (it == oncekiDurum_.end()) { olaylar.yayinla(SurecKatildi{s.pid}, s.belge.c_str()); } else if (it->second != (uint32_t)s.durum) { olaylar.yayinla(SurecDurumDegisti{s.pid, (uint32_t)s.durum}, durumMetin(s.durum)); } } // Ayrılan. for (auto& e : oncekiDurum_) { const bool var = std::any_of(liste_.begin(), liste_.end(), [&](const SurecBilgi& s) { return s.pid == e.first; }); if (!var) olaylar.yayinla(SurecAyrildi{e.first}, "Süreç ayrıldı"); } // Snapshot güncelle. oncekiDurum_.clear(); for (const SurecBilgi& s : liste_) oncekiDurum_.push_back({s.pid, (uint32_t)s.durum}); } void FikoSurecYoneticiPanel::surecTablosu() { const uint32_t benim = kopru_.benimPid(); const uint64_t simdi = (uint64_t)std::time(nullptr); // En eski süreç = "ana" (ikinci-onay guard'ı için). uint32_t anaPid = 0; uint64_t enEski = ~0ull; for (const SurecBilgi& s : liste_) if (s.baslatUnix && s.baslatUnix < enEski) { enEski = s.baslatUnix; anaPid = s.pid; } if (ImGui::BeginTable("surecler", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchProp, ImVec2(0, 150))) { ImGui::TableSetupColumn("PID", ImGuiTableColumnFlags_WidthFixed, 60); ImGui::TableSetupColumn("Belge"); ImGui::TableSetupColumn("Süre", ImGuiTableColumnFlags_WidthFixed, 62); ImGui::TableSetupColumn("Durum", ImGuiTableColumnFlags_WidthFixed, 78); ImGui::TableSetupColumn("Bellek", ImGuiTableColumnFlags_WidthFixed, 72); ImGui::TableHeadersRow(); if (ImGuiTableSortSpecs* ss = ImGui::TableGetSortSpecs(); ss && ss->SpecsCount) { const int kol = ss->Specs[0].ColumnIndex; const bool artan = ss->Specs[0].SortDirection == ImGuiSortDirection_Ascending; std::sort(liste_.begin(), liste_.end(), [&](const SurecBilgi& a, const SurecBilgi& b) { bool r; switch (kol) { case 1: r = a.belge < b.belge; break; case 2: r = a.baslatUnix > b.baslatUnix; break; // uptime = ters baslat case 3: r = (uint32_t)a.durum < (uint32_t)b.durum; break; case 4: r = a.bellekKB < b.bellekKB; break; default: r = a.pid < b.pid; break; } return artan ? r : !r; }); } for (const SurecBilgi& s : liste_) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); const bool ben = (s.pid == benim); char etiket[48]; std::snprintf(etiket, sizeof(etiket), "%u%s", s.pid, ben ? " (ben)" : ""); if (ImGui::Selectable(etiket, hedefPid_ == s.pid, ImGuiSelectableFlags_SpanAllColumns)) hedefPid_ = s.pid; ImGui::TableSetColumnIndex(1); ImGui::TextUnformatted(s.belge.c_str()); if (s.pid == anaPid) { ImGui::SameLine(); ImGui::TextDisabled("[ana]"); } ImGui::TableSetColumnIndex(2); ImGui::TextUnformatted(uptimeMetin(s.baslatUnix ? simdi - s.baslatUnix : 0).c_str()); ImGui::TableSetColumnIndex(3); ImGui::TextColored(durumRenk(s.durum), "%s", durumMetin(s.durum)); ImGui::TableSetColumnIndex(4); if (s.bellekKB) ImGui::Text("%u MB", s.bellekKB / 1024); else ImGui::TextUnformatted("--"); } ImGui::EndTable(); } } void FikoSurecYoneticiPanel::eylemPaneli() { const uint32_t benim = kopru_.benimPid(); const bool secili = hedefPid_ != 0; const bool baska = secili && hedefPid_ != benim; // En eski (ana) süreç — ikinci onay. uint32_t anaPid = 0; uint64_t enEski = ~0ull; for (const SurecBilgi& s : liste_) if (s.baslatUnix && s.baslatUnix < enEski) { enEski = s.baslatUnix; anaPid = s.pid; } // ---- Sonlandırma ---- ImGui::BeginDisabled(!baska); if (ImGui::Button("Sonlandır")) { onayPid_ = hedefPid_; onayZorla_ = false; ImGui::OpenPopup("surec_onay"); } ImGui::SameLine(); if (ImGui::Button("Zorla")) { onayPid_ = hedefPid_; onayZorla_ = true; ImGui::OpenPopup("surec_onay"); } ImGui::SameLine(); if (ImGui::Button("Odakla")) { if (HWND h = pencereBul(hedefPid_)) { if (IsIconic(h)) ShowWindow(h, SW_RESTORE); SetForegroundWindow(h); } } ImGui::EndDisabled(); ImGui::SameLine(); if (!secili) ImGui::TextDisabled("hedef: yok"); else if (baska) ImGui::TextDisabled("hedef: %u", hedefPid_); else ImGui::TextDisabled("hedef: %u (ben)", hedefPid_); // Onay modalı (belge + kaydedilmemiş uyarı; ana süreç ikinci-onay). if (ImGui::BeginPopupModal("surec_onay", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { std::string belge = "?"; for (const SurecBilgi& s : liste_) if (s.pid == onayPid_) belge = s.belge; ImGui::Text("Süreç %u (%s) %s?", onayPid_, belge.c_str(), onayZorla_ ? "ZORLA sonlandırılsın" : "kapatılsın"); if (onayZorla_) ImGui::TextColored(ImVec4(0.95f, 0.5f, 0.35f, 1), "Zorla: kaydedilmemiş değişiklikler KAYBOLUR."); else ImGui::TextDisabled("Nazik: süreç kaydetmeyi soracak."); if (onayPid_ == anaPid) ImGui::TextColored(ImVec4(0.95f, 0.5f, 0.35f, 1), "DİKKAT: bu en eski (ana) süreç."); ImGui::Separator(); if (ImGui::Button("Evet")) { kopru_.sonlandir(onayPid_, onayZorla_); onayPid_ = 0; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Vazgeç")) { onayPid_ = 0; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } // ---- Komut + parametre ---- ImGui::SeparatorText("Komut"); ImGui::SetNextItemWidth(-1); ImGui::InputTextWithHint("##kfiltre", "komut ara…", komutFiltre_, sizeof(komutFiltre_)); if (ImGui::BeginListBox("##komutlar", ImVec2(-1, 90))) { const std::string f = kucult(komutFiltre_); for (const MenuOge& o : cek_.menuler().hepsi()) { if (!f.empty() && kucult(o.ad).find(f) == std::string::npos) continue; if (ImGui::Selectable((o.grup + " / " + o.ad).c_str(), seciliKomut_ == o.ad)) seciliKomut_ = o.ad; } ImGui::EndListBox(); } if (ImGui::TreeNode("Parametreler (isteğe bağlı)")) { for (int i = 0; i < 4; ++i) { ImGui::PushID(i); ImGui::SetNextItemWidth(120); ImGui::InputTextWithHint("##k", "anahtar", pKey_[i], sizeof(pKey_[i])); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); ImGui::InputTextWithHint("##v", "değer", pVal_[i], sizeof(pVal_[i])); ImGui::PopID(); } ImGui::TreePop(); } // Parametreleri topla (boş anahtar atlanır). auto paramTopla = [&]() { std::vector> p; for (int i = 0; i < 4; ++i) if (pKey_[i][0]) p.emplace_back(pKey_[i], pVal_[i]); return p; }; auto kendineCalistir = [&]() { for (const MenuOge& o : cek_.menuler().hepsi()) if (o.ad == seciliKomut_) { cek_.komutCalistir(o, "panel"); break; } }; const bool hazir = !seciliKomut_.empty(); ImGui::TextDisabled("Seçili: %s", hazir ? seciliKomut_.c_str() : "(yok)"); ImGui::BeginDisabled(!hazir || !secili); if (ImGui::Button("Seçili sürece gönder")) { auto p = paramTopla(); if (hedefPid_ == benim) kendineCalistir(); else if (p.empty()) kopru_.gonder(hedefPid_, seciliKomut_); else kopru_.gonder(hedefPid_, seciliKomut_, p); } ImGui::EndDisabled(); ImGui::SameLine(); ImGui::BeginDisabled(!hazir); if (ImGui::Button("Broadcast (hepsi)")) { auto p = paramTopla(); if (p.empty()) kopru_.hepsineGonder(seciliKomut_); else kopru_.hepsineGonder(seciliKomut_, p); kendineCalistir(); // kendi de dahil } ImGui::EndDisabled(); } void FikoSurecYoneticiPanel::olayAkisi() { ImGui::Checkbox("Yalnız seçili süreç", &sadeceSecili_); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); ImGui::InputTextWithHint("##ofiltre", "olay ara…", olayFiltre_, sizeof(olayFiltre_)); const std::string f = kucult(olayFiltre_); std::vector gorunur; gorunur.reserve(olaylar_.size()); for (int i = (int)olaylar_.size() - 1; i >= 0; --i) { // yeni → eski const ForwardOlay& o = olaylar_[i]; if (sadeceSecili_ && hedefPid_ && o.kaynakPid != hedefPid_) continue; if (!f.empty() && kucult(o.ozet).find(f) == std::string::npos) continue; gorunur.push_back(i); } if (ImGui::BeginChild("olayakis", ImVec2(0, 150), ImGuiChildFlags_Borders)) { ImGuiListClipper clipper; clipper.Begin((int)gorunur.size()); while (clipper.Step()) { for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row) { const ForwardOlay& o = olaylar_[gorunur[row]]; ImGui::TextColored(ImVec4(0.55f, 0.7f, 0.95f, 1), "%u", o.kaynakPid); ImGui::SameLine(); ImGui::Text("%.2f", o.zaman); ImGui::SameLine(); ImGui::TextUnformatted(o.ozet); } } clipper.End(); } ImGui::EndChild(); } // Orchestrator node kartları: bugün yalnız yerel makine (kule temeli); LAN/WAN // sürücüleri geldiğinde uzak node'lar da AYNI defterden buraya düşer. void FikoSurecYoneticiPanel::nodeSeridi() { // Bekleyen eşleştirme istekleri — kullanıcı KODLARI karşılaştırıp onaylamalı. const auto bekleyenler = g_orkestraKule.eslesme().bekleyenGelenler(); for (const auto& b : bekleyenler) { ImGui::PushID((int)(b.nodeId ^ (b.nodeId >> 32))); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.16f, 0.08f, 1)); if (ImGui::BeginChild("eslesme_istek", ImVec2(0, 52), ImGuiChildFlags_Borders)) { ImGui::TextColored(ImVec4(0.95f, 0.76f, 0.28f, 1), "⚿ Eşleştirme isteği: %s", b.ad.empty() ? "(adsız)" : b.ad.c_str()); ImGui::Text("Kod: %s — karşı ekranda AYNI kod görünüyorsa onayla.", b.kod.c_str()); if (ImGui::SmallButton("Onayla")) g_orkestraKule.eslesmeOnayla(b.nodeId); ImGui::SameLine(); if (ImGui::SmallButton("Reddet")) g_orkestraKule.eslesmeReddet(b.nodeId); } ImGui::EndChild(); ImGui::PopStyleColor(); ImGui::PopID(); } const auto& nodelar = g_orkestraKule.defter().liste(); if (nodelar.empty()) { ImGui::TextDisabled("Node bilgisi bekleniyor…"); return; } for (const auto& n : nodelar) { const ImVec4 renk = nodeDurumRenk(n.durum); ImGui::TextColored(renk, "●"); ImGui::SameLine(); ImGui::TextUnformatted(n.ad.c_str()); ImGui::SameLine(); ImGui::TextColored(renk, "%s", orkestra::nodeDurumAdi(n.durum)); ImGui::SameLine(); ImGui::TextDisabled("· v%s · %u runtime · %u görev", n.surum.c_str(), (unsigned)n.runtimeSayisi, (unsigned)n.aktifTask); if (n.dogrulanmis) { ImGui::SameLine(); ImGui::TextColored(ImVec4(0.36f, 0.82f, 0.44f, 1), "🔒"); // eşleşmiş (güvenli) } else if (n.nodeId != g_orkestraKule.yerelNodeId() && !n.ip.empty()) { ImGui::SameLine(); ImGui::PushID((int)(n.nodeId ^ (n.nodeId >> 32))); if (ImGui::SmallButton("Eşleştir")) g_orkestraKule.eslesmeBaslat(n.nodeId); ImGui::PopID(); } if (n.sonMetrikMs > 0) { ImGui::TextDisabled(" CPU %%%0.f · RAM %u MB · VRAM %u MB · Disk %u GB boş", n.metrik.cpuYuzde, (unsigned)n.metrik.ramMB, (unsigned)n.metrik.vramMB, (unsigned)n.metrik.diskBosGB); } } if (const orkestra::LanKesif* lan = g_orkestraKule.lan()) { ImGui::TextDisabled("LAN keşif: %llu beacon ↑ · %llu datagram ↓", (unsigned long long)lan->gonderilen(), (unsigned long long)lan->alinan()); } if (g_orkestraKule.tcpDinliyorMu()) { ImGui::TextDisabled("TCP kanal: dinliyor · %zu bağlantı · %llu çerçeve", g_orkestraKule.tcpBaglantiSayisi(), (unsigned long long)g_orkestraKule.tcpAlinanCerceve()); } // Eşleştirilmiş (güvenilir) node'lar — kaldır ile TOFU kimliği silinir. const auto& esli = g_orkestraKule.eslesme().kayitlar(); if (!esli.empty()) { ImGui::Spacing(); ImGui::TextDisabled("Eşleştirmeler:"); for (const auto& k : esli) { ImGui::PushID((int)k.nodeId); ImGui::TextColored(ImVec4(0.36f, 0.82f, 0.44f, 1), "🔒"); ImGui::SameLine(); ImGui::TextUnformatted(k.ad.empty() ? "(adsız node)" : k.ad.c_str()); ImGui::SameLine(); ImGui::SetNextItemWidth(110); int rol = (int)k.rol; if (ImGui::Combo("##rol", &rol, "İzleyici\0Operatör\0Yönetici\0")) g_orkestraKule.rolAyarla(k.nodeId, (orkestra::Rol)rol); ImGui::SameLine(); if (ImGui::SmallButton("Kaldır")) g_orkestraKule.eslesme().kaldir(k.nodeId); ImGui::SameLine(); if (ImGui::SmallButton("Dosya Gönder")) { wchar_t sec[MAX_PATH] = {}; OPENFILENAMEW o{}; o.lStructSize = sizeof(o); o.lpstrFile = sec; o.nMaxFile = MAX_PATH; o.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; if (GetOpenFileNameW(&o)) { char yol[MAX_PATH * 2]; WideCharToMultiByte(CP_UTF8, 0, sec, -1, yol, sizeof(yol), nullptr, nullptr); g_orkestraKule.dosyaGonder(k.nodeId, yol); } } ImGui::PopID(); } } // Görevler (D3): Task Oluştur formu + dağıtık kuyruk listesi. ImGui::Spacing(); ImGui::SetNextItemWidth(140); ImGui::InputText("##tasktur", taskTur_, sizeof(taskTur_)); ImGui::SameLine(); ImGui::TextDisabled("tür"); ImGui::SameLine(); if (ImGui::SmallButton("+ Görev") && taskTur_[0]) { // Otomatik hedef (en boş uygun node); tür alanı param'sız basit iş. g_orkestraKule.taskOlusturVeGonder(taskTur_, {}, orkestra::TaskHedef{orkestra::HedefTipi::Otomatik, 0, ""}); } const auto& tasklar = g_orkestraKule.tasklar().hepsi(); if (!tasklar.empty()) { ImGui::TextDisabled("Görevler (%zu):", tasklar.size()); for (const auto& t : tasklar) { ImGui::Text("#%llu %s — %s", (unsigned long long)t.taskId, t.tur.c_str(), orkestra::taskDurumAdi(t.durum)); if (t.durum == orkestra::TaskDurum::Calisiyor) { ImGui::SameLine(); ImGui::ProgressBar(t.ilerleme / 100.f, ImVec2(120, 0)); } } } } void FikoSurecYoneticiPanel::ciz(UygulamaBaglam&) { if (!acik_) return; kopru_.olayAboneligiYayinla(true); // merkez ilanı (throttle'lı) → süreçler forward etsin listeyiYenile(); ui::Pencere pen{"🛰 Fiko Orchestrator", &acik_, {.boyut = {460, 590}, .bayrak = 0}}; if (pen) { nodeSeridi(); ImGui::SeparatorText("Runtime'lar"); int mesgul = 0, yanitsiz = 0; for (const SurecBilgi& s : liste_) { if (s.durum == SurecDurum::Mesgul) ++mesgul; if (s.durum == SurecDurum::Yanitsiz) ++yanitsiz; } ImGui::Text("%zu süreç · %d meşgul · %d yanıtsız", liste_.size(), mesgul, yanitsiz); ImGui::SameLine(); if (ImGui::SmallButton("Yeni")) { for (const MenuOge& o : cek_.menuler().hepsi()) if (o.ad == "Yeni Belge") { cek_.komutCalistir(o, "panel"); break; } } surecTablosu(); eylemPaneli(); ImGui::SeparatorText("Olay Akışı"); olayAkisi(); } // pen yıkıcısı End() çağırır if (!acik_) kopru_.olayAboneligiYayinla(false); // pencere X ile kapandı → iptal } } // namespace fiko