Compare commits
	
		
			2 Commits
		
	
	
		
			franknoiro
			...
			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
	