Skip to main content

classcad pdm·in development, not released


A PDM for ClassCAD models that you host yourself. Parts, assemblies and the code that generates them, with versions, branches, releases and bills of material. ClassCAD opens, checks and measures every file that comes in.

reads OFB STEPwrites OFB STEP SCGREST APIon Postgres

ENG-3000Enginev1A
ENG-2001Piston assemblyv1A
ENG-2002Connecting rod assemblyv1A
ENG-1001Crankshaftv1A
ENG-1002Connecting rodv1A
ENG-1003Pistonv1A
ENG-1004Piston pinv2B
ENG-1005Origin markerv1A
BRK-2001Base platev2B
BRK-2002L-bracketv1A
BRK-2003Guide rodv1A
BRK-2004Locating pindraft
BRK-2005Square washerdraft
FST-0022Hex bolt M22v1A
FST-0023Hex nut M22v1A
MSC-0001AWV logo cubev1A
MSC-0003Supplier housing (broken export)failed
GEN-0001Flange generatorv1A
DOC-000001Flange 120x12draft
DOC-000002Flange 160x16draft

01Documents

A part, an assembly or a generator is a document with a number. Its file goes to ClassCAD in a job that opens it, checks it and measures it. A file it cannot read fails, and says why.

Drag a file onto the page, or click one

02Scheduler

Jobs, generator runs and editing sessions reach ClassCAD through one port. Behind it, the scheduler keeps a pool of engines on your server. It keeps each session on the engine that holds it, sends new work to the one with the most room, keeps heavy imports away from interactive work, starts engines when it gets busy, retires them when it is quiet and restarts one that crashes.

EditorsWebSocket · one engine per session
Generator runsHTTP session · ClassCAD-Session-Id
PDM jobsHTTP batch · no session
Importsv1/io/import* · heavy
:9091classcad-scheduler
  • affinity
  • placement
  • heavy jobs
  • scaling
  • health
w1127.0.0.1:9094ready0.4 of 3 GB
w2127.0.0.1:9095ready0.4 of 3 GB
h1127.0.0.1:9100ready · heavy0.4 of 8 GB

room for 4 more: max = 6

Engines3· 3 readySessions0In flight0Scaled up0Restarts0Lastlistening on 0.0.0.0:9091

The scheduler's rules with its defaults, except max_sessions = 4 and heavy_workers = 1 to keep the pool small. Times are shortened: it checks the pool every 30 s and retires an engine after 10 idle minutes.

03Versions

Saving writes into the draft and overwrites it. A version is a step you take: a frozen state with a number. Branch from any version, take a branch back over, release what is done. It works the way Onshape does; the difference is where it runs.

The crankshaft, ENG-1001: version 1 is released as revision A. The draft of main builds on it. Save something.

Draft holdsv1
  1. v1InitialA
  2. maindraft · as its versionyou are here

04Assemblies

An assembly names its parts by document number. Making a version pins every one of them to the version it was built with, so a released engine is always the same engine. When a part moves on, the assembly says so, and you take the update when you want it.

362541

AssemblyENG-3000 reserved by anna

Engine

Parts
05 different
Sub-assemblies
02 different
Volume
0cm³
Update available. ENG-1004 Piston pin is at v2, revision B: case hardened, tolerance g6. ENG-2001 still uses v1.
Structure
ItemIn the modelDocumentVersionQty
1CrankshaftENG-1001 Crankshaftv1A×1
2PistonAsmENG-2001 Piston assemblyv1Ainside×3
3BearingENG-1004 Piston pinv1Arev B×1
4PistonENG-1003 Pistonv1A×1
5PleulAsmENG-2002 Connecting rod assemblyv1A×3
6PlENG-1002 Connecting rodv1A×1
7originENG-1005 Origin markerv1A×1

ENG-1004 is used by ENG-2001 Piston assembly, which is used by ENG-3000 Engine. Where-used is one click on every document.

05Editor

Edit opens the draft in Buerligons, our CAD app, right inside the PDM. It reserves the draft while you work, so nobody overwrites what you are changing. ClassCAD runs in your browser tab or on your server.

1 Change LBracket_Thickness on the right
2 Save into the draft: the red dot, or ⌘ S

reserved by you

PartLBracket

Features

  1. 01Sketch
  2. 02Extrusion
  3. 03WCS_Hole2-Bottom
  4. 04Cylinder
  5. 05Subtraction
  6. 06WCS_Hole2-Top
  7. 07WCS_Origin
  8. 08WCS_Hole1-Bottom
  9. 09Cylinder0
  10. 10Subtraction0
  11. 11WCS_Hole3-Bottom
  12. 12Cylinder1
  13. 13Subtraction1
part · lbracketThe L-bracket, 10 mm or 12 mm thick

Expressions

LBracket_Thickness
10mm
LBracket_Height
60mm
LBracket_Width
50mm
LBracket_Depth
100mm
Hole_Diameter
10mm
Rod_Hole_Diameter
10mm

The draft, as ClassCAD measured it

part-derive done

Volume
96.857cm³
Centre of gravity
X14.72Y50Z20.2

06Code

A code document points at a generator in a git repository. The PDM reads its parameters, shows them as a form, runs it on a worker against ClassCAD and files each result as a document of its own. Released code is frozen with its commit, so a released part can always be made again.

flange/flange.tsGEN-0001 · commit 60a2a63
  1. import { defineGenerator } from '@classcad/pdm-generator'
  2. export default defineGenerator(async ({ params, api, savePart }) => {
  3. const { outerDiameter, thickness, boreDiameter, boltCircle, holeCount, holeDiameter } = params
  4. if (boreDiameter >= boltCircle - holeDiameter) throw new Error(`The bore must stay inside the holes`)
  5. const part = await api.v1.part.create({ name: 'Flange' })
  6. const geometry = await api.v1.part.entityInjection({ id: part, name: 'Geometry' })
  7. const disc = await api.v1.solid.cylinder({ id: geometry, diameter: outerDiameter, height: thickness })
  8. const tools = [await api.v1.solid.cylinder({ id: geometry, diameter: boreDiameter, height: thickness + 2 })]
  9. for (let i = 0; i < holeCount; i++) {
  10. const a = (2 * Math.PI * i) / holeCount
  11. const translation = [(boltCircle / 2) * Math.cos(a), (boltCircle / 2) * Math.sin(a), 0]
  12. tools.push(await api.v1.solid.cylinder({ id: geometry, diameter: holeDiameter, height: thickness + 2, translation }))
  13. }
  14. await api.v1.solid.subtraction({ id: geometry, target: disc, tools })
  15. await savePart({ key: 'flange', name: `Flange ${outerDiameter}x${thickness}, ${holeCount} holes` })
  16. })

CodeGEN-0001Flange generator

on a worker · ClassCAD 21.2 · runtime node

Runs

  1. run 2code-execute · doneØ160 × 16 · bore Ø60 · 8 × Ø14 on Ø125DOC-000002 Flange 160x16, 8 holes
  2. run 1code-execute · doneØ120 × 12 · bore Ø40 · 6 × Ø11 on Ø90DOC-000001 Flange 120x12, 6 holes

07Self-hosted

One API, one Postgres database, a folder for files, and as many workers and engines as the work needs. Your models stay on your network. The only call out is the engine fetching its key from classcad.ch, and machines without internet take an offline key instead.

your networkhttps/apiclaim · uploadwss · the editor on the serverkeyBrowsersPDM · editorclasscad.chengine keyReverse proxyhttps · 4431Web appstatic files2APINestJS · 33333Postgresversion 164File storeby SHA-2565Job workers× as many as you need6Schedulerclasscad · 90917Enginesclasscad-cli · 9094…8
ItemPartWhat it doesRuns asPort
1Reverse proxyHTTPS for everything; serves the web app, forwards /api and the editor’s WebSocketnginx, Caddy, …443
2Web appThe PDM in the browser, Buerligons includedstatic files·
3APIDocuments, versions, releases, references, jobs, files; runs its migrations on startNode 203333
4PostgresMetadata, versions, references, the audit logPostgres 165432
5File storeEvery file once, named by its SHA-256a folder·
6Job workersOpen, measure, build and export models, run generators, render thumbnailsNode 20·
7SchedulerOne port in front of the engines: affinity, placement, scaling, restartsclasscad-scheduler9091
8EnginesClassCAD, one process each, started by the schedulerclasscad-cli worker9094…

08API

The web app, the job workers and your own scripts use the same REST API. It is described as OpenAPI, so you can read it in the browser and generate a client from it.

  • /api/v1 REST, with the OpenAPI description at /doc
  • ETag on what changes: send If-Match, and a write based on a stale state gets 412 instead of overwriting a colleague's work
  • Jobs are claimed by workers over the same API, with heartbeats and retries
  • Audit log: every write, who, when, what
a part from start to releasecurl · httpie · fetch
  1. # a part, its file, its job, a version, a release
  2. POST /api/v1/documents {"name": "Bracket", "kind": "part"}
  3. → 201 DOC-000042, with the draft d7… of main
  4. POST /api/v1/versions/d7…/artifacts?kind=part-source
  5. → 200 the source is in the draft, part-derive is queued
  6. GET /api/v1/jobs?versionId=d7…
  7. → 200 part-derive succeeded
  8. POST /api/v1/workspaces/5a…/versions {"name": "Initial"}
  9. → 201 version 1
  10. POST /api/v1/versions/9c…/release
  11. → 200 revision A, released
  12. GET /api/v1/versions/9c…/bom

09Status

The PDM runs today, on a developer’s machine. It is not ready for your servers yet: the pieces under “Before the release” come first. If you want to run it when it is, or have a say in what comes first, write to us.

Product
ClassCAD PDM
State
in development
Revision
none yet
Made by
AWV Informatik, St. Gallen

Works today

  • Documents, folders, search
  • Drafts, versions, branches, take over, restore
  • Releases with revision labels, obsolete
  • Assemblies with pinned references, BOM, where-used, updates
  • Editing in Buerligons, with reservations
  • Generators from git repositories, runs, variants
  • Jobs that measure, render and export with ClassCAD
  • A REST API with its OpenAPI description

Before the release

  • Sign-in through your own identity provider (OIDC)
  • Projects with members and rights
  • The scheduler in a ClassCAD release
  • An install package for your servers
  • S3-compatible file storage
  • Generator runs in a sandbox