// ── fiko_mcp.exe — FikoCad MCP (Model Context Protocol) sunucusu ──────────── // Yapay zeka istemcileri (Claude Desktop/Code, IDE ajanları, otomasyon botları) bu // süreci stdio üzerinden konuşturur ve ÇALIŞAN FikoCad örneklerini yönetir. // // MİMARİ (neden ayrı süreç?): // FikoCad çok-süreçlidir ("Yeni Belge" = yeni fikocad.exe) ve zaten merkezi bir // süreç köprüsü (SurecKopru, named-pipe + tip-çerçeveli protokol) vardır. MCP sunucusu // bu köprüye NORMAL BİR AKRAN olarak katılır: kendi gelen-kutusunu açar, kayıt // dosyasına yazılır, komut gönderir, olay aboneliği ile olayları toplar. // → GUI sürecine tek satır risk eklenmez; MCP çökse bile CAD çalışmaya devam eder. // // LİSANS (Fikri'nin şartı: "yapay zeka her yere hakim olsun ama lisans çerçevesinde"): // Bu süreç kendi başına CAD işlemi YAPMAZ. Her mutasyon, hedef fikocad örneğinde // Cekirdek::komutCalistir üzerinden koşar — lisans kapısı + denetim olayı ORADA // uygulanır. Yani MCP, lisansı atlamak için bir arka kapı DEĞİLDİR; lisansı olmayan // komut yapay zekadan gelince de reddedilir ve denetime "ipc/mcp" kaynağıyla düşer. #include #include #include #include #include #include #include "fiko/mcp/mcp_sunucu.hpp" #include "fiko/mcp/zeka_araclari.hpp" #include "surec_kopru.hpp" using namespace fiko; using namespace fiko::mcp; namespace { SurecKopru g_kopru; // Yapay zekaya verilen Lua API referansı. Motor tarafındaki `fonksiyon(...)` kayıtlarıyla // (motor/betik/src/makro_motor.cpp apiKur) BİREBİR aynı kalmalı — yeni Lua fonksiyonu // eklendiğinde burası da güncellenmeli, yoksa yapay zeka onu bilemez. const char* const kLuaApiReferans = R"API( FikoCad Lua makro API (fikocad_makro_calistir ile calistirilir) TEMEL fiko.log(...) -- cikti yaz (MCP yanitinda geri doner) fiko.guncelle(fn) -- fn'i HER KAREDE cagir (animasyon). DIKKAT: sahne -- tazeleme DEGILDIR ve argumansiz cagrilirsa HATA verir. -- Modelleme sonunda cagirmaniza gerek yok. fiko.geriAl() / fiko.yinele() -- undo / redo SAHNE — GEOMETRI id = fiko.sahne.ekle("kutu"|"silindir"|"kure"|"koni") -- yeni govde, kimlik doner fiko.sahne.parametreAyarla(id, p1, p2, p3) kutu : p1=X boyu, p2=Y boyu, p3=Z boyu (varsayilan 2,2,2) silindir : p1=yaricap, p3=yukseklik (varsayilan 0.8, -, 2) kure : p1=yaricap koni : p1=taban yaricap, p3=yukseklik fiko.sahne.konumAyarla(id, x, y, z) -- merkez konum (mm) x,y,z = fiko.sahne.konum(id) fiko.sahne.renkAyarla(id, r, g, b) -- 0..1 fiko.sahne.adAyarla(id, "Ad") ad = fiko.sahne.ad(id) fiko.sahne.gorunurAyarla(id, true|false) fiko.sahne.sil(id) liste = fiko.sahne.dugumler() -- tum kimlikler (tablo) fiko.sahne.stepIceAktar("C:/yol.stp") fiko.sahne.stepDisaAktar("C:/yol.stp", yalnizSecili) BOOLEAN (ONEMLI: tam 2 govde SECILI olmali) fiko.secim.temizle() fiko.secim.sec(id1); fiko.secim.sec(id2) yeni = fiko.sahne.boolean("birlesim"|"cikarma"|"kesisim") -- "cikarma": once secilen KALIR, ikincisi cikarilir. SKETCH (2B eskiz — parametrik modellemenin girisi) sk = fiko.sketch.ekle("xy"|"yz"|"zx") -- veya duzlem Varlik'i fiko.sketch.dikdortgen(sk, x1,y1, x2,y2) -- kapali profil (4 cizgi doner) fiko.sketch.cizgi(sk, x1,y1, x2,y2) fiko.sketch.daire(sk, cx,cy, r) fiko.sketch.yay(sk, cx,cy, r, basAciDeg, bitisAciDeg) fiko.sketch.yenile(sk) -- eskizi 3B kenarlara isle (gorunur olsun) PART DESIGN (43 feature tipi — katalog VERI-GUDUMLU, tek `ekle` hepsini acar) fiko.parca.tipler() -- {tip, ad, malzemeEkler, parametreler{...}} fk = fiko.parca.ekle(tip, sketchVarlik, {param=deger}, hedefDugum) fiko.parca.guncelle(fk, {param=deger}) -- TEK recompute (parametre basina degil) fiko.parca.featureler() -- {kimlik, tip, dugum, hata} fiko.parca.formul(fk, "uzunluk", "pay") -- f(x) BAGLA (bos ifade = bagi kaldir) fiko.parca.sil(fk) -- Yalniz sketch ile calisanlar: pad pocket hole pattern_dik pattern_dairesel mirror -- stiffener translation rotation symmetry scaling thicksurface closesurface -- Digerleri kenar/yuz/2. govde secimi ister; hata alanindan NE istedigini okuyun. GSD — GENERATIVE SHAPE DESIGN (31 op; katalog veri-gudumlu, tek `ekle` hepsini acar) fiko.gsd.tipler() fk = fiko.gsd.ekle(tip, sketchVarlik, kaynakDugum, {param=deger}, kaynak2) fiko.gsd.guncelle(fk, {param=deger}) fiko.gsd.featureler() fiko.gsd.sil(fk) Wireframe : nokta cizgi cember spline polyline helix konik kose Yuzey : yuzeyExtrude yuzeyRevolve yuzeyFill yuzeyOffset yuzeyExtrapolate yuzeyThicken yuzeyCikart yuzeySisir yuzeyDuzelt sinir Turetme : kenarExtrude telExtrude onar birlestir koprule supurmeYuzey loftYuzey loftKilavuz yuzeyKirp yamaYuzey kenarKaydir kesisimEgri afin NOT: koprule/supurme/loft/kirp/kesisim 2+ KAYNAK ister (kaynak + kaynak2), yoksa nil doner. ANALIZ — kalip/yuzey kalite raporlari (SAYISAL dogrulama) fiko.analiz.tipler() -- draft undercut kalinlik radius kurvatur -- zebra sureklilik reflect fark a = fiko.analiz.ekle("kalinlik", dugum) -- ekler VE hesaplar s = fiko.analiz.sonuc(a) -- {tip, ozet, min, max, birim, kritik, -- hazir, rapor={{ad,deger,kritik,ordinal}}} fiko.analiz.sil(a) NOT: draft/undercut/zebra/reflect CANLI-GPU'dur → hazir=false, sayisal rapor yok (gorsel shader'da uretilir). kalinlik/radius/kurvatur/sureklilik SAYI dondurur. PARAMETRE (CATIA f(x) tablosu) fiko.parametre.ekle("kalinlik", 10, "mm") fiko.parametre.ekle("pay", "kalinlik * 2", "mm") -- ifade: diger parametrelere referansli fiko.parametre.ayarla("kalinlik", 25) -- degistir → bagimli feature'lar guncellenir fiko.parametre.liste() -- {ad, ifade, birim, deger, hata} fiko.parametre.sil("pay") ARKA PLAN ISLERI (async cagrilar HEMEN doner — sure = kuyruga girme suresi!) n = fiko.gorev.mesgul() -- suren is sayisi; 0 = her sey bitti l = fiko.gorev.liste() -- {{kimlik, baslik, oran, durum, mesaj}, ...} -- durum: 0=bekliyor 1=kosuyor 2=bitti 3=hata 4=iptal -- KULLANIM: fiko.belge.kaydet/ac, stepIceAktar gibi cagrilardan SONRA -- mesgul()==0 olana kadar YOKLA; yoksa "bitti" sanip erken okursun. -- (Olculdu 07-21: 0.6 MB belge kaydet 276 ms, ac 273 ms — cagri ise 30 ms'de donuyor.) CAE — LINEER ELASTIK FEM (hex8 voxel kafesi; dis cozucu YOK, yerlesik) r = fiko.cae.coz(dugum, {cozunurluk=24, kalite=0.01, E=210000, nu=0.3, sabitYuz="-x", yukYuz="+x", fx=, fy=, fz=, sabitKip="tam"|"simetri", harita=false, konservatif=true}) -- govde voxellenir (h = en uzun kenar / cozunurluk), sabitYuz'deki dugumler -- 3 yonde ankastre, toplam kuvvet yukYuz dugumlerine ESIT dagitilir. -- yuz adlari KAFES (AABB) yuzudur: "-x","+x","-y","+y","-z","+z" -- Doner: {yakinsadi, adim, artik, yukYuzYer, enBuyukYer, enBuyukGerilme, -- nx, ny, nz, h, dolu} -- DIKKAT (yorumlama): `enBuyukGerilme` ANKASTRE KOKTEKI yigilmadir, nominal -- F/A DEGILDIR (olculdu: 100x20x20 kiriste nominalin %60 ustu). Analitik -- FL/(AE) ile kiyaslanacak buyukluk `yukYuzYer`dir; `enBuyukYer` ucdaki -- Poisson yanal hareketini de icerir. -- sabitKip="tam" (vars.): sabitYuz 3 EKSENDE ankastre. Poisson daralmasini -- engeller → model YAPAY sertlesir (olculdu: eksenel uzamada %-0.80 sapma). -- sabitKip="simetri": her yuz yalniz KENDI NORMALINDE tutulur (simetri BC) -- → sapma %0.00. Gercek ankastre baglanti modellenmiyorsa bunu kullan. -- harita=true: von Mises alani sahneye renkli kup isi-haritasi olarak konur; -- sonuca `haritaDugum` eklenir (fiko.sahne.sil ile kaldirilir). -- yakinsadi=false ise SONUCA GUVENME (kisit eksik / adim tavani yetmedi). -- Ayarlar: cae.fem_en_fazla_adim, cae.fem_tolerans -- konservatif=true (vars., ayar cae.konservatif_kafes): hucre KUTUSU yuzeyi -- kesiyorsa da malzeme sayilir → ince cidar KAYBOLMAZ. Kapatilirsa merkez -- siniflamasi: OLCULDU, 2 mm plaka cozunurluk 12/25'te SIFIR hucre (parca yok). fiko.cae.kurulumKaydet(dugum, {cozunurluk, E, nu, sabitYuz, yukYuz, fx, fy, fz, simetri, konservatif}) -- Analiz kurulumunu BELGEYE yazar (v1.19 CAEK) → fiko.belge.kaydet/ac KORUR. -- Ayni dugum icin varsa uzerine yazar. Dugum kimligi ACARKEN REMAP edilir. k = fiko.cae.kurulumlar() -- {{dugum,cozunurluk,E,nu,sabitYuz,yukYuz,fx,fy,fz,...}} -- NOT: SONUC saklanmaz (her acilista yeniden cozulur) — yalniz PARAMETRELER. KINEMATIK (mekanizma: uzuv + mafsal + yasa + kapali zincir) fiko.kinematik.sifirla() -- uzuv 0 = GOVDE otomatik u = fiko.kinematik.uzuvEkle("Ad", sahneDugum) -- dugumun MEVCUT donusumu taban olur m = fiko.kinematik.mafsalEkle(tip, ana, cocuk, {cx,cy,cz, ccx,ccy,ccz, q,q2,q3, vidaAdim, altSinir, ustSinir}) tip: "sabit"|"doner"|"kayar"|"silindirik"|"vida"|"kuresel"|"duzlemsel"|"serbest" cx/cy/cz = ANA uzuv yerelinde mafsal capasi (+Z = eksen) ccx/ccy/ccz = COCUK uzuv yerelinde capa (montaj "mate") — KAPALI ZINCIRDE SART m = fiko.kinematik.sabitle(u) -- uzvu GOVDEYE, OLDUGU YERDE sabitler -- DIKKAT: mafsalEkle("sabit", 0, u, {}) BIRIM capalidir → uzvu ORIJINE ISINLAR. -- Bir parcayi yerinde dondurmak icin HER ZAMAN sabitle() kullan. o = fiko.kinematik.oner(dugumA, dugumB, yaricapTol, mesafeTol) -- AKILLI MAFSAL ONERISI -- geometriden (es silindir/koaksiyel/koplanar) cikarir: -- { {tip,guven,yaricap,yuzA,yuzB,aciklama}, ... } guven'e gore sirali m = fiko.kinematik.onerEkle(uzuvA, uzuvB, sira) -- oneri #sira'yi mafsala cevirir -- capalar dunya→uzuv yereline cevrilir; parcalar YERINDEN OYNAMAZ fiko.kinematik.surus(m, q, q2, q3) -- elle sur (bu mafsal SURULEN sayilir) fiko.kinematik.yasa(m, "sabit|dogrusal|harmonik", {deger,hiz,genlik,periyot,faz,degisken}) fiko.kinematik.yasaAnahtar(m, {{t,q},...}) fiko.kinematik.zaman(t) r = fiko.kinematik.coz() -- {yakinsadi, artik, halka} kapali zinciri coz n = fiko.kinematik.uygula() -- sahneye yaz (halka varsa ONCE otomatik cozer) d = fiko.kinematik.durum() -- {uzuv, mafsal, yasa, dof, agac} c = fiko.kinematik.carpismaTara(t0, t1, adim, komsuAtla, kesin, isaretle) -- DMU girisim -- { {a,b,adA,adB,zaman,oran}, ... } · uzuvlarin SAHNE sinir kutulari kullanilir -- AABB olcutu: yanlis-pozitif olabilir, gercek carpismayi ASLA kacirmaz -- komsuAtla (vars. true): mafsalla bagli uzuv ciftleri sayilmaz (temas normaldir) -- kesin=true: her AABB adayi GERCEK B-Rep kesisimiyle dogrulanir; yanlis-pozitif -- ELENIR ve sonuca `hacim` (mm3) eklenir. Pahali (OCCT boolean) → vars. false. -- isaretle=true: girisim HACMI sahneye GERCEK govde olarak konur (CATIA DMU clash -- volume) — kirmizi/yari saydam, adi "Girisim: A x B (t=...)"; sonuca `dugum` -- eklenir. Esik/renk/alfa: kinematik.girisim_asgari_hacim|girisim_renk|girisim_alfa n = fiko.kinematik.isaretTemizle() -- YALNIZ bu API'nin koydugu girisim govdelerini siler -- DIKKAT: tarama mafsal degiskenlerini DEGISTIRIR → sonra zaman()+uygula() ile -- istedigin ani geri kur. NOT: DOF = Grubler-Kutzbach (uzaysal). Duzlemsel mekanizmada NEGATIF cikar — bu "asiri kisitli" demek DEGIL, uzaysal formulun duzlemsele uymamasidir. OLCUM / DOGRULAMA (salt okuma — "ne modelledim?") o = fiko.sahne.olcu(dugumKimlik) -- o.hacim, o.alan, o.minX/Y/Z, o.maksX/Y/Z, o.boyX/boyY/boyZ SECIM fiko.secim.secililer() fiko.secim.temizle() fiko.secim.sec(id) fiko.secim.kip("govde"|"yuz"|"kenar"|"nokta") fiko.secim.kimlik() -- secili ogenin KALICI adi ("Kutu.1:F7") fiko.secim.kimlikSec("F7") fiko.secim.kimlikListe(id) KOMUT / WORKBENCH / UI / AYAR fiko.komut.listesi() fiko.komut.calistir("Tumunu Sigdir") fiko.workbench.ayarla("Part Design") fiko.workbench.liste() fiko.ui.tema("koyu") fiko.ayar.al("kimlik") fiko.ayar.ata("kimlik", deger) MALZEME fiko.malzeme.liste() fiko.malzeme.bul("Celik") fiko.malzeme.olustur("Ad") fiko.malzeme.ayarla(mid, {metaliklik=1.0, puruzluluk=0.25, taban={0.8,0.8,0.85}}) fiko.malzeme.ata(id, mid) CAM fiko.cam.opEkle(...) fiko.cam.opHesapla(...) fiko.cam.gkodYaz("C:/yol.nc") NOTLAR * Tum sahne mutasyonlari Islem/KomutYigini uzerinden gider → Ctrl+Z calisir. * Birimler mm. Z yukari. * Yeni eklenen govde tabani Z=0 duzlemine oturur; konumAyarla ile taşiyin. * Cok adimli modellemede her adimda fiko.log ile ilerlemeyi yazin — MCP yanitinda gorursunuz ve hatayi hangi adimda oldugunu anlarsiniz. * ⚠ RECOMPUTE ERTELENIR: parametre/formul degisikligi bir SONRAKI karede uygulanir. Sonucu ayni makro icinde `fiko.sahne.olcu` ile olcerseniz ESKI degeri gorursunuz — dogrulamayi AYRI bir fikocad_makro_calistir cagrisinda yapin (olculdu 07-21). )API"; std::string surecDurumAdi(SurecDurum d) { switch (d) { case SurecDurum::Mesgul: return "mesgul"; case SurecDurum::Yanitsiz: return "yanitsiz"; default: return "aktif"; } } uint64_t simdiUnix() { return (uint64_t)time(nullptr); } // fikocad.exe yolu: MCP exe'si onunla aynı dizinde kurulur. std::wstring fikocadYolu() { wchar_t yol[MAX_PATH]{}; GetModuleFileNameW(nullptr, yol, MAX_PATH); std::wstring s = yol; const size_t k = s.find_last_of(L"\\/"); return (k == std::wstring::npos ? std::wstring() : s.substr(0, k + 1)) + L"fikocad.exe"; } std::wstring genise(const std::string& s) { if (s.empty()) return {}; const int n = MultiByteToWideChar(CP_UTF8, 0, s.data(), (int)s.size(), nullptr, 0); std::wstring o((size_t)(n > 0 ? n : 0), L'\0'); if (n > 0) MultiByteToWideChar(CP_UTF8, 0, s.data(), (int)s.size(), o.data(), n); return o; } // Argümanlardaki key/val listesini IPC parametrelerine çevir. std::vector> parametreleriTopla(const Json& args) { std::vector> p; for (const auto& [k, v] : args["parametreler"].nesneAl()) { switch (v.tip()) { case JsonTip::Metin: p.emplace_back(k, v.metinAl()); break; case JsonTip::Mantik: p.emplace_back(k, v.mantikAl() ? "1" : "0"); break; case JsonTip::Sayi: p.emplace_back(k, Json::sayi(v.sayiAl()).yaz()); break; default: p.emplace_back(k, v.yaz()); break; // dizi/nesne → ham JSON } } return p; } Json surecJson(const SurecBilgi& s) { Json j = Json::nesne(); j.ekle("pid", Json::tam((long long)s.pid)); j.ekle("belge", Json::metin(s.belge)); j.ekle("durum", Json::metin(surecDurumAdi(s.durum))); j.ekle("calisma_sn", Json::tam((long long)(s.baslatUnix ? simdiUnix() - s.baslatUnix : 0))); j.ekle("bellek_mb", Json::sayi(s.bellekKB / 1024.0)); j.ekle("son_heartbeat_sn_once", Json::tam((long long)(s.sonHeartbeatUnix ? simdiUnix() - s.sonHeartbeatUnix : -1))); return j; } // Makro çalıştırma gövdesi — hem `fikocad_makro_calistir` hem de onun üzerine kurulu // keşif araçları (komut listesi) bunu çağırır (tek kaynak, kod kopyası yok). AracSonuc makroCalistir(const Json& a) { const std::string kod = a["kod"].metinAl(); if (kod.empty()) return AracSonuc::hataliDon("'kod' bos olamaz"); uint32_t hedef = (uint32_t)a["pid"].tamAl(0); if (hedef == 0) { for (const auto& p : g_kopru.surecler()) if (p.pid != g_kopru.benimPid()) { hedef = p.pid; break; } if (hedef == 0) return AracSonuc::hataliDon( "Calisan FikoCad ornegi yok — once 'fikocad_belge_ac' ile bir pencere acin"); } // Korelasyon: bu istegin yanitini digerlerinden ayirir (istek/yanit akisi). static uint64_t sayac = 0; const uint64_t kor = ((uint64_t)GetCurrentProcessId() << 32) | ++sayac; g_kopru.beklemedekiOnaylar(); // eski yanitlari temizle if (!g_kopru.gonderKorelasyonlu(hedef, "makro.lua", {{"kod", kod}}, kor)) return AracSonuc::hataliDon("Surec " + std::to_string(hedef) + " ulasilamadi"); const long long zamanAsimi = a["zaman_asimi_ms"].tamAl(8000); const auto basla = std::chrono::steady_clock::now(); while (std::chrono::duration_cast( std::chrono::steady_clock::now() - basla).count() < zamanAsimi) { for (const auto& o : g_kopru.beklemedekiOnaylar()) { if (o.korelasyon != kor) continue; return o.basarili ? AracSonuc::ok(o.sonuc) : AracSonuc::hataliDon(o.sonuc); } std::this_thread::sleep_for(std::chrono::milliseconds(20)); } return AracSonuc::hataliDon( "Zaman asimi: FikoCad yanit vermedi (surec mesgul ya da makro cok uzun surdu)"); } void araclariKaydet(McpSunucu& s) { s.aracKaydet( {.ad = "fikocad_surecler", .aciklama = "Calisan tum FikoCad ornekleridir: PID, acik belge, durum " "(aktif/mesgul/yanitsiz), calisma suresi ve bellek kullanimi. Diger " "araclara verilecek pid degerleri buradan alinir.", .sema = semaNesne({}), .lisansKimlik = "", .calistir = [](const Json&) { Json d = Json::dizi(); for (const auto& p : g_kopru.surecler()) if (p.pid != g_kopru.benimPid()) d.it(surecJson(p)); Json o = Json::nesne(); o.ekle("surec_sayisi", Json::tam((long long)d.diziAl().size())); o.ekle("surecler", std::move(d)); return AracSonuc::ok(o.yaz()); }}); s.aracKaydet( {.ad = "fikocad_komut_calistir", .aciklama = "Calisan bir FikoCad orneginde komut calistirir (menu/palet komut adi). " "pid verilmezse TUM orneklere yayin yapilir. Komut hedef surecte " "Cekirdek::komutCalistir uzerinden kosar → lisans kapisi ve denetim " "kaydi orada uygulanir; lisansiniz kapsamiyorsa komut calismaz.", .sema = semaNesne( {{"komut", semaMetin("Komut adi, ornegin 'Tumunu Sigdir' veya 'Kaydet'")}, {"pid", semaSayi("Hedef surec PID'i; verilmezse tum orneklere yayin")}, {"parametreler", []{ Json p = Json::nesne(); p.ekle("type", Json::metin("object")); p.ekle("description", Json::metin("Komuta gecilecek anahtar/deger ciftleri")); return p; }()}}, {"komut"}), .lisansKimlik = "", // gerçek kapı hedef fikocad'da (komutCalistir) .calistir = [](const Json& a) { const std::string komut = a["komut"].metinAl(); if (komut.empty()) return AracSonuc::hataliDon("'komut' bos olamaz"); const auto par = parametreleriTopla(a); const long long pid = a["pid"].tamAl(0); if (pid > 0) { const bool ok = par.empty() ? g_kopru.gonder((uint32_t)pid, komut) : g_kopru.gonder((uint32_t)pid, komut, par); if (!ok) return AracSonuc::hataliDon("Surec " + std::to_string(pid) + " ulasilamadi (kapanmis olabilir)"); return AracSonuc::ok("Komut gonderildi: " + komut + " → pid " + std::to_string(pid)); } const int ulasan = par.empty() ? g_kopru.hepsineGonder(komut) : g_kopru.hepsineGonder(komut, par); return AracSonuc::ok("Komut " + std::to_string(ulasan) + " surece yayinlandi: " + komut); }}); s.aracKaydet( {.ad = "fikocad_komut_listesi", .aciklama = "Calisan ornekteki TUM komutlari grup+ad olarak listeler. Komut adlari " "TURKCE ve aksanlidir ('Tumunu Sigdir' DEGIL, 'Tümünü Sığdır') — " "fikocad_komut_calistir cagirmadan ONCE dogru adi buradan alin. " "'filtre' verilirse yalnizca iceren adlar/gruplar doner.", .sema = semaNesne({{"filtre", semaMetin("Ad/grup icinde aranacak metin (opsiyonel)")}, {"pid", semaSayi("Hedef surec PID'i; verilmezse ilk calisan ornek")}}), .lisansKimlik = "", .calistir = [](const Json& a) { // Komut listesi Lua API'sinden okunur (tek kaynak; ayri IPC eylemi gerekmez). const std::string filtre = a["filtre"].metinAl(); // Türkçe katlama: komut adları "Tümünü Sığdır" gibi aksanlı; Lua'nın :lower() // yalnız ASCII katlar → "sigdir" araması ıskalıyordu. İki tarafı da ASCII'ye // indirge, sonra karşılaştır (yapay zeka aksansız yazabilsin). Lua kaçışları // ONDALIK (\195 gibi) yazılır — C++ ham baytı kaynak kodlamasına takılmasın. std::string kod = "local M={['\\195\\167']='c',['\\195\\135']='c',['\\196\\159']='g'," "['\\196\\158']='g',['\\196\\177']='i',['\\196\\176']='i',['\\195\\182']='o'," "['\\195\\150']='o',['\\197\\159']='s',['\\197\\158']='s',['\\195\\188']='u'," "['\\195\\156']='u'} " "local function kat(t) t=t:gsub('[\\195\\196\\197][\\128-\\191]',M) return t:lower() end " "local L=fiko.komut.listesi() local f=kat([[" + filtre + "]]) local n=0 " "for i,k in ipairs(L) do local s=k.grup..\" / \"..k.ad " "if f==\"\" or kat(s):find(f,1,true) then fiko.log(s) n=n+1 end end " "fiko.log(\"-- eslesen: \"..n..\" / toplam: \"..#L)"; Json alt = Json::nesne(); alt.ekle("kod", Json::metin(kod)); alt.ekle("pid", a["pid"]); return makroCalistir(alt); // aynı makro kanalı (kod kopyası yok) }}); s.aracKaydet( {.ad = "fikocad_belge_ac", .aciklama = "Verilen dosyayi (.fikocad/.stp/.step/.FikoApt/.FikoOp/.wrl) YENI bir " "FikoCad penceresinde acar. Dosya turunu acma hakki lisansta yoksa " "FikoCad dosyayi acmaz ve kullaniciya gerekce gosterir.", .sema = semaNesne({{"yol", semaMetin("Acilacak dosyanin tam yolu")}}, {"yol"}), .lisansKimlik = "", .calistir = [](const Json& a) { const std::string yol = a["yol"].metinAl(); if (yol.empty()) return AracSonuc::hataliDon("'yol' bos olamaz"); if (GetFileAttributesW(genise(yol).c_str()) == INVALID_FILE_ATTRIBUTES) return AracSonuc::hataliDon("Dosya bulunamadi: " + yol); std::wstring komutSatiri = L"\"" + fikocadYolu() + L"\" \"" + genise(yol) + L"\""; STARTUPINFOW si{}; si.cb = sizeof(si); PROCESS_INFORMATION pi{}; if (!CreateProcessW(nullptr, komutSatiri.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) return AracSonuc::hataliDon("FikoCad baslatilamadi (hata " + std::to_string(GetLastError()) + ")"); const DWORD yeniPid = pi.dwProcessId; CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return AracSonuc::ok("Acildi (pid " + std::to_string(yeniPid) + "): " + yol); }}); s.aracKaydet( {.ad = "fikocad_surec_sonlandir", .aciklama = "Bir FikoCad ornegini kapatir. Varsayilan NAZIK kapanistir (kaydetme " "sorulur). zorla=true yalnizca yanit vermeyen surec icin kullanilmalidir; " "kaydedilmemis calisma kaybolur.", .sema = semaNesne({{"pid", semaSayi("Kapatilacak surec PID'i")}, {"zorla", semaMantik("true = TerminateProcess (veri kaybi riski)")}}, {"pid"}), .lisansKimlik = "", .calistir = [](const Json& a) { const long long pid = a["pid"].tamAl(0); if (pid <= 0) return AracSonuc::hataliDon("Gecerli bir 'pid' verin"); if ((uint32_t)pid == g_kopru.benimPid()) return AracSonuc::hataliDon("MCP sunucusunun kendisi sonlandirilamaz"); const bool zorla = a["zorla"].mantikAl(false); if (!g_kopru.sonlandir((uint32_t)pid, zorla)) return AracSonuc::hataliDon("Sonlandirilamadi (kayitli FikoCad sureci degil?)"); return AracSonuc::ok(std::string(zorla ? "Zorla sonlandirildi" : "Kapanma istegi gonderildi") + ": pid " + std::to_string(pid)); }}); s.aracKaydet( {.ad = "fikocad_makro_calistir", .aciklama = "Calisan bir FikoCad orneginde Lua makrosu calistirir ve CIKTIYI GERI DONDURUR. " "Gercek modelleme burada yapilir: kutu/silindir/kure/koni ekle, boolean, konum, " "renk, secim, malzeme, komut, ayar, CAM. Kullanilabilir API icin once " "'fikocad_makro_api' aracini cagirin. Tum sahne mutasyonlari Islem/KomutYigini " "uzerinden gider → Ctrl+Z ile geri alinabilir. Lisans: 'makro.calistir' hakki " "gerekir; makro icindeki fiko.komut.calistir cagrilari ayrica kendi kapisindan gecer.", .sema = semaNesne({{"kod", semaMetin("Calistirilacak Lua kaynak kodu")}, {"pid", semaSayi("Hedef surec PID'i; verilmezse ilk calisan ornek")}, {"zaman_asimi_ms", semaSayi("Sonuc bekleme suresi (varsayilan 8000)")}}, {"kod"}), .lisansKimlik = "", // gerçek kapı hedef fikocad'da (makro.calistir hakkı) .calistir = makroCalistir}); s.aracKaydet( {.ad = "fikocad_ekran_goruntusu", .aciklama = "Calisan bir FikoCad orneginin 3B goruntu alanini PNG olarak diske yazar. " "Modelleme sonrasi sonucu GORMEK icin kullanin. Once 'Tumunu Sigdir' " "komutunu calistirmak iyi olur (fikocad_komut_calistir).", .sema = semaNesne({{"yol", semaMetin("Yazilacak PNG dosyasinin tam yolu")}, {"pid", semaSayi("Hedef surec PID'i; verilmezse ilk calisan ornek")}}, {"yol"}), .lisansKimlik = "", .calistir = [](const Json& a) { const std::string yol = a["yol"].metinAl(); if (yol.empty()) return AracSonuc::hataliDon("'yol' bos olamaz"); uint32_t hedef = (uint32_t)a["pid"].tamAl(0); if (hedef == 0) { for (const auto& p : g_kopru.surecler()) if (p.pid != g_kopru.benimPid()) { hedef = p.pid; break; } if (hedef == 0) return AracSonuc::hataliDon("Calisan FikoCad ornegi yok"); } static uint64_t sayac = 0; const uint64_t kor = ((uint64_t)GetCurrentProcessId() << 32) | (0x80000000u + ++sayac); g_kopru.beklemedekiOnaylar(); if (!g_kopru.gonderKorelasyonlu(hedef, "ekran.png", {{"yol", yol}}, kor)) return AracSonuc::hataliDon("Surec " + std::to_string(hedef) + " ulasilamadi"); const auto basla = std::chrono::steady_clock::now(); while (std::chrono::duration_cast( std::chrono::steady_clock::now() - basla).count() < 10000) { for (const auto& o : g_kopru.beklemedekiOnaylar()) if (o.korelasyon == kor) return o.basarili ? AracSonuc::ok(o.sonuc) : AracSonuc::hataliDon(o.sonuc); std::this_thread::sleep_for(std::chrono::milliseconds(20)); } return AracSonuc::hataliDon("Zaman asimi: FikoCad yanit vermedi"); }}); s.aracKaydet( {.ad = "fikocad_makro_api", .aciklama = "fikocad_makro_calistir ile kullanilabilecek Lua API referansi. " "Modelleme yapmadan ONCE bunu cagirin.", .sema = semaNesne({}), .lisansKimlik = "", .calistir = [](const Json&) { return AracSonuc::ok(kLuaApiReferans); }}); s.aracKaydet( {.ad = "fikocad_olaylar", .aciklama = "Calisan orneklerden merkeze akan son olaylari dondurur (komut calisti, " "secim degisti, feature eklendi vb.). Olay akisi MCP sunucusu acildiginda " "otomatik abone olunur; ilk cagriya kadar birikenler de gelir.", .sema = semaNesne({{"limit", semaSayi("En fazla kac olay dondurulsun (varsayilan 50)")}}), .lisansKimlik = "", .calistir = [](const Json& a) { const long long limit = a["limit"].tamAl(50); auto olaylar = g_kopru.beklemedekiOlaylar(); Json d = Json::dizi(); const size_t bas = (limit > 0 && olaylar.size() > (size_t)limit) ? olaylar.size() - (size_t)limit : 0; for (size_t i = bas; i < olaylar.size(); ++i) { const auto& o = olaylar[i]; Json j = Json::nesne(); j.ekle("pid", Json::tam((long long)o.kaynakPid)); j.ekle("tur", Json::tam((long long)o.tur)); j.ekle("varlik", Json::tam((long long)o.varlik)); j.ekle("zaman", Json::sayi(o.zaman)); j.ekle("sira", Json::tam((long long)o.sira)); j.ekle("ozet", Json::metin(std::string(o.ozet, strnlen(o.ozet, sizeof(o.ozet))))); d.it(std::move(j)); } Json out = Json::nesne(); out.ekle("olay_sayisi", Json::tam((long long)d.diziAl().size())); out.ekle("olaylar", std::move(d)); return AracSonuc::ok(out.yaz()); }}); } } // namespace int main() { // stdio ikili mod: satır-ayrılmış JSON, CRLF donusumu OLMASIN (protokol bozulur). std::ios::sync_with_stdio(false); g_kopru.baslat("FikoCad MCP", /*anaMi=*/false); g_kopru.olayAboneligiYayinla(true); // calisan orneklerin olaylari bize aksin McpSunucu sunucu("fikocad-mcp", "1.0.0"); araclariKaydet(sunucu); zekaAraclariKaydet(sunucu, [](const Json& a) { return makroCalistir(a); }); // Olay aboneliğini canlı tut (yeni açılan süreçler de yakalansın) — ~2 sn throttle // içeride; MCP çağrıları arasında beklerken de dönmesi için ayrı iplik. std::thread nabiz([] { while (true) { g_kopru.olayAboneligiYayinla(true); g_kopru.kalpAt(); std::this_thread::sleep_for(std::chrono::seconds(2)); } }); nabiz.detach(); std::string satir; while (std::getline(std::cin, satir)) { if (satir.empty()) continue; if (satir.back() == '\r') satir.pop_back(); // CRLF toleransı // UTF-8 BOM toleransı: bazı istemciler/kabuklar ilk satıra EF BB BF koyar → // ayrıştırıcı ilk isteği reddediyordu (ölçüldü: initialize -32700 hatası). if (satir.size() >= 3 && (unsigned char)satir[0] == 0xEF && (unsigned char)satir[1] == 0xBB && (unsigned char)satir[2] == 0xBF) satir.erase(0, 3); if (satir.empty()) continue; const std::string yanit = sunucu.istekIsle(satir); if (yanit.empty()) continue; // bildirim → yanıt yazılmaz std::cout << yanit << "\n" << std::flush; } g_kopru.durdur(); return 0; }