Documentação da API

O otimizador como um serviço HTTP: envie as peças e o stock, receba um plano de corte validado ou um PDF pronto a imprimir. Estável, versionado e com retrocompatibilidade: os campos são adicionados, nunca removidos.

Introdução

URL base: https://api.linearcutting.com. Tudo é JSON, à exceção do endpoint para PDF. Os pedidos são stateless: não armazenamos as suas listas de corte.

Não precisa de uma chave de API para experimentar. A linha abaixo executa, tal como está, agora mesmo. Cole-a num terminal.

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

Isto é uma lista de corte completa: cinco peças de 2400, três de 1800, cortadas de barras de 6000mm. Recebe de volta os layouts, o número de barras necessárias, o desperdício, e se o plano é comprovadamente ótimo.

As chamadas não autenticadas funcionam num nível público gratuito, para que possa avaliar o serviço antes de pagar: 30 pedidos por hora e 250 por mês, por endereço IP. A partir daí, devolve um erro 429 que informa quando o limite é reposto. As chamadas falhadas nunca contam para o limite. Quando estiver a construir algo a sério, obtenha uma chave a partir de 5 $ por mês: aumenta ambos os limites e os tamanhos máximos, e dá-lhe concorrência própria para que uma tarde movimentada no nível público não o atrase.

Clientes oficiais

Sem dependências, licença MIT e também não precisam de chave. Se estiver a usar Python ou JavaScript, não programe um fetch à mão:

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 no PyPI · JavaScript no npm · Código fonte Python · Código fonte JavaScript

Duas garantias com que pode contar. verified é sempre verdadeiro em caso de sucesso: cada plano é verificado com base nos seus dados de entrada antes de ser devolvido. Assim, garante que nunca recebe um plano que não corresponda à sua lista de corte. optimal é uma prova, não uma suposição, verdadeiro apenas quando a pesquisa estabelece que não existe um plano melhor, seja por esgotar as possibilidades ou por meio de um certificado. Quando não pode ser provado dentro do tempo disponível, indicamos false em vez de assumir.

optimal e stockBarsLowerBound são coisas diferentes, e divergem propositadamente. O limite inferior é uma aproximação, sensivelmente "comprimento total ÷ comprimento da barra", e muitas vezes não é exequível. Cinco peças de 2400 e três de 1800 são 17.400mm, o que equivale a 2.9 barras de 6000, portanto o limite indica 3. Na realidade, não pode cortar essas peças a partir de 3 barras; 4 é o verdadeiro mínimo, e o optimizador prova-o. Portanto, stockBarsUsed: 4 com stockBarsLowerBound: 3 e optimal: true não é uma contradição. Significa que o limite era uma aproximação grosseira, não que o plano estava errado. Use optimal para decidir se deve continuar a procurar; use o limite apenas como uma verificação de consistência.

Abreviatura

A forma longa é explícita e algo complexa. Para o caso comum, use a forma curta: um número é um comprimento, e x significa a quantidade:

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

Todos estes significam o mesmo, por isso use o que for mais adequado para o seu código:

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

Um terceiro elemento é uma etiqueta numa peça, e um preço no material: [2400, 5, "rail"], [6000, 20, 45.50]. E stock é o nome amigável para a antiga divisão stockLength / stocks. Um número simples significa "este comprimento, compre tantos quantos forem necessários".

A forma curta é expandida antes de qualquer outra coisa acontecer, por isso é uma forma de escrever em vez de um modo diferente: um pedido curto e o pedido longo para o qual ele expande devolvem planos idênticos ao byte. Misture e combine livremente. Todos os pedidos existentes continuam a funcionar exatamente como antes.

Uma lista de corte num URL

Trabalhos pequenos cabem numa query string, pelo que GET também funciona. É útil para um link que queira partilhar, orçamentar ou incluir num relatório de erro:

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

Mesma autenticação, mesmos limites, mesmo plano que o POST. Pode abri-lo num browser.

Autenticação

O plano público não precisa de chave. Os planos pagos enviam a chave num cabeçalho; as chaves também contêm as origens de browser permitidas, para que as chamadas do seu próprio site funcionem sem um proxy.

http
X-API-Key: lk_live_9f2c...

Uma chave inválida ou expirada devolve 401. As chaves são emitidas por área de trabalho: ver planos.

POST /v1/optimise

Resolve uma lista de corte. Devolve o plano, estatísticas e limites comprováveis.

Corpo do pedido

FieldTipoPadrãoDescrição
parts obrigatório array- As peças de que precisa. Cada uma: length (> 0), quantity (int ≥ 1), label opcional, material, leftAngle, rightAngle.
stockLength número- Um único comprimento de stock ilimitado. Use este ou stocks, não ambos.
stocks array- Comprimentos disponíveis para compra. Cada um: length, quantity (null = ilimitado), price (null = otimizar o material em vez do custo), material opcional, priority.
offcuts array[] Comprimentos que já possui: length, quantity, material opcional, priority. Material gratuito, nunca contabilizado como compra.
kerfnúmero ≥ 00Largura da lâmina consumida por corte.
trimbooleanofalseRetirar a espessura de um corte de ambas as extremidades de cada material de origem antes de cortar.
methodstringbalanced balanced, least_waste, offcuts_first, fewest_setups (alias: fewest_setups). Os preços em stocks mudam o modo balanced para o de menor custo.
minRemnantnúmero ≥ 00As sobras com este comprimento ou superior são devolvidas como usableRemnants em vez de desperdício.
angleCutsobjectdesligado{enabled, width, axialSymmetry}. Ver cortes em ângulo.
unitstring"mm"É devolvido e impresso nos PDFs. Não converte nenhum valor.
unitFormatstringdecimalO modo fraction formata as dimensões do PDF como a fração mais próxima de 1/64.
timeLimitsegundosautomáticoTempo de cálculo, limitado pelo seu plano. A pesquisa pode ser interrompida a qualquer momento: mais tempo de cálculo apenas melhora o resultado.

Resposta

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" }
}
FieldDescrição
plan.combinations[]Uma entrada por cada layout distinto. usedComb.combination lista as peças cortadas dessa barra, length é o comprimento, source é a origem (stock ou offcut), e count é o número de barras a cortar dessa forma.
usedComb.wastetotal = origem − peças; dividido em saw (largura de corte + aparo) e remnant (a ponta que sobra).
plan.purchases[]A lista de compras: length, count, mais price/cost quando forneceu preços.
plan.usableRemnantsSobras ≥ minRemnant, das maiores para as menores: o material de retalhos para amanhã.
plan.unusedOffcutsRetalhos que o plano não precisou.
plan.stockBarsUsedTotal de barras a comprar.
plan.optimalProva. Verdadeiro apenas quando o plano atingiu um mínimo comprovável (ou a pesquisa foi exaustiva). Seguro para mostrar aos utilizadores como "o melhor possível".
plan.verifiedSempre verdadeiro em caso de sucesso: pedido correspondido, cada layout cabe fisicamente, stock de retalhos respeitado.
plan.stockBarsLowerBound, costLowerBoundMínimos comprováveis. Quando optimal é falso, a diferença diz-lhe qual a margem de erro máxima deste plano.
statsTotais do trabalho: peças, comprimentos, yieldPercent, cutCount, layoutCount, totalCost, trueWaste, e byMaterial quando são usados grupos de materiais.

POST /v1/optimise/pdf

Corpo do pedido igual, mais um jobName opcional. Devolve application/pdf: a pág. 1 é um resumo para o gestor de projeto (ordem de compra, custos, certificado de otimalidade); as páginas seguintes são folhas para o operador de serra com posições de corte contínuas, etiquetas de peças e uma caixa de verificação por barra. Chamadas com chave recebem a sua atribuição ou marca.

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

Grupos de materiais

Etiquete parts, stocks e offcuts com material e cada material é resolvido como um trabalho separado dentro de um único pedido: os cortes são sempre feitos em material do mesmo tipo. Os esquemas de corte e as compras são devolvidos com etiquetas, e stats.byMaterial detalha os números por grupo.

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 num material ou sobra é uma preferência não vinculativa: entre planos igualmente bons, os itens prioritários são usados primeiro. Nunca compromete a qualidade do plano.

Cortes em ângulo / esquadria

Atribua leftAngle e rightAngle às peças e ative angleCuts. Os ângulos são os ângulos inferiores da peça na sua posição de corte; insira um ângulo superior como um número negativo (120 e -60 são o mesmo corte). Intervalo válido após a transformação: 10 a 170 graus.

angleCuts fieldDescrição
enabledInterruptor geral. Ângulos nas peças só são aceites quando for 'true'.
widthLargura do material, na mesma unidade que os comprimentos. Um corte em ângulo desvia-se lateralmente em width / tan(angle).
axialSymmetrynone, X, Y, Z ou XYZ: que rotações o material permite, o que decide como as esquadrias se podem encaixar. O tubo quadrado é tipicamente XYZ; material com um só lado é none.

Quando duas esquadrias adjacentes são paralelas, um só corte de serra faz ambas as arestas. Os esquemas de corte em modo de ângulo adicionam uma matriz ordenada parts (com a orientação utilizada) e cutPositions: cada corte medido a partir do início da barra, como um par [topo, base] se for em ângulo, ou um único número se for a direito.

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

Erros

Os corpos dos erros são { "error": "...", "detail": "..." }. O detail é escrito para ser mostrado a uma pessoa: nomeia a peça ou o material em questão.

StatuserroQuando
400validationComprimentos ou quantidades incorretos, método desconhecido, uma peça que não cabe em nenhum material de origem, pedido excede o material finito, regras de ângulo violadas, stocks e stockLength enviados em simultâneo.
401badKeyChave de API em falta, desconhecida ou desativada.
413partsLimitMais peças do que o seu nível permite.
429rateLimitedLimite de pedidos ou limite mensal atingido. Respeite o Retry-After.
503busyOtimizador no limite da capacidade. Tente novamente em breve.

Limites de pedidos

Os limites restringem; nunca geram uma fatura surpresa. Os pedidos que falham a validação não contam para o seu limite mensal. Os números completos estão na página de planos.

GuardGratuitoNíveis pagos
Taxa10 pedidos / 5 min por IP20-240 / min por plano
Total de peças300500 - 5,000
Linhas de peças distintas60até 400
Orçamento de resolução20 s60 - 300 s

O número de linhas de peças distintas é limitado, assim como o total de peças, porque o custo da resolução depende da diversidade de comprimentos: 300 cortes idênticos são otimizados em milissegundos, 300 cortes diferentes não.

Exemplos

Combinação mais barata de vários comprimentos de stock (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

Peças de caixilho em esquadria (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"])

Lidar com um trabalho grande e lento

Listas grandes podem demorar segundos ou minutos. A procura é contínua, por isso defina o timeLimit para o que a sua UI consegue esperar e mostre optimal para indicar aos utilizadores se a solução é final ou apenas a melhor encontrada até ao momento.

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