Compare commits

...

5 Commits

Author SHA1 Message Date
426de2d78d fix batch_id uuids 2024-03-22 20:25:00 -04:00
3011c0d2f8 wip 2024-03-22 19:17:45 -04:00
5b75452f9e wip 2024-03-22 18:33:42 -04:00
46358b41a2 Bind all unary, binary and constants to KCL (#1781) 2024-03-20 13:12:43 -04:00
59274b76bf Add onboarding check workflow (#1764)
* add workflow

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-20 08:28:55 -07:00
18 changed files with 993 additions and 654 deletions

36
.github/workflows/check-exampleKcl.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: Check Onboarding KCL
on:
pull_request:
types: [opened, synchronize]
paths:
- 'src/lib/exampleKcl.ts'
permissions:
contents: read
issues: write
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Comment on PR
uses: actions/github-script@v6
with:
script: |
const message = '`src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
const issue_number = context.payload.pull_request.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
// Post a comment on the PR
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body: message,
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

1159
src/wasm-lib/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -59,7 +59,7 @@ members = [
]
[workspace.dependencies]
kittycad = { version = "0.2.60", default-features = false, features = ["js", "requests"] }
kittycad = { path = "../../../kittycad.rs/kittycad", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-traits = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }

View File

@ -2,13 +2,16 @@ use std::collections::HashMap;
use kcl_lib::ast::types::{LiteralIdentifier, LiteralValue};
use kittycad_execution_plan::constants;
use kittycad_execution_plan_traits::Primitive;
use super::{native_functions, Address};
use crate::{CompileError, KclFunction};
/// KCL values which can be written to KCEP memory.
/// This is recursive. For example, the bound value might be an array, which itself contains bound values.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
#[cfg_attr(test, derive(PartialEq))]
pub enum EpBinding {
/// A KCL value which gets stored in a particular address in KCEP memory.
Single(Address),
@ -23,6 +26,8 @@ pub enum EpBinding {
properties: HashMap<String, EpBinding>,
},
/// Not associated with a KCEP address.
Constant(Primitive),
/// Not associated with a KCEP address.
Function(KclFunction),
/// SketchGroups have their own storage.
SketchGroup { index: usize },
@ -52,11 +57,13 @@ impl EpBinding {
EpBinding::SketchGroup { .. } => Err(CompileError::CannotIndex),
EpBinding::Single(_) => Err(CompileError::CannotIndex),
EpBinding::Function(_) => Err(CompileError::CannotIndex),
EpBinding::Constant(_) => Err(CompileError::CannotIndex),
},
// Objects can be indexed by string properties.
LiteralValue::String(property) => match self {
EpBinding::Single(_) => Err(CompileError::NoProperties),
EpBinding::Function(_) => Err(CompileError::NoProperties),
EpBinding::Constant(_) => Err(CompileError::CannotIndex),
EpBinding::SketchGroup { .. } => Err(CompileError::NoProperties),
EpBinding::Sequence { .. } => Err(CompileError::ArrayDoesNotHaveProperties),
EpBinding::Map {
@ -103,8 +110,58 @@ impl BindingScope {
// TODO: Actually put the stdlib prelude in here,
// things like `startSketchAt` and `line`.
ep_bindings: HashMap::from([
("E".into(), EpBinding::Constant(constants::E)),
("PI".into(), EpBinding::Constant(constants::PI)),
("id".into(), EpBinding::from(KclFunction::Id(native_functions::Id))),
("abs".into(), EpBinding::from(KclFunction::Abs(native_functions::Abs))),
(
"acos".into(),
EpBinding::from(KclFunction::Acos(native_functions::Acos)),
),
(
"asin".into(),
EpBinding::from(KclFunction::Asin(native_functions::Asin)),
),
(
"atan".into(),
EpBinding::from(KclFunction::Atan(native_functions::Atan)),
),
(
"ceil".into(),
EpBinding::from(KclFunction::Ceil(native_functions::Ceil)),
),
("cos".into(), EpBinding::from(KclFunction::Cos(native_functions::Cos))),
(
"floor".into(),
EpBinding::from(KclFunction::Floor(native_functions::Floor)),
),
("ln".into(), EpBinding::from(KclFunction::Ln(native_functions::Ln))),
(
"log10".into(),
EpBinding::from(KclFunction::Log10(native_functions::Log10)),
),
(
"log2".into(),
EpBinding::from(KclFunction::Log2(native_functions::Log2)),
),
("sin".into(), EpBinding::from(KclFunction::Sin(native_functions::Sin))),
(
"sqrt".into(),
EpBinding::from(KclFunction::Sqrt(native_functions::Sqrt)),
),
("tan".into(), EpBinding::from(KclFunction::Tan(native_functions::Tan))),
(
"toDegrees".into(),
EpBinding::from(KclFunction::ToDegrees(native_functions::ToDegrees)),
),
(
"toRadians".into(),
EpBinding::from(KclFunction::ToRadians(native_functions::ToRadians)),
),
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
("log".into(), EpBinding::from(KclFunction::Log(native_functions::Log))),
("max".into(), EpBinding::from(KclFunction::Max(native_functions::Max))),
("min".into(), EpBinding::from(KclFunction::Min(native_functions::Min))),
(
"startSketchAt".into(),
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),

View File

@ -259,6 +259,24 @@ impl Planner {
binding,
} = match callee {
KclFunction::Id(f) => f.call(&mut ctx, args)?,
KclFunction::Abs(f) => f.call(&mut ctx, args)?,
KclFunction::Acos(f) => f.call(&mut ctx, args)?,
KclFunction::Asin(f) => f.call(&mut ctx, args)?,
KclFunction::Atan(f) => f.call(&mut ctx, args)?,
KclFunction::Ceil(f) => f.call(&mut ctx, args)?,
KclFunction::Cos(f) => f.call(&mut ctx, args)?,
KclFunction::Floor(f) => f.call(&mut ctx, args)?,
KclFunction::Ln(f) => f.call(&mut ctx, args)?,
KclFunction::Log10(f) => f.call(&mut ctx, args)?,
KclFunction::Log2(f) => f.call(&mut ctx, args)?,
KclFunction::Sin(f) => f.call(&mut ctx, args)?,
KclFunction::Sqrt(f) => f.call(&mut ctx, args)?,
KclFunction::Tan(f) => f.call(&mut ctx, args)?,
KclFunction::ToDegrees(f) => f.call(&mut ctx, args)?,
KclFunction::ToRadians(f) => f.call(&mut ctx, args)?,
KclFunction::Log(f) => f.call(&mut ctx, args)?,
KclFunction::Max(f) => f.call(&mut ctx, args)?,
KclFunction::Min(f) => f.call(&mut ctx, args)?,
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
@ -634,6 +652,21 @@ impl Eq for UserDefinedFunction {}
#[cfg_attr(test, derive(Eq, PartialEq))]
enum KclFunction {
Id(native_functions::Id),
Abs(native_functions::Abs),
Acos(native_functions::Acos),
Asin(native_functions::Asin),
Atan(native_functions::Atan),
Ceil(native_functions::Ceil),
Cos(native_functions::Cos),
Floor(native_functions::Floor),
Ln(native_functions::Ln),
Log10(native_functions::Log10),
Log2(native_functions::Log2),
Sin(native_functions::Sin),
Sqrt(native_functions::Sqrt),
Tan(native_functions::Tan),
ToDegrees(native_functions::ToDegrees),
ToRadians(native_functions::ToRadians),
StartSketchAt(native_functions::sketch::StartSketchAt),
LineTo(native_functions::sketch::LineTo),
Line(native_functions::sketch::Line),
@ -642,6 +675,9 @@ enum KclFunction {
YLineTo(native_functions::sketch::YLineTo),
YLine(native_functions::sketch::YLine),
Add(native_functions::Add),
Log(native_functions::Log),
Max(native_functions::Max),
Min(native_functions::Min),
UserDefined(UserDefinedFunction),
Extrude(native_functions::sketch::Extrude),
Close(native_functions::sketch::Close),

View File

@ -2,18 +2,15 @@
//! This includes some of the stdlib, e.g. `startSketchAt`.
//! But some other stdlib functions will be written in KCL.
use kittycad_execution_plan::{BinaryArithmetic, Destination, Instruction};
use kittycad_execution_plan::{
BinaryArithmetic, BinaryOperation, Destination, Instruction, Operand, UnaryArithmetic, UnaryOperation,
};
use kittycad_execution_plan_traits::Address;
use crate::{CompileError, EpBinding, EvalPlan};
pub mod sketch;
/// The identity function. Always returns its first input.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Id;
pub trait Callable {
fn call(&self, ctx: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError>;
}
@ -32,6 +29,65 @@ impl<'a> Context<'a> {
}
}
/// Unary operator macro to quickly create new bindings.
macro_rules! define_unary {
() => {};
($h:ident$( $r:ident)*) => {
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct $h;
impl Callable for $h {
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
if args.len() > 1 {
return Err(CompileError::TooManyArgs {
fn_name: "$h".into(),
maximum: 1,
actual: args.len(),
});
}
let not_enough_args = CompileError::NotEnoughArgs {
fn_name: "$h".into(),
required: 1,
actual: args.len(),
};
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args.clone())? else {
return Err(CompileError::InvalidOperand("A single value binding is expected"));
};
let destination = ctx.next_address.offset_by(1);
let instructions = vec![
Instruction::UnaryArithmetic {
arithmetic: UnaryArithmetic {
operation: UnaryOperation::$h,
operand: Operand::Reference(arg0)
},
destination: Destination::Address(destination)
}
];
Ok(EvalPlan {
instructions,
binding: EpBinding::Single(destination),
})
}
}
define_unary!($($r)*);
};
}
define_unary!(Abs Acos Asin Atan Ceil Cos Floor Ln Log10 Log2 Sin Sqrt Tan ToDegrees ToRadians);
/// The identity function. Always returns its first input.
/// Implemented purely on the KCL side so it doesn't need to be in the
/// define_unary! macro above.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Id;
impl Callable for Id {
fn call(&self, _: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
if args.len() > 1 {
@ -56,44 +112,53 @@ impl Callable for Id {
}
}
/// A test function that adds two numbers.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Add;
/// Binary operator macro to quickly create new bindings.
macro_rules! define_binary {
() => {};
($h:ident$( $r:ident)*) => {
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct $h;
impl Callable for Add {
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
let len = args.len();
if len > 2 {
return Err(CompileError::TooManyArgs {
fn_name: "add".into(),
maximum: 2,
actual: len,
});
impl Callable for $h {
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
let len = args.len();
if len > 2 {
return Err(CompileError::TooManyArgs {
fn_name: "$h".into(),
maximum: 2,
actual: len,
});
}
let not_enough_args = CompileError::NotEnoughArgs {
fn_name: "$h".into(),
required: 2,
actual: len,
};
const ERR: &str = "cannot use composite values (e.g. array) as arguments to $h";
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
return Err(CompileError::InvalidOperand(ERR));
};
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args)? else {
return Err(CompileError::InvalidOperand(ERR));
};
let destination = ctx.next_address.offset_by(1);
Ok(EvalPlan {
instructions: vec![Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::$h,
operand0: Operand::Reference(arg0),
operand1: Operand::Reference(arg1),
},
destination: Destination::Address(destination),
}],
binding: EpBinding::Single(destination),
})
}
let not_enough_args = CompileError::NotEnoughArgs {
fn_name: "add".into(),
required: 2,
actual: len,
};
const ERR: &str = "cannot use composite values (e.g. array) as arguments to Add";
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
return Err(CompileError::InvalidOperand(ERR));
};
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args)? else {
return Err(CompileError::InvalidOperand(ERR));
};
let destination = ctx.next_address.offset_by(1);
Ok(EvalPlan {
instructions: vec![Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: kittycad_execution_plan::BinaryOperation::Add,
operand0: kittycad_execution_plan::Operand::Reference(arg0),
operand1: kittycad_execution_plan::Operand::Reference(arg1),
},
destination: Destination::Address(destination),
}],
binding: EpBinding::Single(destination),
})
}
define_binary!($($r)*);
};
}
define_binary!(Add Log Max Min);

View File

@ -67,6 +67,12 @@ pub fn sg_binding(
actual: "function".to_owned(),
arg_number,
}),
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "constant".to_owned(),
arg_number,
}),
}
}
pub fn single_binding(
@ -101,6 +107,12 @@ pub fn single_binding(
actual: "function".to_owned(),
arg_number,
}),
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "constant".to_owned(),
arg_number,
}),
}
}
@ -136,6 +148,12 @@ pub fn sequence_binding(
actual: "function".to_owned(),
arg_number,
}),
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "constant".to_owned(),
arg_number,
}),
}
}

View File

@ -1,7 +1,7 @@
use std::{collections::HashMap, env};
use ep::{sketch_types, Destination, UnaryArithmetic};
use ept::{ListHeader, ObjectHeader};
use ep::{constants, sketch_types, Destination, UnaryArithmetic};
use ept::{ListHeader, ObjectHeader, Primitive};
use kittycad_modeling_cmds::shared::Point2d;
use kittycad_modeling_session::SessionBuilder;
use pretty_assertions::assert_eq;
@ -1414,3 +1414,31 @@ fn mod_and_pow() {
]
);
}
#[tokio::test]
async fn cos_sin_pi() {
let program = "
let x = cos(45.0)*10
let y = sin(45.0)*10
let z = PI
";
let (_plan, scope, _) = must_plan(program);
let Some(EpBinding::Single(x)) = scope.get("x") else {
panic!("Unexpected binding for variable 'x': {:?}", scope.get("x"));
};
let Some(EpBinding::Single(y)) = scope.get("y") else {
panic!("Unexpected binding for variable 'y': {:?}", scope.get("y"));
};
let Some(EpBinding::Constant(z)) = scope.get("z") else {
panic!("Unexpected binding for variable 'z': {:?}", scope.get("z"));
};
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
let mem = crate::execute(ast, &mut None).await.unwrap();
use ept::ReadMemory;
assert_eq!(*mem.get(x).unwrap(), Primitive::from(5.253219888177298));
assert_eq!(*mem.get(y).unwrap(), Primitive::from(8.509035245341185));
// Constants don't live in memory.
assert_eq!(*z, constants::PI);
}

View File

@ -14,6 +14,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
anyhow = { version = "1.0.81", features = ["backtrace"] }
async-recursion = "1.0.5"
async-trait = "0.1.77"
boxcar = "0.2.4"
chrono = "0.4.35"
clap = { version = "4.5.2", features = ["cargo", "derive", "env", "unicode"], optional = true }
dashmap = "5.5.3"

View File

@ -75,6 +75,7 @@ pub async fn modify_ast_for_sketch(
// Let's get the path info.
let resp = engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::PathGetInfo { path_id: sketch_id },
@ -99,6 +100,7 @@ pub async fn modify_ast_for_sketch(
for segment in &path_info.segments {
if let Some(command_id) = &segment.command_id {
let h = engine.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::CurveGetControlPoints { curve_id: *command_id },

View File

@ -29,6 +29,7 @@ pub struct EngineConnection {
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
tcp_read_handle: Arc<TcpReadHandle>,
socket_health: Arc<Mutex<SocketHealth>>,
batch: Arc<Mutex<Vec<WebSocketRequest>>>,
}
pub struct TcpRead {
@ -154,27 +155,136 @@ impl EngineConnection {
}),
responses,
socket_health,
batch: Arc::new(Mutex::new(Vec::new())),
})
}
}
fn is_cmd_with_return_values(cmd: &kittycad::types::ModelingCmd) -> bool {
let (kittycad::types::ModelingCmd::Export { .. }
| kittycad::types::ModelingCmd::Extrude { .. }
| kittycad::types::ModelingCmd::SketchModeDisable { .. }
| kittycad::types::ModelingCmd::ObjectBringToFront { .. }
| kittycad::types::ModelingCmd::SelectWithPoint { .. }
| kittycad::types::ModelingCmd::HighlightSetEntity { .. }
| kittycad::types::ModelingCmd::EntityGetChildUuid { .. }
| kittycad::types::ModelingCmd::EntityGetNumChildren { .. }
| kittycad::types::ModelingCmd::EntityGetParentId { .. }
| kittycad::types::ModelingCmd::EntityGetAllChildUuids { .. }
| kittycad::types::ModelingCmd::CameraDragMove { .. }
| kittycad::types::ModelingCmd::CameraDragEnd { .. }
| kittycad::types::ModelingCmd::DefaultCameraGetSettings { .. }
| kittycad::types::ModelingCmd::DefaultCameraZoom { .. }
| kittycad::types::ModelingCmd::SelectGet { .. }
| kittycad::types::ModelingCmd::Solid3DGetAllEdgeFaces { .. }
| kittycad::types::ModelingCmd::Solid3DGetAllOppositeEdges { .. }
| kittycad::types::ModelingCmd::Solid3DGetOppositeEdge { .. }
| kittycad::types::ModelingCmd::Solid3DGetNextAdjacentEdge { .. }
| kittycad::types::ModelingCmd::Solid3DGetPrevAdjacentEdge { .. }
| kittycad::types::ModelingCmd::GetEntityType { .. }
| kittycad::types::ModelingCmd::CurveGetControlPoints { .. }
| kittycad::types::ModelingCmd::CurveGetType { .. }
| kittycad::types::ModelingCmd::MouseClick { .. }
| kittycad::types::ModelingCmd::TakeSnapshot { .. }
| kittycad::types::ModelingCmd::PathGetInfo { .. }
| kittycad::types::ModelingCmd::PathGetCurveUuidsForVertices { .. }
| kittycad::types::ModelingCmd::PathGetVertexUuids { .. }
| kittycad::types::ModelingCmd::CurveGetEndPoints { .. }
| kittycad::types::ModelingCmd::FaceIsPlanar { .. }
| kittycad::types::ModelingCmd::FaceGetPosition { .. }
| kittycad::types::ModelingCmd::FaceGetGradient { .. }
| kittycad::types::ModelingCmd::PlaneIntersectAndProject { .. }
| kittycad::types::ModelingCmd::ImportFiles { .. }
| kittycad::types::ModelingCmd::Mass { .. }
| kittycad::types::ModelingCmd::Volume { .. }
| kittycad::types::ModelingCmd::Density { .. }
| kittycad::types::ModelingCmd::SurfaceArea { .. }
| kittycad::types::ModelingCmd::CenterOfMass { .. }
| kittycad::types::ModelingCmd::GetSketchModePlane { .. }
| kittycad::types::ModelingCmd::EntityGetDistance { .. }
| kittycad::types::ModelingCmd::EntityLinearPattern { .. }
| kittycad::types::ModelingCmd::EntityCircularPattern { .. }
| kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo { .. }) = cmd
else {
return false;
};
true
}
#[async_trait::async_trait]
impl EngineManager for EngineConnection {
async fn send_modeling_cmd(
&self,
flush_batch: bool,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,
) -> Result<OkWebSocketResponseData, KclError> {
let req = WebSocketRequest::ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: id,
};
println!("req {:?}", req);
if !flush_batch {
self.batch.lock().unwrap().push(req);
}
// If the batch only has this one command that expects a return value,
// fire it right away, or if we want to flush batch queue.
let is_sending = (is_cmd_with_return_values(&cmd) && self.batch.lock().unwrap().len() == 1)
|| flush_batch
|| is_cmd_with_return_values(&cmd);
// Return a fake modeling_request empty response.
if !is_sending {
println!("fake {:?}", cmd);
return Ok(OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Empty {}
});
}
let batched_requests =
WebSocketRequest::ModelingCmdBatchReq {
requests: self.batch.lock().unwrap().iter().fold(vec![], |mut acc, val| {
let WebSocketRequest::ModelingCmdReq { cmd, cmd_id } = val else { return acc; };
acc.push(kittycad::types::ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: *cmd_id,
});
acc
}),
batch_id: uuid::Uuid::new_v4()
};
let final_req = if self.batch.lock().unwrap().len() == 1 {
self.batch.lock().unwrap().get(0).unwrap().clone()
} else {
batched_requests
};
// Throw away the old batch queue.
self.batch.lock().unwrap().clear();
println!("final req {:?}", final_req);
// We pop off the responses to cleanup our mappings.
let id_final = match final_req {
WebSocketRequest::ModelingCmdBatchReq { requests: _, batch_id } => batch_id,
WebSocketRequest::ModelingCmdReq { cmd: _, cmd_id } => cmd_id,
_ => panic!("should not be possible"),
};
let (tx, rx) = oneshot::channel();
// Send the request to the engine, via the actor.
self.engine_req_tx
.send(ToEngineReq {
req: WebSocketRequest::ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: id,
},
req: final_req.clone(),
request_sent: tx,
})
.await
@ -200,6 +310,7 @@ impl EngineManager for EngineConnection {
})
})?;
// Wait for the response.
let current_time = std::time::Instant::now();
while current_time.elapsed().as_secs() < 60 {
@ -211,8 +322,9 @@ impl EngineManager for EngineConnection {
}));
}
}
// We pop off the responses to cleanup our mappings.
if let Some((_, resp)) = self.responses.remove(&id) {
if let Some((_, resp)) = self.responses.remove(&id_final) {
println!("RESP {:?}", resp);
return if let Some(data) = &resp.resp {
Ok(data.clone())
} else {
@ -225,7 +337,7 @@ impl EngineManager for EngineConnection {
}
Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command timed out `{}`", id),
message: format!("Modeling command timed out `{}`", id_final),
source_ranges: vec![source_range],
}))
}

View File

@ -19,6 +19,7 @@ impl EngineConnection {
impl crate::engine::EngineManager for EngineConnection {
async fn send_modeling_cmd(
&self,
_flush_batch: bool,
_id: uuid::Uuid,
_source_range: crate::executor::SourceRange,
_cmd: kittycad::types::ModelingCmd,

View File

@ -13,6 +13,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
/// Send a modeling command and wait for the response message.
async fn send_modeling_cmd(
&self,
flush_batch: bool,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,

View File

@ -1008,6 +1008,7 @@ pub async fn execute(
// Before we even start executing the program, set the units.
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
kittycad::types::ModelingCmd::SetSceneUnits {
@ -1219,6 +1220,17 @@ pub async fn execute(
}
}
// Fire the batch since we've reached the end.
// ctx.engine
// .send_modeling_cmd(
// true,
// uuid::Uuid::new_v4(),
// SourceRange::default(),
// // This is ignored when flush_batch is true.
// kittycad::types::ModelingCmd::EditModeExit {},
// )
// .await?;
Ok(memory.clone())
}

View File

@ -206,7 +206,7 @@ impl Args {
id: uuid::Uuid,
cmd: kittycad::types::ModelingCmd,
) -> Result<OkWebSocketResponseData, KclError> {
self.ctx.engine.send_modeling_cmd(id, self.source_range, cmd).await
self.ctx.engine.send_modeling_cmd(false, id, self.source_range, cmd).await
}
fn make_user_val_from_json(&self, j: serde_json::Value) -> Result<MemoryItem, KclError> {

View File

@ -21,9 +21,9 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
// Create the client.
let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
// uncomment to use a local server
//client.set_base_url("http://system76-pc:8080/");
client.set_base_url("http://localhost:8080/");
let ws = client
.modeling()
@ -45,6 +45,7 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
kcl_lib::executor::SourceRange::default(),
kittycad::types::ModelingCmd::DefaultCameraLookAt {
@ -60,6 +61,7 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
let resp = ctx
.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
kcl_lib::executor::SourceRange::default(),
kittycad::types::ModelingCmd::TakeSnapshot {

View File

@ -49,6 +49,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
let plane_id = uuid::Uuid::new_v4();
ctx.engine
.send_modeling_cmd(
false,
plane_id,
SourceRange::default(),
ModelingCmd::MakePlane {
@ -67,6 +68,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
// You can however get path info without sketch mode.
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::SketchModeEnable {
@ -82,6 +84,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
// We can't get control points of an existing sketch without being in edit mode.
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::EditModeEnter { target: sketch_id },