change everything to rwlocks for thread safety (#5416)
* make everything in engine a rwlock and cleanup repetitive code Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * docs Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
@ -1,7 +1,4 @@
|
|||||||
use std::{
|
use std::{collections::HashMap, sync::Arc};
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
@ -24,22 +21,20 @@ const NEED_PLANES: bool = true;
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EngineConnection {
|
pub struct EngineConnection {
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>>,
|
batch: Arc<RwLock<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>>,
|
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>>,
|
||||||
core_test: Arc<Mutex<String>>,
|
core_test: Arc<RwLock<String>>,
|
||||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EngineConnection {
|
impl EngineConnection {
|
||||||
pub async fn new(result: Arc<Mutex<String>>) -> Result<EngineConnection> {
|
pub async fn new(result: Arc<RwLock<String>>) -> Result<EngineConnection> {
|
||||||
if let Ok(mut code) = result.lock() {
|
result.write().await.push_str(CPP_PREFIX);
|
||||||
code.push_str(CPP_PREFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(EngineConnection {
|
Ok(EngineConnection {
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(RwLock::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||||
core_test: result,
|
core_test: result,
|
||||||
default_planes: Default::default(),
|
default_planes: Default::default(),
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
@ -362,29 +357,29 @@ fn codegen_cpp_repl_uuid_setters(reps_id: &str, entity_ids: &[uuid::Uuid]) -> St
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl kcl_lib::EngineManager for EngineConnection {
|
impl kcl_lib::EngineManager for EngineConnection {
|
||||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>> {
|
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>> {
|
||||||
self.batch.clone()
|
self.batch.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>> {
|
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>> {
|
||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||||
IndexMap::new()
|
Arc::new(RwLock::new(IndexMap::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||||
Vec::new()
|
Arc::new(RwLock::new(Vec::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
async fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.read().await;
|
||||||
*guard
|
*guard
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
let mut guard = self.execution_kind.lock().unwrap();
|
let mut guard = self.execution_kind.write().await;
|
||||||
let original = *guard;
|
let original = *guard;
|
||||||
*guard = execution_kind;
|
*guard = execution_kind;
|
||||||
original
|
original
|
||||||
@ -435,24 +430,18 @@ impl kcl_lib::EngineManager for EngineConnection {
|
|||||||
}) => {
|
}) => {
|
||||||
let mut responses = HashMap::new();
|
let mut responses = HashMap::new();
|
||||||
for request in requests {
|
for request in requests {
|
||||||
let (new_code, this_response);
|
let (new_code, this_response) = self.handle_command(&request.cmd_id, &request.cmd);
|
||||||
|
|
||||||
if let Ok(mut test_code) = self.core_test.lock() {
|
if !new_code.is_empty() {
|
||||||
(new_code, this_response) = self.handle_command(&request.cmd_id, &request.cmd);
|
let new_code = new_code
|
||||||
|
.trim()
|
||||||
if !new_code.is_empty() {
|
.split(' ')
|
||||||
let new_code = new_code
|
.filter(|s| !s.is_empty())
|
||||||
.trim()
|
.collect::<Vec<_>>()
|
||||||
.split(' ')
|
.join(" ")
|
||||||
.filter(|s| !s.is_empty())
|
+ "\n";
|
||||||
.collect::<Vec<_>>()
|
//println!("{new_code}");
|
||||||
.join(" ")
|
self.core_test.write().await.push_str(&new_code);
|
||||||
+ "\n";
|
|
||||||
//println!("{new_code}");
|
|
||||||
test_code.push_str(&new_code);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this_response = OkModelingCmdResponse::Empty {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.insert(
|
responses.insert(
|
||||||
@ -470,24 +459,18 @@ impl kcl_lib::EngineManager for EngineConnection {
|
|||||||
}
|
}
|
||||||
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd, cmd_id }) => {
|
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd, cmd_id }) => {
|
||||||
//also handle unbatched requests inline
|
//also handle unbatched requests inline
|
||||||
let (new_code, this_response);
|
let (new_code, this_response) = self.handle_command(&cmd_id, &cmd);
|
||||||
|
|
||||||
if let Ok(mut test_code) = self.core_test.lock() {
|
if !new_code.is_empty() {
|
||||||
(new_code, this_response) = self.handle_command(&cmd_id, &cmd);
|
let new_code = new_code
|
||||||
|
.trim()
|
||||||
if !new_code.is_empty() {
|
.split(' ')
|
||||||
let new_code = new_code
|
.filter(|s| !s.is_empty())
|
||||||
.trim()
|
.collect::<Vec<_>>()
|
||||||
.split(' ')
|
.join(" ")
|
||||||
.filter(|s| !s.is_empty())
|
+ "\n";
|
||||||
.collect::<Vec<_>>()
|
//println!("{new_code}");
|
||||||
.join(" ")
|
self.core_test.write().await.push_str(&new_code);
|
||||||
+ "\n";
|
|
||||||
//println!("{new_code}");
|
|
||||||
test_code.push_str(&new_code);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this_response = OkModelingCmdResponse::Empty {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
|
Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use kcl_lib::{ExecState, ExecutorContext};
|
use kcl_lib::{ExecState, ExecutorContext};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
mod conn_mock_core;
|
mod conn_mock_core;
|
||||||
@ -10,7 +11,7 @@ mod conn_mock_core;
|
|||||||
pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
||||||
let program = kcl_lib::Program::parse_no_errs(code)?;
|
let program = kcl_lib::Program::parse_no_errs(code)?;
|
||||||
|
|
||||||
let result = Arc::new(Mutex::new("".into()));
|
let result = Arc::new(RwLock::new("".into()));
|
||||||
let ref_result = Arc::clone(&result);
|
let ref_result = Arc::clone(&result);
|
||||||
|
|
||||||
let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new(
|
let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new(
|
||||||
@ -18,6 +19,6 @@ pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
|||||||
)));
|
)));
|
||||||
ctx.run(&program, &mut ExecState::new(&ctx.settings)).await?;
|
ctx.run(&program, &mut ExecState::new(&ctx.settings)).await?;
|
||||||
|
|
||||||
let result = result.lock().expect("mutex lock").clone();
|
let result = result.read().await.clone();
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||||
//! engine.
|
//! engine.
|
||||||
|
|
||||||
use std::{
|
use std::{collections::HashMap, sync::Arc};
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use dashmap::DashMap;
|
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use kcmc::{
|
use kcmc::{
|
||||||
@ -17,9 +13,7 @@ use kcmc::{
|
|||||||
},
|
},
|
||||||
ModelingCmd,
|
ModelingCmd,
|
||||||
};
|
};
|
||||||
use kittycad_modeling_cmds::{
|
use kittycad_modeling_cmds::{self as kcmc, ok_response::OkModelingCmdResponse, websocket::ModelingBatch};
|
||||||
self as kcmc, id::ModelingCmdId, ok_response::OkModelingCmdResponse, websocket::ModelingBatch,
|
|
||||||
};
|
|
||||||
use tokio::sync::{mpsc, oneshot, RwLock};
|
use tokio::sync::{mpsc, oneshot, RwLock};
|
||||||
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -43,21 +37,21 @@ type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocket
|
|||||||
pub struct EngineConnection {
|
pub struct EngineConnection {
|
||||||
engine_req_tx: mpsc::Sender<ToEngineReq>,
|
engine_req_tx: mpsc::Sender<ToEngineReq>,
|
||||||
shutdown_tx: mpsc::Sender<()>,
|
shutdown_tx: mpsc::Sender<()>,
|
||||||
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
|
responses: Arc<RwLock<IndexMap<uuid::Uuid, WebSocketResponse>>>,
|
||||||
pending_errors: Arc<Mutex<Vec<String>>>,
|
pending_errors: Arc<RwLock<Vec<String>>>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
tcp_read_handle: Arc<TcpReadHandle>,
|
tcp_read_handle: Arc<TcpReadHandle>,
|
||||||
socket_health: Arc<Mutex<SocketHealth>>,
|
socket_health: Arc<RwLock<SocketHealth>>,
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||||
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||||
|
|
||||||
/// The default planes for the scene.
|
/// The default planes for the scene.
|
||||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
/// If the server sends session data, it'll be copied to here.
|
/// If the server sends session data, it'll be copied to here.
|
||||||
session_data: Arc<Mutex<Option<ModelingSessionData>>>,
|
session_data: Arc<RwLock<Option<ModelingSessionData>>>,
|
||||||
|
|
||||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TcpRead {
|
pub struct TcpRead {
|
||||||
@ -230,12 +224,12 @@ impl EngineConnection {
|
|||||||
|
|
||||||
let mut tcp_read = TcpRead { stream: tcp_read };
|
let mut tcp_read = TcpRead { stream: tcp_read };
|
||||||
|
|
||||||
let session_data: Arc<Mutex<Option<ModelingSessionData>>> = Arc::new(Mutex::new(None));
|
let session_data: Arc<RwLock<Option<ModelingSessionData>>> = Arc::new(RwLock::new(None));
|
||||||
let session_data2 = session_data.clone();
|
let session_data2 = session_data.clone();
|
||||||
let responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>> = Arc::new(DashMap::new());
|
let responses: Arc<RwLock<IndexMap<uuid::Uuid, WebSocketResponse>>> = Arc::new(RwLock::new(IndexMap::new()));
|
||||||
let responses_clone = responses.clone();
|
let responses_clone = responses.clone();
|
||||||
let socket_health = Arc::new(Mutex::new(SocketHealth::Active));
|
let socket_health = Arc::new(RwLock::new(SocketHealth::Active));
|
||||||
let pending_errors = Arc::new(Mutex::new(Vec::new()));
|
let pending_errors = Arc::new(RwLock::new(Vec::new()));
|
||||||
let pending_errors_clone = pending_errors.clone();
|
let pending_errors_clone = pending_errors.clone();
|
||||||
|
|
||||||
let socket_health_tcp_read = socket_health.clone();
|
let socket_health_tcp_read = socket_health.clone();
|
||||||
@ -260,7 +254,7 @@ impl EngineConnection {
|
|||||||
let id: uuid::Uuid = (*resp_id).into();
|
let id: uuid::Uuid = (*resp_id).into();
|
||||||
match batch_response {
|
match batch_response {
|
||||||
BatchResponse::Success { response } => {
|
BatchResponse::Success { response } => {
|
||||||
responses_clone.insert(
|
responses_clone.write().await.insert(
|
||||||
id,
|
id,
|
||||||
WebSocketResponse::Success(SuccessWebSocketResponse {
|
WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||||
success: true,
|
success: true,
|
||||||
@ -272,7 +266,7 @@ impl EngineConnection {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
BatchResponse::Failure { errors } => {
|
BatchResponse::Failure { errors } => {
|
||||||
responses_clone.insert(
|
responses_clone.write().await.insert(
|
||||||
id,
|
id,
|
||||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||||
success: false,
|
success: false,
|
||||||
@ -288,7 +282,7 @@ impl EngineConnection {
|
|||||||
resp: OkWebSocketResponseData::ModelingSessionData { session },
|
resp: OkWebSocketResponseData::ModelingSessionData { session },
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let mut sd = session_data2.lock().unwrap();
|
let mut sd = session_data2.write().await;
|
||||||
sd.replace(session.clone());
|
sd.replace(session.clone());
|
||||||
}
|
}
|
||||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||||
@ -297,7 +291,7 @@ impl EngineConnection {
|
|||||||
errors,
|
errors,
|
||||||
}) => {
|
}) => {
|
||||||
if let Some(id) = request_id {
|
if let Some(id) = request_id {
|
||||||
responses_clone.insert(
|
responses_clone.write().await.insert(
|
||||||
*id,
|
*id,
|
||||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||||
success: false,
|
success: false,
|
||||||
@ -307,19 +301,20 @@ impl EngineConnection {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Add it to our pending errors.
|
// Add it to our pending errors.
|
||||||
let mut pe = pending_errors_clone.lock().unwrap();
|
let mut pe = pending_errors_clone.write().await;
|
||||||
for error in errors {
|
for error in errors {
|
||||||
if !pe.contains(&error.message) {
|
if !pe.contains(&error.message) {
|
||||||
pe.push(error.message.clone());
|
pe.push(error.message.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
drop(pe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
responses_clone.insert(id, ws_resp.clone());
|
responses_clone.write().await.insert(id, ws_resp.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -327,7 +322,7 @@ impl EngineConnection {
|
|||||||
WebSocketReadError::Read(e) => crate::logln!("could not read from WS: {:?}", e),
|
WebSocketReadError::Read(e) => crate::logln!("could not read from WS: {:?}", e),
|
||||||
WebSocketReadError::Deser(e) => crate::logln!("could not deserialize msg from WS: {:?}", e),
|
WebSocketReadError::Deser(e) => crate::logln!("could not deserialize msg from WS: {:?}", e),
|
||||||
}
|
}
|
||||||
*socket_health_tcp_read.lock().unwrap() = SocketHealth::Inactive;
|
*socket_health_tcp_read.write().await = SocketHealth::Inactive;
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,70 +338,41 @@ impl EngineConnection {
|
|||||||
responses,
|
responses,
|
||||||
pending_errors,
|
pending_errors,
|
||||||
socket_health,
|
socket_health,
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(RwLock::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||||
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||||
default_planes: Default::default(),
|
default_planes: Default::default(),
|
||||||
session_data,
|
session_data,
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_command(
|
|
||||||
&self,
|
|
||||||
cmd: &ModelingCmd,
|
|
||||||
cmd_id: ModelingCmdId,
|
|
||||||
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
|
||||||
) -> Result<(), KclError> {
|
|
||||||
let cmd_id = *cmd_id.as_ref();
|
|
||||||
let range = id_to_source_range
|
|
||||||
.get(&cmd_id)
|
|
||||||
.copied()
|
|
||||||
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
|
||||||
|
|
||||||
// Add artifact command.
|
|
||||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
|
||||||
artifact_commands.push(ArtifactCommand {
|
|
||||||
cmd_id,
|
|
||||||
range,
|
|
||||||
command: cmd.clone(),
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EngineManager for EngineConnection {
|
impl EngineManager for EngineConnection {
|
||||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||||
self.batch.clone()
|
self.batch.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||||
self.responses
|
self.responses.clone()
|
||||||
.iter()
|
|
||||||
.map(|entry| {
|
|
||||||
let (k, v) = entry.pair();
|
|
||||||
(*k, v.clone())
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
self.artifact_commands.clone()
|
||||||
std::mem::take(&mut *artifact_commands)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
async fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.read().await;
|
||||||
*guard
|
*guard
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
let mut guard = self.execution_kind.lock().unwrap();
|
let mut guard = self.execution_kind.write().await;
|
||||||
let original = *guard;
|
let original = *guard;
|
||||||
*guard = execution_kind;
|
*guard = execution_kind;
|
||||||
original
|
original
|
||||||
@ -447,22 +413,10 @@ impl EngineManager for EngineConnection {
|
|||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
cmd: WebSocketRequest,
|
cmd: WebSocketRequest,
|
||||||
id_to_source_range: HashMap<Uuid, SourceRange>,
|
_id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||||
) -> Result<WebSocketResponse, KclError> {
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
match &cmd {
|
|
||||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
|
||||||
for request in requests {
|
|
||||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WebSocketRequest::ModelingCmdReq(request) => {
|
|
||||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In isolated mode, we don't send the command to the engine.
|
// In isolated mode, we don't send the command to the engine.
|
||||||
if self.execution_kind().is_isolated() {
|
if self.execution_kind().await.is_isolated() {
|
||||||
return match &cmd {
|
return match &cmd {
|
||||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||||
let mut responses = HashMap::with_capacity(requests.len());
|
let mut responses = HashMap::with_capacity(requests.len());
|
||||||
@ -524,25 +478,24 @@ impl EngineManager for EngineConnection {
|
|||||||
// Wait for the response.
|
// Wait for the response.
|
||||||
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() {
|
let guard = self.socket_health.read().await;
|
||||||
if *guard == SocketHealth::Inactive {
|
if *guard == SocketHealth::Inactive {
|
||||||
// Check if we have any pending errors.
|
// Check if we have any pending errors.
|
||||||
let pe = self.pending_errors.lock().unwrap();
|
let pe = self.pending_errors.read().await;
|
||||||
if !pe.is_empty() {
|
if !pe.is_empty() {
|
||||||
return Err(KclError::Engine(KclErrorDetails {
|
return Err(KclError::Engine(KclErrorDetails {
|
||||||
message: pe.join(", ").to_string(),
|
message: pe.join(", ").to_string(),
|
||||||
source_ranges: vec![source_range],
|
source_ranges: vec![source_range],
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
return Err(KclError::Engine(KclErrorDetails {
|
return Err(KclError::Engine(KclErrorDetails {
|
||||||
message: "Modeling command failed: websocket closed early".to_string(),
|
message: "Modeling command failed: websocket closed early".to_string(),
|
||||||
source_ranges: vec![source_range],
|
source_ranges: vec![source_range],
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We pop off the responses to cleanup our mappings.
|
// We pop off the responses to cleanup our mappings.
|
||||||
if let Some((_, resp)) = self.responses.remove(&id) {
|
if let Some(resp) = self.responses.write().await.shift_remove(&id) {
|
||||||
return Ok(resp);
|
return Ok(resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -553,17 +506,16 @@ impl EngineManager for EngineConnection {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_session_data(&self) -> Option<ModelingSessionData> {
|
async fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||||
self.session_data.lock().unwrap().clone()
|
self.session_data.read().await.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn close(&self) {
|
async fn close(&self) {
|
||||||
let _ = self.shutdown_tx.send(()).await;
|
let _ = self.shutdown_tx.send(()).await;
|
||||||
loop {
|
loop {
|
||||||
if let Ok(guard) = self.socket_health.lock() {
|
let guard = self.socket_health.read().await;
|
||||||
if *guard == SocketHealth::Inactive {
|
if *guard == SocketHealth::Inactive {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||||
//! engine.
|
//! engine.
|
||||||
|
|
||||||
use std::{
|
use std::{collections::HashMap, sync::Arc};
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
@ -15,7 +12,8 @@ use kcmc::{
|
|||||||
WebSocketResponse,
|
WebSocketResponse,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use kittycad_modeling_cmds::{self as kcmc, id::ModelingCmdId, ModelingCmd};
|
use kittycad_modeling_cmds::{self as kcmc};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::ExecutionKind;
|
use super::ExecutionKind;
|
||||||
@ -28,71 +26,48 @@ use crate::{
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EngineConnection {
|
pub struct EngineConnection {
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||||
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EngineConnection {
|
impl EngineConnection {
|
||||||
pub async fn new() -> Result<EngineConnection> {
|
pub async fn new() -> Result<EngineConnection> {
|
||||||
Ok(EngineConnection {
|
Ok(EngineConnection {
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(RwLock::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||||
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_command(
|
|
||||||
&self,
|
|
||||||
cmd: &ModelingCmd,
|
|
||||||
cmd_id: ModelingCmdId,
|
|
||||||
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
|
||||||
) -> Result<(), KclError> {
|
|
||||||
let cmd_id = *cmd_id.as_ref();
|
|
||||||
let range = id_to_source_range
|
|
||||||
.get(&cmd_id)
|
|
||||||
.copied()
|
|
||||||
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
|
||||||
|
|
||||||
// Add artifact command.
|
|
||||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
|
||||||
artifact_commands.push(ArtifactCommand {
|
|
||||||
cmd_id,
|
|
||||||
range,
|
|
||||||
command: cmd.clone(),
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl crate::engine::EngineManager for EngineConnection {
|
impl crate::engine::EngineManager for EngineConnection {
|
||||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||||
self.batch.clone()
|
self.batch.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||||
IndexMap::new()
|
Arc::new(RwLock::new(IndexMap::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
self.artifact_commands.clone()
|
||||||
std::mem::take(&mut *artifact_commands)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
async fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.read().await;
|
||||||
*guard
|
*guard
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
let mut guard = self.execution_kind.lock().unwrap();
|
let mut guard = self.execution_kind.write().await;
|
||||||
let original = *guard;
|
let original = *guard;
|
||||||
*guard = execution_kind;
|
*guard = execution_kind;
|
||||||
original
|
original
|
||||||
@ -119,7 +94,7 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
_source_range: SourceRange,
|
_source_range: SourceRange,
|
||||||
cmd: WebSocketRequest,
|
cmd: WebSocketRequest,
|
||||||
id_to_source_range: HashMap<Uuid, SourceRange>,
|
_id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||||
) -> Result<WebSocketResponse, KclError> {
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
match cmd {
|
match cmd {
|
||||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
||||||
@ -130,7 +105,6 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
// Create the empty responses.
|
// Create the empty responses.
|
||||||
let mut responses = HashMap::with_capacity(requests.len());
|
let mut responses = HashMap::with_capacity(requests.len());
|
||||||
for request in requests {
|
for request in requests {
|
||||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
|
||||||
responses.insert(
|
responses.insert(
|
||||||
request.cmd_id,
|
request.cmd_id,
|
||||||
BatchResponse::Success {
|
BatchResponse::Success {
|
||||||
@ -144,17 +118,13 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
success: true,
|
success: true,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
WebSocketRequest::ModelingCmdReq(request) => {
|
WebSocketRequest::ModelingCmdReq(_) => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
request_id: Some(id),
|
||||||
|
resp: OkWebSocketResponseData::Modeling {
|
||||||
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
modeling_response: OkModelingCmdResponse::Empty {},
|
||||||
request_id: Some(id),
|
},
|
||||||
resp: OkWebSocketResponseData::Modeling {
|
success: true,
|
||||||
modeling_response: OkModelingCmdResponse::Empty {},
|
})),
|
||||||
},
|
|
||||||
success: true,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||||
request_id: Some(id),
|
request_id: Some(id),
|
||||||
resp: OkWebSocketResponseData::Modeling {
|
resp: OkWebSocketResponseData::Modeling {
|
||||||
|
|||||||
@ -1,25 +1,22 @@
|
|||||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||||
//! engine.
|
//! engine.
|
||||||
use std::{
|
use std::{collections::HashMap, sync::Arc};
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use kcmc::{
|
use kcmc::{
|
||||||
id::ModelingCmdId,
|
|
||||||
ok_response::OkModelingCmdResponse,
|
ok_response::OkModelingCmdResponse,
|
||||||
websocket::{
|
websocket::{
|
||||||
BatchResponse, ModelingBatch, OkWebSocketResponseData, SuccessWebSocketResponse, WebSocketRequest,
|
BatchResponse, ModelingBatch, OkWebSocketResponseData, SuccessWebSocketResponse, WebSocketRequest,
|
||||||
WebSocketResponse,
|
WebSocketResponse,
|
||||||
},
|
},
|
||||||
ModelingCmd,
|
|
||||||
};
|
};
|
||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
use crate::engine::EngineManager;
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::ExecutionKind,
|
engine::ExecutionKind,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
@ -54,11 +51,11 @@ extern "C" {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EngineConnection {
|
pub struct EngineConnection {
|
||||||
manager: Arc<EngineCommandManager>,
|
manager: Arc<EngineCommandManager>,
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||||
responses: Arc<Mutex<IndexMap<Uuid, WebSocketResponse>>>,
|
responses: Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>,
|
||||||
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: WebAssembly will only ever run in a single-threaded context.
|
// Safety: WebAssembly will only ever run in a single-threaded context.
|
||||||
@ -70,66 +67,130 @@ impl EngineConnection {
|
|||||||
#[allow(clippy::arc_with_non_send_sync)]
|
#[allow(clippy::arc_with_non_send_sync)]
|
||||||
Ok(EngineConnection {
|
Ok(EngineConnection {
|
||||||
manager: Arc::new(manager),
|
manager: Arc::new(manager),
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(RwLock::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||||
responses: Arc::new(Mutex::new(IndexMap::new())),
|
responses: Arc::new(RwLock::new(IndexMap::new())),
|
||||||
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl EngineConnection {
|
async fn do_send_modeling_cmd(
|
||||||
fn handle_command(
|
|
||||||
&self,
|
&self,
|
||||||
cmd: &ModelingCmd,
|
id: uuid::Uuid,
|
||||||
cmd_id: ModelingCmdId,
|
source_range: SourceRange,
|
||||||
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
cmd: WebSocketRequest,
|
||||||
) -> Result<(), KclError> {
|
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||||
let cmd_id = *cmd_id.as_ref();
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
let range = id_to_source_range
|
// In isolated mode, we don't send the command to the engine.
|
||||||
.get(&cmd_id)
|
if self.execution_kind().await.is_isolated() {
|
||||||
.copied()
|
return match &cmd {
|
||||||
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||||
|
let mut responses = HashMap::with_capacity(requests.len());
|
||||||
|
for request in requests {
|
||||||
|
responses.insert(
|
||||||
|
request.cmd_id,
|
||||||
|
BatchResponse::Success {
|
||||||
|
response: OkModelingCmdResponse::Empty {},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||||
|
request_id: Some(id),
|
||||||
|
resp: OkWebSocketResponseData::ModelingBatch { responses },
|
||||||
|
success: true,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||||
|
request_id: Some(id),
|
||||||
|
resp: OkWebSocketResponseData::Modeling {
|
||||||
|
modeling_response: OkModelingCmdResponse::Empty {},
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Add artifact command.
|
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
KclError::Engine(KclErrorDetails {
|
||||||
artifact_commands.push(ArtifactCommand {
|
message: format!("Failed to serialize source range: {:?}", e),
|
||||||
cmd_id,
|
source_ranges: vec![source_range],
|
||||||
range,
|
})
|
||||||
command: cmd.clone(),
|
})?;
|
||||||
});
|
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
||||||
Ok(())
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: format!("Failed to serialize modeling command: {:?}", e),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
||||||
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: format!("Failed to serialize id to source range: {:?}", e),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let promise = self
|
||||||
|
.manager
|
||||||
|
.send_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
||||||
|
.map_err(|e| {
|
||||||
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: e.to_string().into(),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
||||||
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: format!("Failed to wait for promise from engine: {:?}", e),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Parse the value as a string.
|
||||||
|
let s = value.as_string().ok_or_else(|| {
|
||||||
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: format!("Failed to get string from response from engine: `{:?}`", value),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let ws_result: WebSocketResponse = serde_json::from_str(&s).map_err(|e| {
|
||||||
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: format!("Failed to deserialize response from engine: {:?}", e),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(ws_result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl crate::engine::EngineManager for EngineConnection {
|
impl crate::engine::EngineManager for EngineConnection {
|
||||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||||
self.batch.clone()
|
self.batch.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||||
let responses = self.responses.lock().unwrap();
|
self.responses.clone()
|
||||||
responses.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
self.artifact_commands.clone()
|
||||||
std::mem::take(&mut *artifact_commands)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
async fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.read().await;
|
||||||
*guard
|
*guard
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
let mut guard = self.execution_kind.lock().unwrap();
|
let mut guard = self.execution_kind.write().await;
|
||||||
let original = *guard;
|
let original = *guard;
|
||||||
*guard = execution_kind;
|
*guard = execution_kind;
|
||||||
original
|
original
|
||||||
@ -214,100 +275,18 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
cmd: WebSocketRequest,
|
cmd: WebSocketRequest,
|
||||||
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||||
) -> Result<WebSocketResponse, KclError> {
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
match &cmd {
|
let ws_result = self
|
||||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
.do_send_modeling_cmd(id, source_range, cmd, id_to_source_range)
|
||||||
for request in requests {
|
.await?;
|
||||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
|
||||||
}
|
// In isolated mode, we don't save the response.
|
||||||
}
|
if self.execution_kind().await.is_isolated() {
|
||||||
WebSocketRequest::ModelingCmdReq(request) => {
|
return Ok(ws_result);
|
||||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In isolated mode, we don't send the command to the engine.
|
let mut responses = self.responses.write().await;
|
||||||
if self.execution_kind().is_isolated() {
|
|
||||||
return match &cmd {
|
|
||||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
|
||||||
let mut responses = HashMap::with_capacity(requests.len());
|
|
||||||
for request in requests {
|
|
||||||
responses.insert(
|
|
||||||
request.cmd_id,
|
|
||||||
BatchResponse::Success {
|
|
||||||
response: OkModelingCmdResponse::Empty {},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
|
||||||
request_id: Some(id),
|
|
||||||
resp: OkWebSocketResponseData::ModelingBatch { responses },
|
|
||||||
success: true,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
|
||||||
request_id: Some(id),
|
|
||||||
resp: OkWebSocketResponseData::Modeling {
|
|
||||||
modeling_response: OkModelingCmdResponse::Empty {},
|
|
||||||
},
|
|
||||||
success: true,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to serialize source range: {:?}", e),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to serialize modeling command: {:?}", e),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to serialize id to source range: {:?}", e),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let promise = self
|
|
||||||
.manager
|
|
||||||
.send_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
|
||||||
.map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: e.to_string().into(),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to wait for promise from engine: {:?}", e),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Parse the value as a string.
|
|
||||||
let s = value.as_string().ok_or_else(|| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to get string from response from engine: `{:?}`", value),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let ws_result: WebSocketResponse = serde_json::from_str(&s).map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to deserialize response from engine: {:?}", e),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut responses = self.responses.lock().unwrap();
|
|
||||||
responses.insert(id, ws_result.clone());
|
responses.insert(id, ws_result.clone());
|
||||||
|
drop(responses);
|
||||||
|
|
||||||
Ok(ws_result)
|
Ok(ws_result)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,14 +8,12 @@ pub mod conn_mock;
|
|||||||
#[cfg(feature = "engine")]
|
#[cfg(feature = "engine")]
|
||||||
pub mod conn_wasm;
|
pub mod conn_wasm;
|
||||||
|
|
||||||
use std::{
|
use std::{collections::HashMap, sync::Arc};
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use kcmc::{
|
use kcmc::{
|
||||||
each_cmd as mcmd,
|
each_cmd as mcmd,
|
||||||
|
id::ModelingCmdId,
|
||||||
length_unit::LengthUnit,
|
length_unit::LengthUnit,
|
||||||
ok_response::OkModelingCmdResponse,
|
ok_response::OkModelingCmdResponse,
|
||||||
shared::Color,
|
shared::Color,
|
||||||
@ -28,6 +26,7 @@ use kcmc::{
|
|||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -62,28 +61,38 @@ impl ExecutionKind {
|
|||||||
#[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 {
|
||||||
/// Get the batch of commands to be sent to the engine.
|
/// Get the batch of commands to be sent to the engine.
|
||||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>;
|
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>;
|
||||||
|
|
||||||
/// Get the batch of end commands to be sent to the engine.
|
/// Get the batch of end commands to be sent to the engine.
|
||||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
|
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
|
||||||
|
|
||||||
/// Get the command responses from the engine.
|
/// Get the command responses from the engine.
|
||||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse>;
|
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>;
|
||||||
|
|
||||||
/// Take the artifact commands generated up to this point and clear them.
|
/// Get the artifact commands that have accumulated so far.
|
||||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand>;
|
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>>;
|
||||||
|
|
||||||
/// Clear all artifact commands that have accumulated so far.
|
/// Clear all artifact commands that have accumulated so far.
|
||||||
fn clear_artifact_commands(&self) {
|
async fn clear_artifact_commands(&self) {
|
||||||
self.take_artifact_commands();
|
self.artifact_commands().write().await.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take the artifact commands that have accumulated so far and clear them.
|
||||||
|
async fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||||
|
std::mem::take(&mut *self.artifact_commands().write().await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take the responses that have accumulated so far and clear them.
|
||||||
|
async fn take_responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
||||||
|
std::mem::take(&mut *self.responses().write().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current execution kind.
|
/// Get the current execution kind.
|
||||||
fn execution_kind(&self) -> ExecutionKind;
|
async fn execution_kind(&self) -> ExecutionKind;
|
||||||
|
|
||||||
/// Replace the current execution kind with a new value and return the
|
/// Replace the current execution kind with a new value and return the
|
||||||
/// existing value.
|
/// existing value.
|
||||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
|
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
|
||||||
|
|
||||||
/// Get the default planes.
|
/// Get the default planes.
|
||||||
async fn default_planes(
|
async fn default_planes(
|
||||||
@ -127,7 +136,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
|
|
||||||
// Ensure artifact commands are cleared so that we don't accumulate them
|
// Ensure artifact commands are cleared so that we don't accumulate them
|
||||||
// across runs.
|
// across runs.
|
||||||
self.clear_artifact_commands();
|
self.clear_artifact_commands().await;
|
||||||
|
|
||||||
// Do the after clear scene hook.
|
// Do the after clear scene hook.
|
||||||
self.clear_scene_post_hook(id_generator, source_range).await?;
|
self.clear_scene_post_hook(id_generator, source_range).await?;
|
||||||
@ -151,6 +160,27 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_artifact_command(
|
||||||
|
&self,
|
||||||
|
cmd: &ModelingCmd,
|
||||||
|
cmd_id: ModelingCmdId,
|
||||||
|
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||||
|
) -> Result<(), KclError> {
|
||||||
|
let cmd_id = *cmd_id.as_ref();
|
||||||
|
let range = id_to_source_range
|
||||||
|
.get(&cmd_id)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||||
|
|
||||||
|
// Add artifact command.
|
||||||
|
self.artifact_commands().write().await.push(ArtifactCommand {
|
||||||
|
cmd_id,
|
||||||
|
range,
|
||||||
|
command: cmd.clone(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn set_units(
|
async fn set_units(
|
||||||
&self,
|
&self,
|
||||||
units: crate::UnitLength,
|
units: crate::UnitLength,
|
||||||
@ -203,7 +233,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add cmd to the batch.
|
// Add cmd to the batch.
|
||||||
self.batch().lock().unwrap().push((req, source_range));
|
self.batch().write().await.push((req, source_range));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -223,7 +253,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add cmd to the batch end.
|
// Add cmd to the batch end.
|
||||||
self.batch_end().lock().unwrap().insert(id, (req, source_range));
|
self.batch_end().write().await.insert(id, (req, source_range));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,11 +279,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
|
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
|
||||||
let all_requests = if batch_end {
|
let all_requests = if batch_end {
|
||||||
let mut requests = self.batch().lock().unwrap().clone();
|
let mut requests = self.batch().read().await.clone();
|
||||||
requests.extend(self.batch_end().lock().unwrap().values().cloned());
|
requests.extend(self.batch_end().read().await.values().cloned());
|
||||||
requests
|
requests
|
||||||
} else {
|
} else {
|
||||||
self.batch().lock().unwrap().clone()
|
self.batch().read().await.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return early if we have no commands to send.
|
// Return early if we have no commands to send.
|
||||||
@ -304,10 +334,27 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do the artifact commands.
|
||||||
|
for (req, _) in all_requests.iter() {
|
||||||
|
match &req {
|
||||||
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||||
|
for request in requests {
|
||||||
|
self.handle_artifact_command(&request.cmd, request.cmd_id, &id_to_source_range)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebSocketRequest::ModelingCmdReq(request) => {
|
||||||
|
self.handle_artifact_command(&request.cmd, request.cmd_id, &id_to_source_range)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Throw away the old batch queue.
|
// Throw away the old batch queue.
|
||||||
self.batch().lock().unwrap().clear();
|
self.batch().write().await.clear();
|
||||||
if batch_end {
|
if batch_end {
|
||||||
self.batch_end().lock().unwrap().clear();
|
self.batch_end().write().await.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We pop off the responses to cleanup our mappings.
|
// We pop off the responses to cleanup our mappings.
|
||||||
@ -596,7 +643,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
|
|
||||||
/// Get session data, if it has been received.
|
/// Get session data, if it has been received.
|
||||||
/// Returns None if the server never sent it.
|
/// Returns None if the server never sent it.
|
||||||
fn get_session_data(&self) -> Option<ModelingSessionData> {
|
async fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ impl ExecutorContext {
|
|||||||
let old_units = exec_state.length_unit();
|
let old_units = exec_state.length_unit();
|
||||||
exec_state.mod_local.settings.update_from_annotation(annotation)?;
|
exec_state.mod_local.settings.update_from_annotation(annotation)?;
|
||||||
let new_units = exec_state.length_unit();
|
let new_units = exec_state.length_unit();
|
||||||
if !self.engine.execution_kind().is_isolated() && old_units != new_units {
|
if !self.engine.execution_kind().await.is_isolated() && old_units != new_units {
|
||||||
self.engine
|
self.engine
|
||||||
.set_units(new_units.into(), annotation.as_source_range())
|
.set_units(new_units.into(), annotation.as_source_range())
|
||||||
.await?;
|
.await?;
|
||||||
@ -393,7 +393,7 @@ impl ExecutorContext {
|
|||||||
exec_state.global.mod_loader.enter_module(path);
|
exec_state.global.mod_loader.enter_module(path);
|
||||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||||
exec_state.mut_memory().push_new_root_env();
|
exec_state.mut_memory().push_new_root_env();
|
||||||
let original_execution = self.engine.replace_execution_kind(exec_kind);
|
let original_execution = self.engine.replace_execution_kind(exec_kind).await;
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.exec_program(program, exec_state, crate::execution::BodyType::Root)
|
.exec_program(program, exec_state, crate::execution::BodyType::Root)
|
||||||
@ -406,7 +406,7 @@ impl ExecutorContext {
|
|||||||
if !exec_kind.is_isolated() && new_units != old_units {
|
if !exec_kind.is_isolated() && new_units != old_units {
|
||||||
self.engine.set_units(old_units.into(), Default::default()).await?;
|
self.engine.set_units(old_units.into(), Default::default()).await?;
|
||||||
}
|
}
|
||||||
self.engine.replace_execution_kind(original_execution);
|
self.engine.replace_execution_kind(original_execution).await;
|
||||||
|
|
||||||
result
|
result
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
|
|||||||
@ -289,7 +289,7 @@ pub struct PreImportedGeometry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_to_engine(pre: PreImportedGeometry, ctxt: &ExecutorContext) -> Result<ImportedGeometry, KclError> {
|
pub async fn send_to_engine(pre: PreImportedGeometry, ctxt: &ExecutorContext) -> Result<ImportedGeometry, KclError> {
|
||||||
if ctxt.no_engine_commands() {
|
if ctxt.no_engine_commands().await {
|
||||||
return Ok(ImportedGeometry {
|
return Ok(ImportedGeometry {
|
||||||
id: pre.id,
|
id: pre.id,
|
||||||
value: pre.command.files.iter().map(|f| f.path.to_string()).collect(),
|
value: pre.command.files.iter().map(|f| f.path.to_string()).collect(),
|
||||||
|
|||||||
@ -484,8 +484,8 @@ impl ExecutorContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if we should not send engine commands for any reason.
|
/// Returns true if we should not send engine commands for any reason.
|
||||||
pub fn no_engine_commands(&self) -> bool {
|
pub async fn no_engine_commands(&self) -> bool {
|
||||||
self.is_mock() || self.engine.execution_kind().is_isolated()
|
self.is_mock() || self.engine.execution_kind().await.is_isolated()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_clear_scene(
|
pub async fn send_clear_scene(
|
||||||
@ -713,7 +713,7 @@ impl ExecutorContext {
|
|||||||
"Post interpretation KCL memory stats: {:#?}",
|
"Post interpretation KCL memory stats: {:#?}",
|
||||||
exec_state.memory().stats
|
exec_state.memory().stats
|
||||||
));
|
));
|
||||||
let session_data = self.engine.get_session_data();
|
let session_data = self.engine.get_session_data().await;
|
||||||
Ok(session_data)
|
Ok(session_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,8 +734,11 @@ impl ExecutorContext {
|
|||||||
exec_state
|
exec_state
|
||||||
.global
|
.global
|
||||||
.artifact_commands
|
.artifact_commands
|
||||||
.extend(self.engine.take_artifact_commands());
|
.extend(self.engine.take_artifact_commands().await);
|
||||||
exec_state.global.artifact_responses.extend(self.engine.responses());
|
exec_state
|
||||||
|
.global
|
||||||
|
.artifact_responses
|
||||||
|
.extend(self.engine.take_responses().await);
|
||||||
// Build the artifact graph.
|
// Build the artifact graph.
|
||||||
match build_artifact_graph(
|
match build_artifact_graph(
|
||||||
&exec_state.global.artifact_commands,
|
&exec_state.global.artifact_commands,
|
||||||
|
|||||||
@ -277,12 +277,12 @@ impl Args {
|
|||||||
// before what ever we call next.
|
// before what ever we call next.
|
||||||
for id in ids {
|
for id in ids {
|
||||||
// Pop it off the batch_end and add it to the batch.
|
// Pop it off the batch_end and add it to the batch.
|
||||||
let Some(item) = self.ctx.engine.batch_end().lock().unwrap().shift_remove(&id) else {
|
let Some(item) = self.ctx.engine.batch_end().write().await.shift_remove(&id) else {
|
||||||
// It might be in the batch already.
|
// It might be in the batch already.
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
// Add it to the batch.
|
// Add it to the batch.
|
||||||
self.ctx.engine.batch().lock().unwrap().push(item);
|
self.ctx.engine.batch().write().await.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run flush.
|
// Run flush.
|
||||||
|
|||||||
@ -232,8 +232,9 @@ pub(crate) async fn do_post_extrude(
|
|||||||
sides: face_id_map,
|
sides: face_id_map,
|
||||||
start_cap_id,
|
start_cap_id,
|
||||||
end_cap_id,
|
end_cap_id,
|
||||||
} = analyze_faces(exec_state, &args, face_infos);
|
} = analyze_faces(exec_state, &args, face_infos).await;
|
||||||
// Iterate over the sketch.value array and add face_id to GeoMeta
|
// Iterate over the sketch.value array and add face_id to GeoMeta
|
||||||
|
let no_engine_commands = args.ctx.no_engine_commands().await;
|
||||||
let new_value = sketch
|
let new_value = sketch
|
||||||
.paths
|
.paths
|
||||||
.iter()
|
.iter()
|
||||||
@ -267,7 +268,7 @@ pub(crate) async fn do_post_extrude(
|
|||||||
Some(extrude_surface)
|
Some(extrude_surface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if args.ctx.no_engine_commands() {
|
} else if no_engine_commands {
|
||||||
// Only pre-populate the extrude surface if we are in mock mode.
|
// Only pre-populate the extrude surface if we are in mock mode.
|
||||||
|
|
||||||
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
|
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
|
||||||
@ -313,12 +314,12 @@ struct Faces {
|
|||||||
start_cap_id: Option<Uuid>,
|
start_cap_id: Option<Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
|
async fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
|
||||||
let mut faces = Faces {
|
let mut faces = Faces {
|
||||||
sides: HashMap::with_capacity(face_infos.len()),
|
sides: HashMap::with_capacity(face_infos.len()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if args.ctx.no_engine_commands() {
|
if args.ctx.no_engine_commands().await {
|
||||||
// Create fake IDs for start and end caps, to make extrudes mock-execute safe
|
// Create fake IDs for start and end caps, to make extrudes mock-execute safe
|
||||||
faces.start_cap_id = Some(exec_state.next_uuid());
|
faces.start_cap_id = Some(exec_state.next_uuid());
|
||||||
faces.end_cap_id = Some(exec_state.next_uuid());
|
faces.end_cap_id = Some(exec_state.next_uuid());
|
||||||
|
|||||||
@ -222,7 +222,7 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
name = "getOppositeEdge",
|
name = "getOppositeEdge",
|
||||||
}]
|
}]
|
||||||
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
|
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
|
||||||
if args.ctx.no_engine_commands() {
|
if args.ctx.no_engine_commands().await {
|
||||||
return Ok(exec_state.next_uuid());
|
return Ok(exec_state.next_uuid());
|
||||||
}
|
}
|
||||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||||
@ -299,7 +299,7 @@ async fn inner_get_next_adjacent_edge(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Uuid, KclError> {
|
) -> Result<Uuid, KclError> {
|
||||||
if args.ctx.no_engine_commands() {
|
if args.ctx.no_engine_commands().await {
|
||||||
return Ok(exec_state.next_uuid());
|
return Ok(exec_state.next_uuid());
|
||||||
}
|
}
|
||||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||||
@ -384,7 +384,7 @@ async fn inner_get_previous_adjacent_edge(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Uuid, KclError> {
|
) -> Result<Uuid, KclError> {
|
||||||
if args.ctx.no_engine_commands() {
|
if args.ctx.no_engine_commands().await {
|
||||||
return Ok(exec_state.next_uuid());
|
return Ok(exec_state.next_uuid());
|
||||||
}
|
}
|
||||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||||
|
|||||||
@ -125,7 +125,7 @@ async fn inner_helix(
|
|||||||
meta: vec![args.source_range.into()],
|
meta: vec![args.source_range.into()],
|
||||||
});
|
});
|
||||||
|
|
||||||
if args.ctx.no_engine_commands() {
|
if args.ctx.no_engine_commands().await {
|
||||||
return Ok(helix_result);
|
return Ok(helix_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -112,7 +112,7 @@ async fn inner_mirror_2d(
|
|||||||
SketchSet::Sketches(sketches) => sketches,
|
SketchSet::Sketches(sketches) => sketches,
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.ctx.no_engine_commands() {
|
if args.ctx.no_engine_commands().await {
|
||||||
return Ok(starting_sketches);
|
return Ok(starting_sketches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |