Sample client application

On this page you will find an example demonstrating how to create a client application that interacts with the Nesting Center API. The example is intended to help developers quickly understand concepts and features required to write a nesting application.

You will learn how to:

  • Prepare nesting data.
  • Start nesting calculations.
  • Receive calculation status.
  • Download nesting layouts.
  • Delete nesting session.
We start with the assumption that you have a basic knowledge about programming languages and software development tools. The description of the code is divided into sections which demonstrate workflow of nesting process. The last section contains complete source code of the application.

Preparing nesting data

The very first step is to prepare required nesting data. You need to collect parts and material in a JSON format and pass it as input data to the Nesting Center API. For the purpose of this example you can use our sample JSON data object with predefined parts and material. The data is a text string with information in JSON format, in the example it is read from a file.

For more information, see How to prepare nesting data.

Starting nesting layout calculations

Interacting with the Nesting Center service is simple. The client application sends requests to the service endpoint and receives responses. The first step is to send a “start” request. The Nesting Center service starts a new nesting session and returns a job ID. The job ID is a key that specifies the computation url used in the following Nesting Center API queries.

START

// Sample nesting data in JSON format.
string nestingData = File.ReadAllText("example.json");

using (HttpClient client = new HttpClient())
{
   // Start a new computation.
   string computationUrl = await Nesting.StartComputation(client, nestingData);

   if (string.IsNullOrEmpty(computationUrl))
   {
      return;
   }

   Console.WriteLine("Nesting computation started.");
}
# Sample nesting data in JSON format.
f = open('example.json')
nesting_data = json.loads(f.read())
f.close()

# Start a new computation.
async with aiohttp.ClientSession() as session:
   computation_url = await Nesting.start_computation(session, nesting_data)

   if not computation_url:
      return

   print('Nesting computation started.')
// Sample nesting data in JSON format.
String data = Files.readString(Path.of("example.json"));
JSONObject nestingData = new JSONObject(data);

// Start a new computation.
HttpClient client = HttpClient.newHttpClient();
String computationUrl = Nesting.startComputation(client, nestingData).get();
if (computationUrl.isEmpty()) {
   return;
}

System.out.println("Nesting computation started.");

Getting calculation status

Once the nesting layout calculations are started, the client application queries the service for status and waits for work completion. The status can be received by sending a request to the computation url generated in the previous step. The status response contains property “StateString” which tells if the work is completed or still in progress. The application checks the state every few seconds and repeats the query until it receives notification that calculations has stopped. The work is completed when the stop criteria are satisfied or the client application may decide to stop the process by sending a “stop” request.

STATUS

string computationState = string.Empty;
JObject status = null;
            
while (computationState != "Stopped")
{
   // Work in progress, check status every 10s.
   await Task.Delay(10000);

   // Get a computation status.
   status = await Nesting.ComputationStatus(client, computationUrl);
   if (status != null)
   {
      computationState = status["StateString"].ToString();
   }
   else
   {
      break;
   }
}
# Check a computation status.
status = None
computation_state = ''

while computation_state != 'Stopped':
   # Work in progress, check status every 10s.
   await asyncio.sleep(10)
   status = await Nesting.computation_status(session, computation_url)

   if status is not None:
      computation_state = status['StateString']
   else:
      break
// Check a computation status.
JSONObject status = null;
boolean stopped = false;

while (!stopped) {
   // Work in progress, check status every 10s.
   TimeUnit.SECONDS.sleep(10);

   status = Nesting.computationStatus(client, computationUrl).get();

   if (status == null) {
      stopped = true;
   } else {
      String computationState = status.getString("StateString");
      stopped = computationState.equals("Stopped");
   }
}

Getting nesting layouts

Success! Nesting layout is calculated and can be read by the client application. Layouts produced by the service are identified by the “ResultVersion” property. The service continuously tries to improve the current layout, every time a new better layout is generated it has a new version number.

RESULT

// Get a computation result.
string resultId = status["ResultVersion"].ToString();
JObject nestingResult = await Nesting.ComputationResult(client, computationUrl, resultId);

// Display all log messages (optional).
string messageId = status["LogEntryIdx"].ToString();
JObject messages = await Nesting.ComputationMessages(client, computationUrl, "1", messageId);

if (messages != null)
{
   foreach (JObject message in messages["Entries"])
   {
      Console.WriteLine(message["Message"].ToString());
   }
}
# Get a computation result.
result_id = status['ResultVersion']
result = await Nesting.computation_result(session, computation_url, result_id)

if result is not None:
   print(F'Material sheets used: {len(result["Result"]["RawPlatesNested"])}'

   # Print all log messages (optional).
   message_id = status['LogEntryIdx']
   messages = await Nesting.computation_messages(session, computation_url, 1, message_id)
   if messages is not None:
      for msg in messages['Entries']:
         print(msg['Message'])
//Get a computation result.
int resultId = status.getInt("ResultVersion");
if (resultId != 0) {
   JSONObject result = Nesting.computationResult(client, computationUrl, resultId).get();
   if (result != null) {
      int length = result.getJSONObject("Result").getJSONArray("RawPlatesNested").length();
      String msg = String.format("Material sheets used: %d", length);
      System.out.println(msg);
   }

   // Print all log messages (optional).
   int messageId = status.getInt("LogEntryIdx");
   JSONObject messages = Nesting.computationMessages(client, computationUrl, 1, messageId).get();

   if (messages != null) {
      for (Object item : messages.getJSONArray("Entries")) {
      System.out.println(((JSONObject) item).getString("Message"));
   }
}

Deleting nesting sessions

Finally, the client application deletes the nesting session. All client data is removed from the service.

DELETE

// Delete a computation.
await Nesting.DeleteComputation(client, computationUrl);
await Nesting.delete_computation(session, computation_url)
Nesting.deleteComputation(client, computationUrl).join();

Complete application

Program code

// This is a sample C# program.
// Required packages: Newtonsoft.Json

using System;
using System.IO;
using System.Net.Http;
using System.Net.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

public class NestingProgram
{
   // Nesting computation.
   public static async Task NestingComputation()
   {
      // Sample nesting data in JSON format.
      string nestingData = File.ReadAllText("example.json");

      using (HttpClient client = new HttpClient())
      {
         // Start a new computation.
         string computationUrl = await Nesting.StartComputation(client, nestingData);

         if (string.IsNullOrEmpty(computationUrl))
         {
            return;
         }

         Console.WriteLine("Nesting computation started.");

         // Check a computation status.
         string computationState = string.Empty;
         JObject status = null;
            
         while (computationState != "Stopped")
         {
            // Wait 10s to get a new status.
            await Task.Delay(10000);

            // Get a computation status.
            status = await Nesting.ComputationStatus(client, computationUrl);
            if (status != null)
            {
               computationState = status["StateString"].ToString();
            }
            else
            {
               break;
            }
         }

         if (status != null)
         {
            // Get a computation result.
            string resultId = status["ResultVersion"].ToString();
            JObject nestingResult = await Nesting.ComputationResult(client, computationUrl, resultId);

            // Print all log messages (optional).
            string messageId = status["LogEntryIdx"].ToString();
            JObject messages = await Nesting.ComputationMessages(client, computationUrl, "1", messageId);

            if (messages != null)
            {
               foreach (JObject message in messages["Entries"])
               {
                  Console.WriteLine(message["Message"].ToString());
               }
            }        
         }

         // Delete a computation.
         await Nesting.DeleteComputation(client, computationUrl);
      }
   }

   // The program start.
   public static async Task Main()
   {
       await Nesting.NestingComputation();
   }
}
# This is a sample Python script.
# Required packages: aiohttp

import aiohttp
import asyncio
import json
from Nesting import Nesting


async def nesting():
   # Sample nesting data in JSON format.
   f = open('example.json')
   nesting_data = json.loads(f.read())
   f.close()

   # Start a new computation.
   async with aiohttp.ClientSession() as session:
      computation_url = await Nesting.start_computation(session, nesting_data)

      if not computation_url:
         return

      print('Nesting computation started.')

      # Check a computation status.
      status = None
      computation_state = ''

      while computation_state != 'Stopped':
         # Work in progress, check status every 10s.
         await asyncio.sleep(10)
         status = await Nesting.computation_status(session, computation_url)

         if status is not None:
            computation_state = status['StateString']
         else:
            break

      # Get a computation result.
      result_id = status['ResultVersion']
      result = await Nesting.computation_result(session, computation_url, result_id)

      if result is not None:
         print(F'Material sheets used: {len(result["Result"]["RawPlatesNested"])}')

         # Print all log messages (optional).
         message_id = status['LogEntryIdx']
         messages = await Nesting.computation_messages(session, computation_url, 1, message_id)
         if messages is not None:
            for msg in messages['Entries']:
               print(msg['Message'])

      await Nesting.delete_computation(session, computation_url)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(nesting())
// This is a sample Java program.
// Required packages: org.json

package nesting.example;
import java.io.IOException;
import java.net.http.HttpClient;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.json.JSONObject;

public class App {

   public static void nesting() throws ExecutionException, InterruptedException, IOException {

      // Sample nesting data in JSON format.
      String data = Files.readString(Path.of("example.json"));
      JSONObject nestingData = new JSONObject(data);

      // Start a new computation.
      HttpClient client = HttpClient.newHttpClient();
      String computationUrl = Nesting.startComputation(client, nestingData).get();
      if (computationUrl.isEmpty()) {
         return;
      }

      System.out.println("Nesting computation started.");

      // Check a computation status.
      JSONObject status = null;
      boolean stopped = false;

      while (!stopped) {
         // Work in progress, check status every 10s.
         TimeUnit.SECONDS.sleep(10);

         status = Nesting.computationStatus(client, computationUrl).get();

         if (status == null) {
            stopped = true;
         } else {
            String computationState = status.getString("StateString");
            stopped = computationState.equals("Stopped");
         }
      }

      //Get a computation result.
      if (status != null) {
          int resultId = status.getInt("ResultVersion");

          if (resultId != 0) {
             JSONObject result = Nesting.computationResult(client, computationUrl, resultId).get();
             if (result != null) {
                int length = result.getJSONObject("Result").getJSONArray("RawPlatesNested").length();
                String msg = String.format("Material sheets used: %d", length);
                System.out.println(msg);
             }
          }

          // Print all log messages (optional).
          int messageId = status.getInt("LogEntryIdx");
          JSONObject messages = Nesting.computationMessages(client, computationUrl, 1, messageId).get();

           if (messages != null) {
              for (Object item : messages.getJSONArray("Entries")) {
                 System.out.println(((JSONObject) item).getString("Message"));
              }
           }
        }

        Nesting.deleteComputation(client, computationUrl).join();
   }

   public static void main(String[] args) {
      CompletableFuture.runAsync(() -> {
         try {
            App.nesting();
         } catch (Exception e) {
            System.out.println(e);
         }
      }).join();
   }
}

Nesting class

// Nesting Center API
public class Nesting
{
   // NestingCenter API url.
   private const string serviceUrl = "https://api-nesting.nestingcenter.com/nesting/";

   // Start a computation.
   public static async Task<string> StartComputation(HttpClient client, string nestingData)
   {
      string startUrl = Nesting.serviceUrl + "start";
      JObject startResult = await Nesting.PostAsync(client, startUrl, nestingData);

      // Get a computation job ID.
      string jobId = string.Empty;
      if (startResult != null)
      {
         jobId = startResult["JobId"].ToString();
      }

      // Get a computation url.
      string computationUrl = string.Empty;
      if (!string.IsNullOrEmpty(jobId))
      {
         computationUrl = Nesting.serviceUrl + jobId;
      }

      return computationUrl;
   }

   // Stop a computation.
   public static async Task StopComputation(HttpClient client, string computationUrl)
   {
      string stopUrl = computationUrl + "/stop";
      await Nesting.PostAsync(client, stopUrl, string.Empty);
   }

   // Get a range of computation messages.
   public static async Task<JObject> ComputationMessages(HttpClient client, string computationUrl, string firstMessage, string lastMessage)
   {
      string messgesUrl = string.Format("{0}/log/{1}/{2}", computationUrl, firstMessage, lastMessage);
      return await Nesting.GetAsync(client, messgesUrl);
   }

   // Get a computation status.
   public static async Task<JObject> ComputationStatus(HttpClient client, string computationUrl)
   {
      return await Nesting.GetAsync(client, computationUrl);
   }

   // Get a computation stop details.
   public static async Task<JObject> ComputationStopDetails(HttpClient client, string computationUrl)
   {
      string stopDetailsUrl = computationUrl + "/stopdetails";
      return await Nesting.GetAsync(client, stopDetailsUrl);
   }

   // Get a computation result.
   public static async Task<JObject> ComputationResult(HttpClient client, string computationUrl, string resultVersion)
   {
      if (!string.IsNullOrEmpty(resultVersion) && resultVersion != "0")
      {
         string resultUrl = computationUrl + "/" + resultVersion;
         return await Nesting.GetAsync(client, resultUrl);
      }
      else
      {
         return null;
      }
   }

   // Delete a computation sesion.
   public static async Task DeleteComputation(HttpClient client, string computationUrl)
   {
      using (var request = new HttpRequestMessage(HttpMethod.Delete, computationUrl))
      {
         await Nesting.SendAsync(client, request);
      }
   }

   // Send a GET request.
   private static async Task<JObject> GetAsync(HttpClient client, string url)
   {
      using (var request = new HttpRequestMessage(HttpMethod.Get, url))
      {
         return await Nesting.SendAsync(client, request);
      }
   }

   // Parse the response and get a JSON object.
   private static async Task<JObject> ParseResponse(HttpResponseMessage response)
   {
      if (response.IsSuccessStatusCode)
      {
         string jsonResponse = await response.Content.ReadAsStringAsync();
         if (!string.IsNullOrEmpty(jsonResponse))
         {
            return JObject.Parse(jsonResponse);
         }
      }
      else
      {
         string msg = await response.Content.ReadAsStringAsync();
         Nesting.AddMessage(msg);
      }

      return null;
   }

   // Send a POST request.
   private static async Task<JObject> PostAsync(HttpClient client, string url, string jsonData)
   {
      using (StringContent content = new StringContent(jsonData, Encoding.UTF8, "application/json"))
      using (var request = new HttpRequestMessage(HttpMethod.Post, url))
      {
         request.Content = content;
         return await Nesting.SendAsync(client, request);
      }
   }

   // Send a request.
   private static async Task<JObject> SendAsync(HttpClient client, HttpRequestMessage request)
   {
      try
      {
         // Set the access token.
         string token = string.Empty;
         request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

         using (HttpResponseMessage response = await client.SendAsync(request))
         {
            return await ParseResponse(response);
         }
      }
      catch (Exception ex)
      {
         Nesting.AddMessage(ex.Message);
      }

      return null;
   }

   // Print a message to the conslole.
   public static void AddMessage(string msg)
   {
      System.Console.WriteLine(msg);
   }
}
class Nesting:
   """NestingCenter API."""

   __serviceUrl = "https://api-nesting.nestingcenter.com/nesting/"

   @staticmethod
   async def start_computation(session, nesting_data):
      """Start a new computation."""
      url = Nesting.__serviceUrl + "start"
      result = await Nesting.__post(session, url, nesting_data, True)
      if result is not None:
         job_id = result['JobId']
         if job_id:
            return Nesting.__serviceUrl + job_id
      return ''

   @staticmethod
   async def stop_computation(session, computation_url):
      """Stop a computation."""

      url = computation_url + '/stop'
      return await Nesting.__post(session, url, None, False)

   @staticmethod
   async def computation_messages(session, computation_url, first, last):
      """Get a range of computation messages."""

      url = f'{computation_url}/log/{first}/{last}'
      return await Nesting.__get(session, url)

   @staticmethod
   async def computation_message(session, computation_url, message_id):
      """Get a computation message."""

      url = f'{computation_url}/log/{message_id}'
      return await Nesting.__get(session, url)

   @staticmethod
   async def computation_status(session, computation_url):
      """Get a computation status"""

      return await Nesting.__get(session, computation_url)

   @staticmethod
   async def computation_stop_details(session, computation_url):
      """Get a computation stop details."""

      url = computation_url + '/stopdetails'
      return await Nesting.__get(session, url)

   @staticmethod
   async def computation_result(session, computation_url, result_id):
      """Get a computation result."""
        
      if result_id:
         url = f'{computation_url}/{result_id}'
         return await Nesting.__get(session, url)
      else:
         return None

   @staticmethod
   async def delete_computation(session, computation_url):
      """Delete a computation sesion."""
        
      try:
         async with session.delete(computation_url, headers=Nesting.__headers()) as response:
            if not response.status < 300:
               Nesting.__print_error(await response.text())
      except Exception as ex:
         Nesting.__print_error(ex)

      return None

   @staticmethod
   def __headers():
      token = ''
      return {'Authorization': 'Bearer ' + token}

   @staticmethod
   async def __post(session, url, nesting_data, get_result):
      try:
         async with session.post(url, json=nesting_data, headers=Nesting.__headers()) as response:
            if response.status < 300:
               if get_result:
                  return await response.json()
            else:
               Nesting.__print_error(await response.text())
      except Exception as ex:
         Nesting.__print_error(ex)

      return None

   @staticmethod
   async def __get(session, url):
      try:
         async with session.get(url, headers=Nesting.__headers()) as response:
            if response.status < 300:
               return await response.json()
            else:
               Nesting.__print_error(await response.text())
      except Exception as ex:
         Nesting.__print_error(ex)

      return None

   @staticmethod
   def __print_error(error):
      print(error)
package nesting.example;

import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;

import org.json.JSONObject;

public class Nesting {

   private static final String serviceUrl = "https://api-nesting.nestingcenter.com/nesting/";

   public static CompletableFuture<String> startComputation(HttpClient client, JSONObject json) {
      HttpRequest request = Nesting.requestPost(Nesting.serviceUrl + "/start", json.toString());
      return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
             .thenApply(Nesting::parseResponse)
             .thenApply(x -> x == null ? "" : Nesting.serviceUrl + x.getString("JobId"));
   }

   public static CompletableFuture<Void> stopComputation(HttpClient client, String url) {
      HttpRequest request = Nesting.requestPost(url + "/stop", "");
      return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
             .thenAccept(Nesting::parseResponseStatus);
   }

   public static CompletableFuture<JSONObject> computationMessages(HttpClient client, String url, int first, int last) {
      String messagesUrl = url + "/log/" + first + "/" + last;
      HttpRequest request = requestGet(messagesUrl);
      return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
             .thenApply(Nesting::parseResponse);
   }

   public static CompletableFuture<JSONObject> computationStatus(HttpClient client, String url) {
      HttpRequest request = requestGet(url);
      return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
             .thenApply(Nesting::parseResponse);
   }

   public static CompletableFuture<JSONObject> computationStopDetails(HttpClient client, String url) {
      HttpRequest request = requestGet(url + "/stopdetails");
      return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
             .thenApply(Nesting::parseResponse);
   }

   public static CompletableFuture<JSONObject> computationResult(HttpClient client, String url, int resultId) {
      String resultUrl = url + "/" + resultId;
      HttpRequest request = requestGet(resultUrl);
      return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
             .thenApply(Nesting::parseResponse);
   }

   public static CompletableFuture<Void> deleteComputation(HttpClient client, String url) {
      HttpRequest request = HttpRequest.newBuilder()
             .DELETE()
             .uri(URI.create(url))
             .header("Authorization", Nesting.headerToken())
             .build();
      return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
             .thenAccept(Nesting::parseResponseStatus);
    }

   private static HttpRequest requestGet(String url) {
      return HttpRequest.newBuilder()
             .GET()
             .uri(URI.create(url))
             .header("Authorization", Nesting.headerToken())
             .build();
   }

   private static HttpRequest requestPost(String url, String data) {
      return HttpRequest.newBuilder()
             .POST(HttpRequest.BodyPublishers.ofString(data))
             .uri(URI.create(url))
             .header("Content-Type", "application/json")
             .header("Authorization", Nesting.headerToken())
             .build();
   }

   private static String headerToken() {
      String token = "";
      return "Bearer " + token;
   }

   private static JSONObject parseResponse(HttpResponse<String> response) {
      if (response.statusCode() == 200) {
         String result = response.body();
         if (!result.isEmpty()) {
            return new JSONObject(result);
         }
      } else {
         System.out.println(response.body());
      }
      return null;
   }

   private static void parseResponseStatus(HttpResponse<String> response) {
      if (response.statusCode() != 200 && response.statusCode() != 204) {
         System.out.println(response.body());
      }
   }
}