Compare commits
2 Commits
pierremtb/
...
batch-old-
Author | SHA1 | Date | |
---|---|---|---|
0bebd544a3 | |||
64c9de09aa |
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::executor::SourceRange;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
@ -71,20 +72,113 @@ struct ToEngineReq {
|
|||||||
request_sent: oneshot::Sender<Result<()>>,
|
request_sent: oneshot::Sender<Result<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
impl EngineConnection {
|
impl EngineConnection {
|
||||||
/// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine.
|
/// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine.
|
||||||
async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
|
async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
|
||||||
|
let mut batch: Vec<kittycad::types::ModelingCmdReq> = vec![];
|
||||||
|
|
||||||
while let Some(req) = engine_req_rx.recv().await {
|
while let Some(req) = engine_req_rx.recv().await {
|
||||||
let ToEngineReq { req, request_sent } = req;
|
let ToEngineReq { req, request_sent } = req;
|
||||||
let res = if let kittycad::types::WebSocketRequest::ModelingCmdReq {
|
let kittycad::types::WebSocketRequest::ModelingCmdReq { cmd, cmd_id } = &req else {
|
||||||
cmd: kittycad::types::ModelingCmd::ImportFiles { .. },
|
return;
|
||||||
cmd_id: _,
|
};
|
||||||
} = &req
|
|
||||||
{
|
let res = if let kittycad::types::ModelingCmd::ImportFiles { .. } = cmd {
|
||||||
// Send it as binary.
|
// Send it as binary.
|
||||||
Self::inner_send_to_engine_binary(req, &mut tcp_write).await
|
Self::inner_send_to_engine_binary(req, &mut tcp_write).await
|
||||||
} else {
|
} else {
|
||||||
Self::inner_send_to_engine(req, &mut tcp_write).await
|
// Backported from the new Grackle-core KCL.
|
||||||
|
// We will batch all commands until we hit one which has
|
||||||
|
// return values we want to wait for. Currently, that means
|
||||||
|
// waiting for API requests with return values that are not just
|
||||||
|
// request confirmations (ex. face or edge data).
|
||||||
|
|
||||||
|
batch.push(kittycad::types::ModelingCmdReq {
|
||||||
|
cmd: cmd.clone(),
|
||||||
|
cmd_id: *cmd_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_cmd_with_return_values(cmd) || *cmd_id == uuid::Uuid::nil() {
|
||||||
|
// If the batch has zero commands, and we're about to load
|
||||||
|
// a command that has return values, don't wrap it in a
|
||||||
|
// ModelingCmdBatchReq.
|
||||||
|
let future = if batch.len() == 1 {
|
||||||
|
Self::inner_send_to_engine(
|
||||||
|
kittycad::types::WebSocketRequest::ModelingCmdReq {
|
||||||
|
cmd: cmd.clone(),
|
||||||
|
cmd_id: *cmd_id,
|
||||||
|
},
|
||||||
|
&mut tcp_write,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Serde will properly serialize these.
|
||||||
|
Self::inner_send_to_engine(
|
||||||
|
kittycad::types::WebSocketRequest::ModelingCmdBatchReq {
|
||||||
|
requests: batch.clone(),
|
||||||
|
},
|
||||||
|
&mut tcp_write,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare for a new batch of instructions.
|
||||||
|
batch.clear();
|
||||||
|
|
||||||
|
future.await
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let _ = request_sent.send(res);
|
let _ = request_sent.send(res);
|
||||||
}
|
}
|
||||||
@ -163,7 +257,7 @@ impl EngineManager for EngineConnection {
|
|||||||
async fn send_modeling_cmd(
|
async fn send_modeling_cmd(
|
||||||
&self,
|
&self,
|
||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
source_range: crate::executor::SourceRange,
|
source_range: SourceRange,
|
||||||
cmd: kittycad::types::ModelingCmd,
|
cmd: kittycad::types::ModelingCmd,
|
||||||
) -> Result<OkWebSocketResponseData, KclError> {
|
) -> Result<OkWebSocketResponseData, KclError> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -200,7 +294,18 @@ impl EngineManager for EngineConnection {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Wait for the response.
|
// Only wait for a response if it's a command *with* return values
|
||||||
|
// So most of the time, this condition will be true.
|
||||||
|
if !is_cmd_with_return_values(&cmd) {
|
||||||
|
// Simulate an empty response type
|
||||||
|
return Ok(OkWebSocketResponseData::Modeling {
|
||||||
|
modeling_response: kittycad::types::OkModelingCmdResponse::Empty {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a submitted batch, we want to check the batch return value
|
||||||
|
// in case of any errors.
|
||||||
|
|
||||||
let current_time = std::time::Instant::now();
|
let current_time = std::time::Instant::now();
|
||||||
while current_time.elapsed().as_secs() < 60 {
|
while current_time.elapsed().as_secs() < 60 {
|
||||||
if let Ok(guard) = self.socket_health.lock() {
|
if let Ok(guard) = self.socket_health.lock() {
|
||||||
|
@ -10,6 +10,24 @@ pub mod conn_wasm;
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||||
|
/// Tell the EngineManager there will be no more commands.
|
||||||
|
/// We send a "dummy command" to signal EngineConnection that we're
|
||||||
|
/// at the end of the program and there'll be no more requests.
|
||||||
|
/// This means in tests, where it's impossible to look ahead, we'll need to
|
||||||
|
/// add this to mark the end of commands.
|
||||||
|
/// In compiled KCL tests, it will be auto-inserted.
|
||||||
|
async fn signal_end(&self) -> Result<kittycad::types::OkWebSocketResponseData, crate::errors::KclError> {
|
||||||
|
self.send_modeling_cmd(
|
||||||
|
// THE NIL UUID IS THE SIGNAL OF THE END OF TIMES FOR THIS POOR PROGRAM.
|
||||||
|
uuid::Uuid::nil(),
|
||||||
|
// This will be ignored.
|
||||||
|
crate::executor::SourceRange([0, 0]),
|
||||||
|
// This will be ignored. It was one I found with no fields.
|
||||||
|
kittycad::types::ModelingCmd::EditModeExit {},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a modeling command and wait for the response message.
|
/// Send a modeling command and wait for the response message.
|
||||||
async fn send_modeling_cmd(
|
async fn send_modeling_cmd(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1219,6 +1219,9 @@ pub async fn execute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signal to engine we're done. Flush the batch.
|
||||||
|
ctx.engine.signal_end().await?;
|
||||||
|
|
||||||
Ok(memory.clone())
|
Ok(memory.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +165,7 @@ async fn inner_get_opposite_edge(tag: String, extrude_group: Box<ExtrudeGroup>,
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let kittycad::types::OkWebSocketResponseData::Modeling {
|
let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||||
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetOppositeEdge { data: opposite_edge },
|
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetOppositeEdge { data: opposite_edge },
|
||||||
} = &resp
|
} = &resp
|
||||||
|
@ -915,7 +915,7 @@ async fn start_sketch_on_face(
|
|||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
message: format!("Expected a face with the tag `{}`", tag),
|
message: format!("Expected a face with the tag `{}` for sketch", tag),
|
||||||
source_ranges: vec![args.source_range],
|
source_ranges: vec![args.source_range],
|
||||||
})
|
})
|
||||||
})??,
|
})??,
|
||||||
|
Reference in New Issue
Block a user