summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SomhairlesDream.Cli/Arguments.fs32
-rw-r--r--src/SomhairlesDream.Cli/Program.fs2
-rw-r--r--src/SomhairlesDream.Frontend/App.fs91
-rw-r--r--src/SomhairlesDream.Modeling/BlenderBridge.fs5
-rw-r--r--src/SomhairlesDream.Modeling/Definitions.fs86
5 files changed, 161 insertions, 55 deletions
diff --git a/src/SomhairlesDream.Cli/Arguments.fs b/src/SomhairlesDream.Cli/Arguments.fs
index fa9087b..c1c7bff 100644
--- a/src/SomhairlesDream.Cli/Arguments.fs
+++ b/src/SomhairlesDream.Cli/Arguments.fs
@@ -21,7 +21,7 @@ module Arguments =
let usage =
"usage: dotnet run --project src/SomhairlesDream.Cli -- run [options]\n"
+ " dotnet run --project src/SomhairlesDream.Cli -- verify <manifest.json>\n"
- + "options: --project-id --run-id --base-id --target-id --output-root --stage-root --python --render --design heritage|probe"
+ + "options: --project-id --run-id --base-id --target-id --output-root --stage-root --python --render --design heritage|probe|ship"
let private defaultPython () =
let configured = Environment.GetEnvironmentVariable("SOMHAIRLES_BPYTHON")
@@ -39,6 +39,12 @@ module Arguments =
BaseId = "probe-base-000"
TargetId = "probe-target-007" }
+ let private shipIds =
+ { ProjectId = "ship-001"
+ RunId = "run-001"
+ BaseId = "ship-base-000"
+ TargetId = "ship-target-007" }
+
let private defaults () =
{ Ids = heritageIds
OutputRoot = ".artifacts"
@@ -66,21 +72,27 @@ module Arguments =
| _ -> Error $"unknown option: {optionName}"
let private resolveIds (options: RunOptions) =
- if options.Design = "probe" then
- let swap heritage probe value = if value = heritage then probe else value
+ if options.Design = "heritage" then
+ options
+ else
+ let designIds =
+ match options.Design with
+ | "probe" -> probeIds
+ | "ship" -> shipIds
+ | _ -> heritageIds
+
+ let swap heritage design value = if value = heritage then design else value
let ids =
- { ProjectId = swap heritageIds.ProjectId probeIds.ProjectId options.Ids.ProjectId
- RunId = swap heritageIds.RunId probeIds.RunId options.Ids.RunId
- BaseId = swap heritageIds.BaseId probeIds.BaseId options.Ids.BaseId
- TargetId = swap heritageIds.TargetId probeIds.TargetId options.Ids.TargetId }
+ { ProjectId = swap heritageIds.ProjectId designIds.ProjectId options.Ids.ProjectId
+ RunId = swap heritageIds.RunId designIds.RunId options.Ids.RunId
+ BaseId = swap heritageIds.BaseId designIds.BaseId options.Ids.BaseId
+ TargetId = swap heritageIds.TargetId designIds.TargetId options.Ids.TargetId }
{ options with Ids = ids }
- else
- options
let private validateRunOptions (options: RunOptions) =
- if options.Design <> "heritage" && options.Design <> "probe" then
+ if options.Design <> "heritage" && options.Design <> "probe" && options.Design <> "ship" then
Error $"invalid design: {options.Design}"
else
let resolved = resolveIds options
diff --git a/src/SomhairlesDream.Cli/Program.fs b/src/SomhairlesDream.Cli/Program.fs
index dd58ff0..7f33e1c 100644
--- a/src/SomhairlesDream.Cli/Program.fs
+++ b/src/SomhairlesDream.Cli/Program.fs
@@ -14,7 +14,7 @@ module Program =
let pipelineOptions : PipelineOptions =
{ ArtifactRoot = options.OutputRoot
Ids = options.Ids
- Steps = if options.Design = "probe" then Design.probeSteps else Design.steps
+ Steps = Design.forProject options.Ids.ProjectId
Render = options.Render
Bridge = bridge
Clock = fun () -> DateTimeOffset.UtcNow
diff --git a/src/SomhairlesDream.Frontend/App.fs b/src/SomhairlesDream.Frontend/App.fs
index e6edd51..0e6bcd4 100644
--- a/src/SomhairlesDream.Frontend/App.fs
+++ b/src/SomhairlesDream.Frontend/App.fs
@@ -707,48 +707,7 @@ let selectionForLiveStart (selection: RunSelection) =
else
selection
-let startRun (selection: RunSelection) =
- let requestBody =
- createObj
- [ "projectId" ==> selection.ProjectId
- "runId" ==> selection.RunId
- "baseId" ==> selection.BaseId
- "targetId" ==> selection.TargetId
- "render" ==> selection.Render ]
-
- let requestOptions =
- createObj
- [ "method" ==> "POST"
- "headers" ==> createObj [ "Content-Type" ==> "application/json" ]
- "body" ==> JS.JSON.stringify requestBody ]
-
- setText "run-message" "正在启动设计运行"
- updateStreamState "实时链路 · 等待服务端确认"
-
- async {
- try
- let! response = fetch ("/api/runs/start", requestOptions) |> Async.AwaitPromise
-
- if response.ok then
- let! value = response.json() |> Async.AwaitPromise
- let payload = value :?> SnapshotPayload
- updateState payload
- connectToServer selection
- updateStreamState "实时链路 · SSE 已连接"
- else
- setText "run-message" (sprintf "启动失败 · HTTP %d" response.status)
- updateStreamState "实时链路 · 启动失败"
- with _ ->
- setText "run-message" "启动失败 · 无法连接服务端"
- updateStreamState "实时链路 · 连接失败"
- }
- |> Async.StartImmediate
-
-/// Boot-time historical load: surface the pre-published probe build replay so
-/// first-time visitors see the real vehicle instead of an empty placeholder.
-/// Any failure leaves the honest placeholder idle state untouched.
-let loadBootHistory () =
- let selection = selectedRun ()
+let loadHistoryFor (selection: RunSelection) =
let projectId = JS.encodeURIComponent selection.ProjectId
let runId = JS.encodeURIComponent selection.RunId
let url = sprintf "/api/artifacts/manifest?projectId=%s&runId=%s" projectId runId
@@ -793,6 +752,54 @@ let loadBootHistory () =
()
}
|> Async.StartImmediate
+let startRun (selection: RunSelection) =
+ let requestBody =
+ createObj
+ [ "projectId" ==> selection.ProjectId
+ "runId" ==> selection.RunId
+ "baseId" ==> selection.BaseId
+ "targetId" ==> selection.TargetId
+ "render" ==> selection.Render ]
+
+ let requestOptions =
+ createObj
+ [ "method" ==> "POST"
+ "headers" ==> createObj [ "Content-Type" ==> "application/json" ]
+ "body" ==> JS.JSON.stringify requestBody ]
+
+ setText "run-message" "正在启动设计运行"
+ updateStreamState "实时链路 · 等待服务端确认"
+
+ async {
+ try
+ let! response = fetch ("/api/runs/start", requestOptions) |> Async.AwaitPromise
+
+ if response.ok then
+ let! value = response.json() |> Async.AwaitPromise
+ let payload = value :?> SnapshotPayload
+ updateState payload
+ connectToServer selection
+ updateStreamState "实时链路 · SSE 已连接"
+ elif response.status = 409 then
+ // The run id already exists on the server. If it completed
+ // earlier, replay its published history instead of treating
+ // the submit as a failure.
+ setText "run-message" "该运行已完成 · 载入历史回放"
+ loadHistoryFor selection
+ else
+ setText "run-message" (sprintf "启动失败 · HTTP %d" response.status)
+ updateStreamState "实时链路 · 启动失败"
+ with _ ->
+ setText "run-message" "启动失败 · 无法连接服务端"
+ updateStreamState "实时链路 · 连接失败"
+ }
+ |> Async.StartImmediate
+
+/// Boot-time historical load: surface the pre-published probe build replay so
+/// first-time visitors see the real vehicle instead of an empty placeholder.
+/// Any failure leaves the honest placeholder idle state untouched.
+let loadBootHistory () =
+ loadHistoryFor (selectedRun ())
let boot () =
showFrameMetadata frames[0]
diff --git a/src/SomhairlesDream.Modeling/BlenderBridge.fs b/src/SomhairlesDream.Modeling/BlenderBridge.fs
index c4215ae..0f605b0 100644
--- a/src/SomhairlesDream.Modeling/BlenderBridge.fs
+++ b/src/SomhairlesDream.Modeling/BlenderBridge.fs
@@ -102,7 +102,10 @@ type BlenderProcessBridge(python: string, stageRoot: string, ?timeout: TimeSpan)
File.WriteAllText(path, content)
member private _.ScriptPath(stepId: string) =
- let scriptName = if stepId.StartsWith("probe-") then "probe.py" else "fixture.py"
+ let scriptName =
+ if stepId.StartsWith("probe-") then "probe.py"
+ elif stepId.StartsWith("ship-") then "ship.py"
+ else "fixture.py"
Path.GetFullPath(Path.Combine(stageRoot, "blender", scriptName))
diff --git a/src/SomhairlesDream.Modeling/Definitions.fs b/src/SomhairlesDream.Modeling/Definitions.fs
index 9a9256e..e1d2739 100644
--- a/src/SomhairlesDream.Modeling/Definitions.fs
+++ b/src/SomhairlesDream.Modeling/Definitions.fs
@@ -92,5 +92,89 @@ module Design =
ObjectIds = prior (prior (prior (prior (prior (prior bus booms) rtgs) hga) instruments) mast) [||]
Changes = liveryChanges } |]
+ // HNX-01 crewed interstellar starship (Concept A "Keel"): fusion-torch
+ // study with a spin-gravity habitat torus, water-shadow radiation
+ // shield and an axial keel separating crew from the reactor section.
+ // Display-scale visual research; no mass/thrust/shielding closure is
+ // claimed — the ship.py module header lists the deliberately open
+ // engineering problems.
+ let shipSteps : DesignStep array =
+ let keel =
+ [| "starship.keel.spine"; "starship.nose.cone"; "starship.dock.ring" |]
+ let hab =
+ [| "starship.hab.torus"; "starship.hub.core"
+ "starship.spoke.00"; "starship.spoke.01"; "starship.spoke.02"
+ "starship.spoke.03"; "starship.spoke.04"; "starship.spoke.05" |]
+ let radiation =
+ [| "starship.shadow.collar"; "starship.storm.cellar" |]
+ let reactor =
+ [| "starship.core.pressure"; "starship.core.shield.plug"
+ "starship.nozzle.magnetic"
+ "starship.radiator.00"; "starship.radiator.01"; "starship.radiator.02"
+ "starship.radiator.03"; "starship.radiator.04"; "starship.radiator.05" |]
+ let power =
+ [| "starship.power.truss"; "starship.capacitor.ring"
+ "starship.comm.dish"; "starship.comm.feed" |]
+ let cabin =
+ [| "starship.window.00"; "starship.window.01"; "starship.window.02"
+ "starship.window.03"
+ "starship.airlock.ring"; "starship.airlock.tunnel"
+ "starship.cargo.pod.00"; "starship.cargo.pod.01" |]
+ let prior (prefix: string array) (current: string array) =
+ Array.append prefix current
+
+ let keelChanges =
+ [| { ObjectId = "starship.keel.spine"; Kind = "add"; Summary = "Add axial keel spine from bow to nozzle bay" }
+ { ObjectId = "starship.nose.cone"; Kind = "add"; Summary = "Add forward nose cone" }
+ { ObjectId = "starship.dock.ring"; Kind = "add"; Summary = "Add forward docking ring" } |]
+ let habChanges =
+ Array.append
+ [| { ObjectId = "starship.hab.torus"; Kind = "add"; Summary = "Add rotating spin-gravity habitat torus" }
+ { ObjectId = "starship.hub.core"; Kind = "add"; Summary = "Add zero-g hub with bearing core" } |]
+ (Array.init 6 (fun index ->
+ { ObjectId = sprintf "starship.spoke.%02d" index; Kind = "add"; Summary = "Add pressurized transfer spoke" }))
+ let radiationChanges =
+ [| { ObjectId = "starship.shadow.collar"; Kind = "add"; Summary = "Add water-shadow radiation collar" }
+ { ObjectId = "starship.storm.cellar"; Kind = "add"; Summary = "Add storm cellar shelter volume" } |]
+ let reactorChanges =
+ Array.append
+ [| { ObjectId = "starship.core.pressure"; Kind = "add"; Summary = "Add fusion core pressure vessel" }
+ { ObjectId = "starship.core.shield.plug"; Kind = "add"; Summary = "Add reactor shield plug" }
+ { ObjectId = "starship.nozzle.magnetic"; Kind = "add"; Summary = "Add magnetic exhaust nozzle" } |]
+ (Array.init 6 (fun index ->
+ { ObjectId = sprintf "starship.radiator.%02d" index; Kind = "add"; Summary = "Add waste-heat radiator panel" }))
+ let powerChanges =
+ [| { ObjectId = "starship.power.truss"; Kind = "add"; Summary = "Add power conditioning truss" }
+ { ObjectId = "starship.capacitor.ring"; Kind = "add"; Summary = "Add pulsed-power capacitor hoop" }
+ { ObjectId = "starship.comm.dish"; Kind = "add"; Summary = "Add high-gain communication dish" }
+ { ObjectId = "starship.comm.feed"; Kind = "add"; Summary = "Add communication feed horn" } |]
+ let cabinChanges =
+ Array.append
+ (Array.init 4 (fun index ->
+ { ObjectId = sprintf "starship.window.%02d" index; Kind = "add"; Summary = "Add habitat observation window" }))
+ [| { ObjectId = "starship.airlock.ring"; Kind = "add"; Summary = "Add forward airlock ring" }
+ { ObjectId = "starship.airlock.tunnel"; Kind = "add"; Summary = "Add pressurized airlock tunnel" }
+ { ObjectId = "starship.cargo.pod.00"; Kind = "add"; Summary = "Add port cargo pod" }
+ { ObjectId = "starship.cargo.pod.01"; Kind = "add"; Summary = "Add starboard cargo pod" } |]
+ let liveryChanges =
+ [| { ObjectId = "starship.keel.spine"; Kind = "update"; Summary = "Livery pass: bone-white hull structure" }
+ { ObjectId = "starship.hab.torus"; Kind = "update"; Summary = "Livery pass: habitat ring bone finish" }
+ { ObjectId = "starship.shadow.collar"; Kind = "update"; Summary = "Livery pass: ceramic shield darkening" }
+ { ObjectId = "starship.storm.cellar"; Kind = "update"; Summary = "Livery pass: ceramic cellar finish" }
+ { ObjectId = "starship.dock.ring"; Kind = "update"; Summary = "Livery pass: amber accent on dock ring" }
+ { ObjectId = "starship.capacitor.ring"; Kind = "update"; Summary = "Livery pass: amber capacitor hoop" }
+ { ObjectId = "starship.window.00"; Kind = "update"; Summary = "Livery pass: glass glazing" }
+ { ObjectId = "starship.nozzle.magnetic"; Kind = "update"; Summary = "Livery pass: nozzle coil graphite" } |]
+
+ [| { StepId = "ship-01-keel"; Index = 0; ObjectIds = keel; Changes = keelChanges }
+ { StepId = "ship-02-hab"; Index = 1; ObjectIds = Array.append keel hab; Changes = habChanges }
+ { StepId = "ship-03-radiation"; Index = 2; ObjectIds = Array.append (Array.append keel hab) radiation; Changes = radiationChanges }
+ { StepId = "ship-04-reactor"; Index = 3; ObjectIds = Array.append (Array.append (Array.append keel hab) radiation) reactor; Changes = reactorChanges }
+ { StepId = "ship-05-power"; Index = 4; ObjectIds = Array.append (Array.append (Array.append (Array.append keel hab) radiation) reactor) power; Changes = powerChanges }
+ { StepId = "ship-06-cabin"; Index = 5; ObjectIds = Array.append (Array.append (Array.append (Array.append (Array.append keel hab) radiation) reactor) power) cabin; Changes = cabinChanges }
+ { StepId = "ship-07-livery"; Index = 6; ObjectIds = Array.append (Array.append (Array.append (Array.append (Array.append (Array.append keel hab) radiation) reactor) power) cabin) [||]; Changes = liveryChanges } |]
+
let forProject (projectId: string) : DesignStep array =
- if projectId.StartsWith("probe-") then probeSteps else steps
+ if projectId.StartsWith("probe-") then probeSteps
+ elif projectId.StartsWith("ship-") then shipSteps
+ else steps