Documentazione API

L'ottimizzatore come servizio HTTP: invia tagli e materiale, ricevi un piano di taglio verificato o un PDF pronto per la stampa. Stabile, versionato e retrocompatibile: i campi vengono solo aggiunti, mai rimossi.

Introduzione

URL di base https://api.linearcutting.com. È tutto JSON, tranne l'endpoint PDF. Le richieste sono stateless: non memorizziamo le tue liste di taglio.

Non serve una chiave API per provare. La riga qui sotto funziona così com'è, subito. Incollala in un terminale.

Copy, paste, run
curl -X POST https://api.linearcutting.com/v1/optimise \
  -H "Content-Type: application/json" \
  -d '{"parts": "2400x5, 1800x3", "stock": 6000}'

Questa è una lista di taglio completa: cinque pezzi da 2400, tre da 1800, tagliati da barre da 6000mm. In risposta ottieni gli schemi di taglio, quante barre servono, quanto materiale viene sprecato, e se il piano è dimostrabilmente ottimo.

Le chiamate non autenticate utilizzano un piano pubblico gratuito, così puoi valutare il servizio prima di pagare: 30 richieste all'ora e 250 al mese per indirizzo IP. Superato questo limite, il sistema restituisce un errore 429 che indica quando si resetta. Le chiamate fallite non vengono conteggiate. Per progetti di produzione, acquista una chiave API a partire da 5 $ al mese: rimuove entrambi i limiti, aumenta le dimensioni massime e ti dà una concorrenza dedicata, così un pomeriggio intenso sul piano pubblico non può rallentarti.

Client ufficiali

Nessuna dipendenza, licenza MIT, e non richiedono neanche una chiave. Se usi Python o JavaScript, non creare a mano una richiesta 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 su PyPI · JavaScript su npm · Sorgente Python · Sorgente JavaScript

Due garanzie su cui puoi contare. verified è sempre vero in caso di successo: ogni piano viene riverificato confrontandolo con i dati da te inseriti prima di essere restituito, così che un piano che non taglia effettivamente la tua lista non possa mai arrivarti. optimal è una prova, non una stima. È vero solo quando la ricerca ha stabilito che non esiste un piano migliore, o perché ha esaurito le possibilità o tramite un certificato. Quando non può essere provato entro il tempo limite, restituiamo false invece di tirare a indovinare.

optimal e stockBarsLowerBound sono due cose diverse, e non concordano di proposito. Il limite inferiore è una semplificazione, circa "lunghezza totale ÷ lunghezza barra", e spesso non è raggiungibile. Cinque pezzi da 2400 e tre da 1800 fanno 17.400 mm, che equivalgono a 2,9 barre da 6000, quindi il limite è 3. In pratica non puoi tagliare quei pezzi da 3 barre; 4 è il vero minimo, e il solutore lo dimostra. Quindi stockBarsUsed: 4 con stockBarsLowerBound: 3 e optimal: true non è una contraddizione. Significa che il limite era approssimativo, non che il piano era sbagliato. Usa optimal per decidere se continuare la ricerca; usa il limite solo come verifica di massima.

Sintassi abbreviata

Il formato esteso è esplicito e un po' verboso. Per i casi più comuni, usa il formato breve: un numero indica una lunghezza e x la quantità:

The short form
{"parts": "2400x5, 1800x3, 600", "stock": 6000}

Ognuno di questi esempi significa la stessa cosa, quindi usa quello che preferisci per il tuo codice:

All equivalent
{"parts": "2400x5"}
{"parts": ["2400x5"]}
{"parts": [[2400, 5]]}
{"parts": [{"length": 2400, "quantity": 5}]}

Un terzo elemento è un'etichetta per un pezzo e un prezzo per il materiale: [2400, 5, "rail"], [6000, 20, 45.50]. stock è il nome più comodo per la vecchia suddivisione stockLength / stocks. Un numero semplice significa "questa lunghezza, compra la quantità che ti serve".

Il formato breve viene espanso prima di ogni altra operazione, quindi è un modo di inserire i dati, non una modalità diversa: una richiesta breve e la richiesta estesa in cui viene espansa restituiscono piani identici al byte. Puoi usare entrambi i formati liberamente. Ogni richiesta esistente funziona esattamente come prima.

Una lista di taglio in un URL

I lavori piccoli entrano in una stringa di query, quindi funziona anche GET. Utile per un link da condividere, per un preventivo o da inserire in una segnalazione di bug:

GET /v1/optimise
curl "https://api.linearcutting.com/v1/optimise?parts=2400x5,1800x3&stock=6000"

Stessa autenticazione, stessi limiti, stesso piano di taglio del POST. Puoi anche aprirlo in un browser.

Autenticazione

Il livello pubblico non richiede una chiave. I livelli a pagamento inviano la chiave in un header; le chiavi contengono anche le origini browser consentite, quindi le chiamate dal tuo sito funzionano senza proxy.

http
X-API-Key: lk_live_9f2c...

Una chiave errata o scaduta restituisce 401. Le chiavi sono emesse per ogni area di lavoro: vedi i piani.

POST /v1/optimise

Calcola una lista di taglio. Restituisce il piano, le statistiche e i limiti dimostrabili.

Corpo della richiesta

FieldTipoPredefinitoDescrizione
parts richiesto array- I pezzi da tagliare. Ciascuno: length (> 0), quantity (int ≥ 1), opzionali label, material, leftAngle, rightAngle.
stockLength numero- Una singola lunghezza di materiale illimitata. Usa questo o stocks, non entrambi.
stocks array- Barre acquistabili. Ognuna: length, quantity (null = illimitata), price (null = ottimizza il materiale invece del costo), material opzionale, priority.
offcuts array[] Barre già a magazzino: length, quantity, material opzionale, priority. Materiale a costo zero, mai conteggiato come acquisto.
kerfnumero ≥ 00Larghezza della lama consumata per ogni taglio.
trimbooleanofalseEsegue un taglio di intestatura su entrambe le estremità di ogni barra prima di iniziare.
methodstringbalanced balanced, least_waste, offcuts_first, fewest_setups (alias: fewest_setups). I prezzi sulle stocks cambiano balanced in modalità minor costo.
minRemnantnumero ≥ 00I pezzi rimanenti di questa lunghezza o superiori vengono restituiti come usableRemnants invece che come scarto.
angleCutsobjectdisattivato{enabled, width, axialSymmetry}. Vedi tagli angolari.
unitstring"mm"Riportato nella risposta e stampato sui PDF. Non converte nessuna unità di misura.
unitFormatstringdecimalfraction formatta le dimensioni nel PDF come frazioni al 1/64 più vicino.
timeLimitsecondiautomaticoTempo di calcolo, limitato dal piano. La ricerca è di tipo 'anytime': più tempo a disposizione migliora solo il risultato.

Risposta

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" }
}
FieldDescrizione
plan.combinations[]Una voce per ogni schema di taglio distinto. usedComb.combination elenca i pezzi tagliati da quella barra, length è la fonte, source è stock o offcut, e count indica quante barre tagliare in quel modo.
usedComb.wastetotal = fonte − pezzi; suddiviso in saw (spessore lama + rifilo) e remnant (l'eccedenza finale).
plan.purchases[]La lista della spesa: length, count, più price/cost se hai fornito i prezzi.
plan.usableRemnantsEccedenze ≥ minRemnant, dalla più grande alla più piccola: il magazzino sfridi di domani.
plan.unusedOffcutsSfridi che il piano non ha richiesto.
plan.stockBarsUsedBarre totali da acquistare.
plan.optimalProva. Vero solo quando il piano ha raggiunto un minimo dimostrabile (o la ricerca è stata completata in modo esaustivo). Si può mostrare agli utenti come "miglior risultato possibile".
plan.verifiedSempre vero in caso di successo: domanda soddisfatta, ogni schema di taglio è fisicamente possibile, scorta di sfridi rispettata.
plan.stockBarsLowerBound, costLowerBoundMinimi dimostrabili. Quando optimal è falso, il divario indica di quanto il piano potrebbe al massimo discostarsi.
statsTotali del lavoro: pezzi, lunghezze, yieldPercent, cutCount, layoutCount, totalCost, trueWaste, e byMaterial quando si usano i gruppi di materiali.

POST /v1/optimise/pdf

Stesso corpo della richiesta, più un jobName opzionale. Restituisce application/pdf: pagina 1 è un riepilogo per il capoprogetto (ordine d'acquisto, costi, certificato di ottimalità); le pagine seguenti sono schede per l'operatore alla sega con posizioni di taglio progressive, etichette dei pezzi e una casella di spunta per ogni barra. Per i chiamanti con chiave, vengono renderizzati l'attribuzione o il marchio.

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" }

Gruppi di materiali

Contrassegna parts, stocks e offcuts con material e ogni materiale viene risolto come un lavoro a sé stante all'interno di una singola richiesta: i tagli vengono eseguiti solo su stock dello stesso materiale. I layout e gli acquisti vengono restituiti con il tag e stats.byMaterial suddivide i numeri per gruppo.

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 su uno stock o uno spezzone è una preferenza non vincolante: tra piani di pari qualità, gli elementi prioritari vengono usati per primi. Non compromette mai la qualità del piano.

Tagli angolati / obliqui

Assegna ai pezzi un leftAngle e un rightAngle e abilita angleCuts. Gli angoli sono quelli inferiori del pezzo nella sua posizione di taglio; inserisci un angolo superiore come numero negativo (120 e -60 sono lo stesso taglio). L'intervallo valido dopo la trasformazione: da 10 a 170 gradi.

angleCuts fieldDescrizione
enabledInterruttore generale. Gli angoli sui pezzi sono accettati solo se impostato su true.
widthLarghezza del materiale, stessa unità delle lunghezze. Un taglio angolato si estende lateralmente di width / tan(angle).
axialSymmetrynone, X, Y, Z o XYZ: definisce quali rotazioni del materiale sono consentite, il che determina come i tagli obliqui possono essere annidati. Il tubolare quadro è tipicamente XYZ; il materiale con un solo lato lavorabile è none.

Quando due tagli obliqui adiacenti sono paralleli, un unico taglio di sega crea entrambi i bordi. I layout in modalità angolata aggiungono un array ordinato parts (con l'orientamento usato) e cutPositions: ogni taglio misurato dall'estremità zero della barra, come coppia [superiore, inferiore] se angolato, o come numero singolo se dritto.

angle-mode layout (excerpt)
"cutPositions": [[1200, 1300], [2442.265, 2500], 3300]

Errori

Il corpo degli errori è { "error": "...", "detail": "..." }. Il campo detail è scritto per essere mostrato a un operatore umano: nomina il pezzo o il materiale che causa il problema.

StatuserroreQuando
400validationLunghezze o quantità errate, metodo sconosciuto, un pezzo che non entra in nessun materiale di partenza, richiesta che supera lo stock finito, regole degli angoli violate, invio contemporaneo di stocks e stockLength.
401badKeyChiave API mancante, sconosciuta o disabilitata.
413partsLimitPiù pezzi di quanto consentito dal tuo piano.
429rateLimitedLimite di richieste o cap mensile raggiunto. Rispetta l'header Retry-After.
503busySolutore al massimo della capacità. Riprova a breve.

Limiti di richieste

I limiti bloccano; non generano mai un addebito per l'eccesso. Le richieste con convalida fallita non contano per il cap mensile. I numeri completi sono sulla pagina dei piani.

GuardGratuitoPiani a pagamento
Frequenza10 richieste / 5 min per IP20-240 / min per livello
Pezzi totali300500 - 5,000
Linee pezzo distinte60fino a 400
Budget di calcolo20 s60 - 300 s

Le linee pezzo distinte sono limitate così come i pezzi totali, perché il costo del calcolo deriva dalla diversità delle lunghezze: 300 tagli identici si ottimizzano in millisecondi, 300 diversi no.

Esempi

Combinazione più economica da diverse lunghezze di materiale (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

Pezzi per telai con tagli inclinati (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"])

Gestire un lavoro grande e lento

Le liste lunghe possono richiedere da secondi a minuti. La ricerca è progressiva, quindi imposta timeLimit al tempo massimo che la tua interfaccia può attendere e mostra optimal per indicare se il risultato è dimostrato ottimo o solo il migliore trovato finora.

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