Documentație API
Optimizatorul ca serviciu HTTP: trimiteți piese și stoc, primiți un plan de tăiere verificat sau un PDF gata de tipărit. Stabil, versionat și compatibil cu versiunile anterioare: se adaugă câmpuri, nu se elimină niciodată.
Introducere
URL de bază: https://api.linearcutting.com. Totul este JSON, cu excepția endpoint-ului PDF. Cererile sunt stateless: nu stocăm listele dumneavoastră de tăiere.
Nu aveți nevoie de o cheie API pentru a încerca. Linia de mai jos rulează exact așa cum este scrisă. Lansați-o într-un terminal.
curl -X POST https://api.linearcutting.com/v1/optimise \
-H "Content-Type: application/json" \
-d '{"parts": "2400x5, 1800x3", "stock": 6000}' Aceasta este o listă de debitare completă: cinci piese de 2400, trei de 1800, tăiate din bare de 6000mm. Primești înapoi aranjamentele, de câte bare e nevoie, ce pierdere rezultă și dacă planul este demonstrabil optim.
Apelurile fără autentificare rulează pe nivelul public gratuit. Îl poți evalua înainte să plătești: 30 de cereri pe oră și 250 pe lună calendaristică, per adresă IP. Dacă depășești limita, primești un 429 care îți spune când se resetează contorul. Apelurile eșuate nu se contorizează. Când construiești ceva serios, ia o cheie de la 5$ pe lună. Elimină ambele limite, mărește pragurile și îți alocă resurse dedicate, ca să nu te încetinească aglomerația de pe nivelul public.
Clienți oficiali
Zero dependențe, licență MIT și nu necesită cheie. Dacă folosești Python sau JavaScript, nu scrie manual un fetch:
import linearcutting as lc
plan = lc.optimise(parts="2400x5, 1800x3", stock=6000)
plan.bars_used # 4
plan.optimal # True, proved, not guessed import { optimise } from "linearcutting";
const plan = await optimise({ parts: "2400x5, 1800x3", stock: 6000 });
plan.barsUsed; // 4
plan.optimal; // true Python pe PyPI · JavaScript pe npm · Sursă Python · Sursă JavaScript
Două garanții pe care te poți baza. verificat este mereu adevărat la succes: fiecare plan e re-verificat cu datele tale de intrare înainte de a fi returnat, deci un plan care nu taie efectiv lista ta nu poate ajunge la tine. optimal e o dovadă, nu o presupunere, adevărat doar când căutarea a stabilit că nu există un plan mai bun, fie epuizând posibilitățile, fie printr-un certificat. Când nu poate fi dovedit în timpul alocat, răspunsul e false, în loc să sperăm.
optimal și stockBarsLowerBound sunt lucruri diferite și nu corespund intenționat. Limita inferioară e o relaxare, aproximativ "lungime totală ÷ lungime bară", și adesea e imposibil de atins. Cinci piese de 2400 și trei de 1800 înseamnă 17,400mm, adică 2.9 bare de 6000, deci limita indică 3. În realitate, nu poți tăia acele piese din 3 bare; 4 este minimul real, și optimizatorul o dovedește. Deci stockBarsUsed: 4 cu stockBarsLowerBound: 3 și optimal: true nu e o contradicție, înseamnă că limita a fost laxă, nu planul greșit. Folosește optimal pentru a decide dacă să continui căutarea; folosește limita inferioară doar ca o verificare de bun simț.
Prescurtare
Forma lungă e explicită și cam complicată. Pentru cazul uzual, folosește forma scurtă: un număr e o lungime, și x înseamnă cantitatea:
{"parts": "2400x5, 1800x3, 600", "stock": 6000} Fiecare dintre acestea înseamnă același lucru, așa că folosiți oricare se potrivește codului pe care îl scrieți:
{"parts": "2400x5"}
{"parts": ["2400x5"]}
{"parts": [[2400, 5]]}
{"parts": [{"length": 2400, "quantity": 5}]} Un al treilea element este o etichetă pentru piesă, și un preț pentru material: [2400, 5, "șină"], [6000, 20, 45.50]. Iar stock e denumirea prietenoasă pentru vechea împărțire stockLength / stocks; un simplu număr înseamnă "lungimea asta, cumpără câte ai nevoie".
Forma prescurtată este expandată înainte de orice altceva, deci e un mod de a scrie, nu un mod de operare diferit: o cerere scurtă și cererea lungă la care se expandează returnează planuri identice, la nivel de byte. Combină-le liber, fiecare cerere existentă încă funcționează exact ca înainte.
O listă de tăiere într-un URL
Lucrările mici încap într-un query string, deci și GET funcționează. Acest lucru este util pentru un link pe care doriți să-l partajați, să-l includeți într-o ofertă sau într-un raport de eroare:
curl "https://api.linearcutting.com/v1/optimise?parts=2400x5,1800x3&stock=6000" Aceeași autentificare, aceleași limite, același plan ca la POST. Îl puteți deschide într-un browser dacă doriți.
Autentificare
Nivelul public nu necesită cheie. Nivelurile plătite își trimit cheia într-un antet; cheile conțin și originile de browser permise, astfel încât apelurile de pe propriul site funcționează fără un proxy.
X-API-Key: lk_live_9f2c... O cheie greșită sau expirată returnează 401. Cheile sunt emise per spațiu de lucru: vezi planurile.
POST /v1/optimise
Rezolvă o listă de tăiere. Returnează planul, statisticile și limitele demonstrabile.
Corp cerere
| Field | Tip | Implicit | Descriere |
|---|---|---|---|
parts obligatoriu | matrice | - | Repererele de care aveți nevoie. Fiecare: length (> 0), quantity (int ≥ 1), opțional label, material, leftAngle, rightAngle. |
stockLength | număr | - | O singură lungime de material brut nelimitată. Folosiți aceasta sau stocks, nu ambele. |
stocks | matrice | - | Lungimi de stoc achiziționabile. Fiecare: length, quantity (null = nelimitat), price (null = optimizare pentru material în loc de bani), opțional material, priority. |
offcuts | matrice | [] | Resturi utilizabile pe care le aveți deja: length, quantity, opțional material, priority. Material gratuit, necontorizat ca achiziție. |
kerf | număr ≥ 0 | 0 | Lățimea pânzei consumată la fiecare tăietură. |
trim | boolean | false | Curățare la capete: elimină o lățime de pânză de la ambele capete ale fiecărui material brut înainte de tăiere. |
method | șir | balanced | balanced, least_waste, offcuts_first, fewest_setups (alias: fewest_setups). Prețurile definite pentru stocks comută modul balanced la cel mai mic cost. |
minRemnant | număr ≥ 0 | 0 | Resturile cu această lungime sau mai mari sunt returnate ca usableRemnants (resturi utilizabile), nu ca pierdere. |
angleCuts | obiect | inactiv | {enabled, width, axialSymmetry}. Vedeți tăieturi în unghi. |
unit | șir | "mm" | Returnat în răspuns și tipărit pe PDF-uri. Nu convertește nimic. |
unitFormat | șir | decimal | fraction afișează dimensiunile din PDF ca fracții rotunjite la 1/64. |
timeLimit | secunde | automat | Timp de procesare, limitat de abonament. Căutarea este de tip 'anytime': un timp mai lung alocat nu face decât să îmbunătățească soluția. |
Răspuns
{
"plan": {
"combinations": [
{
"usedComb": {
"combination": [2400, 2400, 1200],
"length": 6500,
"source": "stock",
"waste": { "total": 492, "remnant": 484, "saw": 8 }
},
"count": 2
}
],
"purchases": [{ "length": 6500, "count": 2 }],
"usableRemnants": [],
"unusedOffcuts": [],
"stockBarsUsed": 2,
"stockBarsLowerBound": 2,
"totalWaste": 984,
"optimal": true,
"verified": true,
"solveTimeMs": 12.5
},
"stats": {
"totalParts": 8, "totalPartsLength": 15600, "totalUsedLength": 16600,
"yieldPercent": 93.98, "cutCount": 8, "layoutCount": 1,
"totalCost": null, "trueWaste": 984
},
"costLowerBound": null,
"meta": { "method": "balanced", "kerf": 4, "tier": "public",
"poweredBy": "linearcutting.com" }
} | Field | Descriere |
|---|---|
plan.combinations[] | O intrare pentru fiecare aranjament distinct. usedComb.combination listează piesele tăiate din bara respectivă, length este sursa, source este stock sau offcut, iar count indică numărul de bare de tăiat în acel mod. |
usedComb.waste | total = sursă − piese; împărțit în saw (lățimea tăieturii + capete) și remnant (restul rămas). |
plan.purchases[] | Lista de cumpărături: length, count, plus price/cost dacă ai furnizat prețuri. |
plan.usableRemnants | Resturi ≥ minRemnant, de la cel mai mare: stocul de resturi utilizabile pentru a doua zi. |
plan.unusedOffcuts | Resturi utilizabile de care planul nu a avut nevoie. |
plan.stockBarsUsed | Total de bare de cumpărat. |
plan.optimal | Dovadă. Adevărat doar când planul atinge un minim demonstrabil (sau căutarea a fost exhaustivă). Poate fi prezentat utilizatorilor ca „cel mai bun rezultat posibil”. |
plan.verified | Întotdeauna adevărat la succes: cererea acoperită, fiecare aranjament se încadrează fizic, stocul de resturi utilizabile respectat. |
plan.stockBarsLowerBound, costLowerBound | Valori minime demonstrabile. Când optimal e fals, diferența îți arată eroarea maximă posibilă a planului. |
stats | Totaluri comandă: piese, lungimi, yieldPercent, cutCount, layoutCount, totalCost, trueWaste și byMaterial când se folosesc grupuri de materiale. |
POST /v1/optimise/pdf
Același corp de cerere, plus un jobName opțional. Returnează application/pdf: prima pagină e un sumar pentru șeful de proiect (necesar de achiziție, costuri, certificat de optimalitate); paginile următoare sunt fișe de tăiere pentru operator, cu pozițiile de tăiere, etichete pentru piese și o căsuță de bifat per bară. Apelurile cu cheie API afișează atribuirea sau brandingul clientului.
curl -X POST https://api.linearcutting.com/v1/optimise/pdf \
-H "Content-Type: application/json" \
-H "X-API-Key: lk_live_9f2c..." \
-d '{ "parts": [{"length": 2400, "quantity": 4}], "stockLength": 6500,
"kerf": 4, "jobName": "Smith deck order" }' \
--output cutting-plan.pdf GET /v1/health
{ "status": "ok", "version": "1.3.0", "solver": "Linear Cutlist Optimizer v2" } Grupuri de materiale
Etichetează parts, stocks și offcuts cu material și fiecare material este procesat ca o lucrare separată într-o singură cerere: tăieturile se fac doar din materialul de același tip. Diagramele și achizițiile se returnează etichetate, iar stats.byMaterial detaliază cifrele pe grup.
{
"parts": [
{ "length": 2400, "quantity": 4, "material": "40x40 SHS" },
{ "length": 1300, "quantity": 6, "material": "Pine 90x45" }
],
"stocks": [
{ "length": 6500, "quantity": null, "material": "40x40 SHS" },
{ "length": 5400, "quantity": null, "material": "Pine 90x45", "priority": true }
],
"kerf": 3
} priority: true pe un material sau rest utilizabil este o preferință relativă: dintre planuri la fel de bune, se folosesc primele articolele prioritizate. Nu scade niciodată calitatea planului.
Tăieri în unghi / oblice
Dă pieselor un leftAngle și rightAngle și activează angleCuts. Unghiurile sunt cele de jos ale piesei în poziție orizontală; introdu unghiul de sus ca număr negativ (120 și -60 sunt aceeași tăietură). Intervalul valid după transformare: 10 - 170 de grade.
| angleCuts field | Descriere |
|---|---|
enabled | Comutator principal. Unghiurile pieselor sunt acceptate doar dacă e setat pe true. |
width | Lățimea materialului, în aceeași unitate ca lungimile. O tăietură în unghi se extinde lateral cu width / tan(angle). |
axialSymmetry | none, X, Y, Z sau XYZ: ce răsturnări permite materialul, ceea ce decide cum se pot cuibări tăieturile în unghi. Țeava pătrată este de obicei XYZ; materialul cu o singură față este none. |
Unde două tăieturi în unghi vecine sunt paralele, o singură tăietură cu fierăstrăul creează ambele margini. Diagramele în modul unghi adaugă un array parts ordonat (cu orientarea folosită) și cutPositions: fiecare tăietură măsurată de la capătul zero al barei, ca o pereche [sus, jos] când este în unghi, sau un singur număr când este dreaptă.
"cutPositions": [[1200, 1300], [2442.265, 2500], 3300] Erori
Corpurile erorilor sunt { "error": "...", "detail": "..." }. Câmpul detail este scris pentru a fi arătat unui om: numește piesa sau materialul care a cauzat problema.
| Status | eroare | Când |
|---|---|---|
| 400 | validation | Lungimi sau cantități greșite, metodă necunoscută, o piesă care nu încape pe niciun material disponibil, cerere mai mare decât stocul finit, reguli de unghi încălcate, s-au trimis și stocks și stockLength simultan. |
| 401 | badKey | Cheie API lipsă, necunoscută sau dezactivată. |
| 413 | partsLimit | Mai multe piese decât permite nivelul tău. |
| 429 | rateLimited | Limită de cereri sau plafon lunar atins. Respectă Retry-After. |
| 503 | busy | Solver la capacitate maximă. Reîncearcă în curând. |
Limite de cereri
Limitele restricționează; nu generează niciodată facturi suplimentare. Cererile care eșuează la validare nu se contorizează în plafonul lunar. Cifrele complete sunt pe pagina de abonamente.
| Guard | Gratuit | Abonamente plătite |
|---|---|---|
| Rată | 10 cereri / 5 min per IP | 20-240 / min pe nivel |
| Total repere | 300 | 500 - 5,000 |
| Linii de repere distincte | 60 | până la 400 |
| Buget de rezolvare | 20 s | 60 - 300 s |
Liniile de repere distincte sunt limitate, la fel ca totalul de repere, deoarece costul de rezolvare depinde de diversitatea lungimilor: 300 de tăieturi identice se dovedesc optime în milisecunde, 300 de tăieturi diferite nu.
Rețete
Cel mai ieftin mix din mai multe lungimi de material brut (JavaScript)
const res = await fetch("https://api.linearcutting.com/v1/optimise", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "lk_live_9f2c...",
},
body: JSON.stringify({
parts: [
{ length: 3000, quantity: 8 },
{ length: 1200, quantity: 10 },
],
stocks: [
{ length: 6500, quantity: null, price: 45 }, // unlimited
{ length: 12000, quantity: 4, price: 70 }, // only four on hand
],
offcuts: [{ length: 2600, quantity: 1 }],
kerf: 4,
minRemnant: 400,
method: "balanced", // prices present -> optimises dollars
}),
});
const { plan, stats } = await res.json();
console.log(plan.purchases); // what to buy
console.log(stats.totalCost); // what it costs
console.log(plan.optimal); // true = provably the cheapest Repere de cadru tăiate la unghi (Python)
import requests
r = requests.post(
"https://api.linearcutting.com/v1/optimise",
json={
"parts": [
{"length": 1300, "quantity": 2, "leftAngle": 90, "rightAngle": 45},
{"length": 1200, "quantity": 2, "leftAngle": 135, "rightAngle": 60},
],
"stockLength": 3310,
"kerf": 0,
"angleCuts": {"enabled": True, "width": 100, "axialSymmetry": "none"},
},
timeout=30,
)
r.raise_for_status()
for combo in r.json()["plan"]["combinations"]:
print(combo["count"], "x", combo["usedComb"]["cutPositions"]) Gestionarea unei lucrări lente, de mari dimensiuni
Listele mari pot dura de la secunde la minute. Căutarea este de tip "anytime", așa că setați timeLimit la cât poate aștepta interfața dvs. și afișați optimal pentru a le spune utilizatorilor dacă soluția este demonstrată sau doar cea mai bună găsită până acum.
{ "parts": [ /* 900 parts */ ], "stockLength": 6500,
"kerf": 4, "timeLimit": 60 }