2
0
mirror of https://github.com/9ParsonsB/Pulsar.git synced 2025-04-05 17:39:39 -04:00

Update Status, Power now look nice, also shows est refuel time

Status now shows alerts when taking damage
JournalService support more journals
Added enums to frontend, not quite working
Added More examples to Explorer component
This commit is contained in:
Ben Parsons 2024-05-15 01:00:56 +10:00
parent bd811c861c
commit 2ac1a927ca
11 changed files with 310 additions and 39 deletions

@ -10,7 +10,8 @@ public class JournalService(
ILogger<JournalService> logger,
IOptions<PulsarConfiguration> options,
IEventHubContext hub,
PulsarContext context
PulsarContext context,
IServiceProvider serviceProvider
) : IJournalService
{
public string FileName => FileHandlerService.JournalLogFileName;
@ -32,7 +33,7 @@ public class JournalService(
new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
Converters = { new JournalConverter(logger) }
Converters = { ActivatorUtilities.CreateInstance<JournalConverter>(serviceProvider) }
}));
foreach (var journal in select)

@ -1,26 +0,0 @@
namespace Pulsar.Utils;
public sealed class HttpClient
{
private HttpClient()
{ }
private static readonly Lazy<System.Net.Http.HttpClient> lazy = new Lazy<System.Net.Http.HttpClient>(() => new System.Net.Http.HttpClient());
public static System.Net.Http.HttpClient Client => lazy.Value;
public static string GetString(string url)
{
return lazy.Value.GetStringAsync(url).Result;
}
public static HttpResponseMessage SendRequest(HttpRequestMessage request)
{
return lazy.Value.SendAsync(request).Result;
}
public static Task<HttpResponseMessage> SendRequestAsync(HttpRequestMessage request)
{
return lazy.Value.SendAsync(request);
}
}

@ -288,6 +288,59 @@ public class JournalConverter(ILogger logger) : JsonConverter<JournalBase>
return JsonSerializer.Deserialize<MaterialDiscovered>(ref reader)!;
case "storedships":
return JsonSerializer.Deserialize<StoredShips>(ref reader)!;
case "scanorganic":
return JsonSerializer.Deserialize<ScanOrganic>(ref reader)!;
case "market":
return JsonSerializer.Deserialize<Market>(ref reader)!;
case "missioncompleted":
return JsonSerializer.Deserialize<MissionCompleted>(ref reader)!;
case "sellshiponrebuy":
return JsonSerializer.Deserialize<SellShipOnRebuy>(ref reader)!;
case "missionaccepted":
return JsonSerializer.Deserialize<MissionAccepted>(ref reader)!;
case "approachsettlement":
return JsonSerializer.Deserialize<ApproachSettlement>(ref reader)!;
case "screenshot":
return JsonSerializer.Deserialize<Screenshot>(ref reader)!;
case "moduleswap":
return JsonSerializer.Deserialize<ModuleSwap>(ref reader)!;
case "underattack":
return JsonSerializer.Deserialize<UnderAttack>(ref reader)!;
case "datascanned":
return JsonSerializer.Deserialize<DataScanned>(ref reader)!;
case "dockingdenied":
return JsonSerializer.Deserialize<DockingDenied>(ref reader)!;
case "fetchremotemodule":
return JsonSerializer.Deserialize<FetchRemoteModule>(ref reader)!;
case "engineercontribution":
return JsonSerializer.Deserialize<EngineerContribution>(ref reader)!;
case "collectcargo":
return JsonSerializer.Deserialize<CollectCargo>(ref reader)!;
case "moduleretrieve":
return JsonSerializer.Deserialize<ModuleRetrieve>(ref reader)!;
case "marketbuy":
return JsonSerializer.Deserialize<MarketBuy>(ref reader)!;
case "selldrones":
return JsonSerializer.Deserialize<SellDrones>(ref reader)!;
case "interdicted":
return JsonSerializer.Deserialize<Interdicted>(ref reader)!;
case "sellorganicdata":
return JsonSerializer.Deserialize<SellOrganicData>(ref reader)!;
case "wingadd":
return JsonSerializer.Deserialize<WingAdd>(ref reader)!;
case "winginvite":
return JsonSerializer.Deserialize<WingInvite>(ref reader)!;
case "wingjoin":
return JsonSerializer.Deserialize<WingJoin>(ref reader)!;
case "wingleave":
return JsonSerializer.Deserialize<WingLeave>(ref reader)!;
case "bounty":
return JsonSerializer.Deserialize<Bounty>(ref reader)!;
case "commitcrime":
return JsonSerializer.Deserialize<CommitCrime>(ref reader)!;
case "modulestore":
return JsonSerializer.Deserialize<ModuleStore>(ref reader)!;
default:
logger.LogWarning("Unknown Journal event type {EventName}", eventName);
return JsonSerializer.Deserialize<JournalBase>(ref reader)!;

21
Pulsar/WebApp/.vscode/launch.json vendored Normal file

@ -0,0 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "npm dev",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script",
"dev"
],
"skipFiles": [
"<node_internals>/**"
]
}
]
}

@ -18,14 +18,14 @@
<section>
<div class="title">
<h1>Journals</h1>
<h1>Live Journals</h1>
</div>
<button
onclick={() => {
fetch("http://localhost:5000/api/journal");
}}
>
Refresh (debug)
Fetch All (debug)
</button>
<ul>
{#each values as value (value.timestamp + value.event)}

@ -2,18 +2,65 @@
import { onMount } from "svelte";
import { statusStore } from "./stores/Status.store";
import connection from "./stores/Connection.store";
import { StatusFlags, StatusFlags2 } from "../types/api/enums";
import type JournalBase from "../types/api/JournalBase";
import { slide } from "svelte/transition";
const x: string | null = $state(null);
const last: number[] = $state([]);
let timeToMax = $state(0);
let loading = $state(true);
let alert: JournalBase[] = $state([]);
onMount(async () => {
await $connection.start();
loading = false;
$connection.on("StatusUpdated", (message) => {
statusStore.update((s) => {
return { ...s, ...message };
});
// only 3 in array
if (last.length >= 3) {
last.shift();
}
last.push(message.fuel?.fuelMain ?? 0);
const change = [];
for (let i = last.length - 1; i === 0; i--) {
change.push(last[i] - last[i - 1]);
}
const avg = change.reduce((a, b) => a + b, 0) / change.length;
const max = 32;
if ($statusStore.fuel?.fuelMain) {
timeToMax = (max - $statusStore.fuel?.fuelMain) / avg;
}
console.log($statusStore);
});
$connection.on("JournalUpdated", (message) => {
const journals = message as JournalBase[];
const targetEvents = ["HullDamage", "UnderAttack"];
const events = journals.filter((j) =>
targetEvents.find((t) => t.toLowerCase() === j.event.toLowerCase())
);
if (events.length) {
alert = events;
}
});
if (!$statusStore.pips) {
statusStore.set(
await (await fetch("http://localhost:5000/api/status")).json()
);
}
});
</script>
@ -21,14 +68,48 @@
<br />
{#if loading}
<h1>LOADING ....</h1>
{/if}
{#if alert.length}
<h1>Alert!</h1>
{#each alert as a}
<input readonly value={JSON.stringify(a)} />
{/each}
<button onclick={() => (alert = [])}>Clear</button>
{/if}
<div>
{#if $statusStore}
<span>Fuel%: {(($statusStore.fuel?.fuelMain ?? 0) / 32) * 100}%</span>
<span
>Sys: {$statusStore?.pips?.sys ?? "?"} Eng: {$statusStore?.pips?.eng ??
"?"} Wep:
{$statusStore?.pips?.wep ?? "?"}</span
>Fuel%: {(($statusStore.fuel?.fuelMain ?? 0) / 32) * 100}% est: {timeToMax}s</span
>
<div class="power">
<div class="sys">
{#each [...Array($statusStore?.pips?.sys ?? 0)] as sys}
<div class="pip" transition:slide></div>
{/each}
<div>{$statusStore?.pips?.sys ?? "?"}</div>
<div>Sys</div>
</div>
<div class="eng">
{#each [...Array($statusStore?.pips?.eng ?? 0)] as eng}
<div class="pip" transition:slide></div>
{/each}
<div>{$statusStore?.pips?.eng ?? "?"}</div>
<div>Eng</div>
</div>
<div class="wep">
{#each [...Array($statusStore?.pips?.wep ?? 0)] as wep}
<div class="pip" transition:slide></div>
{/each}
<div>{$statusStore?.pips?.wep ?? "?"}</div>
<div>Wep</div>
</div>
</div>
<span>dest?: {$statusStore?.destination?.name}</span>
<span>gui focus: {$statusStore.guiFocus}</span>
<span>cargo: {$statusStore.cargo}</span>
@ -39,9 +120,34 @@
{/if}
</div>
<style>
<style lang="scss">
div {
display: flex;
flex-direction: column;
height: 100%;
.power {
display: flex;
flex-direction: row;
height: 100%;
align-items: flex-end;
div {
display: flex;
height: 100%;
flex-direction: column;
align-items: center;
div.pip {
min-width: 2vw;
min-height: 1vh;
background-color: #d06527;
border: 1px solid #96491c;
}
}
}
}
input {
width: 100%;
background-color: transparent;
color: white;
}
</style>

@ -33,5 +33,6 @@
div {
flex: 50%;
max-width: 100%;
height: 100%;
}
</style>

@ -1,11 +1,34 @@
<script lang="ts">
const data: unknown[] = [{}, {}, {}, {}];
// number of scans completed
const scanned = 2;
// total bodies in the current system (FSSDiscovery event)
const totalBodies = 12;
// accumulated list of bodies in the current system (Scan events)
const bodies = $state([
{ value: 50 },
{ value: 1000 },
{ value: 800000 },
{ value: 800000 },
]);
</script>
<section>
<div class="title">
<h1>Explorer</h1>
</div>
<!-- summary & high value targets -->
<h1>Bodies</h1>
Scan:&nbsp;<span>{scanned}</span>/<span>{totalBodies}</span>
<div class="title">High Value (>500kcr)</div>
<ol>
{#each bodies.filter((b) => b.value > 500000) as body}
<li>[HMC/WW/ELT/ELN] $body.name - {body.value}cr</li>
{/each}
</ol>
<br />
<br />
<!-- Full system data -->
<div class="box">
{#each data as row}
<div class="group">

@ -0,0 +1,4 @@
export default interface JournalBase {
event: string;
timestamp: Date;
}

@ -1,16 +1,22 @@
import type Destination from "./Destination";
import type Fuel from "./Fuel";
import type JournalBase from "./JournalBase";
import type {
FocusStatus,
LegalStatus,
StatusFlags,
StatusFlags2,
} from "./enums";
export default interface Status extends JournalBase {
event: "Status";
flags: number;
flags2: number;
flags: StatusFlags;
flags2: StatusFlags2;
pips: { eng: number; sys: number; wep: number };
guiFocus: number;
guiFocus: FocusStatus;
fuel: Fuel;
cargo: number;
legalState: string;
legalState: LegalStatus;
balance: number;
destination: Destination;
FireGroup: number;

@ -0,0 +1,82 @@
export enum StatusFlags {
Docked = 1,
Landed = 1 << 1,
LandingGear = 1 << 2,
Shields = 1 << 3,
Supercruise = 1 << 4,
FAOff = 1 << 5,
Hardpoints = 1 << 6,
Wing = 1 << 7,
Lights = 1 << 8,
CargoScoop = 1 << 9,
SilentRunning = 1 << 10,
FuelScooping = 1 << 11,
SRVBrake = 1 << 12,
SRVTurret = 1 << 13,
SRVProximity = 1 << 14,
SRVDriveAssist = 1 << 15,
Masslock = 1 << 16,
FSDCharging = 1 << 17,
FSDCooldown = 1 << 18,
LowFuel = 1 << 19,
Overheat = 1 << 20,
LatLongValid = 1 << 21,
InDanger = 1 << 22,
Interdiction = 1 << 23,
MainShip = 1 << 24,
Fighter = 1 << 25,
SRV = 1 << 26,
AnalysisHUD = 1 << 27,
NightVision = 1 << 28,
RadialAltitude = 1 << 29,
FSDJump = 1 << 30,
SRVHighBeam = 1 << 31,
}
export enum StatusFlags2 {
OnFoot = 1,
InTaxi = 1 << 1,
InMulticrew = 1 << 2,
OnFootInStation = 1 << 3,
OnFootOnPlanet = 1 << 4,
AimDownSight = 1 << 5,
LowOxygen = 1 << 6,
LowHealth = 1 << 7,
Cold = 1 << 8,
Hot = 1 << 9,
VeryCold = 1 << 10,
VeryHot = 1 << 11,
GlideMode = 1 << 12,
OnFootInHangar = 1 << 13,
OnFootInSocialSpace = 1 << 14,
OnFootExterior = 1 << 15,
BreathableAtmosphere = 1 << 16,
TelepresenceMulticrew = 1 << 17,
PhysicalMulticrew = 1 << 18,
FsdHyperdriveCharging = 1 << 19,
}
export type LegalStatus =
| "Clean"
| "IllegalCargo"
| "Speeding"
| "Wanted"
| "Hostile"
| "PassengerWanted"
| "Warrant"
| "Thargoid";
export enum FocusStatus {
NoFocus = 0,
InternalPanel = 1,
ExternalPanel = 2,
CommsPanel = 3,
RolePanel = 4,
StationServices = 5,
GalaxyMap = 6,
SystemMap = 7,
Orrery = 8,
FSS = 9,
SAA = 10,
Codex = 11,
}