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.

Copy, paste, run
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:

pip install linearcutting
import linearcutting as lc

plan = lc.optimise(parts="2400x5, 1800x3", stock=6000)
plan.bars_used   # 4
plan.optimal     # True, proved, not guessed
npm install linearcutting
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:

The short form
{"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:

All equivalent
{"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:

GET /v1/optimise
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.

http
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

FieldTipImplicitDescriere
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.
kerfnumăr ≥ 00Lățimea pânzei consumată la fiecare tăietură.
trimbooleanfalseCurățare la capete: elimină o lățime de pânză de la ambele capete ale fiecărui material brut înainte de tăiere.
methodșirbalanced balanced, least_waste, offcuts_first, fewest_setups (alias: fewest_setups). Prețurile definite pentru stocks comută modul balanced la cel mai mic cost.
minRemnantnumăr ≥ 00Resturile cu această lungime sau mai mari sunt returnate ca usableRemnants (resturi utilizabile), nu ca pierdere.
angleCutsobiectinactiv{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șirdecimalfraction afișează dimensiunile din PDF ca fracții rotunjite la 1/64.
timeLimitsecundeautomatTimp 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

200 application/json
{
  "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" }
}
FieldDescriere
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.wastetotal = 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.usableRemnantsResturi ≥ minRemnant, de la cel mai mare: stocul de resturi utilizabile pentru a doua zi.
plan.unusedOffcutsResturi utilizabile de care planul nu a avut nevoie.
plan.stockBarsUsedTotal de bare de cumpărat.
plan.optimalDovadă. 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, costLowerBoundValori minime demonstrabile. Când optimal e fals, diferența îți arată eroarea maximă posibilă a planului.
statsTotaluri 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.

bash
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

json
{ "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.

json
{
  "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 fieldDescriere
enabledComutator principal. Unghiurile pieselor sunt acceptate doar dacă e setat pe true.
widthLățimea materialului, în aceeași unitate ca lungimile. O tăietură în unghi se extinde lateral cu width / tan(angle).
axialSymmetrynone, 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ă.

angle-mode layout (excerpt)
"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.

StatuseroareCând
400validationLungimi 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.
401badKeyCheie API lipsă, necunoscută sau dezactivată.
413partsLimitMai multe piese decât permite nivelul tău.
429rateLimitedLimită de cereri sau plafon lunar atins. Respectă Retry-After.
503busySolver 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.

GuardGratuitAbonamente plătite
Rată10 cereri / 5 min per IP20-240 / min pe nivel
Total repere300500 - 5,000
Linii de repere distincte60până la 400
Buget de rezolvare20 s60 - 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)

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)

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.

json
{ "parts": [ /* 900 parts */ ], "stockLength": 6500,
  "kerf": 4, "timeLimit": 60 }