namespace SomhairlesDream.Shared open System [] type RunStatus = | Idle = 0 | Queued = 1 | Running = 2 | Succeeded = 3 | Failed = 4 | Stale = 5 type Vector3 = { X: float Y: float Z: float } type MeshSnapshot = { Version: int Label: string Vertices: Vector3 array Faces: int array array } type LiveSnapshot = { ProjectId: string RunId: string Status: RunStatus CurrentVersion: int TargetVersion: int HeartbeatAt: DateTimeOffset option UpdatedAt: DateTimeOffset Message: string Mesh: MeshSnapshot option NextDesignRunScheduled: bool } module MeshData = let positionData (mesh: MeshSnapshot) = mesh.Vertices |> Array.collect (fun vertex -> [| vertex.X; vertex.Y; vertex.Z |]) let triangleIndices (mesh: MeshSnapshot) = mesh.Faces |> Array.collect (fun face -> if face.Length < 3 then [||] else [| for index in 1 .. face.Length - 2 do yield face[0] yield face[index] yield face[index + 1] |]) module RunState = let initial projectId runId now = { ProjectId = projectId RunId = runId Status = RunStatus.Idle CurrentVersion = 0 TargetVersion = 0 HeartbeatAt = None UpdatedAt = now Message = "等待设计运行" Mesh = None NextDesignRunScheduled = false } let start now state = let targetVersion = state.TargetVersion + 1 { state with Status = RunStatus.Running TargetVersion = targetVersion HeartbeatAt = Some now UpdatedAt = now Message = $"正在生成第 {targetVersion} 版几何" NextDesignRunScheduled = false } let heartbeat now state = if state.Status = RunStatus.Running then { state with HeartbeatAt = Some now UpdatedAt = now } else state let statusAt timeout now state = match state.HeartbeatAt with | Some heartbeatAt when state.Status = RunStatus.Running && now - heartbeatAt > timeout -> { state with Status = RunStatus.Stale UpdatedAt = now Message = "心跳超时,等待重连" } | _ -> state let publish now mesh state = { state with Status = RunStatus.Succeeded CurrentVersion = mesh.Version HeartbeatAt = Some now UpdatedAt = now Message = $"已发布{mesh.Label}" Mesh = Some mesh NextDesignRunScheduled = false }