Create G-Code
This example demonstrates how to upload a G-code file and create a G-Code entry in the Helio Additive platform. This is a prerequisite for running simulations and optimizations.
GraphQL Operations
Section titled “GraphQL Operations”Create the following GraphQL files, then run your language’s codegen tool to generate the types.
query GetPresignedUrl($fileName: String!) { getPresignedUrl(fileName: $fileName) { mimeType url key }}mutation CreateGcodeV2($input: CreateGcodeInputV2!) { createGcodeV2(input: $input) { id insertedAt }}query GcodeV2($id: ID!) { gcodeV2(id: $id) { id name status progress }}API Reference:
- CreateGcodeInputV2 - Input parameters for creating a G-Code
- GcodeStatus enum - Possible G-Code status values
- GcodeV2 type - Full G-Code object structure
Code Example
Section titled “Code Example”-
Setup the client and configuration
index.ts import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client";import * as fs from "fs";import * as path from "path";import { GetPresignedUrlDocument } from "./queries/__generated/GetPresignedUrl.generated";import { CreateGcodeV2Document } from "./mutations/__generated/CreateGcodeV2.generated";import { GcodeV2Document } from "./queries/__generated/GcodeV2.generated";// Load PAT from environmentconst PAT = process.env.PAT;if (!PAT) {throw new Error("PAT environment variable is required");}// Create Apollo Clientconst client = new ApolloClient({cache: new InMemoryCache(),link: new HttpLink({uri: "https://api.helioadditive.com/graphql",headers: {"Content-Type": "application/json",Authorization: `Bearer ${PAT}`,},}),});// Replace these with actual IDs from your materials and printers queriesconst PRINTER_ID = process.env.PRINTER_ID ?? "your-printer-id";const MATERIAL_ID = process.env.MATERIAL_ID ?? "your-material-id";main.py import asyncioimport osimport httpxfrom pathlib import Pathfrom client.client import Client# Load PAT from environmentPAT = os.environ.get("PAT")if not PAT:raise ValueError("PAT environment variable is required")# Replace these with actual IDs from your materials and printers queriesPRINTER_ID = os.environ.get("PRINTER_ID", "your-printer-id")MATERIAL_ID = os.environ.get("MATERIAL_ID", "your-material-id")# Create GraphQL clientheaders = {"Authorization": f"Bearer {PAT}"}graphql_client = Client(url="https://api.helioadditive.com/graphql",headers=headers,http_client=httpx.AsyncClient(headers=headers, timeout=60))src/main.rs use graphql_client::{GraphQLQuery, Response};use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};use std::fs;use std::path::Path;use std::time::{Duration, SystemTime, UNIX_EPOCH};#[derive(GraphQLQuery)]#[graphql(schema_path = "schema.graphqls",query_path = "queries/GetPresignedUrl.graphql",response_derives = "Debug")]struct GetPresignedUrl;#[derive(GraphQLQuery)]#[graphql(schema_path = "schema.graphqls",query_path = "mutations/CreateGcodeV2.graphql",response_derives = "Debug")]struct CreateGcodeV2;#[derive(GraphQLQuery)]#[graphql(schema_path = "schema.graphqls",query_path = "queries/GcodeV2.graphql",response_derives = "Debug")]struct GcodeV2;// Load from environment in main()let pat = std::env::var("PAT").expect("PAT environment variable must be set");let printer_id = std::env::var("PRINTER_ID").unwrap_or_else(|_| "your-printer-id".to_string());let material_id = std::env::var("MATERIAL_ID").unwrap_or_else(|_| "your-material-id".to_string());src/main.cpp #include <iostream>#include <fstream>#include <string>#include <vector>#include <thread>#include <chrono>#include <cstdlib>#include <filesystem>#include <cpr/cpr.h>#include <boost/json.hpp>#include "GetPresignedUrlClient.h"#include "CreateGcodeV2Client.h"#include "GcodeV2Client.h"#include "graphqlservice/GraphQLResponse.h"#include "graphqlservice/JSONResponse.h"namespace GetPresignedUrl = graphql::client::query::GetPresignedUrl;namespace CreateGcodeV2 = graphql::client::mutation::CreateGcodeV2;namespace GcodeV2 = graphql::client::query::GcodeV2;// Helper function to send GraphQL HTTP requestsgraphql::response::Value sendHttpRequest(const std::string& query,graphql::response::Value& variables,const std::string& pat) {std::string body = boost::json::serialize(boost::json::object{{"query", query},{"variables", boost::json::parse(graphql::response::toJSON(std::move(variables))).as_object()}});auto response = cpr::Post(cpr::Url{"https://api.helioadditive.com/graphql"},cpr::Header{{"Content-Type", "application/json"},{"Authorization", "Bearer " + pat}},cpr::Body{body});return graphql::response::parseJSON(boost::json::serialize(boost::json::parse(response.text).as_object()["data"].as_object()));}// Load PAT from environment in main()const char* pat_env = std::getenv("PAT");if (!pat_env) {std::cerr << "PAT environment variable is required" << std::endl;return 1;}std::string pat(pat_env);// Replace these with actual IDs from your materials and printers queriesconst char* printer_env = std::getenv("PRINTER_ID");std::string printerId = printer_env ? printer_env : "your-printer-id";const char* material_env = std::getenv("MATERIAL_ID");std::string materialId = material_env ? material_env : "your-material-id"; -
Upload G-code file to presigned URL
First, request a presigned URL from the API, then upload your G-code file directly to S3.
async function uploadToS3(filePath: string) {const fileName = path.basename(filePath);// Get presigned URLconst { data } = await client.query({query: GetPresignedUrlDocument,variables: { fileName },fetchPolicy: "no-cache",});const presigned = data?.getPresignedUrl;if (!presigned) {throw new Error("Failed to get presigned URL");}const { key, url, mimeType } = presigned;// Upload file to S3const fileBuffer = fs.readFileSync(filePath);const uploadResponse = await fetch(url, {method: "PUT",body: fileBuffer,headers: { "Content-Type": mimeType },});if (!uploadResponse.ok) {throw new Error("Failed to upload file to S3");}return { key, url, mimeType };}async def upload_to_s3(file_path: str) -> str:"""Upload a file to S3 and return the key."""file_name = Path(file_path).name# Get presigned URLpresigned = await graphql_client.get_presigned_url(file_name=file_name)# Upload file to S3async with httpx.AsyncClient() as http_client:with open(file_path, "rb") as f:file_content = f.read()response = await http_client.put(presigned.url,content=file_content,headers={"Content-Type": presigned.mime_type})response.raise_for_status()return presigned.keyasync fn upload_to_s3(client: &reqwest::Client,pat: &str,file_path: &str,) -> Result<String, Box<dyn std::error::Error>> {let file_name = Path::new(file_path).file_name().unwrap().to_str().unwrap();// Get presigned URLlet variables = get_presigned_url::Variables {file_name: file_name.to_string(),};let response = client.post("https://api.helioadditive.com/graphql").header(CONTENT_TYPE, "application/json").header(AUTHORIZATION, format!("Bearer {}", pat)).json(&GetPresignedUrl::build_query(variables)).send().await?;let body: Response<get_presigned_url::ResponseData> = response.json().await?;let presigned = body.data.unwrap().get_presigned_url;// Upload file to S3let file_content = fs::read(file_path)?;client.put(&presigned.url).header(CONTENT_TYPE, &presigned.mime_type).body(file_content).send().await?.error_for_status()?;Ok(presigned.key)}struct PresignedResult {std::string key;std::string url;std::string mimeType;};PresignedResult uploadToS3(const std::string& filePath, const std::string& pat) {// Extract filename from pathstd::filesystem::path p(filePath);std::string fileName = p.filename().string();// Get presigned URLconst std::string query = GetPresignedUrl::GetRequestText();graphql::response::Value variables = GetPresignedUrl::serializeVariables(GetPresignedUrl::Variables{.fileName = fileName});auto response = GetPresignedUrl::parseResponse(sendHttpRequest(query, variables, pat));std::string key = response.getPresignedUrl.key;std::string url = response.getPresignedUrl.url;std::string mimeType = response.getPresignedUrl.mimeType;// Read file contentstd::ifstream file(filePath, std::ios::binary);std::vector<char> fileContent((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());// Upload file to S3auto uploadResponse = cpr::Put(cpr::Url{url},cpr::Header{{"Content-Type", mimeType}},cpr::Body{std::string(fileContent.begin(), fileContent.end())});if (uploadResponse.status_code != 200) {throw std::runtime_error("Failed to upload file to S3");}return {key, url, mimeType};} -
Create the G-Code entry
Once the file is uploaded, create a G-Code entry in the system using the returned key.
async function createGcodeEntry(key: string, name: string) {const { data } = await client.mutate({mutation: CreateGcodeV2Document,variables: {input: {gcodeKey: key,printerId: PRINTER_ID,materialId: MATERIAL_ID,isSingleShell: true,name: name,},},});const gcodeId = data?.createGcodeV2?.id;if (!gcodeId) {throw new Error("Failed to create G-Code");}return gcodeId;}async def create_gcode_entry(key: str, name: str) -> str:"""Create a G-Code entry and return its ID."""result = await graphql_client.create_gcode_v2(input={"gcodeKey": key,"printerId": PRINTER_ID,"materialId": MATERIAL_ID,"isSingleShell": True,"name": name,})return result.idasync fn create_gcode_entry(client: &reqwest::Client,pat: &str,key: &str,name: &str,printer_id: &str,material_id: &str,) -> Result<String, Box<dyn std::error::Error>> {let variables = create_gcode_v2::Variables {input: create_gcode_v2::CreateGcodeInputV2 {gcode_key: key.to_string(),printer_id: printer_id.to_string(),material_id: material_id.to_string(),is_single_shell: true,name: name.to_string(),description: None,is_multi_color: None,is_multi_material: None,},};let response = client.post("https://api.helioadditive.com/graphql").header(CONTENT_TYPE, "application/json").header(AUTHORIZATION, format!("Bearer {}", pat)).json(&CreateGcodeV2::build_query(variables)).send().await?;let body: Response<create_gcode_v2::ResponseData> = response.json().await?;Ok(body.data.unwrap().create_gcode_v2.id)}std::string createGcodeEntry(const std::string& key,const std::string& name,const std::string& printerId,const std::string& materialId,const std::string& pat) {const std::string query = CreateGcodeV2::GetRequestText();graphql::response::Value variables = CreateGcodeV2::serializeVariables(CreateGcodeV2::Variables{.input = CreateGcodeV2::CreateGcodeInputV2{.gcodeKey = key,.printerId = printerId,.materialId = materialId,.isSingleShell = true,.name = name}});auto response = CreateGcodeV2::parseResponse(sendHttpRequest(query, variables, pat));return response.createGcodeV2.id;} -
Poll and wait for G-Code to be ready
After creation, the G-Code needs to be processed. Poll the status until it’s ready.
async function waitForGcodeReady(gcodeId: string): Promise<void> {return new Promise((resolve, reject) => {const checkStatus = async () => {const { data } = await client.query({query: GcodeV2Document,variables: { id: gcodeId },fetchPolicy: "no-cache",});const status = data?.gcodeV2?.status;const progress = data?.gcodeV2?.progress;console.log(`G-Code status: ${status}, progress: ${progress}%`);if (status === "READY") {resolve();} else if (status === "ERROR") {reject(new Error("G-Code processing failed"));} else {// Poll every 2 secondssetTimeout(checkStatus, 2000);}};checkStatus();});}async def wait_for_gcode_ready(gcode_id: str) -> None:"""Poll until the G-Code is ready."""while True:result = await graphql_client.gcode_v2(id=gcode_id)status = result.statusprogress = result.progressprint(f"G-Code status: {status}, progress: {progress}%")if status == "READY":returnelif status == "ERROR":raise Exception("G-Code processing failed")await asyncio.sleep(2)async fn wait_for_gcode_ready(client: &reqwest::Client,pat: &str,gcode_id: &str,) -> Result<(), Box<dyn std::error::Error>> {loop {let variables = gcode_v2::Variables {id: gcode_id.to_string(),};let response = client.post("https://api.helioadditive.com/graphql").header(CONTENT_TYPE, "application/json").header(AUTHORIZATION, format!("Bearer {}", pat)).json(&GcodeV2::build_query(variables)).send().await?;let body: Response<gcode_v2::ResponseData> = response.json().await?;let gcode = body.data.unwrap().gcode_v2.unwrap();println!("G-Code status: {:?}, progress: {}%", gcode.status, gcode.progress);match gcode.status.as_str() {"READY" => return Ok(()),"ERROR" => return Err("G-Code processing failed".into()),_ => tokio::time::sleep(Duration::from_secs(2)).await,}}}void waitForGcodeReady(const std::string& gcodeId, const std::string& pat) {while (true) {const std::string query = GcodeV2::GetRequestText();graphql::response::Value variables = GcodeV2::serializeVariables(GcodeV2::Variables{.id = gcodeId});auto response = GcodeV2::parseResponse(sendHttpRequest(query, variables, pat));std::string status = response.gcodeV2->status;int progress = response.gcodeV2->progress;std::cout << "G-Code status: " << status << ", progress: " << progress << "%" << std::endl;if (status == "READY") {return;} else if (status == "ERROR") {throw std::runtime_error("G-Code processing failed");}// Poll every 2 secondsstd::this_thread::sleep_for(std::chrono::seconds(2));}} -
Put it all together
Combine all the steps into a single function and run it.
async function createGcode(filePath: string, name: string) {console.log(`Uploading G-code file: ${filePath}`);// Upload to S3const { key } = await uploadToS3(filePath);console.log("File uploaded successfully");// Create G-Code entryconst gcodeId = await createGcodeEntry(key, name);console.log(`G-Code created with ID: ${gcodeId}`);// Wait for G-Code to be readyconsole.log("Waiting for G-Code processing to complete...");await waitForGcodeReady(gcodeId);console.log("G-Code is ready!");return gcodeId;}// Example usageconst gcodeFilePath = "./my-print.gcode";const gcodeName = `my-gcode-${Date.now()}`;createGcode(gcodeFilePath, gcodeName).then((id) => console.log(`Successfully created G-Code: ${id}`)).catch(console.error);async def create_gcode(file_path: str, name: str) -> str:"""Upload and create a G-Code entry."""print(f"Uploading G-code file: {file_path}")# Upload to S3key = await upload_to_s3(file_path)print("File uploaded successfully")# Create G-Code entrygcode_id = await create_gcode_entry(key, name)print(f"G-Code created with ID: {gcode_id}")# Wait for G-Code to be readyprint("Waiting for G-Code processing to complete...")await wait_for_gcode_ready(gcode_id)print("G-Code is ready!")return gcode_idif __name__ == "__main__":import sysimport timegcode_file_path = sys.argv[1] if len(sys.argv) > 1 else "./my-print.gcode"gcode_name = f"my-gcode-{int(time.time())}"gcode_id = asyncio.run(create_gcode(gcode_file_path, gcode_name))print(f"Successfully created G-Code: {gcode_id}")#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {let pat = std::env::var("PAT").expect("PAT environment variable must be set");let printer_id = std::env::var("PRINTER_ID").unwrap_or_else(|_| "your-printer-id".to_string());let material_id = std::env::var("MATERIAL_ID").unwrap_or_else(|_| "your-material-id".to_string());let args: Vec<String> = std::env::args().collect();let file_path = args.get(1).map(|s| s.as_str()).unwrap_or("./my-print.gcode");let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();let gcode_name = format!("my-gcode-{}", timestamp);let client = reqwest::Client::new();println!("Uploading G-code file: {}", file_path);// Upload to S3let key = upload_to_s3(&client, &pat, file_path).await?;println!("File uploaded successfully");// Create G-Code entrylet gcode_id = create_gcode_entry(&client, &pat, &key, &gcode_name, &printer_id, &material_id).await?;println!("G-Code created with ID: {}", gcode_id);// Wait for G-Code to be readyprintln!("Waiting for G-Code processing to complete...");wait_for_gcode_ready(&client, &pat, &gcode_id).await?;println!("G-Code is ready!");println!("Successfully created G-Code: {}", gcode_id);Ok(())}std::string createGcode(const std::string& filePath,const std::string& name,const std::string& printerId,const std::string& materialId,const std::string& pat) {std::cout << "Uploading G-code file: " << filePath << std::endl;// Upload to S3auto presigned = uploadToS3(filePath, pat);std::cout << "File uploaded successfully" << std::endl;// Create G-Code entrystd::string gcodeId = createGcodeEntry(presigned.key, name, printerId, materialId, pat);std::cout << "G-Code created with ID: " << gcodeId << std::endl;// Wait for G-Code to be readystd::cout << "Waiting for G-Code processing to complete..." << std::endl;waitForGcodeReady(gcodeId, pat);std::cout << "G-Code is ready!" << std::endl;return gcodeId;}int main(int argc, char* argv[]) {const char* pat_env = std::getenv("PAT");if (!pat_env) {std::cerr << "PAT environment variable is required" << std::endl;return 1;}std::string pat(pat_env);const char* printer_env = std::getenv("PRINTER_ID");std::string printerId = printer_env ? printer_env : "your-printer-id";const char* material_env = std::getenv("MATERIAL_ID");std::string materialId = material_env ? material_env : "your-material-id";std::string filePath = argc > 1 ? argv[1] : "./my-print.gcode";std::string gcodeName = "my-gcode-" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count());try {std::string gcodeId = createGcode(filePath, gcodeName, printerId, materialId, pat);std::cout << "Successfully created G-Code: " << gcodeId << std::endl;} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;return 1;}return 0;}
Running the Example
Section titled “Running the Example”-
Create a
.env.localfile with your PAT and IDs:Terminal window PAT="your-personal-access-token"PRINTER_ID="your-printer-id"MATERIAL_ID="your-material-id" -
Update the
PRINTER_IDandMATERIAL_IDwith valid IDs from your account. You can get these by running the Listing Printers and Listing Materials examples. -
Place your G-code file in the project directory.
-
Run the example:
Terminal window bun run index.ts ./my-print.gcodeTerminal window uv run main.py ./my-print.gcodeTerminal window cargo run -- ./my-print.gcodeTerminal window mkdir build && cd buildcmake .. -DVCPKG_DIR=$(pwd)/../vcpkg/installed/arm64-osxmake./ExampleApp ./my-print.gcode