Statically typed SQL for F# compiled to SQLite/TSQL/Postgres
github.com8 pointsby rspeele1 comments
type WorkpiecePlacement =
{ Id : WorkpieceId
/// Corner of the workpiece we'll attach to the machine.
WorkpieceCorner : WorkpieceSpace.Corner3D
/// Point in machine-space we'll anchor this corner to.
MachinePoint : MachineSpace.Point
/// Which face of the workpiece is on top.
FaceUp : WorkpieceSpace.Face
/// Rotation around the up-axis.
Yaw : WorkpieceSpace.Yaw
}
type OperationType =
| PlaceWorkpiece of placement : Operation.WorkpiecePlacement
| InstallTool of id : ToolId * slot : int option
| ZeroAt of point : MachineSpace.Point
| RunGCode of source : GCode.Source
| RemoveWorkpiece of id : WorkpieceId
For the GCode simulator I needed a parser for GCode files, which produces a type with 1:1 equivalence to the GCode instruction set: type GCodeInstruction =
// --- Motion ---
| G0_RapidMove of axisMoves : (Axis * float<gcodeunit>) array
| G1_Move of feedRate : float<gcodeunit/minute> option * axisMoves : (Axis * float<gcodeunit>) array
| G2_ClockwiseArc of ArcParams
| G3_CounterClockwiseArc of ArcParams
| G4_Dwell of seconds : double
// --- Plane selection ---
| G17_SelectXYPlane
| G18_SelectXZPlane
| G19_SelectYZPlane
// --- Unit selection ---
| G20_Inches
| G21_Millimeters
// --- Distance mode ---
| G90_AbsoluteDistance
| G91_RelativeDistance
// ... etc truncated, more instructions in real code
But my tool supports doing transforms on toolpaths, like rotating 90 degrees or offsetting so I can easily define that I want to make tiling copies of the same project.
To implement those transforms straight up as GCodeInstruction[] -> GCodeInstruction[] is a bad call. GCode is very stateful and lets you switch units, relative vs. absolute coordinate spaces, etc. in instructions. That makes the transform awkward and tricky to write. type ToolPathInstruction =
| Rapid of From : Point * To : Point
| Linear of From : Point * To : Point * Feed : FeedRate
| Arc of
From : Point *
To : Point *
Center : Point *
Plane : Plane *
Direction : ArcDirection *
Feed : FeedRate
| ... etc truncated
That is the appropriate level for the transforms like offset, rotate, scale, etc. to operate on. type ApproxMove =
{ From : Vector3
To : Vector3
FeedRate : double<m/minute>
IsRapid : bool
}
type ToolPathApproximation =
{ StartPosition : Vector3
Moves : ApproxMove[]
}
Having defined all these types it's clear that I need operations like: parse: string -> GCode
serialize : GCode -> string
normalizeToToolPath : GCode -> ToolPath
denormalizeToGCode : ToolPath -> GCode
offset : Vector3 -> ToolPath -> ToolPath
rotate90 : ToolPath -> ToolPath
scale : Vector3 -> ToolPath -> ToolPath
approximate : ToolPath -> ToolPathApproximation
simulate : ToolPathApproximation -> MachineState -> MachineState
renderToolPathWireframe : ToolPathApproximation -> VBO
renderMachineState : MachineState -> VBO
And so on. An LLM is absolutely awesome at one-shotting the implementations.