summaryrefslogtreecommitdiff
path: root/src/SomhairlesDream.Shared/Domain.fs
blob: fd229fae7b996bc46e76ca624e6e102509c155c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
namespace SomhairlesDream.Shared

open System

[<RequireQualifiedAccess>]
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 }