#include #include #include #include #include #include "fiko/cam/apt.hpp" #include "fiko/cam/takim.hpp" #include "fiko/cam/takim_yolu.hpp" #include "fiko_test.hpp" using namespace fiko; using namespace fiko::cam; namespace { // Veri dizini (CMake FIKO_APT_VERI define ile mutlak yol). Yoksa "veri/". std::string veriDizin() { #ifdef FIKO_APT_VERI return std::string(FIKO_APT_VERI) + "/"; #else return "veri/"; #endif } // Sentetik 3-hamle yol: 1 rapid (yaklasma) + 2 kesme. TakimYolu sentetikYol() { TakimYolu y; Hamle a; a.tip = HamleTip::Hizli; a.nokta = {10.0, 20.0, 50.0}; y.hamleler.push_back(a); Hamle b; b.tip = HamleTip::Kesme; b.nokta = {10.0, 20.0, -5.0}; b.ilerleme = 800.0; y.hamleler.push_back(b); Hamle c; c.tip = HamleTip::Kesme; c.nokta = {110.0, 20.0, -5.0}; c.ilerleme = 1500.0; y.hamleler.push_back(c); y.istatistikGuncelle(); return y; } Takim sentetikTakim() { Takim t; t.numara = 1; t.cap = 12.0; t.koseYaricap = 2.0; t.boy = 45.0; return t; } // Bir alt-dizeyin metinde kac kez gectigini say. std::size_t say(const std::string& metin, const std::string& parca) { std::size_t n = 0, p = 0; while ((p = metin.find(parca, p)) != std::string::npos) { ++n; p += parca.size(); } return n; } } // namespace // (a) Yaz → satirlari dogrula (GOTO sayisi, FEDRAT, CUTTER, PARTNO). FIKO_TEST(apt_yazici_satirlar) { const TakimYolu y = sentetikYol(); AptSecenek sec; sec.partNo = "TEST"; sec.programAdi = "TEST"; sec.devir = 4500.0; const std::string metin = aptYaz(y, sentetikTakim(), sec); FIKO_KONTROL(say(metin, "GOTO") == 3); // 3 hamle → 3 GOTO FIKO_KONTROL(say(metin, "PARTNO TEST") == 1); FIKO_KONTROL(say(metin, "CUTTER/") == 1); FIKO_KONTROL(say(metin, "SPINDL/ ") == 1); // devir > 0 FIKO_KONTROL(say(metin, "TLAXIS/") == 1); FIKO_KONTROL(metin.find("FINI") != std::string::npos); // Iki farkli ilerleme (800, 1500) → 2 FEDRAT satiri. FIKO_KONTROL(say(metin, "FEDRAT/") == 2); // Ilk hamle Hizli → RAPID isareti. FIKO_KONTROL(say(metin, "RAPID") >= 1); } // (b) Round-trip: oku(yaz(yol)) == yol (float toleransi). FIKO_TEST(apt_roundtrip) { const TakimYolu y = sentetikYol(); AptSecenek sec; sec.devir = 4500.0; const std::string metin = aptYaz(y, sentetikTakim(), sec); const AptSonuc s = aptCoz(metin); FIKO_KONTROL(s.basarili); FIKO_KONTROL(s.yol.hamleler.size() == y.hamleler.size()); FIKO_KONTROL(s.gotoSayisi == 3); FIKO_KONTROL(s.devir == 4500.0); // Takim round-trip (cap/koseR/boy). FIKO_YAKIN(s.takim.cap, 12.0, 1e-3); FIKO_YAKIN(s.takim.koseYaricap, 2.0, 1e-3); FIKO_YAKIN(s.takim.boy, 45.0, 1e-3); // Nokta koordinatlari (÷ olcekleme YOK — ham mm). for (std::size_t i = 0; i < y.hamleler.size(); ++i) { FIKO_YAKIN(s.yol.hamleler[i].nokta.x, y.hamleler[i].nokta.x, 1e-4); FIKO_YAKIN(s.yol.hamleler[i].nokta.y, y.hamleler[i].nokta.y, 1e-4); FIKO_YAKIN(s.yol.hamleler[i].nokta.z, y.hamleler[i].nokta.z, 1e-4); } // Ilk hamle rapid (Hizli), digerleri kesme. FIKO_KONTROL(s.yol.hamleler[0].tip == HamleTip::Hizli); FIKO_KONTROL(s.yol.hamleler[1].tip == HamleTip::Kesme); } // (b2) AKTIVITE (operasyon) segmentasyonu — web APTCAT esligi. Eskiden yalniz ILK // "$$ OPERATION NAME" okunup gerisi atiliyordu (tek dev blok, takim degisimi kayip). FIKO_TEST(apt_aktivite_segmentasyon) { const std::string metin = "PARTNO TEST\n" "$$ OPERATION NAME : Kaba.1\n" "LOADTL/3\n" "CUTTER/12.000000,2.000000,4.000000,2.000000,0.000000,0.000000,45.000000\n" "SPINDL/3000.0\n" "FEDRAT/1000.0\n" "GOTO/0.0,0.0,10.0\n" "GOTO/10.0,0.0,10.0\n" "$$ OPERATION NAME : Finis.2\n" "LOADTL/7\n" "CUTTER/6.000000,3.000000,0.000000,3.000000,0.000000,0.000000,30.000000\n" "SPINDL/8000.0\n" "TLAXIS/0.0,0.0,1.0\n" "FEDRAT/500.0\n" "GOTO/20.0,0.0,5.0\n" "GOTO/30.0,0.0,5.0\n" "GOTO/40.0,0.0,5.0\n" "FINI\n"; const AptSonuc s = aptCoz(metin); FIKO_KONTROL(s.basarili); FIKO_KONTROL(s.gotoSayisi == 5); // Iki aktivite, dogru isim + hamle araliklari (ardisik, ortusmez). FIKO_KONTROL(s.aktiviteler.size() == 2); FIKO_KONTROL(s.aktiviteler[0].ad == "Kaba.1"); FIKO_KONTROL(s.aktiviteler[0].hamleBas == 0 && s.aktiviteler[0].hamleAdet == 2); FIKO_KONTROL(s.aktiviteler[1].ad == "Finis.2"); FIKO_KONTROL(s.aktiviteler[1].hamleBas == 2 && s.aktiviteler[1].hamleAdet == 3); // Aktivite basina TAKIM (eskiden yalniz ilk CUTTER okunuyordu). FIKO_YAKIN(s.aktiviteler[0].takim.cap, 12.0, 1e-3); FIKO_YAKIN(s.aktiviteler[1].takim.cap, 6.0, 1e-3); FIKO_YAKIN(s.aktiviteler[1].takim.koseYaricap, 3.0, 1e-3); // LOADTL takim numarasi + aktivite devri. FIKO_KONTROL(s.aktiviteler[0].takimNo == 3 && s.aktiviteler[1].takimNo == 7); FIKO_KONTROL(s.aktiviteler[0].devir == 3000.0 && s.aktiviteler[1].devir == 8000.0); // Geriye donuk alanlar: ILK operasyon + ILK takim korunur. FIKO_KONTROL(s.operasyonAdi == "Kaba.1"); FIKO_YAKIN(s.takim.cap, 12.0, 1e-3); // LOADTL/COOLNT gibi satirlar artik "bilinmeyen" sayilmaz. FIKO_KONTROL(s.bilinmeyenSatir == 0); } // (b3) TAM APT KAPSAMI: FROM / GODLTA / CYCLE / 6-degerli GOTO / birim / kip ifadeleri. FIKO_TEST(apt_tam_kapsam_ifadeler) { const std::string metin = "PARTNO TAMKAPSAM\n" "MACHIN/MILL,1\n" "ORIGIN/1.0,2.0,3.0\n" "CLEARP/100.0\n" "PPRINT OPERATOR MESAJI\n" "$$ OPERATION NAME : Kapsam.1\n" "LOADTL/5\n" "CUTTER/10.000000,0.000000,5.000000,0.000000,0.000000,0.000000,50.000000\n" "SPINDL/2000.0,CCLW\n" "COOLNT/FLOOD\n" "MULTAX/ON\n" "FEDRAT/MMPM,1000.0\n" "FROM/0.0,0.0,50.0\n" "GOTO/10.0,0.0,50.0\n" "GODLTA/0.0,0.0,-10.0\n" // → (10,0,40) "GODLTA/5.0\n" // tek deger = takim ekseni boyunca → (10,0,45) "GOTO/20.0,0.0,45.0,0.0,0.0,1.0\n" // 6 degerli → hamle-basi eksen "FEDRAT/IPM,10.0\n" // 10 inch/dk = 254 mm/dk "GOTO/30.0,0.0,45.0\n" "CYCLE/DRILL,20.0,FEDRAT,200.0\n" "GOTO/40.0,0.0,45.0\n" // cevrim acik → DELIK "GOTO/50.0,0.0,45.0\n" // cevrim acik → DELIK "CYCLE/OFF\n" "COOLNT/OFF\n" "FINI\n"; const AptSonuc s = aptCoz(metin); FIKO_KONTROL(s.basarili); FIKO_KONTROL(s.bilinmeyenSatir == 0); // hepsi taninmali // GOTO ifadesi 5 tane; hamle 8 (FROM + 2 GODLTA dahil). FIKO_KONTROL(s.gotoSayisi == 5); FIKO_KONTROL(s.yol.hamleler.size() == 8); // FROM baslangic konumu. FIKO_YAKIN(s.yol.hamleler[0].nokta.z, 50.0, 1e-6); // GODLTA artimli: (10,0,50) → (10,0,40) → takim ekseni boyunca +5 → (10,0,45). FIKO_YAKIN(s.yol.hamleler[2].nokta.z, 40.0, 1e-6); FIKO_YAKIN(s.yol.hamleler[3].nokta.x, 10.0, 1e-6); FIKO_YAKIN(s.yol.hamleler[3].nokta.z, 45.0, 1e-6); // 6-degerli GOTO → hamle-basi eksen dizisi acilir, boyu hamlelerle birebir. FIKO_KONTROL(s.hamleEksen.size() == s.yol.hamleler.size()); FIKO_YAKIN(s.hamleEksen[4].z, 1.0f, 1e-6f); FIKO_KONTROL(s.multax); // IPM → mm/dk cevrimi (10 inch/dk = 254 mm/dk); bu hamle kesme (esik altinda). FIKO_KONTROL(s.ilerlemeBirim == AptIlerlemeBirim::InchDk); FIKO_YAKIN(s.yol.hamleler[5].ilerleme, 254.0, 1e-3); // CYCLE: iki GOTO delik olarak da kaydedildi (post G81/G83 bunlardan uretir). FIKO_KONTROL(s.cevrimDelik == 2); FIKO_KONTROL(s.yol.delikler.size() == 2); FIKO_YAKIN(s.yol.delikler[1].x, 50.0, 1e-6); FIKO_KONTROL(s.aktiviteler.size() == 1); FIKO_KONTROL(s.aktiviteler[0].cevrim.tip == AptCevrimTip::Delme); FIKO_YAKIN(s.aktiviteler[0].cevrim.derinlik, 20.0, 1e-6); // Kip/metadata ifadeleri. FIKO_KONTROL(s.igYonu == AptIgYonu::SaatTers); FIKO_KONTROL(s.devir == 2000.0); FIKO_KONTROL(s.sogutma == AptSogutma::Kapali); // son COOLNT/OFF FIKO_KONTROL(s.makine == "MILL"); FIKO_YAKIN(s.origin.x, 1.0, 1e-6); FIKO_YAKIN(s.origin.z, 3.0, 1e-6); FIKO_YAKIN(s.clearP, 100.0, 1e-6); FIKO_KONTROL(!s.mesajlar.empty() && s.mesajlar[0] == "OPERATOR MESAJI"); FIKO_KONTROL(s.aktiviteler[0].takimNo == 5); FIKO_YAKIN(s.takim.cap, 10.0, 1e-6); // CUTTER/10,0,... → köşe radyusu 0 → DÜZ uç (parmak freze). FIKO_KONTROL(s.takim.tip == TakimTip::ParmakFreze); } // (b5) CUTTER → TAKIM TİPİ çıkarımı: r≈d/2 küresel, 0= 10 && s.yayNokta <= 40); FIKO_KONTROL(s.yol.hamleler.size() == s.yayNokta + 2); // FROM + ara + son // TUM ara noktalar yay uzerinde (yaricap 10, z=0) ve dogru yonde (CCW: x azalir). for (std::size_t i = 1; i + 1 < s.yol.hamleler.size(); ++i) { const Vec3d& p = s.yol.hamleler[i].nokta; FIKO_YAKIN(std::sqrt(p.x * p.x + p.y * p.y), 10.0, 1e-3); FIKO_YAKIN(p.z, 0.0, 1e-9); FIKO_KONTROL(p.x >= -1e-6 && p.y >= -1e-6); // birinci çeyrekte kaliyor } // Son nokta tam hedefte. const Vec3d& sonN = s.yol.hamleler.back().nokta; FIKO_YAKIN(sonN.x, 0.0, 1e-9); FIKO_YAKIN(sonN.y, 10.0, 1e-9); } // (c) Fixture dosyasi: bozuk satir toleransi + eksen + CUTTER + GOTO sayimi. FIKO_TEST(apt_fixture_ornek) { const AptSonuc s = aptOku(veriDizin() + "ornek.apt"); if (!s.basarili) { // Fixture bulunamadi → SKIP (build ortamina bagli); ama uyar. std::printf(" [SKIP] ornek.apt bulunamadi: %s\n", s.hata.c_str()); return; } FIKO_KONTROL(s.gotoSayisi == 8); // fixture'da 8 GOTO FIKO_KONTROL(s.bilinmeyenSatir >= 1); // "XYZBOZUK" satiri FIKO_YAKIN(s.takim.cap, 30.0, 1e-3); // CUTTER cap FIKO_YAKIN(s.takim.koseYaricap, 15.0, 1e-3); FIKO_KONTROL(s.devir == 4500.0); // Eksen: baslik satirlarindan origin O = (1120, 0, -21.57288). FIKO_YAKIN(s.eksenOrigin.x, 1120.0, 1e-2); FIKO_YAKIN(s.eksenOrigin.z, -21.57288, 1e-2); FIKO_KONTROL(s.operasyonAdi == "Roughing.1"); // Ilk GOTO (25000 = max FEDRAT → rapid). FIKO_KONTROL(!s.yol.hamleler.empty()); FIKO_KONTROL(s.yol.hamleler[0].tip == HamleTip::Hizli); FIKO_YAKIN(s.yol.hamleler[0].nokta.x, 248.45516, 1e-4); // 10000 FEDRAT hamleleri kesme. FIKO_KONTROL(s.yol.hamleler[2].tip == HamleTip::Kesme); // NaN yok. for (const Hamle& h : s.yol.hamleler) FIKO_KONTROL(!std::isnan(h.nokta.x) && !std::isnan(h.nokta.y) && !std::isnan(h.nokta.z)); } // (d) GERCEK DOSYA smoke: repo-disi yol; yoksa SKIP (CI kirilmaz). FIKO_TEST(apt_gercek_smoke) { const std::string yol = "D:/__sistem__/Downloads/Test/Ust_Kalip_202510-UST-FORM-ON.FikoApt"; std::ifstream var(yol, std::ios::binary); if (!var) { std::printf(" [SKIP] gercek dosya yok (repo disi): %s\n", yol.c_str()); return; } var.close(); const AptSonuc s = aptOku(yol); FIKO_KONTROL(s.basarili); FIKO_KONTROL(s.gotoSayisi == 223970); // spec beklentisi FIKO_KONTROL(s.yol.hamleler.size() == 223970); // NaN yok (buyuk dosya, tum noktalar). bool nanVar = false; for (const Hamle& h : s.yol.hamleler) if (std::isnan(h.nokta.x) || std::isnan(h.nokta.y) || std::isnan(h.nokta.z)) { nanVar = true; break; } FIKO_KONTROL(!nanVar); // Aktivite segmentasyonu gercek CATIA ciktisinda: dosyada 6 "$$ OPERATION NAME" // blogu var (Tool Change.2 / Roughing.2-3 / Advanced Finishing.2-3 ...). Eskiden // yalniz ILKI okunup tum dosya tek "Tool Change.2" operasyonu gorunuyordu. FIKO_KONTROL(s.aktiviteler.size() == 6); FIKO_KONTROL(s.aktiviteler[0].ad == "Tool Change.2"); FIKO_KONTROL(s.aktiviteler[1].ad == "Roughing.2"); // Aktivite hamleleri ardisik + ortusmez + toplami tum yolu kapsar. std::size_t toplam = 0, beklenenBas = 0; for (const AptAktivite& a : s.aktiviteler) { FIKO_KONTROL(a.hamleBas == beklenenBas); beklenenBas = a.hamleBas + a.hamleAdet; toplam += a.hamleAdet; } FIKO_KONTROL(toplam == s.yol.hamleler.size()); std::printf(" [BILGI] gercek dosya: %zu GOTO, %zu bilinmeyen satir, %zu aktivite\n", s.gotoSayisi, s.bilinmeyenSatir, s.aktiviteler.size()); for (const AptAktivite& a : s.aktiviteler) std::printf(" - %-26s %7zu hamle Ø%.1f\n", a.ad.c_str(), a.hamleAdet, a.takim.cap); } // (e) HIZ BENCHMARK: 10 MB gercek dosya parse suresi (hedef < 300ms Release). // Sadece raporlar (<1sn assert etmez — CI donanimi degisken); yoksa SKIP. FIKO_TEST(apt_gercek_benchmark) { const std::string yol = "D:/__sistem__/Downloads/Test/Ust_Kalip_202510-UST-FORM-ON.FikoApt"; std::ifstream var(yol, std::ios::binary); if (!var) { std::printf(" [SKIP] gercek dosya yok (benchmark)\n"); return; } var.close(); // Disk cache ısıtma turu + 2 olcum turu (en iyi). double enIyiMs = 1e18; std::size_t got = 0; for (int t = 0; t < 3; ++t) { const auto t0 = std::chrono::high_resolution_clock::now(); const AptSonuc s = aptOku(yol); const auto t1 = std::chrono::high_resolution_clock::now(); const double ms = std::chrono::duration_cast(t1 - t0).count() / 1000.0; FIKO_KONTROL(s.basarili); got = s.gotoSayisi; if (ms < enIyiMs) enIyiMs = ms; } FIKO_KONTROL(got == 223970); // 223970 GOTO dogrula std::printf(" [BENCHMARK] 10MB APT parse: %.1f ms (%zu GOTO)\n", enIyiMs, got); // Yumusak hedef: <1sn olmali (sadece uyar, CI kirmaz). if (enIyiMs > 1000.0) std::printf(" [UYARI] parse 1sn ustunde (%.1f ms) — beklenmedik yavaslik\n", enIyiMs); } // (f) ASYNC YOL BIRIM: saf parse hızı + parçalı ilerleme callback simülasyonu. // GorevSistemi'siz — worker'da çalışacak saf CPU işini dogrudan olcer ve // ilerleme yuzdesini adim adim raporlar (GorevBaglam.ilerlet kalıbı). FIKO_TEST(apt_async_saf_parse) { // Sentetik buyuk metin uret (20K GOTO) — dosyaya bagimli degil. std::string metin = "$$ TEST\nPARTNO SENTETIK\nTLAXIS/ 0.0, 0.0, 1.0\n" "CUTTER/ 10.0, 0.0, 0.0, 0.0, 0.0,$\n 0.0, 40.0\n" "SPINDL/ 3000.0,RPM,CLW\nFEDRAT/500.0,MMPM\n"; metin.reserve(20000 * 40 + 256); for (int i = 0; i < 20000; ++i) { char b[64]; std::snprintf(b, sizeof(b), "GOTO /%11.5f,%11.5f,%11.5f\n", (double)i * 0.5, (double)(-i) * 0.25, 10.0 - i * 0.001); metin += b; } metin += "REWIND/0\nFINI\n"; // "Worker" işi: parse (saf CPU). İlerleme callback simülasyonu (0.1 → 1.0). float sonOran = 0.0f; auto ilerlet = [&](float o) { sonOran = o; }; ilerlet(0.1f); const AptSonuc s = aptCoz(metin); ilerlet(1.0f); FIKO_KONTROL(s.basarili); FIKO_KONTROL(s.gotoSayisi == 20000); FIKO_KONTROL(s.yol.hamleler.size() == 20000); FIKO_KONTROL(s.devir == 3000.0); FIKO_YAKIN(s.takim.cap, 10.0, 1e-3); FIKO_YAKIN(s.takim.boy, 40.0, 1e-3); // CUTTER çok-satırlı devam parse FIKO_KONTROL(sonOran == 1.0f); // NaN yok + koordinat dogru. FIKO_YAKIN(s.yol.hamleler[100].nokta.x, 50.0, 1e-3); for (const Hamle& h : s.yol.hamleler) FIKO_KONTROL(!std::isnan(h.nokta.x) && !std::isnan(h.nokta.z)); }